# ../lights/lights.py
# Source.Python
from colors import Color
from effects.base import TempEntity
from effects.hooks import TempEntityPreHook
from entities.entity import Entity, BaseEntity
from entities.hooks import EntityPreHook, EntityCondition
from filters.recipients import RecipientFilter
from filters.entities import BaseEntityIter
from listeners import OnEntityOutput, OnEntitySpawned, OnEntityDeleted
from mathlib import QAngle
from stringtables import string_tables
# Colors when the 'prop_combine_ball' bounces and explodes.
COLOR_BALL_BOUNCE = Color(0, 191, 255)
COLOR_BALL_EXPLODE = Color(170, 0, 255)
# Color when the 'crossbow_bolt' bounces.
COLOR_BOLT_BOUNCE = Color(255, 88, 0)
# Color when the 'npc_grenade_frag' bounces.
COLOR_FRAG_BOUNCE = Color(0, 255, 88)
# How wide should the 'point_spotlight' beam be?
SPOTLIGHT_DEFAULT_WIDTH = 20
# How tall should the 'point_spotlight' beam be?
SPOTLIGHT_DEFAULT_LENGTH = 300
item_colors = {
'item_battery': Color(255, 255, 25),
'item_healthkit': Color(25, 255, 55),
'item_healthvial': Color(25, 255, 55)
}
# Entities for which we hooked 'start_touch', along with the effect arguments.
start_touch_entities = {
'crossbow_bolt': (COLOR_BOLT_BOUNCE, 200, 3, 8, 150),
'npc_grenade_frag': (COLOR_FRAG_BOUNCE, 100, 3, 8, 50)
}
spotlight_pairs = {}
# Angle to make the 'point_spotlight' point upwards.
SPOTLIGHT_ANGLE = QAngle(-90, 0, 0)
def load():
"""Called when the plugin is loaded."""
# In case of a late plugin load, find the items that need spotlights.
for base_entity in BaseEntityIter(item_colors.keys()):
create_item_spotlight(
base_entity.classname, base_entity.index, base_entity.origin)
def unload():
"""Called when the plugin is unloaded."""
# Go through all the 'point_spotlight' entities and remove them.
for spotlight in spotlight_pairs.values():
spotlight.remove()
# =============================================================================
# >> DYNAMIC_LIGHT: crossbow_bolt, prop_combine_ball, npc_grenade_frag
# =============================================================================
@TempEntityPreHook('EffectDispatch')
def effect_dispatch_pre(temp_entity, recipient_filter):
# Get the name of the effect.
effect_name = string_tables.EffectDispatch[temp_entity.effect_name_index]
# Did the 'prop_combine_ball' bounce?
if 'cball_bounce' in effect_name:
# Delay the effect by a single frame, otherwise the server will crash!
temp_entity.entity.delay(0, create_dynamic_light,
(temp_entity.origin, COLOR_BALL_BOUNCE, 200, 3, 8, 150))
# Or did it explode?
if 'cball_explode' in effect_name:
temp_entity.entity.delay(0, create_dynamic_light,
(temp_entity.origin, COLOR_BALL_EXPLODE, 600, 1, 8, 600))
@EntityPreHook(
EntityCondition.equals_entity_classname('crossbow_bolt'), 'start_touch')
def bolt_touch_pre(stack_data):
entity = Entity._obj(stack_data[0])
# Is this an entity we're looking for? (crossbow_bolt, npc_grenade_frag)
if entity.classname in start_touch_entities:
create_dynamic_light(
entity.origin, *start_touch_entities[entity.classname])
parent = entity.parent
if parent is not None:
parent.start_touch.skip_hooks(stack_data[1])
return False
def create_dynamic_light(
origin, color, radius, life_time, exponent, decay, *recipients):
"""Creates a dynamic light effect at the specified origin.
Args:
origin (Vector): Spawn position of the effect.
color (Color): Color of the effect.
radius (float): How wide the glow is.
life_time (float): How long should the effect last? (in seconds)
exponent (int): Strength of the glow.
decay (float): By how much the radius is lowered each second.
recipients (RecipientFilter): Players that should see the effect.
"""
light = TempEntity('Dynamic Light')
light.origin = origin
light.color = color
light.radius = radius
light.life_time = life_time
light.exponent = exponent
light.decay = decay
light.create(RecipientFilter())
# =============================================================================
# >> POINT_SPOTLIGHT: item_battery, item_healthkit, item_healthvial
# =============================================================================
class Spotlight(Entity):
"""Modified Entity class for properly removing a 'point_spotlight'."""
caching = True
def remove(self):
"""Turns off the 'point_spotlight' before removing it."""
self.call_input('LightOff')
super().remove()
def create_spotlight(origin, color, **kwargs):
"""Creates a 'point_spotlight' at the specified origin.
Args:
origin (Vector): Spawn position of the 'point_spotlight'.
color (Color): Color of the light.
**kwargs: Additional keywords arguments.
"""
spotlight = Spotlight.create('point_spotlight')
spotlight.origin = origin
spotlight.angles = kwargs.get('angle', SPOTLIGHT_ANGLE)
spotlight.color = color
spotlight.set_key_value_bool('disablereceiveshadows', False)
spotlight.set_key_value_float('HDRColorScale', 1)
spotlight.set_key_value_int('rendermode', 0)
spotlight.set_key_value_int('renderamt', 0)
spotlight.set_key_value_int('renderfx', 0)
spotlight.set_key_value_int(
'spotlightwidth', kwargs.get('width', SPOTLIGHT_DEFAULT_WIDTH))
spotlight.set_key_value_int(
'spotlightlength', kwargs.get('length', SPOTLIGHT_DEFAULT_LENGTH))
# Make sure the 'point_spotlight' spawns turned on.
spotlight.spawn_flags = 1
spotlight.spawn()
return spotlight
def create_item_spotlight(classname, index, origin):
# Is there a color set for this item?
if classname in item_colors:
spotlight = create_spotlight(origin, item_colors[classname])
# Store the 'point_spotlight' instance in a dictionary, but tie it to
# the index of the specified item.
spotlight_pairs[index] = spotlight
@OnEntityOutput
def on_entity_output(output, activator, caller, value, delay):
# Items (item_battery, item_healthkit) send out 'OnPlayerTouch' whenever
# they are applied to a player.
if output == 'OnPlayerTouch':
index = caller.index
# Is there a 'point_spotlight' for this item?
if index in spotlight_pairs:
spotlight = spotlight_pairs[index]
# Delay the removal by a single frame to avoid crashing the server.
spotlight.delay(0, spotlight.remove)
# Remove the Spotlight instance from the dictionary.
del spotlight_pairs[index]
@EntityPreHook(
EntityCondition.equals_entity_classname('item_battery'), 'materialize')
def item_materialize_pre(stack_data):
"""Called when an item (item_battery, item_healthkit) becomes enabled."""
base_entity = BaseEntity._obj(stack_data[0])
create_item_spotlight(
base_entity.classname, base_entity.index, base_entity.origin)
@OnEntitySpawned
def on_entity_spawned(base_entity):
"""Called when an entity is spawned."""
try:
index = base_entity.index
except ValueError:
return
create_item_spotlight(
base_entity.classname, base_entity.index, base_entity.origin)
@OnEntityDeleted
def on_entity_deleted(base_entity):
"""Called when an entity is removed."""
try:
index = base_entity.index
except ValueError:
return
# Does this entity have a 'point_spotlight' tied to it?
if index in spotlight_pairs:
spotlight_pairs[index].remove()
del spotlight_pairs[index]