boiii-scripts/shared/weapons/_bouncingbetty.gsc
2023-04-13 17:30:38 +02:00

395 lines
20 KiB
Plaintext

#using scripts\codescripts\struct;
#using scripts\shared\callbacks_shared;
#using scripts\shared\challenges_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\scoreevents_shared;
#using scripts\shared\system_shared;
#using scripts\shared\util_shared;
#using scripts\shared\weapons\_weaponobjects;
#precache( "fx", "weapon/fx_betty_exp_destroyed" );
#precache( "fx", "weapon/fx_betty_light_blue" );
#precache( "fx", "weapon/fx_betty_light_orng" );
#precache( "model", "wpn_t7_grenade_incendiary_world" );
#using_animtree ( "bouncing_betty" );
#namespace bouncingbetty;
function init_shared()
{
level.bettyDestroyedFX = "weapon/fx_betty_exp_destroyed";
level._effect["fx_betty_friendly_light"] = "weapon/fx_betty_light_blue";
level._effect["fx_betty_enemy_light"] = "weapon/fx_betty_light_orng";
level.bettyMinDist = 20;
level.bettyStunTime = 1;
bettyExplodeAnim = %o_spider_mine_detonate;
bettyDeployAnim = %o_spider_mine_deploy;
level.bettyRadius = getDvarInt( "betty_detect_radius", 180 );
level.bettyActivationDelay = getDvarFloat( "betty_activation_delay", 1 );
level.bettyGracePeriod = getDvarFloat( "betty_grace_period", 0.0 );
level.bettyDamageRadius = getDvarInt( "betty_damage_radius", 180 );
level.bettyDamageMax = getDvarInt( "betty_damage_max", 180 );
level.bettyDamageMin = getDvarInt( "betty_damage_min", 70 );
level.bettyDamageHeight = getDvarInt( "betty_damage_cylinder_height", 200 );
level.bettyJumpHeight = getDvarInt( "betty_jump_height_onground", 55 );
level.bettyJumpHeightWall = getDvarInt( "betty_jump_height_wall", 20 );
level.bettyJumpHeightWallAngle = getDvarInt( "betty_onground_angle_threshold", 30 );
level.bettyJumpHeightWallAngleCos = cos( level.bettyJumpHeightWallAngle );
level.bettyJumpTime = getDvarFloat( "betty_jump_time", 0.7 );
level.bettyBombletSpawnDistance = 20;
level.bettyBombletCount = 4;
level thread register();
/#
level thread bouncingBettyDvarUpdate();
#/
callback::add_weapon_watcher( &createBouncingBettyWatcher );
}
function register()
{
clientfield::register( "missile", "bouncingbetty_state", 1, 2, "int" );
clientfield::register( "scriptmover", "bouncingbetty_state", 1, 2, "int" );
}
/#
function bouncingBettyDvarUpdate()
{
for(;;)
{
level.bettyRadius = getDvarInt( "betty_detect_radius", level.bettyRadius );
level.bettyActivationDelay = getDvarFloat( "betty_activation_delay", level.bettyActivationDelay );
level.bettyGracePeriod = getDvarFloat( "betty_grace_period", level.bettyGracePeriod );
level.bettyDamageRadius = getDvarInt( "betty_damage_radius", level.bettyDamageRadius );
level.bettyDamageMax = getDvarInt( "betty_damage_max", level.bettyDamageMax );
level.bettyDamageMin = getDvarInt( "betty_damage_min", level.bettyDamageMin );
level.bettyDamageHeight = getDvarInt( "betty_damage_cylinder_height", level.bettyDamageHeight );
level.bettyJumpHeight = getDvarInt( "betty_jump_height_onground", level.bettyJumpHeight );
level.bettyJumpHeightWall = getDvarInt( "betty_jump_height_wall", level.bettyJumpHeightWall );
level.bettyJumpHeightWallAngle = getDvarInt( "betty_onground_angle_threshold", level.bettyJumpHeightWallAngle );
level.bettyJumpHeightWallAngleCos = cos( level.bettyJumpHeightWallAngle );
level.bettyJumpTime = getDvarFloat( "betty_jump_time", level.bettyJumpTime );
wait( 3 );
}
}
#/
//******************************************************************
// *
// *
//******************************************************************
function createBouncingBettyWatcher() // self == player
{
watcher = self weaponobjects::createProximityWeaponObjectWatcher( "bouncingbetty", self.team );
watcher.onSpawn =&onSpawnBouncingBetty;
watcher.watchForFire = true;
watcher.onDetonateCallback =&bouncingBettyDetonate;
//Eckert - playing sound later
//watcher.activateSound = "fly_betty_exp";
watcher.activateSound = "wpn_betty_alert";
watcher.hackable = true;
watcher.hackerToolRadius = level.equipmentHackerToolRadius;
watcher.hackerToolTimeMs = level.equipmentHackerToolTimeMs;
watcher.ownerGetsAssist = true;
watcher.ignoreDirection = true;
watcher.immediateDetonation = true;
watcher.immunespecialty = "specialty_immunetriggerbetty";
watcher.detectionMinDist = level.bettyMinDist;
watcher.detectionGracePeriod = level.bettyGracePeriod;
watcher.detonateRadius = level.bettyRadius;
watcher.onFizzleOut = &bouncingbetty::onBouncingBettyFizzleOut;
watcher.stun =&weaponobjects::weaponStun;
watcher.stunTime = level.bettyStunTime;
watcher.activationDelay = level.bettyActivationDelay;
}
function onBouncingBettyFizzleOut()
{
if ( isdefined( self.mineMover ) )
{
if ( isdefined( self.mineMover.killcament ) )
{
self.mineMover.killcament delete();
}
self.mineMover delete();
}
self delete();
}
//******************************************************************
// *
// *
//******************************************************************
function onSpawnBouncingBetty( watcher, owner ) // self == betty ent
{
weaponobjects::onSpawnProximityWeaponObject( watcher, owner );
self.originalOwner = owner;
self thread spawnMineMover();
self trackOnOwner( owner );
self thread trackUsedStatOnDeath();
self thread doNoTrackUsedStatOnPickup();
self thread trackUsedOnHack();
}
function trackUsedStatOnDeath()
{
self endon( "do_not_track_used" );
self waittill( "death" );
waittillframeend; // to compensate for timiing issues with notifies and death
if ( isdefined( self.owner ) )
{
self.owner trackBouncingBettyAsUsed(); // since betties can be picked up or hacked, we track them as used on death
}
self notify( "end_doNoTrackUsedOnPickup" );
self notify( "end_doNoTrackUsedOnHacked" );
}
function doNoTrackUsedStatOnPickup() // self == betty ent
{
self endon( "end_doNoTrackUsedOnPickup" );
self waittill( "picked_up" );
self notify( "do_not_track_used" );
}
function trackUsedOnHack() // self == betty ent
{
self endon( "end_doNoTrackUsedOnHacked" );
self waittill( "hacked" );
self.originalOwner trackBouncingBettyAsUsed(); // since betties can be picked up or hacked, we track them as used on hack too
self notify( "do_not_track_used" ); // remove old one as it was hacked
}
function trackBouncingBettyAsUsed()
{
if ( IsPlayer( self ) )
{
self AddWeaponStat( GetWeapon( "bouncingbetty" ), "used", 1 );
}
}
function trackOnOwner( owner )
{
if ( level.trackBouncingBettiesOnOwner === true )
{
if ( !isdefined( owner ) )
return;
if ( !isdefined( owner.activeBouncingBetties ) )
{
owner.activeBouncingBetties = [];
}
else
{
ArrayRemoveValue( owner.activeBouncingBetties, undefined );
}
owner.activeBouncingBetties[ owner.activeBouncingBetties.size ] = self;
}
}
//******************************************************************
// *
// *
//******************************************************************
function spawnMineMover() // self == betty ent
{
self endon( "death" );
self util::waitTillNotMoving();
self clientfield::set( "bouncingbetty_state", 2 );
self UseAnimTree( #animtree );
self SetAnim( %o_spider_mine_deploy, 1.0, 0.0, 1.0 );
mineMover = spawn( "script_model", self.origin );
mineMover.angles = self.angles;
mineMover SetModel( "tag_origin" );
mineMover.owner = self.owner;
mineUp = AnglesToUp( mineMover.angles );
z_offset = GetDvarFloat( "scr_bouncing_betty_killcam_offset", 18.0 );
mineMover EnableLinkTo();
mineMover LinkTo( self );
mineMover.killCamOffset = VectorScale( mineUp, z_offset );
mineMover.weapon = self.weapon;
mineMover playsound ("wpn_betty_arm");
killcamEnt = spawn( "script_model", mineMover.origin + mineMover.killCamOffset );
killcamEnt.angles = ( 0,0,0 );
killcamEnt SetModel( "tag_origin" );
killcamEnt SetWeapon( self.weapon );
mineMover.killcamEnt = killcamEnt;
self.mineMover = mineMover;
self thread killMineMoverOnPickup();
}
//******************************************************************
// *
// *
//******************************************************************
function killMineMoverOnPickup() // self == betty ent
{
self.mineMover endon( "death" );
self util::waittill_any( "picked_up", "hacked" );
self killMineMover();
}
//******************************************************************
// *
// *
//******************************************************************
function killMineMover() // self == betty ent
{
if ( isdefined( self.mineMover ))
{
if ( isdefined( self.mineMover.killcamEnt ) )
{
self.mineMover.killcamEnt delete();
}
self.mineMover delete();
}
}
//******************************************************************
// *
// *
//******************************************************************
function bouncingBettyDetonate( attacker, weapon, target ) // self == betty
{
if ( IsDefined( weapon ) && weapon.isValid )
{
self.destroyedBy = attacker;
if ( isdefined( attacker ) )
{
if ( self.owner util::IsEnemyPlayer( attacker ) )
{
attacker challenges::destroyedExplosive( weapon );
scoreevents::processScoreEvent( "destroyed_bouncingbetty", attacker, self.owner, weapon );
}
}
self bouncingBettyDestroyed();
}
else if ( isdefined( self.mineMover ))
{
self.mineMover.ignore_team_kills = true;
self.mineMover SetModel( self.model );
self.mineMover thread bouncingBettyJumpAndExplode();
self delete();
}
else
{
// tagTMR<NOTE>: special case where betty hasn't settled yet but something has triggered a detonate
// i.e. the moving platforms doors on drone, etc
self bouncingBettyDestroyed();
}
}
//******************************************************************
// *
// *
//******************************************************************
function bouncingBettyDestroyed( ) // self == betty
{
PlayFX( level.bettyDestroyedFX, self.origin );
PlaySoundAtPosition ( "dst_equipment_destroy", self.origin );
if ( isdefined( self.trigger ) )
{
self.trigger delete();
}
self killMineMover();
self RadiusDamage( self.origin, 128, 110, 10, self.owner, "MOD_EXPLOSIVE", self.weapon );
self delete();
}
//******************************************************************
// *
// *
//******************************************************************
function bouncingBettyJumpAndExplode() // self == script mover spawned at weaponobject location
{
jumpDir = VectorNormalize( AnglesToUp( self.angles ) );
if ( jumpDir[2] > level.bettyJumpHeightWallAngleCos )
{
jumpHeight = level.bettyJumpHeight;
}
else
{
jumpHeight = level.bettyJumpHeightWall;
}
explodePos = self.origin + jumpDir * jumpHeight;
self.killCamEnt MoveTo( explodePos + self.killCamOffset, level.bettyJumpTime, 0, level.bettyJumpTime );
self clientfield::set( "bouncingbetty_state", 1 );
wait( level.bettyJumpTime );
self thread mineExplode( jumpDir, explodePos );
}
function mineExplode( explosionDir, explodePos )
{
if ( !isdefined( self ) || !isdefined( self.owner ) )
return;
self playsound( "wpn_betty_explo" );
self clientfield::set( "sndRattle", 1 );
{wait(.05);}; // needed or the effect doesn't play
if ( !isdefined( self ) || !isdefined(self.owner) )
return;
self CylinderDamage( explosionDir * level.bettyDamageHeight, explodePos, level.bettyDamageRadius, level.bettyDamageRadius, level.bettyDamageMax, level.bettyDamageMin, self.owner, "MOD_EXPLOSIVE", self.weapon );
self ghost();
wait( 0.1 );
if ( !isdefined( self ) || !isdefined(self.owner) )
return;
if ( isdefined( self.trigger ) )
self.trigger delete();
self.killCamEnt delete();
self delete();
}