HL2:DM Npc points/score

A place for requesting new Source.Python plugins to be made for your server.

Please request only one plugin per thread.
User avatar
daren adler
Senior Member
Posts: 348
Joined: Sat May 18, 2019 7:42 pm

HL2:DM Npc points/score

Postby daren adler » Wed Sep 09, 2020 2:47 am

Hello python script makers :cool:, Is there a way to get points/score from killing npc ?. ( like you do in coop). or a plugin already made ?. Also, I was wandering if there was a way to have a scanner spawn at every map also ?. Just 1 at a time per map. Thank you python script makers for all the help you are doing for players and servers like me. :eek: :cool:
User avatar
VinciT
Senior Member
Posts: 331
Joined: Thu Dec 18, 2014 2:41 am

Re: HL2:DM Npc points/score

Postby VinciT » Fri Sep 11, 2020 3:44 am

Hi daren, try this for getting points from NPCs:

Syntax: Select all

# ../npc_points/npc_points.py

# Source.Python
from entities.entity import BaseEntity
from events import Event
from messages import HudMsg
from players.entity import Player


# Send a message to the player when they kill an NPC? (True/False)
NOTIFY_PLAYER = True

# Dictionary for how many points each NPC is worth.
points_per_npc = {
'npc_zombie': 3,
'npc_manhack': 2,
'npc_headcrab': 2
}


@Event('entity_killed')
def npc_killed(event):
"""Called when an entity gets killed."""
try:
# Try to get a Player instance of the killer.
player = Player(event['entindex_attacker'])
except ValueError:
# Not a player, don't go further.
return

# Get the name of the NPC that got killed.
classname = BaseEntity(event['entindex_killed']).classname

try:
# Are there predefined points for this NPC?
points = points_per_npc[classname]
except KeyError:
# Nope, default to 1.
points = 1

# Increase the player's kill/score count.
player.kills += points

# Should we tell the player how many points they just received?
if NOTIFY_PLAYER:
word = 'points' if points > 1 else 'point'
HudMsg(
message=f'+{points} {word} for killing the {classname[4::]}',
x=-1,
y=0.58,
fade_in=0.2,
fade_out=0.3,
hold_time=1.25
).send(player.index)

Can you tell me a bit more about the scanner? What would you have it do?
ImageImageImageImageImage
User avatar
Painkiller
Senior Member
Posts: 726
Joined: Sun Mar 01, 2015 8:09 am
Location: Germany
Contact:

Re: HL2:DM Npc points/score

Postby Painkiller » Fri Sep 11, 2020 10:08 am

I have added some stuff but unfortunately it doesn't show anything anymore.
What is the problem ?

Syntax: Select all

# ../npc_points/npc_points.py

# Source.Python
from entities.entity import BaseEntity
from events import Event
from messages import HudMsg
from players.entity import Player
from colors import GREEN, YELLOW


# Send a message to the player when they kill an NPC? (True/False)
NOTIFY_PLAYER = True

# Dictionary for how many points each NPC is worth.
points_per_npc = {
'npc_zombie': 3,
'npc_manhack': 2,
'npc_headcrab': 2,
'npc_antilion': 3,
'npc_antilionguard': 2,
'npc_clawscanner': 2,
'npc_combinedropship': 3,
'npc_combinegunship': 2,
'npc_crow': 2,
'combine_mine': 3,
'npc_headcrab_black': 2,
'npc_headcrab_fast': 2,
'npc_helicopter': 3,
'npc_hunter': 2,
'npc_ichthyosaur': 2,
'npc_ministriper': 3,
'npc_missildefense': 2,
'npc_mortarsynth': 2,
'npc_pigeon': 3,
'npc_poisonzombie': 2,
'npc_rollermine': 2,
'npc_sniper': 3,
'npc_stalker': 2,
'npc_strider': 2,
'npc_turret_ceiling': 3,
'npc_turret_floor': 2,
'npc_turret_ground': 2,
'npc_vortigaunt': 3,
'npc_zombie:torso': 2,
'npc_zombine': 2
}


@Event('entity_killed')
def npc_killed(event):
"""Called when an entity gets killed."""
try:
# Try to get a Player instance of the killer.
player = Player(event['entindex_attacker'])
except ValueError:
# Not a player, don't go further.
return

# Get the name of the NPC that got killed.
classname = BaseEntity(event['entindex_killed']).classname

try:
# Are there predefined points for this NPC?
points = points_per_npc[classname]
except KeyError:
# Nope, default to 1.
points = 1

# Increase the player's kill/score count.
player.kills += points

# Should we tell the player how many points they just received?
if NOTIFY_PLAYER:
word = 'points' if points > 1 else 'point'
HudMsg(
message=f'+{points} {word} for killing the {classname[4::]}',
x=0.73,
y=0.85,
color1=GREEN,
color2=YELLOW,
effect=0,
fade_in=0.05,
fade_out=1.5,
hold_time=1.25,
fx_time=1.0,
channel=1
).send(player.index)
User avatar
VinciT
Senior Member
Posts: 331
Joined: Thu Dec 18, 2014 2:41 am

Re: HL2:DM Npc points/score

Postby VinciT » Fri Sep 11, 2020 4:19 pm

With your changes I get this:
Image

Since you used two colors in the code, I'm guessing you want to see this:
Image
To get that effect you need to change the effect value to 2.
If you still can't see the message, try changing the channel value (0 - 3) as another plugin might be using the channel you've selected.

Edit: I just noticed a typo in one of the npc names you've added: npc_zombie:torso should be npc_zombie_torso.
ImageImageImageImageImage
User avatar
Painkiller
Senior Member
Posts: 726
Joined: Sun Mar 01, 2015 8:09 am
Location: Germany
Contact:

Re: HL2:DM Npc points/score

Postby Painkiller » Fri Sep 11, 2020 5:46 pm

It seems to work now.
Thanks for the mistake I have corrected.

I would like a few more things:

- physics does not yet count as kill
(that means physicgun kills, saw blades, barrels)

-Damage display over the heads of npc and automats (charger)
Example: 80 /100

- a line break of the current font
(+3 points for killing the zombie)
Example: How to proceed in the chat,
+3 points for killing the zombie
+3 points for killing the zombie
+3 points for killing the zombie
slowly fading downwards.

It seems more alive like in a pinball machine.
Because you kill several npc at the same time.

Thanks in Advance Painkiller
User avatar
daren adler
Senior Member
Posts: 348
Joined: Sat May 18, 2019 7:42 pm

Re: HL2:DM Npc points/score

Postby daren adler » Sat Sep 12, 2020 2:15 am

VinciT wrote:Hi daren, try this for getting points from NPCs:

Syntax: Select all

# ../npc_points/npc_points.py

# Source.Python
from entities.entity import BaseEntity
from events import Event
from messages import HudMsg
from players.entity import Player


# Send a message to the player when they kill an NPC? (True/False)
NOTIFY_PLAYER = True

# Dictionary for how many points each NPC is worth.
points_per_npc = {
'npc_zombie': 3,
'npc_manhack': 2,
'npc_headcrab': 2
}


@Event('entity_killed')
def npc_killed(event):
"""Called when an entity gets killed."""
try:
# Try to get a Player instance of the killer.
player = Player(event['entindex_attacker'])
except ValueError:
# Not a player, don't go further.
return

# Get the name of the NPC that got killed.
classname = BaseEntity(event['entindex_killed']).classname

try:
# Are there predefined points for this NPC?
points = points_per_npc[classname]
except KeyError:
# Nope, default to 1.
points = 1

# Increase the player's kill/score count.
player.kills += points

# Should we tell the player how many points they just received?
if NOTIFY_PLAYER:
word = 'points' if points > 1 else 'point'
HudMsg(
message=f'+{points} {word} for killing the {classname[4::]}',
x=-1,
y=0.58,
fade_in=0.2,
fade_out=0.3,
hold_time=1.25
).send(player.index)

Can you tell me a bit more about the scanner? What would you have it do?


Thank you,,ill will try it out,,sorry so late chating back, been real busy!!! and the scanner i would just like to have 1 or 2 of them just flying around bugging players.
User avatar
VinciT
Senior Member
Posts: 331
Joined: Thu Dec 18, 2014 2:41 am

Re: HL2:DM Npc points/score

Postby VinciT » Sat Sep 12, 2020 3:55 am

I'll try to finish your requests tomorrow if I have time, but I can't promise anything.
ImageImageImageImageImage
User avatar
daren adler
Senior Member
Posts: 348
Joined: Sat May 18, 2019 7:42 pm

Re: HL2:DM Npc points/score

Postby daren adler » Sun Sep 13, 2020 2:52 am

VinciT wrote:I'll try to finish your requests tomorrow if I have time, but I can't promise anything.

:cool: :cool: No problem :cool: :cool:
User avatar
VinciT
Senior Member
Posts: 331
Joined: Thu Dec 18, 2014 2:41 am

Re: HL2:DM Npc points/score

Postby VinciT » Sun Sep 20, 2020 5:35 am

Painkiller wrote:physics does not yet count as kill
(that means physicgun kills, saw blades, barrels)
Do you want these to NOT count as kills? Because right now I'm getting notifications whenever I kill an NPC with a prop.

Painkiller wrote:- a line break of the current font
(+3 points for killing the zombie)
Example: How to proceed in the chat,
+3 points for killing the zombie
+3 points for killing the zombie
+3 points for killing the zombie
slowly fading downwards.

It seems more alive like in a pinball machine.
Because you kill several npc at the same time.
How about something like this?

Syntax: Select all

# ../npc_points/npc_points.py

# Python
from collections import deque
from colorsys import hsv_to_rgb
from itertools import cycle
from time import time

# Source.Python
from colors import Color, GREEN, YELLOW
from entities.entity import BaseEntity, Entity
from events import Event
from listeners.tick import Delay, RepeatStatus
from messages import HudMsg
from players.entity import Player


# Send a message to the player when they kill an NPC? (True/False)
NOTIFY_PLAYER = True
# Maximum number of lines for notifications. (1 - 4)
NOTIFICATION_MAX = 3
# Offset for additional notifications. This determines how far apart they will
# be. (negative number - up, positive number - down)
NOTIFICATION_OFFSET = -0.04

NOTIFICATION_SETTINGS = {
# Horizontal position on the screen.
'x': -0.01,
# Vertical position on the screen.
'y': 0.85,
# Primary color of the message.
'color1': GREEN,
# Color of the swipe/scan effect.
'color2': YELLOW,
# Time it takes for the notification to fully show up. The swipe/scan
# effect uses this value for each letter that appears. Which is why I
# recommend keeping it set very low (0.02 - 0.04).
'fade_in': 0.035,
# Time it takes for the notification to start fading out.
'hold_time': 1.25,
# Time it takes for the notification to disappear.
'fade_out': 1.5,
# Time it takes for the color to change from 'color2' to 'color1'.
'fx_time': 0.35,
'channel': 1,
'effect': 2
}

# Cycle through the colors of the rainbow when creating notifications?
# (True/False)
RAINBOW_MODE = False

# Dictionary for how many points each NPC is worth.
points_per_npc = {
'npc_zombie': 3,
'npc_manhack': 2,
'npc_headcrab': 2,
'npc_antilion': 3,
'npc_antilionguard': 2,
'npc_clawscanner': 2,
'npc_combinedropship': 3,
'npc_combinegunship': 2,
'npc_crow': 2,
'combine_mine': 3,
'npc_headcrab_black': 2,
'npc_headcrab_fast': 2,
'npc_helicopter': 3,
'npc_hunter': 2,
'npc_ichthyosaur': 2,
'npc_ministriper': 3,
'npc_missildefense': 2,
'npc_mortarsynth': 2,
'npc_pigeon': 3,
'npc_poisonzombie': 2,
'npc_rollermine': 2,
'npc_sniper': 3,
'npc_stalker': 2,
'npc_strider': 2,
'npc_turret_ceiling': 3,
'npc_turret_floor': 2,
'npc_turret_ground': 2,
'npc_vortigaunt': 3,
'npc_zombie_torso': 2,
'npc_zombine': 2
}


# Dictionary used to store properly formatted/prettified NPC names.
_npc_names = {}

# List used to store vibrant/pretty colors.
pretty_colors = []

# Message that will be sent to the player if they kill themselves.
suicide_msg = '-1 point for killing yourself'


def load():
"""Called when the plugin gets loaded."""
# Go through all the NPC classnames and format their names properly.
for npc_classname in points_per_npc:
_npc_names[npc_classname] = name_from_classname(npc_classname)

pretty_colors.extend(get_pretty_colors(10))


class Notification:
"""Class used to send a HudMsg.

Args:
message (str): Message to send.
**kwargs (dict): Additional keyword arguments.

Attributes:
color1 (Color): Color used to override the primary color of the HudMsg.
color2 (Color): Color used to override the effect color of the HudMsg.
life_time (float): How long should the message be valid for?
_sent_to (set of int): Set used to keep track of which channels the
notification has already been sent to.
"""
# Go through all the primary HudMsg settings/parameters.
for attribute, value in NOTIFICATION_SETTINGS.items():
# Set the parameter and it's value as an attribute of this class.
vars()[attribute] = value

# Calculate how often we should push notifications.
tick_rate = (hold_time + fade_out) * fade_in * 15
life_times = {1: tick_rate * 1.85}
settings = {1: NOTIFICATION_SETTINGS}

# Generate the settings for the other channels.
for channel in range(2, NOTIFICATION_MAX + 1):
# Is this the last channel we'll be using?
if channel == NOTIFICATION_MAX:
hold_time = fade_in * 16
fade_out = fade_in * 30
else:
hold_time = tick_rate
fade_out = tick_rate * 0.73 * (NOTIFICATION_MAX - channel)

settings[channel] = {
'x': x,
'y': y + NOTIFICATION_OFFSET * (channel - 1),
'color1': color1,
'fade_in': 0.01,
'hold_time': hold_time,
'fade_out': fade_out,
'channel': channel
}

# Get the life time of the message - time it takes for the message to
# fade out to about 50% of it's initial opacity.
life_times[channel] = hold_time + fade_out * 0.5

# Empty HudMsg used remove any active HudMsg in the primary channel.
wipe_hud_msg = HudMsg(message=' ', hold_time=0.01, channel=1)

def __init__(self, message, **kwargs):
"""Initializes the object."""
self.message = message
self.color1 = kwargs.get('color1', None)
self.color2 = kwargs.get('color2', None)
self.life_time = time() + Notification.life_times[1]
self._sent_to = set()

@property
def expired(self):
"""Checks if the Notification has run it's course (mostly faded)."""
return time() > self.life_time

def push(self, channel, index):
"""Creates and sends a HudMsg through the proper channel."""
# Have we already sent the notification through this channel?
if channel in self._sent_to:
return

# Has the message mostly faded out?
if self.expired:
return

try:
# Try to get the parameters for this channel.
settings = Notification.settings[channel]
except KeyError:
# Seems we're missing data, don't go further.
return

hud_msg = HudMsg(self.message, **settings)

# Should we override the primary color of the HudMsg?
if self.color1 is not None:
hud_msg.color1 = self.color1

# Should we override the effect color of the HudMsg?
if self.color2 is not None:
hud_msg.color2 = self.color2

# Is this the primary channel (the one with the swipe/scan effect)?
if channel == 1:
# Remove the previous HudMsg, if there is one.
Notification.wipe_hud_msg.send(index)
# Send the new HudMsg after a short delay.
Delay(0.15, hud_msg.send, (index,))
else:
hud_msg.send(index)

# Adjust the life time of the notification.
self.life_time = time() + Notification.life_times[channel]
self._sent_to.add(channel)


class PlayerNP(Player):
"""Extended Player class.

Args:
index (int): A valid player index.
caching (bool): Check for a cached instance?

Attributes:
queued_messages (deque): Queue for kill messages that should be turned
into Notification objects.
notifications (deque): Queue for storing Notification instances. This
queue is limited to `NOTIFICATION_MAX` number of items.
notify_think (Repeat): Instance of Repeat used for looping the
`_notify_think()` function.
color_cycle (cycle): Iterator used to get the next color from the
`pretty_colors` list. The cycle repeats once it reaches the last
color.
"""

def __init__(self, index, caching=True):
"""Initializes the object."""
super().__init__(index, caching)

self.queued_messages = deque()
self.notifications = deque(maxlen=NOTIFICATION_MAX)
self.notify_think = self.repeat(self._notify_think)
self.color_cycle = cycle(pretty_colors)

def notify(self, message):
"""Adds a message to the queue, which will then be turned into a
Notification and sent to the player.

Args:
message (str): Message to be sent.
"""
self.queued_messages.appendleft(message)

if self.notify_think.status is not RepeatStatus.RUNNING:
self.notify_think.start(
interval=Notification.tick_rate, execute_on_start=True)

def _notify_think(self):
try:
# Try to get the oldest kill message.
message = self.queued_messages.pop()
except IndexError:
# We've reached the end of the message queue, stop the loop.
self.notify_think.stop()
return

self.notifications.appendleft(
Notification(
message=message,
# Get the next pretty color if `RAINBOW_MODE` is set to True.
color1=next(self.color_cycle) if RAINBOW_MODE else None)
)

for channel, notification in enumerate(self.notifications, 1):
notification.push(channel, self.index)


@Event('entity_killed')
def entity_killed(event):
"""Called when an entity gets killed."""
index_a = event['entindex_attacker']

try:
# Try to get a Player instance of the killer.
player = PlayerNP(index_a)
except ValueError:
# The index doesn't belong to a player.
try:
# Let's see if the entity was killed by a physical object.
inthandle = Entity(event['entindex_inflictor']).get_property_int(
'm_hPhysicsAttacker')
player = PlayerNP.from_inthandle(inthandle)
except (ValueError, OverflowError):
# ValueError (inthandle): Missing property 'm_hPhysicsAttacker'.
# ValueError (player): This inthandle doesn't belong to a player.
# OverflowError: Invalid inthandle (-1).
return

index_k = event['entindex_killed']
# Get the classname of the entity that got killed.
classname = BaseEntity(index_k).classname

# Is the entity that got killed a player?
if 'player' in classname:
# Was this a suicide?
if index_k == index_a:
message = suicide_msg
else:
message = f'+1 point for killing {PlayerNP(index_k).name}'

# Or was it something else (an NPC)?
else:
try:
# Are there predefined points for this NPC?
points = points_per_npc[classname]
except KeyError:
# Nope, default to 1.
points = 1

# Increase the player's kill/score count.
player.kills += points

try:
# Let's see if we have a pretty name for this NPC.
npc_name = _npc_names[classname]
except KeyError:
# Nope, convert it.
npc_name = name_from_classname(classname)
# And add it to the dictionary for later use.
_npc_names[classname] = npc_name

# Make sure to use the correct noun (singular/plural).
word = 'points' if points > 1 else 'point'
message = f'+{points} {word} for killing the {npc_name}'

# Should we tell the player how many points they just received?
if NOTIFY_PLAYER:
player.notify(message)


def name_from_classname(classname):
"""Converts an NPC's classname into a pretty name."""
# First remove 'npc_' from the string. After that, replace underscores with
# spaces. And lastly, turn the string into a list of strings.
# 'npc_headcrab_fast' -> ['headcrab', 'fast']
name = classname.replace('npc_', '').replace('_', ' ').split()
# Don't reverse 'combine mine' and 'zombie torso'.
if 'combine' not in name and 'zombie' not in name:
# Reverse the order of the strings.
# ['headcrab', 'fast'] -> ['fast', 'headcrab']
name.reverse()

# Convert the list of strings into a string and return it.
# ['fast', 'headcrab'] -> 'fast headcrab'
return ' '.join(name)


def get_pretty_colors(amount):
"""Returns a list of vibrant colors.

Args:
amount (int): How many colors should be generated?

Returns:
list of Color: A list containing Color instances.
"""
colors = []
step = 1 / amount

for hue in range(0, amount):
colors.append(
Color(*(int(255 * x) for x in hsv_to_rgb(step * hue, 1, 1))))

return colors


daren adler wrote::cool: :cool: No problem :cool: :cool:
Had some issues with scanners, can't get them to do what I want. I'll try some workarounds tomorrow and see if I can get them working properly.
Last edited by VinciT on Sat Nov 07, 2020 5:11 am, edited 3 times in total.
ImageImageImageImageImage
User avatar
Painkiller
Senior Member
Posts: 726
Joined: Sun Mar 01, 2015 8:09 am
Location: Germany
Contact:

Re: HL2:DM Npc points/score

Postby Painkiller » Sun Sep 20, 2020 9:05 am

VinciT wrote:
Painkiller wrote:physics does not yet count as kill
(that means physicgun kills, saw blades, barrels)
Do you want these to NOT count as kills? Because right now I'm getting notifications whenever I kill an NPC with a prop.

Painkiller wrote:- a line break of the current font
(+3 points for killing the zombie)
Example: How to proceed in the chat,
+3 points for killing the zombie
+3 points for killing the zombie
+3 points for killing the zombie
slowly fading downwards.

It seems more alive like in a pinball machine.
Because you kill several npc at the same time.
How about something like this?

Syntax: Select all

# ../npc_points/npc_points.py

# Python
from collections import deque
from colorsys import hsv_to_rgb
from itertools import cycle
from time import time

# Source.Python
from colors import Color, GREEN, YELLOW
from entities.entity import BaseEntity
from events import Event
from listeners.tick import Delay, RepeatStatus
from messages import HudMsg
from players.entity import Player


# Send a message to the player when they kill an NPC? (True/False)
NOTIFY_PLAYER = True
# Maximum number of lines for notifications. (1 - 4)
NOTIFICATION_MAX = 3
# Offset for additional notifications. This determines how far apart they will
# be. (negative number - up, positive number - down)
NOTIFICATION_OFFSET = -0.04

NOTIFICATION_SETTINGS = {
# Horizontal position on the screen.
'x': -0.01,
# Vertical position on the screen.
'y': 0.85,
# Primary color of the message.
'color1': GREEN,
# Color of the swipe/scan effect.
'color2': YELLOW,
# Time it takes for the notification to fully show up. The swipe/scan
# effect uses this value for each letter that appears. Which is why I
# recommend keeping it set very low (0.02 - 0.04).
'fade_in': 0.035,
# Time it takes for the notification to start fading out.
'hold_time': 1.25,
# Time it takes for the notification to disappear.
'fade_out': 1.5,
# Time it takes for the color to change from 'color2' to 'color1'.
'fx_time': 0.35,
'channel': 1,
'effect': 2
}

# Cycle through the colors of the rainbow when creating notifications?
# (True/False)
RAINBOW_MODE = False

# Dictionary for how many points each NPC is worth.
points_per_npc = {
'npc_zombie': 3,
'npc_manhack': 2,
'npc_headcrab': 2,
'npc_antilion': 3,
'npc_antilionguard': 2,
'npc_clawscanner': 2,
'npc_combinedropship': 3,
'npc_combinegunship': 2,
'npc_crow': 2,
'combine_mine': 3,
'npc_headcrab_black': 2,
'npc_headcrab_fast': 2,
'npc_helicopter': 3,
'npc_hunter': 2,
'npc_ichthyosaur': 2,
'npc_ministriper': 3,
'npc_missildefense': 2,
'npc_mortarsynth': 2,
'npc_pigeon': 3,
'npc_poisonzombie': 2,
'npc_rollermine': 2,
'npc_sniper': 3,
'npc_stalker': 2,
'npc_strider': 2,
'npc_turret_ceiling': 3,
'npc_turret_floor': 2,
'npc_turret_ground': 2,
'npc_vortigaunt': 3,
'npc_zombie_torso': 2,
'npc_zombine': 2
}


# Dictionary used to store properly formatted/prettified NPC names.
_npc_names = {}

# List used to store vibrant/pretty colors.
pretty_colors = []


def load():
"""Called when the plugin gets loaded."""
# Go through all the NPC classnames and format their names properly.
for npc_classname in points_per_npc:
_npc_names[npc_classname] = name_from_classname(npc_classname)


pretty_colors.extend(get_pretty_colors(10))


class Notification:
"""Class used to send a HudMsg.

Args:
message (str): Message to send.
**kwargs (dict): Additional keyword arguments.

Attributes:
color1 (Color): Color used to override the primary color of the HudMsg.
color2 (Color): Color used to override the effect color of the HudMsg.
life_time (float): How long should the message be valid for?
_sent_to (set of int): Set used to keep track of which channels the
notification has already been sent to.
"""
# Go through all the primary HudMsg settings/parameters.
for attribute, value in NOTIFICATION_SETTINGS.items():
# Set the parameter and it's value as an attribute of this class.
vars()[attribute] = value

# Calculate how often we should push notifications.
tick_rate = (hold_time + fade_out) * fade_in * 15
life_times = {1: tick_rate * 1.85}
settings = {1: NOTIFICATION_SETTINGS}

# Generate the settings for the other channels.
for channel in range(2, NOTIFICATION_MAX + 1):
# Is this the last channel we'll be using?
if channel == NOTIFICATION_MAX:
hold_time = fade_in * 16
fade_out = fade_in * 30
else:
hold_time = tick_rate
fade_out = tick_rate * 0.73 * (NOTIFICATION_MAX - channel)

settings[channel] = {
'x': x,
'y': y + NOTIFICATION_OFFSET * (channel - 1),
'color1': color1,
'fade_in': 0.01,
'hold_time': hold_time,
'fade_out': fade_out,
'channel': channel
}

# Get the life time of the message - time it takes for the message to
# fade out to about 50% of it's initial opacity.
life_times[channel] = hold_time + fade_out * 0.5

# Empty HudMsg used remove any active HudMsg in the primary channel.
wipe_hud_msg = HudMsg(message=' ', hold_time=0.01, channel=1)

def __init__(self, message, **kwargs):
"""Initializes the object."""
self.message = message
self.color1 = kwargs.get('color1', None)
self.color2 = kwargs.get('color2', None)
self.life_time = time() + Notification.life_times[1]
self._sent_to = set()

@property
def expired(self):
"""Checks if the Notification has run it's course (mostly faded)."""
return time() > self.life_time

def push(self, channel, index):
"""Creates and sends a HudMsg through the proper channel."""
# Have we already sent the notification through this channel?
if channel in self._sent_to:
return

# Has the message mostly faded out?
if self.expired:
return

try:
# Try to get the parameters for this channel.
settings = Notification.settings[channel]
except KeyError:
# Seems we're missing data, don't go further.
return

hud_msg = HudMsg(self.message, **settings)

# Should we override the primary color of the HudMsg?
if self.color1 is not None:
hud_msg.color1 = self.color1

# Should we override the effect color of the HudMsg?
if self.color2 is not None:
hud_msg.color2 = self.color2

# Is this the primary channel (the one with the swipe/scan effect)?
if channel == 1:
# Remove the previous HudMsg, if there is one.
Notification.wipe_hud_msg.send(index)
# Send the new HudMsg after a short delay.
Delay(0.15, hud_msg.send, (index,))
else:
hud_msg.send(index)

# Adjust the life time of the notification.
self.life_time = time() + Notification.life_times[channel]
self._sent_to.add(channel)


class PlayerNP(Player):
"""Extended Player class.

Args:
index (int): A valid player index.
caching (bool): Check for a cached instance?

Attributes:
queued_messages (deque): Queue for kill messages that should be turned
into Notification objects.
notifications (deque): Queue for storing Notification instances. This
queue is limited to `NOTIFICATION_MAX` number of items.
notify_think (Repeat): Instance of Repeat used for looping the
`_notify_think()` function.
color_cycle (cycle): Iterator used to get the next color from the
`pretty_colors` list. The cycle repeats once it reaches the last
color.
"""

def __init__(self, index, caching=True):
"""Initializes the object."""
super().__init__(index, caching)

self.queued_messages = deque()
self.notifications = deque(maxlen=NOTIFICATION_MAX)
self.notify_think = self.repeat(self._notify_think)
self.color_cycle = cycle(pretty_colors)

def notify(self, points, npc_classname):
"""Adds a message to the queue, which will then be turned into a
Notification and sent to the player.

Args:
points (int): How many points did the player get?
npc_classname (str): Which NPC did the player kill?
"""
try:
# Let's see if we have a pretty name for this NPC.
npc_name = _npc_names[npc_classname]
except KeyError:
# Nope, convert it.
npc_name = name_from_classname(npc_classname)
# And add it to the dictionary for later use.
_npc_names[npc_classname] = npc_name

# Make sure to use the correct noun (singular/plural).
word = 'points' if points > 1 else 'point'
self.queued_messages.appendleft(
f'+{points} {word} for killing the {npc_name}')

if self.notify_think.status is not RepeatStatus.RUNNING:
self.notify_think.start(
interval=Notification.tick_rate, execute_on_start=True)

def _notify_think(self):
try:
# Try to get the oldest kill message.
message = self.queued_messages.pop()
except IndexError:
# We've reached the end of the message queue, stop the loop.
self.notify_think.stop()
return

self.notifications.appendleft(
Notification(
message=message,
# Get the next pretty color if `RAINBOW_MODE` is set to True.
color1=next(self.color_cycle) if RAINBOW_MODE else None)
)

for channel, notification in enumerate(self.notifications, 1):
notification.push(channel, self.index)


@Event('entity_killed')
def npc_killed(event):
"""Called when an entity gets killed."""
try:
# Try to get a Player instance of the killer.
player = PlayerNP(event['entindex_attacker'])
except ValueError:
# Not a player, don't go further.
return

# Get the name of the NPC that got killed.
classname = BaseEntity(event['entindex_killed']).classname

try:
# Are there predefined points for this NPC?
points = points_per_npc[classname]
except KeyError:
# Nope, default to 1.
points = 1

# Increase the player's kill/score count.
player.kills += points

# Should we tell the player how many points they just received?
if NOTIFY_PLAYER:
player.notify(points=points, npc_classname=classname)


def name_from_classname(classname):
"""Converts an NPC's classname into a pretty name."""
# First remove 'npc_' from the string. After that, replace underscores with
# spaces. And lastly, turn the string into a list of strings.
# 'npc_headcrab_fast' -> ['headcrab', 'fast']
name = classname.replace('npc_', '').replace('_', ' ').split()
# Don't reverse 'combine mine' and 'zombie torso'.
if 'combine' not in name and 'zombie' not in name:
# Reverse the order of the strings.
# ['headcrab', 'fast'] -> ['fast', 'headcrab']
name.reverse()

# Convert the list of strings into a string and return it.
# ['fast', 'headcrab'] -> 'fast headcrab'
return ' '.join(name)


def get_pretty_colors(amount):
"""Returns a list of vibrant colors.

Args:
amount (int): How many colors should be generated?

Returns:
list of Color: A list containing Color instances.
"""
colors = []
step = 1 / amount

for hue in range(0, amount):
colors.append(
Color(*(int(255 * x) for x in hsv_to_rgb(step * hue, 1, 1))))

return colors


daren adler wrote::cool: :cool: No problem :cool: :cool:
Had some issues with scanners, can't get them to do what I want. I'll try some workarounds tomorrow and see if I can get them working properly.


It seems to work so far.
I also noticed the
- physics does not yet count as kill
(that means physicgun kills, saw blades, barrels)
that seems to work now.

Now it would be nice to see the charger option.

Greeting Pain
User avatar
daren adler
Senior Member
Posts: 348
Joined: Sat May 18, 2019 7:42 pm

Re: HL2:DM Npc points/score

Postby daren adler » Sun Sep 20, 2020 1:39 pm

VinciT wrote:
Painkiller wrote:physics does not yet count as kill
(that means physicgun kills, saw blades, barrels)
Do you want these to NOT count as kills? Because right now I'm getting notifications whenever I kill an NPC with a prop.

Painkiller wrote:- a line break of the current font
(+3 points for killing the zombie)
Example: How to proceed in the chat,
+3 points for killing the zombie
+3 points for killing the zombie
+3 points for killing the zombie
slowly fading downwards.

It seems more alive like in a pinball machine.
Because you kill several npc at the same time.
How about something like this?

Syntax: Select all

# ../npc_points/npc_points.py

# Python
from collections import deque
from colorsys import hsv_to_rgb
from itertools import cycle
from time import time

# Source.Python
from colors import Color, GREEN, YELLOW
from entities.entity import BaseEntity
from events import Event
from listeners.tick import Delay, RepeatStatus
from messages import HudMsg
from players.entity import Player


# Send a message to the player when they kill an NPC? (True/False)
NOTIFY_PLAYER = True
# Maximum number of lines for notifications. (1 - 4)
NOTIFICATION_MAX = 3
# Offset for additional notifications. This determines how far apart they will
# be. (negative number - up, positive number - down)
NOTIFICATION_OFFSET = -0.04

NOTIFICATION_SETTINGS = {
# Horizontal position on the screen.
'x': -0.01,
# Vertical position on the screen.
'y': 0.85,
# Primary color of the message.
'color1': GREEN,
# Color of the swipe/scan effect.
'color2': YELLOW,
# Time it takes for the notification to fully show up. The swipe/scan
# effect uses this value for each letter that appears. Which is why I
# recommend keeping it set very low (0.02 - 0.04).
'fade_in': 0.035,
# Time it takes for the notification to start fading out.
'hold_time': 1.25,
# Time it takes for the notification to disappear.
'fade_out': 1.5,
# Time it takes for the color to change from 'color2' to 'color1'.
'fx_time': 0.35,
'channel': 1,
'effect': 2
}

# Cycle through the colors of the rainbow when creating notifications?
# (True/False)
RAINBOW_MODE = False

# Dictionary for how many points each NPC is worth.
points_per_npc = {
'npc_zombie': 3,
'npc_manhack': 2,
'npc_headcrab': 2,
'npc_antilion': 3,
'npc_antilionguard': 2,
'npc_clawscanner': 2,
'npc_combinedropship': 3,
'npc_combinegunship': 2,
'npc_crow': 2,
'combine_mine': 3,
'npc_headcrab_black': 2,
'npc_headcrab_fast': 2,
'npc_helicopter': 3,
'npc_hunter': 2,
'npc_ichthyosaur': 2,
'npc_ministriper': 3,
'npc_missildefense': 2,
'npc_mortarsynth': 2,
'npc_pigeon': 3,
'npc_poisonzombie': 2,
'npc_rollermine': 2,
'npc_sniper': 3,
'npc_stalker': 2,
'npc_strider': 2,
'npc_turret_ceiling': 3,
'npc_turret_floor': 2,
'npc_turret_ground': 2,
'npc_vortigaunt': 3,
'npc_zombie_torso': 2,
'npc_zombine': 2
}


# Dictionary used to store properly formatted/prettified NPC names.
_npc_names = {}

# List used to store vibrant/pretty colors.
pretty_colors = []


def load():
"""Called when the plugin gets loaded."""
# Go through all the NPC classnames and format their names properly.
for npc_classname in points_per_npc:
_npc_names[npc_classname] = name_from_classname(npc_classname)


pretty_colors.extend(get_pretty_colors(10))


class Notification:
"""Class used to send a HudMsg.

Args:
message (str): Message to send.
**kwargs (dict): Additional keyword arguments.

Attributes:
color1 (Color): Color used to override the primary color of the HudMsg.
color2 (Color): Color used to override the effect color of the HudMsg.
life_time (float): How long should the message be valid for?
_sent_to (set of int): Set used to keep track of which channels the
notification has already been sent to.
"""
# Go through all the primary HudMsg settings/parameters.
for attribute, value in NOTIFICATION_SETTINGS.items():
# Set the parameter and it's value as an attribute of this class.
vars()[attribute] = value

# Calculate how often we should push notifications.
tick_rate = (hold_time + fade_out) * fade_in * 15
life_times = {1: tick_rate * 1.85}
settings = {1: NOTIFICATION_SETTINGS}

# Generate the settings for the other channels.
for channel in range(2, NOTIFICATION_MAX + 1):
# Is this the last channel we'll be using?
if channel == NOTIFICATION_MAX:
hold_time = fade_in * 16
fade_out = fade_in * 30
else:
hold_time = tick_rate
fade_out = tick_rate * 0.73 * (NOTIFICATION_MAX - channel)

settings[channel] = {
'x': x,
'y': y + NOTIFICATION_OFFSET * (channel - 1),
'color1': color1,
'fade_in': 0.01,
'hold_time': hold_time,
'fade_out': fade_out,
'channel': channel
}

# Get the life time of the message - time it takes for the message to
# fade out to about 50% of it's initial opacity.
life_times[channel] = hold_time + fade_out * 0.5

# Empty HudMsg used remove any active HudMsg in the primary channel.
wipe_hud_msg = HudMsg(message=' ', hold_time=0.01, channel=1)

def __init__(self, message, **kwargs):
"""Initializes the object."""
self.message = message
self.color1 = kwargs.get('color1', None)
self.color2 = kwargs.get('color2', None)
self.life_time = time() + Notification.life_times[1]
self._sent_to = set()

@property
def expired(self):
"""Checks if the Notification has run it's course (mostly faded)."""
return time() > self.life_time

def push(self, channel, index):
"""Creates and sends a HudMsg through the proper channel."""
# Have we already sent the notification through this channel?
if channel in self._sent_to:
return

# Has the message mostly faded out?
if self.expired:
return

try:
# Try to get the parameters for this channel.
settings = Notification.settings[channel]
except KeyError:
# Seems we're missing data, don't go further.
return

hud_msg = HudMsg(self.message, **settings)

# Should we override the primary color of the HudMsg?
if self.color1 is not None:
hud_msg.color1 = self.color1

# Should we override the effect color of the HudMsg?
if self.color2 is not None:
hud_msg.color2 = self.color2

# Is this the primary channel (the one with the swipe/scan effect)?
if channel == 1:
# Remove the previous HudMsg, if there is one.
Notification.wipe_hud_msg.send(index)
# Send the new HudMsg after a short delay.
Delay(0.15, hud_msg.send, (index,))
else:
hud_msg.send(index)

# Adjust the life time of the notification.
self.life_time = time() + Notification.life_times[channel]
self._sent_to.add(channel)


class PlayerNP(Player):
"""Extended Player class.

Args:
index (int): A valid player index.
caching (bool): Check for a cached instance?

Attributes:
queued_messages (deque): Queue for kill messages that should be turned
into Notification objects.
notifications (deque): Queue for storing Notification instances. This
queue is limited to `NOTIFICATION_MAX` number of items.
notify_think (Repeat): Instance of Repeat used for looping the
`_notify_think()` function.
color_cycle (cycle): Iterator used to get the next color from the
`pretty_colors` list. The cycle repeats once it reaches the last
color.
"""

def __init__(self, index, caching=True):
"""Initializes the object."""
super().__init__(index, caching)

self.queued_messages = deque()
self.notifications = deque(maxlen=NOTIFICATION_MAX)
self.notify_think = self.repeat(self._notify_think)
self.color_cycle = cycle(pretty_colors)

def notify(self, points, npc_classname):
"""Adds a message to the queue, which will then be turned into a
Notification and sent to the player.

Args:
points (int): How many points did the player get?
npc_classname (str): Which NPC did the player kill?
"""
try:
# Let's see if we have a pretty name for this NPC.
npc_name = _npc_names[npc_classname]
except KeyError:
# Nope, convert it.
npc_name = name_from_classname(npc_classname)
# And add it to the dictionary for later use.
_npc_names[npc_classname] = npc_name

# Make sure to use the correct noun (singular/plural).
word = 'points' if points > 1 else 'point'
self.queued_messages.appendleft(
f'+{points} {word} for killing the {npc_name}')

if self.notify_think.status is not RepeatStatus.RUNNING:
self.notify_think.start(
interval=Notification.tick_rate, execute_on_start=True)

def _notify_think(self):
try:
# Try to get the oldest kill message.
message = self.queued_messages.pop()
except IndexError:
# We've reached the end of the message queue, stop the loop.
self.notify_think.stop()
return

self.notifications.appendleft(
Notification(
message=message,
# Get the next pretty color if `RAINBOW_MODE` is set to True.
color1=next(self.color_cycle) if RAINBOW_MODE else None)
)

for channel, notification in enumerate(self.notifications, 1):
notification.push(channel, self.index)


@Event('entity_killed')
def npc_killed(event):
"""Called when an entity gets killed."""
try:
# Try to get a Player instance of the killer.
player = PlayerNP(event['entindex_attacker'])
except ValueError:
# Not a player, don't go further.
return

# Get the name of the NPC that got killed.
classname = BaseEntity(event['entindex_killed']).classname

try:
# Are there predefined points for this NPC?
points = points_per_npc[classname]
except KeyError:
# Nope, default to 1.
points = 1

# Increase the player's kill/score count.
player.kills += points

# Should we tell the player how many points they just received?
if NOTIFY_PLAYER:
player.notify(points=points, npc_classname=classname)


def name_from_classname(classname):
"""Converts an NPC's classname into a pretty name."""
# First remove 'npc_' from the string. After that, replace underscores with
# spaces. And lastly, turn the string into a list of strings.
# 'npc_headcrab_fast' -> ['headcrab', 'fast']
name = classname.replace('npc_', '').replace('_', ' ').split()
# Don't reverse 'combine mine' and 'zombie torso'.
if 'combine' not in name and 'zombie' not in name:
# Reverse the order of the strings.
# ['headcrab', 'fast'] -> ['fast', 'headcrab']
name.reverse()

# Convert the list of strings into a string and return it.
# ['fast', 'headcrab'] -> 'fast headcrab'
return ' '.join(name)


def get_pretty_colors(amount):
"""Returns a list of vibrant colors.

Args:
amount (int): How many colors should be generated?

Returns:
list of Color: A list containing Color instances.
"""
colors = []
step = 1 / amount

for hue in range(0, amount):
colors.append(
Color(*(int(255 * x) for x in hsv_to_rgb(step * hue, 1, 1))))

return colors


daren adler wrote::cool: :cool: No problem :cool: :cool:
Had some issues with scanners, can't get them to do what I want. I'll try some workarounds tomorrow and see if I can get them working properly.


Ok, Im sure you will find somthing,,thank you again.
User avatar
daren adler
Senior Member
Posts: 348
Joined: Sat May 18, 2019 7:42 pm

Re: HL2:DM Npc points/score

Postby daren adler » Sun Sep 20, 2020 2:35 pm

daren adler wrote:
VinciT wrote:
Painkiller wrote:physics does not yet count as kill
(that means physicgun kills, saw blades, barrels)
Do you want these to NOT count as kills? Because right now I'm getting notifications whenever I kill an NPC with a prop.

Painkiller wrote:- a line break of the current font
(+3 points for killing the zombie)
Example: How to proceed in the chat,
+3 points for killing the zombie
+3 points for killing the zombie
+3 points for killing the zombie
slowly fading downwards.

It seems more alive like in a pinball machine.
Because you kill several npc at the same time.
How about something like this?

Syntax: Select all

# ../npc_points/npc_points.py

# Python
from collections import deque
from colorsys import hsv_to_rgb
from itertools import cycle
from time import time

# Source.Python
from colors import Color, GREEN, YELLOW
from entities.entity import BaseEntity
from events import Event
from listeners.tick import Delay, RepeatStatus
from messages import HudMsg
from players.entity import Player


# Send a message to the player when they kill an NPC? (True/False)
NOTIFY_PLAYER = True
# Maximum number of lines for notifications. (1 - 4)
NOTIFICATION_MAX = 3
# Offset for additional notifications. This determines how far apart they will
# be. (negative number - up, positive number - down)
NOTIFICATION_OFFSET = -0.04

NOTIFICATION_SETTINGS = {
# Horizontal position on the screen.
'x': -0.01,
# Vertical position on the screen.
'y': 0.85,
# Primary color of the message.
'color1': GREEN,
# Color of the swipe/scan effect.
'color2': YELLOW,
# Time it takes for the notification to fully show up. The swipe/scan
# effect uses this value for each letter that appears. Which is why I
# recommend keeping it set very low (0.02 - 0.04).
'fade_in': 0.035,
# Time it takes for the notification to start fading out.
'hold_time': 1.25,
# Time it takes for the notification to disappear.
'fade_out': 1.5,
# Time it takes for the color to change from 'color2' to 'color1'.
'fx_time': 0.35,
'channel': 1,
'effect': 2
}

# Cycle through the colors of the rainbow when creating notifications?
# (True/False)
RAINBOW_MODE = False

# Dictionary for how many points each NPC is worth.
points_per_npc = {
'npc_zombie': 3,
'npc_manhack': 2,
'npc_headcrab': 2,
'npc_antilion': 3,
'npc_antilionguard': 2,
'npc_clawscanner': 2,
'npc_combinedropship': 3,
'npc_combinegunship': 2,
'npc_crow': 2,
'combine_mine': 3,
'npc_headcrab_black': 2,
'npc_headcrab_fast': 2,
'npc_helicopter': 3,
'npc_hunter': 2,
'npc_ichthyosaur': 2,
'npc_ministriper': 3,
'npc_missildefense': 2,
'npc_mortarsynth': 2,
'npc_pigeon': 3,
'npc_poisonzombie': 2,
'npc_rollermine': 2,
'npc_sniper': 3,
'npc_stalker': 2,
'npc_strider': 2,
'npc_turret_ceiling': 3,
'npc_turret_floor': 2,
'npc_turret_ground': 2,
'npc_vortigaunt': 3,
'npc_zombie_torso': 2,
'npc_zombine': 2
}


# Dictionary used to store properly formatted/prettified NPC names.
_npc_names = {}

# List used to store vibrant/pretty colors.
pretty_colors = []


def load():
"""Called when the plugin gets loaded."""
# Go through all the NPC classnames and format their names properly.
for npc_classname in points_per_npc:
_npc_names[npc_classname] = name_from_classname(npc_classname)


pretty_colors.extend(get_pretty_colors(10))


class Notification:
"""Class used to send a HudMsg.

Args:
message (str): Message to send.
**kwargs (dict): Additional keyword arguments.

Attributes:
color1 (Color): Color used to override the primary color of the HudMsg.
color2 (Color): Color used to override the effect color of the HudMsg.
life_time (float): How long should the message be valid for?
_sent_to (set of int): Set used to keep track of which channels the
notification has already been sent to.
"""
# Go through all the primary HudMsg settings/parameters.
for attribute, value in NOTIFICATION_SETTINGS.items():
# Set the parameter and it's value as an attribute of this class.
vars()[attribute] = value

# Calculate how often we should push notifications.
tick_rate = (hold_time + fade_out) * fade_in * 15
life_times = {1: tick_rate * 1.85}
settings = {1: NOTIFICATION_SETTINGS}

# Generate the settings for the other channels.
for channel in range(2, NOTIFICATION_MAX + 1):
# Is this the last channel we'll be using?
if channel == NOTIFICATION_MAX:
hold_time = fade_in * 16
fade_out = fade_in * 30
else:
hold_time = tick_rate
fade_out = tick_rate * 0.73 * (NOTIFICATION_MAX - channel)

settings[channel] = {
'x': x,
'y': y + NOTIFICATION_OFFSET * (channel - 1),
'color1': color1,
'fade_in': 0.01,
'hold_time': hold_time,
'fade_out': fade_out,
'channel': channel
}

# Get the life time of the message - time it takes for the message to
# fade out to about 50% of it's initial opacity.
life_times[channel] = hold_time + fade_out * 0.5

# Empty HudMsg used remove any active HudMsg in the primary channel.
wipe_hud_msg = HudMsg(message=' ', hold_time=0.01, channel=1)

def __init__(self, message, **kwargs):
"""Initializes the object."""
self.message = message
self.color1 = kwargs.get('color1', None)
self.color2 = kwargs.get('color2', None)
self.life_time = time() + Notification.life_times[1]
self._sent_to = set()

@property
def expired(self):
"""Checks if the Notification has run it's course (mostly faded)."""
return time() > self.life_time

def push(self, channel, index):
"""Creates and sends a HudMsg through the proper channel."""
# Have we already sent the notification through this channel?
if channel in self._sent_to:
return

# Has the message mostly faded out?
if self.expired:
return

try:
# Try to get the parameters for this channel.
settings = Notification.settings[channel]
except KeyError:
# Seems we're missing data, don't go further.
return

hud_msg = HudMsg(self.message, **settings)

# Should we override the primary color of the HudMsg?
if self.color1 is not None:
hud_msg.color1 = self.color1

# Should we override the effect color of the HudMsg?
if self.color2 is not None:
hud_msg.color2 = self.color2

# Is this the primary channel (the one with the swipe/scan effect)?
if channel == 1:
# Remove the previous HudMsg, if there is one.
Notification.wipe_hud_msg.send(index)
# Send the new HudMsg after a short delay.
Delay(0.15, hud_msg.send, (index,))
else:
hud_msg.send(index)

# Adjust the life time of the notification.
self.life_time = time() + Notification.life_times[channel]
self._sent_to.add(channel)


class PlayerNP(Player):
"""Extended Player class.

Args:
index (int): A valid player index.
caching (bool): Check for a cached instance?

Attributes:
queued_messages (deque): Queue for kill messages that should be turned
into Notification objects.
notifications (deque): Queue for storing Notification instances. This
queue is limited to `NOTIFICATION_MAX` number of items.
notify_think (Repeat): Instance of Repeat used for looping the
`_notify_think()` function.
color_cycle (cycle): Iterator used to get the next color from the
`pretty_colors` list. The cycle repeats once it reaches the last
color.
"""

def __init__(self, index, caching=True):
"""Initializes the object."""
super().__init__(index, caching)

self.queued_messages = deque()
self.notifications = deque(maxlen=NOTIFICATION_MAX)
self.notify_think = self.repeat(self._notify_think)
self.color_cycle = cycle(pretty_colors)

def notify(self, points, npc_classname):
"""Adds a message to the queue, which will then be turned into a
Notification and sent to the player.

Args:
points (int): How many points did the player get?
npc_classname (str): Which NPC did the player kill?
"""
try:
# Let's see if we have a pretty name for this NPC.
npc_name = _npc_names[npc_classname]
except KeyError:
# Nope, convert it.
npc_name = name_from_classname(npc_classname)
# And add it to the dictionary for later use.
_npc_names[npc_classname] = npc_name

# Make sure to use the correct noun (singular/plural).
word = 'points' if points > 1 else 'point'
self.queued_messages.appendleft(
f'+{points} {word} for killing the {npc_name}')

if self.notify_think.status is not RepeatStatus.RUNNING:
self.notify_think.start(
interval=Notification.tick_rate, execute_on_start=True)

def _notify_think(self):
try:
# Try to get the oldest kill message.
message = self.queued_messages.pop()
except IndexError:
# We've reached the end of the message queue, stop the loop.
self.notify_think.stop()
return

self.notifications.appendleft(
Notification(
message=message,
# Get the next pretty color if `RAINBOW_MODE` is set to True.
color1=next(self.color_cycle) if RAINBOW_MODE else None)
)

for channel, notification in enumerate(self.notifications, 1):
notification.push(channel, self.index)


@Event('entity_killed')
def npc_killed(event):
"""Called when an entity gets killed."""
try:
# Try to get a Player instance of the killer.
player = PlayerNP(event['entindex_attacker'])
except ValueError:
# Not a player, don't go further.
return

# Get the name of the NPC that got killed.
classname = BaseEntity(event['entindex_killed']).classname

try:
# Are there predefined points for this NPC?
points = points_per_npc[classname]
except KeyError:
# Nope, default to 1.
points = 1

# Increase the player's kill/score count.
player.kills += points

# Should we tell the player how many points they just received?
if NOTIFY_PLAYER:
player.notify(points=points, npc_classname=classname)


def name_from_classname(classname):
"""Converts an NPC's classname into a pretty name."""
# First remove 'npc_' from the string. After that, replace underscores with
# spaces. And lastly, turn the string into a list of strings.
# 'npc_headcrab_fast' -> ['headcrab', 'fast']
name = classname.replace('npc_', '').replace('_', ' ').split()
# Don't reverse 'combine mine' and 'zombie torso'.
if 'combine' not in name and 'zombie' not in name:
# Reverse the order of the strings.
# ['headcrab', 'fast'] -> ['fast', 'headcrab']
name.reverse()

# Convert the list of strings into a string and return it.
# ['fast', 'headcrab'] -> 'fast headcrab'
return ' '.join(name)


def get_pretty_colors(amount):
"""Returns a list of vibrant colors.

Args:
amount (int): How many colors should be generated?

Returns:
list of Color: A list containing Color instances.
"""
colors = []
step = 1 / amount

for hue in range(0, amount):
colors.append(
Color(*(int(255 * x) for x in hsv_to_rgb(step * hue, 1, 1))))

return colors


daren adler wrote::cool: :cool: No problem :cool: :cool:
Had some issues with scanners, can't get them to do what I want. I'll try some workarounds tomorrow and see if I can get them working properly.


Ok, Im sure you will find somthing,,thank you again.

UPdate-- I just tryed the new one you put up and all works good to me,,i even throwed a rock/berrel at them and gave points,,thank you.
User avatar
daren adler
Senior Member
Posts: 348
Joined: Sat May 18, 2019 7:42 pm

Re: HL2:DM Npc points/score

Postby daren adler » Sun Sep 20, 2020 2:41 pm

daren adler wrote:
daren adler wrote:
VinciT wrote:Do you want these to NOT count as kills? Because right now I'm getting notifications whenever I kill an NPC with a prop.

How about something like this?

Syntax: Select all

# ../npc_points/npc_points.py

# Python
from collections import deque
from colorsys import hsv_to_rgb
from itertools import cycle
from time import time

# Source.Python
from colors import Color, GREEN, YELLOW
from entities.entity import BaseEntity
from events import Event
from listeners.tick import Delay, RepeatStatus
from messages import HudMsg
from players.entity import Player


# Send a message to the player when they kill an NPC? (True/False)
NOTIFY_PLAYER = True
# Maximum number of lines for notifications. (1 - 4)
NOTIFICATION_MAX = 3
# Offset for additional notifications. This determines how far apart they will
# be. (negative number - up, positive number - down)
NOTIFICATION_OFFSET = -0.04

NOTIFICATION_SETTINGS = {
# Horizontal position on the screen.
'x': -0.01,
# Vertical position on the screen.
'y': 0.85,
# Primary color of the message.
'color1': GREEN,
# Color of the swipe/scan effect.
'color2': YELLOW,
# Time it takes for the notification to fully show up. The swipe/scan
# effect uses this value for each letter that appears. Which is why I
# recommend keeping it set very low (0.02 - 0.04).
'fade_in': 0.035,
# Time it takes for the notification to start fading out.
'hold_time': 1.25,
# Time it takes for the notification to disappear.
'fade_out': 1.5,
# Time it takes for the color to change from 'color2' to 'color1'.
'fx_time': 0.35,
'channel': 1,
'effect': 2
}

# Cycle through the colors of the rainbow when creating notifications?
# (True/False)
RAINBOW_MODE = False

# Dictionary for how many points each NPC is worth.
points_per_npc = {
'npc_zombie': 3,
'npc_manhack': 2,
'npc_headcrab': 2,
'npc_antilion': 3,
'npc_antilionguard': 2,
'npc_clawscanner': 2,
'npc_combinedropship': 3,
'npc_combinegunship': 2,
'npc_crow': 2,
'combine_mine': 3,
'npc_headcrab_black': 2,
'npc_headcrab_fast': 2,
'npc_helicopter': 3,
'npc_hunter': 2,
'npc_ichthyosaur': 2,
'npc_ministriper': 3,
'npc_missildefense': 2,
'npc_mortarsynth': 2,
'npc_pigeon': 3,
'npc_poisonzombie': 2,
'npc_rollermine': 2,
'npc_sniper': 3,
'npc_stalker': 2,
'npc_strider': 2,
'npc_turret_ceiling': 3,
'npc_turret_floor': 2,
'npc_turret_ground': 2,
'npc_vortigaunt': 3,
'npc_zombie_torso': 2,
'npc_zombine': 2
}


# Dictionary used to store properly formatted/prettified NPC names.
_npc_names = {}

# List used to store vibrant/pretty colors.
pretty_colors = []


def load():
"""Called when the plugin gets loaded."""
# Go through all the NPC classnames and format their names properly.
for npc_classname in points_per_npc:
_npc_names[npc_classname] = name_from_classname(npc_classname)


pretty_colors.extend(get_pretty_colors(10))


class Notification:
"""Class used to send a HudMsg.

Args:
message (str): Message to send.
**kwargs (dict): Additional keyword arguments.

Attributes:
color1 (Color): Color used to override the primary color of the HudMsg.
color2 (Color): Color used to override the effect color of the HudMsg.
life_time (float): How long should the message be valid for?
_sent_to (set of int): Set used to keep track of which channels the
notification has already been sent to.
"""
# Go through all the primary HudMsg settings/parameters.
for attribute, value in NOTIFICATION_SETTINGS.items():
# Set the parameter and it's value as an attribute of this class.
vars()[attribute] = value

# Calculate how often we should push notifications.
tick_rate = (hold_time + fade_out) * fade_in * 15
life_times = {1: tick_rate * 1.85}
settings = {1: NOTIFICATION_SETTINGS}

# Generate the settings for the other channels.
for channel in range(2, NOTIFICATION_MAX + 1):
# Is this the last channel we'll be using?
if channel == NOTIFICATION_MAX:
hold_time = fade_in * 16
fade_out = fade_in * 30
else:
hold_time = tick_rate
fade_out = tick_rate * 0.73 * (NOTIFICATION_MAX - channel)

settings[channel] = {
'x': x,
'y': y + NOTIFICATION_OFFSET * (channel - 1),
'color1': color1,
'fade_in': 0.01,
'hold_time': hold_time,
'fade_out': fade_out,
'channel': channel
}

# Get the life time of the message - time it takes for the message to
# fade out to about 50% of it's initial opacity.
life_times[channel] = hold_time + fade_out * 0.5

# Empty HudMsg used remove any active HudMsg in the primary channel.
wipe_hud_msg = HudMsg(message=' ', hold_time=0.01, channel=1)

def __init__(self, message, **kwargs):
"""Initializes the object."""
self.message = message
self.color1 = kwargs.get('color1', None)
self.color2 = kwargs.get('color2', None)
self.life_time = time() + Notification.life_times[1]
self._sent_to = set()

@property
def expired(self):
"""Checks if the Notification has run it's course (mostly faded)."""
return time() > self.life_time

def push(self, channel, index):
"""Creates and sends a HudMsg through the proper channel."""
# Have we already sent the notification through this channel?
if channel in self._sent_to:
return

# Has the message mostly faded out?
if self.expired:
return

try:
# Try to get the parameters for this channel.
settings = Notification.settings[channel]
except KeyError:
# Seems we're missing data, don't go further.
return

hud_msg = HudMsg(self.message, **settings)

# Should we override the primary color of the HudMsg?
if self.color1 is not None:
hud_msg.color1 = self.color1

# Should we override the effect color of the HudMsg?
if self.color2 is not None:
hud_msg.color2 = self.color2

# Is this the primary channel (the one with the swipe/scan effect)?
if channel == 1:
# Remove the previous HudMsg, if there is one.
Notification.wipe_hud_msg.send(index)
# Send the new HudMsg after a short delay.
Delay(0.15, hud_msg.send, (index,))
else:
hud_msg.send(index)

# Adjust the life time of the notification.
self.life_time = time() + Notification.life_times[channel]
self._sent_to.add(channel)


class PlayerNP(Player):
"""Extended Player class.

Args:
index (int): A valid player index.
caching (bool): Check for a cached instance?

Attributes:
queued_messages (deque): Queue for kill messages that should be turned
into Notification objects.
notifications (deque): Queue for storing Notification instances. This
queue is limited to `NOTIFICATION_MAX` number of items.
notify_think (Repeat): Instance of Repeat used for looping the
`_notify_think()` function.
color_cycle (cycle): Iterator used to get the next color from the
`pretty_colors` list. The cycle repeats once it reaches the last
color.
"""

def __init__(self, index, caching=True):
"""Initializes the object."""
super().__init__(index, caching)

self.queued_messages = deque()
self.notifications = deque(maxlen=NOTIFICATION_MAX)
self.notify_think = self.repeat(self._notify_think)
self.color_cycle = cycle(pretty_colors)

def notify(self, points, npc_classname):
"""Adds a message to the queue, which will then be turned into a
Notification and sent to the player.

Args:
points (int): How many points did the player get?
npc_classname (str): Which NPC did the player kill?
"""
try:
# Let's see if we have a pretty name for this NPC.
npc_name = _npc_names[npc_classname]
except KeyError:
# Nope, convert it.
npc_name = name_from_classname(npc_classname)
# And add it to the dictionary for later use.
_npc_names[npc_classname] = npc_name

# Make sure to use the correct noun (singular/plural).
word = 'points' if points > 1 else 'point'
self.queued_messages.appendleft(
f'+{points} {word} for killing the {npc_name}')

if self.notify_think.status is not RepeatStatus.RUNNING:
self.notify_think.start(
interval=Notification.tick_rate, execute_on_start=True)

def _notify_think(self):
try:
# Try to get the oldest kill message.
message = self.queued_messages.pop()
except IndexError:
# We've reached the end of the message queue, stop the loop.
self.notify_think.stop()
return

self.notifications.appendleft(
Notification(
message=message,
# Get the next pretty color if `RAINBOW_MODE` is set to True.
color1=next(self.color_cycle) if RAINBOW_MODE else None)
)

for channel, notification in enumerate(self.notifications, 1):
notification.push(channel, self.index)


@Event('entity_killed')
def npc_killed(event):
"""Called when an entity gets killed."""
try:
# Try to get a Player instance of the killer.
player = PlayerNP(event['entindex_attacker'])
except ValueError:
# Not a player, don't go further.
return

# Get the name of the NPC that got killed.
classname = BaseEntity(event['entindex_killed']).classname

try:
# Are there predefined points for this NPC?
points = points_per_npc[classname]
except KeyError:
# Nope, default to 1.
points = 1

# Increase the player's kill/score count.
player.kills += points

# Should we tell the player how many points they just received?
if NOTIFY_PLAYER:
player.notify(points=points, npc_classname=classname)


def name_from_classname(classname):
"""Converts an NPC's classname into a pretty name."""
# First remove 'npc_' from the string. After that, replace underscores with
# spaces. And lastly, turn the string into a list of strings.
# 'npc_headcrab_fast' -> ['headcrab', 'fast']
name = classname.replace('npc_', '').replace('_', ' ').split()
# Don't reverse 'combine mine' and 'zombie torso'.
if 'combine' not in name and 'zombie' not in name:
# Reverse the order of the strings.
# ['headcrab', 'fast'] -> ['fast', 'headcrab']
name.reverse()

# Convert the list of strings into a string and return it.
# ['fast', 'headcrab'] -> 'fast headcrab'
return ' '.join(name)


def get_pretty_colors(amount):
"""Returns a list of vibrant colors.

Args:
amount (int): How many colors should be generated?

Returns:
list of Color: A list containing Color instances.
"""
colors = []
step = 1 / amount

for hue in range(0, amount):
colors.append(
Color(*(int(255 * x) for x in hsv_to_rgb(step * hue, 1, 1))))

return colors


Had some issues with scanners, can't get them to do what I want. I'll try some workarounds tomorrow and see if I can get them working properly.


Ok, Im sure you will find somthing,,thank you again.

UPdate-- I just tryed the new one you put up and all works good to me,,i even throwed a rock/berrel at them and gave points,,thank you.


Heres what i use to spawn the scanner.

sm_rcon sv_cheats 1
npc_create npc_cscanner
ent_setname exile
ent_fire npc_cscanner addoutput "classname name"
ent_fire exile setrelationship "!activator D_li 99"
ent_fire exile addoutput "health 9999999999999"
ent_fire exile setfollowtarget !activator
sm_rcon sv_cheats 0

I have that as a cfg and you point anywhere and either use a bind or console "exec npc_scanner" and where you point they spawn. would love to have it spawn 1 or 2 time a map and you cant kill them,,that way they just fly around flashing players.....i know you will figer out somthing,,thanks again.
User avatar
Painkiller
Senior Member
Posts: 726
Joined: Sun Mar 01, 2015 8:09 am
Location: Germany
Contact:

Re: HL2:DM Npc points/score

Postby Painkiller » Sun Sep 20, 2020 4:26 pm

Daren you little spammer. ^^ :D
User avatar
daren adler
Senior Member
Posts: 348
Joined: Sat May 18, 2019 7:42 pm

Re: HL2:DM Npc points/score

Postby daren adler » Sun Sep 20, 2020 5:13 pm

Painkiller wrote:Daren you little spammer. ^^ :D


OK, i see what you are saying, i did not mean for all of the HL2:DM Npc points/score posts, so sorry, You can delete all of this guys. again sorry for all the posts. You can delete the others also if its to much. I will try and stay off of here,,sorry for the trouble guys.
User avatar
Painkiller
Senior Member
Posts: 726
Joined: Sun Mar 01, 2015 8:09 am
Location: Germany
Contact:

Re: HL2:DM Npc points/score

Postby Painkiller » Sun Sep 20, 2020 7:57 pm

daren adler wrote:
Painkiller wrote:Daren you little spammer. ^^ :D


OK, i see what you are saying, i did not mean for all of the HL2:DM Npc points/score posts, so sorry, You can delete all of this guys. again sorry for all the posts. You can delete the others also if its to much. I will try and stay off of here,,sorry for the trouble guys.


It was fun, it would have fit all in one.
User avatar
Painkiller
Senior Member
Posts: 726
Joined: Sun Mar 01, 2015 8:09 am
Location: Germany
Contact:

Re: HL2:DM Npc points/score

Postby Painkiller » Fri Sep 25, 2020 6:49 am

Good morning and good start into the weekend.

Is there any news about the charger percentage ?
User avatar
PEACE
Member
Posts: 50
Joined: Mon Oct 12, 2020 1:13 pm

Re: HL2:DM Npc points/score

Postby PEACE » Wed Nov 04, 2020 3:21 pm

Hey my friends I have a bug to report on this plugin , when i kill a non NPC , like a player or a bot since we only kill bots on my server I get 2 points per kill not 1 point as it should be ...
can some one look at this as to why this happens ?

Thank you

PEACE
User avatar
VinciT
Senior Member
Posts: 331
Joined: Thu Dec 18, 2014 2:41 am

Re: HL2:DM Npc points/score

Postby VinciT » Wed Nov 04, 2020 9:35 pm

I've modified the plugin a bit to resolve the issue you encountered. While fixing that issue, I also added better notifications when killing another player (it now uses the player's name instead of just being 'the player'), as well as a notification when the player commits suicide.
ImageImageImageImageImage
User avatar
PEACE
Member
Posts: 50
Joined: Mon Oct 12, 2020 1:13 pm

Re: HL2:DM Npc points/score

Postby PEACE » Wed Nov 04, 2020 10:01 pm

I have to say WoW you fixed it fast , great work as always VinciT , I just tested it and works perfect and realy like the notifications you added , nice touch for sure . Extremely impressed with your work :)
At 1st I was like what is going on and was turning off plugins in SM and ES thinking there was a conflict some place since i have 100's of plugins Lol

Thank you so much

PEACE

Return to “Plugin Requests”

Who is online

Users browsing this forum: No registered users and 6 guests