HL2DM-cscanner spawned

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: 202
Joined: Sat May 18, 2019 7:42 pm

HL2DM-cscanner spawned

Postby daren adler » Fri Oct 09, 2020 8:37 pm

Hello scripters :grin: . Was wondering if someone could make a script for my hl2dm server, I would like to have a npc_scanner spawn at starting of each map and have health at 9999999, so it will fly around and bug players. :tongue: :tongue: Thank you and have a great weekend. :cool:
User avatar
VinciT
Senior Member
Posts: 295
Joined: Thu Dec 18, 2014 2:41 am

Re: HL2DM-cscanner spawned

Postby VinciT » Thu Oct 22, 2020 9:00 pm

Hi daren, took a while but I managed to get the scanner working:

Syntax: Select all

# ../temp_scanner/temp_scanner.py

# Python
from random import choice

# Source.Python
from core import PLATFORM
from core.cache import cached_property
from cvars import ConVar
from engines.trace import (ContentMasks, engine_trace, GameTrace, Ray,
MAX_TRACE_LENGTH)
from entities.constants import WORLD_ENTITY_INDEX, RenderEffects
from entities.entity import Entity
from entities.helpers import index_from_pointer
from entities.hooks import EntityPreHook, EntityCondition
from events import Event
from filters.entities import BaseEntityIter
from filters.players import PlayerIter
from listeners import OnLevelInit
from listeners.tick import Delay
from mathlib import Vector
from memory import Convention, DataType
from memory.helpers import MemberFunction
from memory.manager import TypeManager


# How many scanners should there be?
MAX_SCANNERS = 1
# How often should the scanner look for a new target? (in seconds)
TARGET_INTERVAL = 30


UP = Vector(0, 0, 1)

manager = TypeManager()
ACTIVATE_OFFSET = 33 if PLATFORM == 'windows' else 34

is_cscanner = EntityCondition.equals_entity_classname('npc_cscanner')


# =============================================================================
# >> EVENTS AND LISTENERS
# =============================================================================
def load():
"""Called when the plugin gets loaded."""
Scanner.spawn_health.set_int(100)

try:
# Try to spawn the scanners in case the plugin was loaded on the fly.
Scanner.spawn_scanners(MAX_SCANNERS)
except IndexError:
pass


def unload():
"""Called when the plugin gets unloaded."""
for scanner in Scanner.cache.copy().values():
scanner.remove()


@OnLevelInit
def on_level_init(map_name):
"""Called when a map starts."""
Delay(5, Scanner.spawn_scanners, (MAX_SCANNERS,), cancel_on_level_end=True)


@Event('round_start')
def round_start(event):
"""Called when a new round starts."""
Scanner.spawn_scanners(MAX_SCANNERS)


@Event('player_death')
@Event('player_disconnect')
def player_state_changed(event):
"""Called when a player dies or leaves the server."""
# Notify all the scanners that a player has just died.
for scanner in Scanner.cache.values():
scanner.on_player_state_changed(event['userid'])


# =============================================================================
# >> ON TAKE DAMAGE ALIVE
# =============================================================================
@EntityPreHook(is_cscanner, 'on_take_damage_alive')
def on_take_damage_alive_pre(stack_data):
"""Called when an 'npc_cscanner' is about to take damage."""
# Is this one of our scanners?
if index_from_pointer(stack_data[0]) in Scanner.cache:
# Block all damage.
return False


# =============================================================================
# >> SCANNER
# =============================================================================
class Scanner(Entity):
"""Extended Entity class.

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

Attributes:
target_userid (int): Userid of the player we're currently following.
think (Repeat): Instance of Repeat() used for looping the `_think()`
function.
"""

spawn_health = ConVar('sk_scanner_health')

def __init__(self, index, caching=True):
"""Initializes the object."""
super().__init__(index, caching)
self.target_userid = None
self.think = self.repeat(self._think)

def _think(self):
player = self.get_random_player()

try:
# Try to store the player's userid for later use.
self.target_userid = player.userid
except AttributeError:
# Didn't find a player, try again in 5 seconds.
self.delay(5, self.restart_thinking)
return

# Is this player missing a 'target_name'?
if not player.target_name:
# Give them one.
player.target_name = f'player_{player.userid}'

self.call_input('SetFollowTarget', player.target_name)

@staticmethod
def spawn_scanners(number):
for i in range(number):
Scanner.create(origin=get_random_origin())

@classmethod
def create(cls, origin):
"""Creates the 'npc_cscanner' at the specified origin."""
scanner = super().create('npc_cscanner')
scanner.origin = origin
# Make the NPC fully transparent.
scanner.render_amt = 1
# Slowly fade it into existence.
scanner.render_fx = RenderEffects.SOLID_SLOW
scanner.spawn()
# If we don't activate the Scanner, the server will crash if it tries
# to inspect/photograph a player.
scanner.activate()
scanner.become_hostile()
# It's time for the Scanner to start thinking..
scanner.delay(1, scanner.restart_thinking)
return scanner

@cached_property
def activate(self):
"""Activates the entity."""
return MemberFunction(
manager,
DataType.VOID,
self.make_virtual_function(
ACTIVATE_OFFSET,
Convention.THISCALL,
(DataType.POINTER,),
DataType.VOID
),
self
)

def become_hostile(self):
"""Makes the NPC hate the players."""
self.call_input('SetRelationship', 'player D_HT 99')

def get_random_player(self):
"""Returns a randomly chosen player that is currently alive."""
players = []

for player in PlayerIter('alive'):
players.append(player)

# Did we not find any players?
if not players:
return None

return choice(players)

def restart_thinking(self):
"""Restarts the Scanner's thinking loop."""
self.think.stop()
self.think.start(interval=TARGET_INTERVAL, execute_on_start=True)

def on_player_state_changed(self, userid):
"""Called when a player dies or disconnects."""
# Was the Scanner following this player?
if userid == self.target_userid:
# Pick a new target.
self.delay(1, self.restart_thinking)


# =============================================================================
# >> HELPERS
# =============================================================================
def get_random_origin():
"""Returns a randomly chosen origin based on player spawnpoints."""
origins = []

# Go through all the 'info_player_*' entities.
# (combine, deathmatch, rebel, start).
for base_entity in BaseEntityIter(('info_player_',), False):
origins.append(base_entity.origin)

# Pick a random origin.
origin = choice(origins)
# Calculate the highest possible position.
ceiling = origin + UP * MAX_TRACE_LENGTH

# Let's fire a GameTrace() and see what we hit.
trace = GameTrace()
engine_trace.clip_ray_to_entity(
Ray(origin, ceiling),
ContentMasks.ALL,
Entity(WORLD_ENTITY_INDEX),
trace
)

# If the GameTrace() hit something, return that position, otherwise return
# the regular origin.
return trace.end_position if trace.did_hit() else origin
Server crashes kinda got in the way. :tongue:

On another note, I'm working on a special type of scanner (something akin to a boss) with some pretty powerful abilities, and if everything goes well, you can expect to play around with it in a couple of days. In the meantime, here are some teasers:
Image Image
Image
Last edited by VinciT on Fri Oct 23, 2020 5:33 pm, edited 1 time in total.
ImageImageImageImageImage
User avatar
PEACE
Member
Posts: 50
Joined: Mon Oct 12, 2020 1:13 pm

Re: HL2DM-cscanner spawned

Postby PEACE » Thu Oct 22, 2020 10:09 pm

Wow wonder if he saw this i have him on Steam so ill let him know , you do some amazing work VinciT good going
User avatar
daren adler
Senior Member
Posts: 202
Joined: Sat May 18, 2019 7:42 pm

Re: HL2DM-cscanner spawned

Postby daren adler » Thu Oct 22, 2020 10:13 pm

:cool: Thank you so much :cool: I will give it a try :grin: :grin: UPDATE-
Gave me this error

Code: Select all

2020-10-22 17:19:19 - sp   -   MESSAGE   [SP] Loading plugin 'temp_scanner'...
2020-10-22 17:19:19 - sp   -   EXCEPTION   
[SP] Caught an Exception:
Traceback (most recent call last):
  File "..\addons\source-python\packages\source-python\plugins\command.py", line 164, in load_plugin
    plugin = self.manager.load(plugin_name)
  File "..\addons\source-python\packages\source-python\plugins\manager.py", line 194, in load
    plugin._load()
  File "..\addons\source-python\packages\source-python\plugins\instance.py", line 76, in _load
    self.module.load()
  File "..\addons\source-python\plugins\temp_scanner\temp_scanner.py", line 41, in load
    Scanner.spawn_scanners(MAX_SCANNERS)
  File "..\addons\source-python\plugins\temp_scanner\temp_scanner.py", line 126, in spawn_scanners
    Scanner.create(origin=get_random_origin())
  File "..\addons\source-python\plugins\temp_scanner\temp_scanner.py", line 200, in get_random_origin
    origin = choice(origins)
  File "..\addons\source-python\Python3\random.py", line 257, in choice
    raise IndexError('Cannot choose from an empty sequence') from None

IndexError: Cannot choose from an empty sequence
User avatar
L'In20Cible
Project Leader
Posts: 1414
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: HL2DM-cscanner spawned

Postby L'In20Cible » Fri Oct 23, 2020 12:15 am

VinciT wrote:...


Looks good! Here some points to consider:

VinciT wrote:

Syntax: Select all

if index_from_pointer(stack_data[0]) in Scanner.cache:
Is it guaranteed to only be called for networked entities? Not fully sure, but I don't think damage event are limited to them, which would raise here in such case. A try/except may be safer.

VinciT wrote:

Syntax: Select all

scanner.set_key_value_int('renderfx', 7)
Would probably be better to use RenderEffects instead of hard-coded value.

VinciT wrote:

Syntax: Select all

def activate(self):
"""Activates the entity."""
Activate = self.make_virtual_function(
ACTIVATE_OFFSET,
Convention.THISCALL,
(DataType.POINTER,),
DataType.VOID
)

Activate(self)
Not that it really matter here since you call it only once anyways, but I think it is generally better to cache dynamic functions where possible because making them on-the-fly can prove to be extremely costly in comparison. For example:

Syntax: Select all

@cached_property
def activate(self):
"""Activates the entity."""
return MemberFunction(
manager,
DataType.VOID,
self.make_virtual_function(
ACTIVATE_OFFSET,
Convention.THISCALL,
(DataType.POINTER,),
DataType.VOID
),
self
)


VinciT wrote:

Syntax: Select all

self.restart_thinking()
Unless I missed something obvious, a new target will not be selected if the current one is leaving the server while being alive because on_player_death will never be called.
User avatar
VinciT
Senior Member
Posts: 295
Joined: Thu Dec 18, 2014 2:41 am

Re: HL2DM-cscanner spawned

Postby VinciT » Fri Oct 23, 2020 5:38 pm

L'In20Cible wrote:Is it guaranteed to only be called for networked entities? Not fully sure, but I don't think damage event are limited to them, which would raise here in such case. A try/except may be safer.
Since CNPC_CScanner has its own OnTakeDamage_Alive() function, I believe the hook will only be limited to npc_cscanner entities. Would there still be a reason to use a try/except here?
L'In20Cible wrote:Would probably be better to use RenderEffects instead of hard-coded value.
Agreed and done!
L'In20Cible wrote:Not that it really matter here since you call it only once anyways, but I think it is generally better to cache dynamic functions where possible because making them on-the-fly can prove to be extremely costly in comparison. For example:

Syntax: Select all

@cached_property
def activate(self):
"""Activates the entity."""
return MemberFunction(
manager,
DataType.VOID,
self.make_virtual_function(
ACTIVATE_OFFSET,
Convention.THISCALL,
(DataType.POINTER,),
DataType.VOID
),
self
)
Ooooh that's awesome! Thank you for this!

PEACE wrote:Wow wonder if he saw this i have him on Steam so ill let him know , you do some amazing work VinciT good going
Thank you for the kind words, PEACE. Can't wait to get the boss scanner done so you guys can mess around with it. :grin:

daren adler wrote:Gave me this error

Code: Select all

2020-10-22 17:19:19 - sp   -   MESSAGE   [SP] Loading plugin 'temp_scanner'...
2020-10-22 17:19:19 - sp   -   EXCEPTION   
[SP] Caught an Exception:
Traceback (most recent call last):
  File "..\addons\source-python\packages\source-python\plugins\command.py", line 164, in load_plugin
    plugin = self.manager.load(plugin_name)
  File "..\addons\source-python\packages\source-python\plugins\manager.py", line 194, in load
    plugin._load()
  File "..\addons\source-python\packages\source-python\plugins\instance.py", line 76, in _load
    self.module.load()
  File "..\addons\source-python\plugins\temp_scanner\temp_scanner.py", line 41, in load
    Scanner.spawn_scanners(MAX_SCANNERS)
  File "..\addons\source-python\plugins\temp_scanner\temp_scanner.py", line 126, in spawn_scanners
    Scanner.create(origin=get_random_origin())
  File "..\addons\source-python\plugins\temp_scanner\temp_scanner.py", line 200, in get_random_origin
    origin = choice(origins)
  File "..\addons\source-python\Python3\random.py", line 257, in choice
    raise IndexError('Cannot choose from an empty sequence') from None

IndexError: Cannot choose from an empty sequence
Should be fixed with this update.
ImageImageImageImageImage
User avatar
L'In20Cible
Project Leader
Posts: 1414
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: HL2DM-cscanner spawned

Postby L'In20Cible » Fri Oct 23, 2020 6:21 pm

VinciT wrote:Since CNPC_CScanner has its own OnTakeDamage_Alive() function, I believe the hook will only be limited to npc_cscanner entities. Would there still be a reason to use a try/except here?
Then that should do. If they have their own override in their dispatch table they shouldn't be called by any other entity types.
User avatar
daren adler
Senior Member
Posts: 202
Joined: Sat May 18, 2019 7:42 pm

Re: HL2DM-cscanner spawned

Postby daren adler » Fri Oct 23, 2020 7:06 pm

Works :smile: , Thank you. You VinciT are very good at this sort of stuff :cool: , Thanks again, ive been wanting this for years, PEACE has somthing like that on his server and ive always wanted somthing like that and BOOM, you made it,,THANK YOU :grin: :grin:
User avatar
PEACE
Member
Posts: 50
Joined: Mon Oct 12, 2020 1:13 pm

Re: HL2DM-cscanner spawned

Postby PEACE » Mon Oct 26, 2020 2:52 am

Yes it does work well i have tested it on my server as well , but wonders if VinciT will make it work like in the vid clips so it attacks , that looks pretty mean . VinciT does good work so do many others in here and the fact they are willing to support HL2DM is so awesome of them . thanks guys ^5
User avatar
Painkiller
Senior Member
Posts: 642
Joined: Sun Mar 01, 2015 8:09 am
Location: Germany
Contact:

Re: HL2DM-cscanner spawned

Postby Painkiller » Mon Oct 26, 2020 8:43 am

I think the things on the pictures, he is still working on it, it is only a preview.
User avatar
VinciT
Senior Member
Posts: 295
Joined: Thu Dec 18, 2014 2:41 am

Re: HL2DM-cscanner spawned

Postby VinciT » Thu Oct 29, 2020 8:10 am

PEACE wrote:Yes it does work well i have tested it on my server as well , but wonders if VinciT will make it work like in the vid clips so it attacks , that looks pretty mean . VinciT does good work so do many others in here and the fact they are willing to support HL2DM is so awesome of them . thanks guys ^5
Painkiller wrote:I think the things on the pictures, he is still working on it, it is only a preview.
Painkiller is correct, the gifs I posted are just a preview of what I'm currently working on. The plan was to post the special scanner as a single file plugin in this thread, but as I kept adding more and more features, I decided to turn it into a full blown plugin.

Most of the abilities are done. And I've started working on the decision-making system for the scanner. As soon as it becomes somewhat playable - I'll upload the alpha version of the plugin. :smile:

So here's one last preview before the initial alpha release - the finished combine ball barrage ability:
Image
(the number of combine balls can be adjusted via the configuration file)
ImageImageImageImageImage

Return to “Plugin Requests”

Who is online

Users browsing this forum: No registered users and 3 guests