CSGO : Adding Parachutes (from DZ) into DM

Please post any questions about developing your plugin here. Please use the search function before posting!
JustGR
Junior Member
Posts: 20
Joined: Tue Apr 28, 2020 12:39 pm

CSGO : Adding Parachutes (from DZ) into DM

Postby JustGR » Sun Jun 14, 2020 4:50 am

As title says, I would like to give all players a parachute for unlimited use in Deathmatch. This is just me wanting to mess with props and understand how they work, for the most part. I could probably get away with creating a 'prop_weapon_upgrade_chute' and having players pick them up, but was wondering if there was a more elegant solution to the problem.
User avatar
VinciT
Senior Member
Posts: 331
Joined: Thu Dec 18, 2014 2:41 am

Re: CSGO : Adding Parachutes (from DZ) into DM

Postby VinciT » Sun Jun 14, 2020 1:00 pm

I believe this should work:

Syntax: Select all

# ../chute/chute.py

# Source.Python
from engines.server import engine_server
from events import Event
from listeners import OnLevelInit
from players.dictionary import PlayerDictionary


parachute_models = (
'models/props_survival/parachute/chute.mdl',
'models/props_survival/upgrades/parachutepack.mdl',
'models/weapons/v_parachute.mdl'
)

player_instances = PlayerDictionary()


@OnLevelInit
def on_map_loaded(map_name):
"""Precaches parachute models when the map loads to avoid crashing the
server (with the following error: UTIL_SetModel: not precached)."""
for model in parachute_models:
engine_server.precache_model(model, preload=True)


@Event('player_spawn')
def player_spawn(event):
"""Gives the player a parachute when they spawn."""
player = player_instances.from_userid(event['userid'])
player.give_named_item('parachute')
Also a note in case you want to add in more Danger Zone items/weapons to non-DZ gamemodes: always precache the models for said items/weapons, otherwise you'll crash the server.
ImageImageImageImageImage
JustGR
Junior Member
Posts: 20
Joined: Tue Apr 28, 2020 12:39 pm

Re: CSGO : Adding Parachutes (from DZ) into DM

Postby JustGR » Sun Jun 14, 2020 9:33 pm

Tried it out. Would it be possible to disengage the parachute (like BOTW's paraglider), or stop ourselves from gliding down to places we aren't supposed to be (death pits for instance)?
User avatar
VinciT
Senior Member
Posts: 331
Joined: Thu Dec 18, 2014 2:41 am

Re: CSGO : Adding Parachutes (from DZ) into DM

Postby VinciT » Mon Jun 15, 2020 4:46 am

Try this for disengaging/cutting the parachute:

Syntax: Select all

# ../chute/chute.py

# Source.Python
from commands.client import ClientCommand
from engines.server import engine_server
from entities.constants import WORLD_ENTITY_INDEX
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 OnLevelInit, OnEntitySpawned, OnEntityDeleted
from players.dictionary import PlayerDictionary
from players.entity import Player


parachute_models = (
'models/props_survival/parachute/chute.mdl',
'models/props_survival/upgrades/parachutepack.mdl',
'models/weapons/v_parachute.mdl'
)

# Dictionary for holding references of parachutes and their users (players).
parachutes = {}


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

def __init__(self, index, caching=True):
super().__init__(index, caching)

self.cutting_parachute = False
self.has_parachute = False

def cut_parachute(self):
# Is the player currently parachuting?
if self.has_parachute:
self.cutting_parachute = True
self.has_parachute = False


player_instances = PlayerDictionary(PlayerC)


@EntityPreHook(EntityCondition.is_player, 'post_think')
def post_think_pre(stack_data):
player = player_instances[index_from_pointer(stack_data[0])]

# Bots don't need this, so skip them.
if player.is_bot():
return

# Is the player trying to get rid of their parachute?
if player.cutting_parachute:
# Trick the player into thinking they've landed.
player.ground_entity = Entity(WORLD_ENTITY_INDEX).inthandle
player.cutting_parachute = False


@OnLevelInit
def on_map_loaded(map_name):
"""Precaches parachute models when the map loads to avoid crashing the
server (with the following error: UTIL_SetModel: not precached)."""
for model in parachute_models:
engine_server.precache_model(model, preload=True)


@Event('player_spawn')
def player_spawn(event):
"""Gives the player a parachute when they spawn."""
player = player_instances.from_userid(event['userid'])
player.give_named_item('parachute')


@ClientCommand('+lookatweapon')
def player_inspect(command, index):
player_instances[index].cut_parachute()


@OnEntitySpawned
def on_entity_spawned(base_entity):
try:
index = base_entity.index
except ValueError:
return

# The parachute that's attached to the player is actually a 'prop_dynamic'
# entity. Let's check if any of them spawned.
if base_entity.classname == 'prop_dynamic':
entity = Entity(index)
# When an entity spawns, not all properties are setup right away, so we
# delay our property based checks for a single frame.
entity.delay(0, late_check, (entity,))


def late_check(entity):
# Is this a parachute? (models/props_survival/parachute/chute.mdl)
if 'parachute/chute.mdl' in entity.model.path:
# Get the player that the 'prop_dynamic' is tied to.
player = player_instances[entity.parent.index]
player.has_parachute = True
# Make a reference between the parachute and the player. We'll use this
# later when the parachute gets removed.
parachutes[entity.index] = player.index


@OnEntityDeleted
def on_entity_deleted(base_entity):
try:
index = base_entity.index
except ValueError:
return

# Check if the entity that's about to be removed is a parachute.
try:
# Notify the player that they've lost their parachute.
player_instances[parachutes.pop(index)].has_parachute = False
except KeyError:
# Nope, not a parachute. Moving on..
pass
The way it's setup - once the player presses their inspect key, if they have an active parachute, they'll get rid of it.
JustGR wrote:..stop ourselves from gliding down to places we aren't supposed to be (death pits for instance)?
Could you clarify this a bit? Do you mean some kind of a push upwards? Like a gust of wind?
ImageImageImageImageImage
JustGR
Junior Member
Posts: 20
Joined: Tue Apr 28, 2020 12:39 pm

Re: CSGO : Adding Parachutes (from DZ) into DM

Postby JustGR » Mon Jun 15, 2020 8:31 am

VinciT wrote:
JustGR wrote:..stop ourselves from gliding down to places we aren't supposed to be (death pits for instance)?
Could you clarify this a bit? Do you mean some kind of a push upwards? Like a gust of wind?


Well, "Revali's Gale" would be nice, but would break the CS:GO spirit so hard it's not even funny :tongue:
Was thinking about the case where it would be possible to glide all the way down to death pits. In my eyes, the best fix for this would be a "stamina meter", but I'm open to suggestions.
User avatar
VinciT
Senior Member
Posts: 331
Joined: Thu Dec 18, 2014 2:41 am

Re: CSGO : Adding Parachutes (from DZ) into DM

Postby VinciT » Tue Jun 16, 2020 12:07 am

Haha, I guess I was thinking about pushing players away from the deathpits. Your way seems more reasonable, here you go:

Syntax: Select all

# ../chute/chute.py

# Source.Python
from commands.client import ClientCommand
from cvars import ConVar
from engines.server import engine_server
from entities.constants import WORLD_ENTITY_INDEX
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 OnLevelInit, OnEntitySpawned, OnEntityDeleted
from messages import HintText
from players.dictionary import PlayerDictionary
from players.entity import Player


# Time (in seconds) until the parachute automatically gets cut.
MAX_PARACHUTE_STAMINA = 3.0
# How quickly should the player regain their parachute stamina compared to
# losing it?
STAMINA_REGEN_MULTIPLIER = 1.75
# Should the player be able to re-open their parachute while they have stamina?
REUSABLE_PARACHUTE = True

# Find and store the 'sv_dz_parachute_reuse' convar.
CVAR_PARACHUTE_REUSE = ConVar('sv_dz_parachute_reuse')


MESSAGE_NEW_PARACHUTE = HintText('Parachute ready')
MESSAGE_LIMIT_REACHED = HintText('Out of stamina')


parachute_models = (
'models/props_survival/parachute/chute.mdl',
'models/props_survival/upgrades/parachutepack.mdl',
'models/weapons/v_parachute.mdl'
)

# Dictionary for holding references of parachutes and their users (players).
parachutes = {}


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

def __init__(self, index, caching=True):
super().__init__(index, caching)

self.cutting_parachute = False
self.has_parachute = False
self.parachute_stamina = MAX_PARACHUTE_STAMINA
self.stamina_think = self.repeat(self._stamina_think)

def cut_parachute(self):
# Is the player currently parachuting?
if self.has_parachute:
self.cutting_parachute = True
self.has_parachute = False

def on_started_parachuting(self):
"""Called when the player opens their parachute."""
self.has_parachute = True
# Start calling the `_stamina_think()` function once every 250ms.
self.stamina_think.start(0.25)

def on_stopped_parachuting(self):
"""Called when the player loses their parachute."""
self.has_parachute = False

if REUSABLE_PARACHUTE and self.parachute_stamina > 0:
# Give the player a new parachute after a single frame delay.
# NOTE: Without the delay, this wouldn't work.
self.delay(0, self.give_parachute)

def give_parachute(self):
"""Gives the player a parachute."""
self.give_named_item('parachute')
MESSAGE_NEW_PARACHUTE.send(self.index)

def _stamina_think(self):
# Is the player currently parachuting?
if self.has_parachute:
# Has the player used up all of their parachute stamina?
if self.parachute_stamina <= 0:
self.parachute_stamina = 0
self.cut_parachute()
MESSAGE_LIMIT_REACHED.send(self.index)
return

# Reduce their stamina.
self.parachute_stamina -= 0.25
else:
# Has the player regained all of their parachute stamina?
if self.parachute_stamina >= MAX_PARACHUTE_STAMINA:
# Cap their stamina.
self.parachute_stamina = MAX_PARACHUTE_STAMINA
# Stop the loop.
self.stamina_think.stop()
self.give_parachute()
return

# Is the player on the ground?
if self.ground_entity > 0:
# Give them back some stamina.
self.parachute_stamina += 0.25 * STAMINA_REGEN_MULTIPLIER


player_instances = PlayerDictionary(PlayerC)


@EntityPreHook(EntityCondition.is_player, 'post_think')
def post_think_pre(stack_data):
player = player_instances[index_from_pointer(stack_data[0])]

# Bots don't need this, so skip them.
if player.is_bot():
return

# Is the player trying to get rid of their parachute?
if player.cutting_parachute:
# Trick the player into thinking they've landed.
player.ground_entity = Entity(WORLD_ENTITY_INDEX).inthandle
player.cutting_parachute = False


@OnLevelInit
def on_map_loaded(map_name):
"""Precaches parachute models when the map loads to avoid crashing the
server (with the following error: UTIL_SetModel: not precached)."""
for model in parachute_models:
engine_server.precache_model(model, preload=True)

# Don't notify the players when we change the convar.
CVAR_PARACHUTE_REUSE.remove_public()
# Change 'sv_dz_parachute_reuse' to 0.
CVAR_PARACHUTE_REUSE.set_int(0)


@Event('player_spawn')
def player_spawn(event):
"""Gives the player a parachute when they spawn."""
player_instances.from_userid(event['userid']).give_parachute()


@ClientCommand('+lookatweapon')
def player_inspect(command, index):
player_instances[index].cut_parachute()


@OnEntitySpawned
def on_entity_spawned(base_entity):
try:
index = base_entity.index
except ValueError:
return

# The parachute that's attached to the player is actually a 'prop_dynamic'
# entity. Let's check if any of them spawned.
if base_entity.classname == 'prop_dynamic':
entity = Entity(index)
# When an entity spawns, not all properties are setup right away, so we
# delay our property based checks for a single frame.
entity.delay(0, late_check, (entity,))


def late_check(entity):
# Is this a parachute? (models/props_survival/parachute/chute.mdl)
if 'parachute/chute.mdl' in entity.model.path:
# Get the player that the 'prop_dynamic' is tied to.
player = player_instances[entity.parent.index]
player.on_started_parachuting()
# Make a reference between the parachute and the player. We'll use this
# later when the parachute gets removed.
parachutes[entity.index] = player.index


@OnEntityDeleted
def on_entity_deleted(base_entity):
try:
index = base_entity.index
except ValueError:
return

# Check if the entity that's about to be removed is a parachute.
try:
# Notify the player that they've lost their parachute.
player_instances[parachutes.pop(index)].on_stopped_parachuting()
except KeyError:
# Nope, not a parachute. Moving on..
pass
ImageImageImageImageImage

Return to “Plugin Development Support”

Who is online

Users browsing this forum: No registered users and 29 guests