


Syntax: Select all
# ../fiery_bolts/fiery_bolts.py
# Python
import random
# Source.Python
from engines.precache import Model
from engines.sound import Sound
from entities.constants import (EntityStates, RenderMode, RenderEffects,
MoveType, WORLD_ENTITY_INDEX)
from entities.dictionary import EntityDictionary
from entities.entity import Entity
from entities.helpers import index_from_pointer
from entities.hooks import EntityPreHook, EntityCondition
from events import Event
from listeners import OnNetworkedEntityCreated
from players.entity import Player
from stringtables import string_tables
# Material/sprite used for the trail.
TRAIL_MODEL = Model('materials/sprites/laser.vmt')
class Bolts(EntityDictionary):
"""Modified EntityDictionary class for storing Bolt instances."""
def on_automatically_removed(self, index):
"""Called when an index is automatically removed."""
self[index].detach_effects()
class BoltEffects(EntityDictionary):
"""Class used for cleaning up after the particle and trail effects."""
def on_automatically_removed(self, index):
"""Called when an index is automatically removed."""
effect = self[index]
try:
# Try to get the Bolt instance the effect was parented to.
bolt = bolts.from_inthandle(effect.parent_inthandle)
except (KeyError, OverflowError):
# KeyError: Bolt instance no longer exists.
# OverflowError: Invalid parent_inthandle (-1).
return
# Remove the reference to the removed effect from the Bolt instance.
setattr(
object=bolt,
name='flame' if 'particle' in effect.classname else 'trail',
value=None
)
# Dictionary used to store Bolt instances.
bolts = Bolts(None)
# Dictionary used to store Entity instances for 'info_particle_system' and
# 'env_sprite_trail' entities.
bolt_effects = BoltEffects(None)
is_crossbow_bolt = EntityCondition.equals_entity_classname('crossbow_bolt')
@Event('player_death')
def player_death(event):
"""Called when a player dies."""
player = Player.from_userid(event['userid'])
# Was the player on fire prior to dying?
if player.get_property_int('m_fFlags') & EntityStates.ONFIRE:
# Extinguish them.
player.ignite_lifetime(0)
@OnNetworkedEntityCreated
def on_networked_entity_created(entity):
"""Called when a networked entity gets created."""
if 'crossbow_bolt' not in entity.classname:
return
# Create a Bolt instance.
bolt = Bolt(entity.index)
# Some properties are still uninitialized - delay the creation of the
# flame effect for a single frame.
bolt.delay(0, bolt.flame_on)
@EntityPreHook(is_crossbow_bolt, 'start_touch')
def start_touch_pre(stack_data):
try:
bolt = bolts[index_from_pointer(stack_data[0])]
except KeyError:
return
index = index_from_pointer(stack_data[1])
# Did the bolt hit the world?
if index == WORLD_ENTITY_INDEX:
return
try:
# Try to ignite the entity that was hit.
Entity(index).ignite()
except AttributeError:
pass
class Bolt(Entity):
"""Extended Entity class for manipulating the 'crossbow_bolt' entity.
Args:
index (int): A valid Entity index.
caching (bool): Check for a cached instance?
Attributes:
flame (Entity): Instance of the 'info_particle_system' entity.
trail (Entity): Instance of the 'env_sprite_trail' entity.
sound (Sound): Looping sound used for the projectile.
"""
# Alternative particle effects:
# burning_engine_fire
# burning_gib_01
# burning_gib_01_follower1
# burning_gib_01_follower2
# fire_medium_02_nosmoke
# fire_small_02
# fire_small_03
effect_name = 'fire_small_01'
# Colors used for the bolt trail.
trail_colors = ('255 162 2', '255 95 12', '255 129 33', '255 100 0')
def __init__(self, index, caching=True):
"""Initializes the object."""
super().__init__(index, caching)
self.flame = None
self.trail = None
self.sound = Sound(
sample=f'ambient/fire/fire_small_loop{random.randint(1, 2)}.wav',
index=index,
attenuation=0.85
)
# Add the Bolt instance to the dictionary.
bolts[index] = self
def flame_on(self):
"""Creates the fire particle effect, as well as the sprite trail."""
self.flame = Entity.create('info_particle_system')
self.flame.effect_name = Bolt.effect_name
self.flame.effect_index = string_tables.ParticleEffectNames.add_string(
Bolt.effect_name)
self.flame.origin = self.origin
self.flame.set_parent(self)
self.flame.start()
# Save the 'info_particle_system' instance in a dictionary.
bolt_effects[self.flame.index] = self.flame
self.trail = create_sprite_trail(
origin=self.origin,
sprite_path=TRAIL_MODEL.path,
start_width=15,
end_width=5,
color_str=random.choice(Bolt.trail_colors),
life_time=1
)
self.trail.set_parent(self)
# Save the 'env_sprite_trail' instance in a dictionary.
bolt_effects[self.trail.index] = self.trail
self.sound.play()
def detach_effects(self):
"""Detaches the particle effect and trail from the bolt."""
# Save the last known position of the bolt.
last_origin = self.origin
try:
# Try to detach the sprite trail.
self.trail.clear_parent()
self.trail.teleport(origin=last_origin)
# Freeze it in place.
self.trail.move_type = MoveType.NONE
# Remove it after a short delay.
self.trail.delay(1.5, self.trail.remove)
except AttributeError:
pass
try:
# Try to detach the particle effect.
self.flame.clear_parent()
self.flame.teleport(origin=last_origin)
self.flame.move_type = MoveType.NONE
self.flame.delay(1.2, self.flame.remove)
except AttributeError:
pass
# Stop the looping sound.
self.sound.stop()
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
VinciT wrote:This should do the trick:Syntax: Select all
# Dictionary used to store Bolt instances.
bolts = {}
@OnEntityDeleted
def on_entity_deleted(base_entity):
"""Called when an entity gets deleted."""
try:
index = base_entity.index
except ValueError:
return
try:
# Unparent the particle effect and trail before the 'crossbow_bolt'
# gets removed to avoid them bugging out.
bolts.pop(index).detach_effects()
except KeyError:
pass
Syntax: Select all
class Bolts(EntityDictionary):
def on_automatically_removed(self, index):
self[index].detach_effects()
bolts = Bolts()
VinciT wrote:This should do the trick:Syntax: Select all
# ../fiery_bolts/fiery_bolts.py
# Python
import random
# Source.Python
from engines.precache import Model
from engines.sound import Sound
from entities.constants import (RenderMode, RenderEffects, MoveType,
WORLD_ENTITY_INDEX)
from entities.entity import Entity
from entities.helpers import index_from_pointer
from entities.hooks import EntityPreHook, EntityCondition
from listeners import OnEntityCreated, OnEntityDeleted
from stringtables import string_tables
# Material/sprite used for the trail.
TRAIL_MODEL = Model('materials/sprites/laser.vmt')
# Dictionary used to store Bolt instances.
bolts = {}
is_crossbow_bolt = EntityCondition.equals_entity_classname('crossbow_bolt')
@OnEntityCreated
def on_entity_created(base_entity):
"""Called when an entity gets created."""
try:
index = base_entity.index
except ValueError:
return
if 'crossbow_bolt' in base_entity.classname:
# Create a Bolt instance.
bolt = Bolt(index)
# Some properties are still uninitialized - delay the creation of the
# flame effect for a single frame.
bolt.delay(0, bolt.flame_on)
@OnEntityDeleted
def on_entity_deleted(base_entity):
"""Called when an entity gets deleted."""
try:
index = base_entity.index
except ValueError:
return
try:
# Unparent the particle effect and trail before the 'crossbow_bolt'
# gets removed to avoid them bugging out.
bolts.pop(index).detach_effects()
except KeyError:
pass
@EntityPreHook(is_crossbow_bolt, 'start_touch')
def start_touch_pre(stack_data):
try:
bolt = bolts[index_from_pointer(stack_data[0])]
except KeyError:
return
index = index_from_pointer(stack_data[1])
# Did the bolt hit the world?
if index == WORLD_ENTITY_INDEX:
return
# Ignite the entity that was hit.
Entity(index).ignite()
bolt.detach_effects()
class Bolt(Entity):
"""Extended Entity class for manipulating the 'crossbow_bolt' entity.
Args:
index (int): A valid Entity index.
caching (bool): Check for a cached instance?
Attributes:
flame (Entity): Instance of the 'info_particle_system' entity.
trail (Entity): Instance of the 'env_sprite_trail' entity.
sound (Sound): Looping sound used for the projectile.
"""
# Alternative particle effects:
# burning_engine_fire
# burning_gib_01
# burning_gib_01_follower1
# burning_gib_01_follower2
# fire_medium_02_nosmoke
# fire_small_02
# fire_small_03
effect_name = 'fire_small_01'
# Colors used for the bolt trail.
trail_colors = ('255 162 2', '255 95 12', '255 129 33', '255 100 0')
def __init__(self, index, caching=True):
"""Initializes the object."""
super().__init__(index, caching)
self.flame = None
self.trail = None
self.sound = Sound(
sample=f'ambient/fire/fire_small_loop{random.randint(1, 2)}.wav',
index=index,
attenuation=0.85
)
# Add the Bolt instance to the dictionary.
bolts[index] = self
def flame_on(self):
"""Creates the fire particle effect, as well as the sprite trail."""
particle = Entity.create('info_particle_system')
particle.effect_name = Bolt.effect_name
particle.effect_index = string_tables.ParticleEffectNames.add_string(
Bolt.effect_name)
particle.origin = self.origin
particle.set_parent(self)
particle.start()
self.trail = create_sprite_trail(
origin=self.origin,
sprite_path=TRAIL_MODEL.path,
start_width=15,
end_width=5,
color_str=random.choice(Bolt.trail_colors),
life_time=1
)
self.trail.set_parent(self)
self.sound.play()
def detach_effects(self):
"""Detaches the particle effect and trail from the bolt."""
# Save the last known position of the bolt.
last_origin = self.origin
try:
# Try to detach the sprite trail.
self.trail.clear_parent()
self.trail.teleport(origin=last_origin)
# Freeze it in place.
self.trail.move_type = MoveType.NONE
# Remove it after a short delay.
self.trail.delay(1.5, self.trail.remove)
except AttributeError:
pass
try:
# Try to detach the particle effect.
self.flame.clear_parent()
self.flame.teleport(origin=last_origin)
self.flame.move_type = MoveType.NONE
self.flame.delay(1.2, self.flame.remove)
except AttributeError:
pass
# Stop the looping sound.
self.sound.stop()
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
Code: Select all
2020-09-22 10:44:25 - sp - EXCEPTION
[SP] Caught an Exception:
Traceback (most recent call last):
File "..\addons\source-python\plugins\fiery_bolts\fiery_bolts.py", line 73, in start_touch_pre
Entity(index).ignite()
File "..\addons\source-python\packages\source-python\entities\_base.py", line 231, in __getattr__
raise AttributeError('Attribute "{0}" not found'.format(attr))
AttributeError: Attribute "ignite" not found
Glad it works for you! I've updated the code in my previous post to fix the error you got.daren adler wrote:Thank you. I will give it a try tomarow, again thank you for all your help. =UPDATE= Works great![]()
You do a very good job
I wholeheartedly agree, I wanted to use that at first, but this part of the plugin made me go with the default dictionary:L'In20Cible wrote:Sounds like a perfect situation to use an EntityDictionary.on_automatically_removed callback.
Syntax: Select all
@EntityPreHook(is_crossbow_bolt, 'start_touch')
def start_touch_pre(stack_data):
try:
bolt = bolts[index_from_pointer(stack_data[0])]
except KeyError:
return
VinciT wrote:Glad it works for you! I've updated the code in my previous post to fix the error you got.daren adler wrote:Thank you. I will give it a try tomarow, again thank you for all your help. =UPDATE= Works great![]()
You do a very good job
I wholeheartedly agree, I wanted to use that at first, but this part of the plugin made me go with the default dictionary:L'In20Cible wrote:Sounds like a perfect situation to use an EntityDictionary.on_automatically_removed callback.I guess I could replace the try/except with an if and retrieve the instance later.. Do you think the performance would be any different?Syntax: Select all
@EntityPreHook(is_crossbow_bolt, 'start_touch')
def start_touch_pre(stack_data):
try:
bolt = bolts[index_from_pointer(stack_data[0])]
except KeyError:
return
VinciT wrote:daren adler wrote:I wholeheartedly agree, I wanted to use that at first, but this part of the plugin made me go with the default dictionary:I guess I could replace the try/except with an if and retrieve the instance later.. Do you think the performance would be any different?Syntax: Select all
@EntityPreHook(is_crossbow_bolt, 'start_touch')
def start_touch_pre(stack_data):
try:
bolt = bolts[index_from_pointer(stack_data[0])]
except KeyError:
return
Syntax: Select all
class Bolts(EntityDictionary):
def __missing__(self, index):
raise KeyError
def on_automatically_removed(self, index):
self[index].detach_effects()
bolts = Bolts()
Syntax: Select all
class Bolts(EntityDictionary):
def on_automatically_removed(self, index):
self[index].detach_effects()
bolts = Bolts(None)
try:
...
except TypeError:
return
Syntax: Select all
def __missing__(self, index):
"""Add and return the entity instance for the given index."""
# Get the factory
factory = self._factory
# Let's mimic dict's behaviour if the factory is set to None
if factory is None:
raise KeyError(index)
# For uniformity reasons, ensure we only raise a KeyError
try:
instance = factory(index, *self._args, **self._kwargs)
except Exception as e:
raise KeyError(str(e))
# Only cache entities that are not marked for deletion.
# This is required, because if someone request an entity instance
# after we invalidated our cache but before the engine processed
# the deletion we would now have an invalid instance in the cache.
if not instance.is_marked_for_deletion():
self[index] = instance
return instance
Code: Select all
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'alpha_props'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'alpha_props'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'fiery_bolts'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'fiery_bolts'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'npc_points'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'npc_points'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'sawblade_trail'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'sawblade_trail'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'silent_hill'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'silent_hill'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'weapon_effects'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'weapon_effects'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'weapon_manger'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'weapon_manger'.
2020-09-25 19:10:26 - sp - EXCEPTION
[SP] Caught an Exception:
Traceback (most recent call last):
File "..\addons\source-python\plugins\fiery_bolts\fiery_bolts.py", line 55, in on_entity_deleted
bolts.pop(index).detach_effects()
File "..\addons\source-python\plugins\fiery_bolts\fiery_bolts.py", line 151, in detach_effects
self.trail.clear_parent()
File "..\addons\source-python\packages\source-python\entities\_base.py", line 216, in __getattr__
value = getattr(instance, attr)
File "..\addons\source-python\packages\source-python\entities\classes.py", line 542, in fget
return InputFunction(desc, make_object(BaseEntity, pointer))
RuntimeError: Access violation - no RTTI data!
daren adler wrote:Error stillCode: Select all
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'alpha_props'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'alpha_props'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'fiery_bolts'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'fiery_bolts'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'npc_points'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'npc_points'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'sawblade_trail'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'sawblade_trail'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'silent_hill'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'silent_hill'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'weapon_effects'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'weapon_effects'.
2020-09-25 19:05:46 - sp - MESSAGE [SP] Loading plugin 'weapon_manger'...
2020-09-25 19:05:46 - sp - MESSAGE [SP] Successfully loaded plugin 'weapon_manger'.
2020-09-25 19:10:26 - sp - EXCEPTION
[SP] Caught an Exception:
Traceback (most recent call last):
File "..\addons\source-python\plugins\fiery_bolts\fiery_bolts.py", line 55, in on_entity_deleted
bolts.pop(index).detach_effects()
File "..\addons\source-python\plugins\fiery_bolts\fiery_bolts.py", line 151, in detach_effects
self.trail.clear_parent()
File "..\addons\source-python\packages\source-python\entities\_base.py", line 216, in __getattr__
value = getattr(instance, attr)
File "..\addons\source-python\packages\source-python\entities\classes.py", line 542, in fget
return InputFunction(desc, make_object(BaseEntity, pointer))
RuntimeError: Access violation - no RTTI data!
I dont know what it is, i am thinking it might be the kill of a bot or the npc's. I just cant figer it out.
Syntax: Select all
except AttributeError:
Syntax: Select all
except (AttributeError, RuntimeError):
Man.. I'm so used to using the OnEntityDeleted listener, I completely forgot that I could do that. Thanks for the tip!L'In20Cible wrote:You could simply override __missing__ to disable the factory:
I'll add that in tomorrow.L'In20Cible wrote:Clearing it from its parent when it is removed would be the best option though.
VinciT wrote:Man.. I'm so used to using the OnEntityDeleted listener
Syntax: Select all
from entities.dictionary import EntityDictionary
ents = EntityDictionary(None)
ents[0]
# KeyError: 0
Syntax: Select all
from entities.dictionary import EntityDictionary
ents = EntityDictionary()
ents[123456789]
# KeyError: 'Conversion from "Index" (123456789) to "BaseEntity" failed.'
ents = EntityDictionary(lambda index: 1/0)
ents[0]
# KeyError: 'division by zero'
Awesome! Guess I'll go ahead and start using that straight away.L'In20Cible wrote:Side note, I made the changes to EntityDictionary so that the factory can be set to None to explicitly disable it:
I've updated the plugin in my original post to include L'In20Cible's suggestions, a fix for the particle effect (the instance wasn't being stored in self.flame), as well as a fix for the burning issue. Make sure to update to the latest Source.Python version - as the plugin requires the recent EntityDictionary changes.daren adler wrote:I did find out that if shot by arrow and you are still on fire before you die, then when you re-spawn, you are still on fire and kills you again and again, dont do it everytime, just 2 or 3 times a map. Anyway to fix that?
VinciT wrote:Awesome! Guess I'll go ahead and start using that straight away.![]()
VinciT wrote:Syntax: Select all
@OnEntityCreated
def on_entity_created(base_entity):
"""Called when an entity gets created."""
try:
index = base_entity.index
except ValueError:
return
if 'crossbow_bolt' in base_entity.classname:
# Create a Bolt instance.
bolt = Bolt(index)
# Some properties are still uninitialized - delay the creation of the
# flame effect for a single frame.
bolt.delay(0, bolt.flame_on)
Syntax: Select all
@OnNetworkedEntityCreated
def on_networked_entity_created(entity):
"""Called when a networked entity gets created."""
if 'crossbow_bolt' not in entity.classname:
return
# Create a Bolt instance.
bolt = Bolt(entity.index)
# Some properties are still uninitialized - delay the creation of the
# flame effect for a single frame.
bolt.delay(0, bolt.flame_on)
Syntax: Select all
from time import time
from entities.entity import BaseEntity
from entities.entity import Entity
from listeners import OnNetworkedEntityCreated
t = time()
for i in range(1000000):
index = BaseEntity(0).index
print(time() - t)
t = time()
for i in range(1000000):
index = Entity(0).index
print(time() - t)
"""
21.112711906433105
0.706554651260376
"""
Jeez.. look at those numbers! Amazing stuff L'In20Cible! Plugin has been supercharged in the original post.L'In20Cible wrote:Not only it is shorter, but faster due to the internal caching.![]()
VinciT wrote:Jeez.. look at those numbers! Amazing stuff L'In20Cible! Plugin has been supercharged in the original post.
Syntax: Select all
# Some properties are still uninitialized - delay the creation of the
# flame effect for a single frame.
bolt.delay(0, bolt.flame_on)
daren adler wrote:you scripters do a very good job
Just tested it - the OnEntitySpawned/OnNetworkedEntitySpawned listener doesn't even get called when a crossbow_bolt spawns. Looking back at some requests I've done, I'm pretty sure I've had to use the single frame delay even with the OnEntitySpawned listener, at least when I needed to modify certain properties.L'In20Cible wrote:Perhaps they are set when the entity actually spawns as opposed to being created?
A critic - sure, but not an annoying one. You've helped me improve both my programming and problem solving skills a ton. I'm very grateful for that, and I always look forward to your suggestions and pointers.L'In20Cible wrote:I deserve no credit here; I'm just an annoying critic.![]()
Code: Select all
2020-10-10 16:26:58 - sp - EXCEPTION
[SP] Caught an Exception:
Traceback (most recent call last):
File "..\addons\source-python\packages\source-python\entities\dictionary.py", line 135, in _on_networked_entity_deleted
self.on_automatically_removed(index)
File "..\addons\source-python\plugins\fiery_bolts\fiery_bolts.py", line 30, in on_automatically_removed
self[index].detach_effects()
File "..\addons\source-python\plugins\fiery_bolts\fiery_bolts.py", line 185, in detach_effects
self.trail.clear_parent()
File "..\addons\source-python\packages\source-python\entities\_base.py", line 216, in __getattr__
value = getattr(instance, attr)
File "..\addons\source-python\packages\source-python\entities\classes.py", line 542, in fget
return InputFunction(desc, make_object(BaseEntity, pointer))
Users browsing this forum: No registered users and 59 guests