# ../weapon_effects/weapon_effects.py
# Source.Python
from colors import Color
from engines.precache import Model
from entities.constants import RenderMode, RenderEffects
from entities.entity import BaseEntity, Entity
from entities.hooks import EntityPreHook, EntityCondition
from listeners import OnEntityDeleted, OnEntityOutput
from mathlib import Vector
class ColorEx(Color):
"""Extended Color class."""
def __init__(self, r, g, b, a=255):
super().__init__(r, g, b, a)
self.raw = r + (g << 8) + (b << 16) + (a << 24)
if self.raw >= 2**31: self.raw -= 2**32
self.string = f'{r} {g} {b}'
# Sprite used for the spinning trails.
WEAPON_TRAIL_MODEL = Model('sprites/laser.vmt')
weapon_trails = {
'weapon_default': (ColorEx(255, 127, 000), ColorEx(255, 255, 000)),
'weapon_rpg': (ColorEx(255, 50, 20), ColorEx(255, 150, 50))
}
trail_pairs = {}
@EntityPreHook(
EntityCondition.equals_entity_classname('weapon_frag'), 'materialize')
def weapon_materialize_pre(stack_data):
"""Called when a weapon becomes enabled."""
base_entity = BaseEntity._obj(stack_data[0])
create_weapon_trail(
base_entity.classname, base_entity.index, base_entity.origin)
@OnEntityOutput
def on_entity_output(output, activator, caller, value, delay):
if output == 'OnPlayerPickup':
try:
trail_pairs.pop(caller.index).remove()
except KeyError:
return
@OnEntityDeleted
def on_entity_deleted(base_entity):
"""Called when an entity is being removed."""
try:
index = base_entity.index
except ValueError:
return
try:
trail_pairs.pop(index).remove()
except KeyError:
pass
def create_weapon_trail(classname, index, origin):
# Is this a weapon without a set color?
if classname not in weapon_trails and 'weapon' in classname:
classname = 'weapon_default'
# Is there a color set for this weapon?
if classname in weapon_trails:
# Check if there's a 'point_spotlight' tied to this index already.
try:
# If there is, remove it.
trail_pairs.pop(index).remove()
except KeyError:
pass
trail_pairs[index] = SpinningTrails(
origin, WEAPON_TRAIL_MODEL, colors=weapon_trails[classname])
class SpinningTrails:
"""Class used to create a spinning effect using 'func_rotating' and
'env_spritetrail' entities.
Args:
origin (Vector): Spawn position of the effect.
sprite (Model): Sprite for the 'env_spritetrail' entity.
**kwargs: Additional keyword arguments.
"""
def __init__(self, origin, sprite, **kwargs):
self.rotators = []
self.trails = []
self.create(origin, sprite, **kwargs)
def create(self, origin, sprite, **kwargs):
# Lift the effect a bit from the ground.
origin.z += 0.5
# How many trails should there be?
number_of_trails = kwargs.get('number_of_trails', 2)
# Rotation speed.
max_speed = kwargs.get('max_speed', 250)
# The radius of the first trail.
min_radius = kwargs.get('min_radius', 16)
# How far apart should the trails be?
spacing = kwargs.get('spacing', 4)
# Colors of the trails. If more than one color is specified, the colors
# will alternate. These should be instances of ColorEx.
colors = kwargs.get('colors', (ColorEx(255, 255, 255),))
colors_num = len(colors)
# Speed at which the trail fades (in seconds).
life_time = kwargs.get('life_time', 0.8)
for i in range(0, number_of_trails):
# No need to create more than two of these. One for each direction.
if i < 2:
rotator = Entity.create('func_rotating')
rotator.origin = origin
rotator.max_speed = max_speed
# How quickly does the 'func_rotating' speed up or slow down?
# (0 - very slowly, 100 - very quickly)
rotator.fan_friction = 100
# Change the direction of every other trail.
rotator.reversed = bool(i % 2)
# Set certain spawn flags for the entity.
# 1: Starts rotating as soon as it spawns.
# 64: Not solid.
rotator.spawn_flags = 1 + 64
rotator.spawn()
# Add the rotator to a list.
self.rotators.append(rotator)
# Was more than one color specified?
if colors_num > 1:
# Change the colors based on trail order.
color = colors[i % colors_num]
else:
color = colors[0]
trail = create_sprite_trail(
origin, sprite.path, 4, 1, color.string, life_time)
# Alternate the parent every other trail.
trail.set_parent(self.rotators[i % 2], -1)
# Offset the position of the 'env_spritetrail' on the
# 'func_rotating' entity. The amount of offset is based on the
# given 'min_radius', 'spacing', and order of the trail.
trail.set_property_vector(
'm_vecOrigin', Vector(min_radius + spacing * i, 0, 0))
# Add the 'env_spritetrail' to the list.
self.trails.append(trail)
def remove(self):
"""Removes both 'func_rotating' entities after a single frame delay."""
for rotator in self.rotators:
rotator.delay(0, rotator.remove)
def create_sprite_trail(
origin, sprite_path, start_width, end_width, color_str, life_time):
"""Creates an 'env_spritetrail' entity.
Args:
origin (Vector): Spawn position.
sprite_path (string): Path to the sprite material.
start_width (float): Starting width of the trail.
end_width (float): Ending width of the trail.
color_str (str): String containing the RGB values of the color.
(e.g. '255 255 255' for white)
life_time (float): How long does the trail last before it starts to
fade (in seconds)?
Returns:
Entity: The entity instance of the created 'env_spritetrail'.
"""
trail = Entity.create('env_spritetrail')
trail.sprite_name = sprite_path
trail.origin = origin
trail.life_time = life_time
trail.start_width = start_width
trail.end_width = end_width
trail.render_mode = RenderMode.TRANS_ADD
trail.render_amt = 255
trail.render_fx = RenderEffects.NONE
trail.set_key_value_string('rendercolor', color_str)
trail.spawn()
# Texture resolution of the trail.
trail.texture_res = 0.05
return trail