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

660 lines
26 KiB
Plaintext

#using scripts\codescripts\struct;
#using scripts\shared\callbacks_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\killstreaks_shared;
#using scripts\shared\scoreevents_shared;
#using scripts\shared\system_shared;
#using scripts\shared\util_shared;
#precache( "fx", "_t6/weapon/riotshield/fx_riotshield_depoly_lights" );
#precache( "fx", "_t6/weapon/riotshield/fx_riotshield_depoly_dust" );
#using_animtree ( "mp_riotshield" );
#namespace riotshield;
function init_shared()
{
if ( !isdefined( level.weaponRiotshield ) )
{
level.weaponRiotshield = GetWeapon( "riotshield" );
}
level.deployedShieldModel = "t6_wpn_shield_carry_world";
level.stowedShieldModel = "t6_wpn_shield_stow_world";
level.carriedShieldModel = "t6_wpn_shield_carry_world";
level.detectShieldModel = "t6_wpn_shield_carry_world_detect";
level.riotshieldDestroyAnim = %o_riot_stand_destroyed;
level.riotshieldDeployAnim = %o_riot_stand_deploy;
level.riotshieldShotAnimFront = %o_riot_stand_shot;
level.riotshieldShotAnimBack = %o_riot_stand_shot_back;
level.riotshieldMeleeAnimFront = %o_riot_stand_melee_front;
level.riotshieldMeleeAnimBack = %o_riot_stand_melee_back;
level.riotshield_placement_zoffset = 26;
thread register();
callback::on_spawned( &on_player_spawned );
}
//******************************************************************
// *
// *
//******************************************************************
function register()
{
clientfield::register( "scriptmover", "riotshield_state", 1, 2, "int" );
}
//******************************************************************
// *
// *
//******************************************************************
function watchPregameClassChange()
{
self endon ( "death" );
self endon ( "disconnect" );
self endon ( "track_riot_shield" );
self waittill( "changed_class" );
if (( level.inGracePeriod && !self.hasDoneCombat ))
{
self ClearStowedWeapon();
self RefreshShieldAttachment();
self thread trackRiotShield();
}
}
//******************************************************************
// *
// *
//******************************************************************
function watchRiotshieldPickup() // self == player
{
self endon ( "death" );
self endon ( "disconnect" );
self endon ( "track_riot_shield" );
self notify( "watch_riotshield_pickup" );
self endon( "watch_riotshield_pickup" );
// tagTMR<NOTE>: fix for rare case when riotshield is given by the server
// but the client fails to equip because of prone
self waittill( "pickup_riotshield" );
self endon( "weapon_change" );
/#println( "Picked up riotshield, expecting weapon_change notify..." );#/
wait 0.5;
/#println( "picked up shield but didn't change weapons, attach it!" );#/
currentWeapon = self getCurrentWeapon();
self.hasRiotShield = self HasRiotShield();
self.hasRiotShieldEquipped = ( currentWeapon.isRiotshield );
self RefreshShieldAttachment();
}
function trackRiotShield() // self == player
{
self endon( "death" );
self endon( "disconnect" );
self notify ( "track_riot_shield" );
self endon ( "track_riot_shield" );
self thread watchPregameClassChange();
self waittill( "weapon_change", newWeapon );
self RefreshShieldAttachment();
currentWeapon = self getCurrentWeapon();
self.hasRiotShield = self HasRiotShield();
self.hasRiotShieldEquipped = ( currentWeapon.isRiotshield );
self.lastNonShieldWeapon = level.weaponNone;
while( true )
{
self thread watchRiotshieldPickup();
currentWeapon = self getCurrentWeapon();
currentWeapon = self getCurrentWeapon();
self.hasRiotShield = self HasRiotShield();
self.hasRiotShieldEquipped = ( currentWeapon.isRiotshield );
refresh_attach = false;
self waittill( "weapon_change", newWeapon );
if ( newWeapon.isRiotShield )
{
refresh_attach = true;
// if we have a deployed riotshield in the world, delete if we pickup another
if ( isdefined( self.riotshieldEntity ))
{
self notify( "destroy_riotshield" );
}
if ( self.hasRiotShield )
{
if ( isdefined( self.riotshieldTakeWeapon ))
{
self TakeWeapon( self.riotshieldTakeWeapon );
self.riotshieldTakeWeapon = undefined;
}
}
if( isValidNonShieldWeapon( currentWeapon ))
{
self.lastNonShieldWeapon = currentWeapon;
}
}
if ( self.hasRiotShield || ( refresh_attach == true ))
{
self RefreshShieldAttachment();
}
}
}
function isValidNonShieldWeapon( weapon )
{
if( killstreaks::is_killstreak_weapon( weapon ) )
return false;
if( weapon.isCarriedKillstreak )
return false;
if( weapon.isGameplayWeapon )
return false;
if( weapon == level.weaponNone )
return false;
if ( weapon.isEquipment )
return false;
return true;
}
//******************************************************************
// *
// *
//******************************************************************
function startRiotshieldDeploy() // self == player
{
self notify( "start_riotshield_deploy" );
self thread watchRiotshieldDeploy();
}
//******************************************************************
// *
// *
//******************************************************************
function resetReconModelVisibility( owner ) // self == recon model
{
if ( !isdefined( self ) )
return;
self SetInvisibleToAll();
self SetForceNoCull();
if ( !isdefined( owner ) )
return;
for ( i = 0 ; i < level.players.size ; i++ )
{
if( level.players[i] HasPerk( "specialty_showenemyequipment" ) )
{
if ( level.players[i].team == "spectator" )
continue;
isEnemy = true;
if ( level.teamBased )
{
if ( level.players[i].team == owner.team )
isEnemy = false;
}
else
{
if ( level.players[i] == owner )
isEnemy = false;
}
if ( isEnemy )
{
self SetVisibleToPlayer( level.players[i] );
}
}
}
}
//******************************************************************
// *
// *
//******************************************************************
function resetReconModelOnEvent( eventName, owner ) // self == reconModel
{
self endon( "death" );
for ( ;; )
{
level waittill( eventName, newOwner );
if( isdefined( newOwner ) )
{
owner = newOwner;
}
self resetReconModelVisibility( owner );
}
}
//******************************************************************
// *
// *
//******************************************************************
function attachReconModel( modelName, owner ) // self == shield model
{
if ( !isdefined( self ) )
return;
reconModel = spawn( "script_model", self.origin );
reconModel.angles = self.angles;
reconModel SetModel( modelName );
reconModel.model_name = modelName;
reconModel linkto( self );
reconModel SetContents( 0 );
reconModel resetReconModelVisibility( owner );
reconModel thread resetReconModelOnEvent( "joined_team", owner );
reconModel thread resetReconModelOnEvent( "player_spawned", owner );
self.reconModel = reconModel;
}
//******************************************************************
// *
// *
//******************************************************************
function spawnRiotshieldCover( origin, angles ) // self == player
{
shield_ent = spawn( "script_model", origin, 1 ); // third param is spawn flag for dynamic pathing
shield_ent.targetname = "riotshield_mp";
shield_ent.angles = angles;
shield_ent SetModel( level.deployedShieldModel );
shield_ent SetOwner( self );
shield_ent.owner = self;
shield_ent.team = self.team;
shield_ent SetTeam( self.team );
shield_ent attachReconModel( level.detectShieldModel, self );
shield_ent UseAnimTree( #animtree );
shield_ent SetScriptMoverFlag( 0 ); // SCRIPTMOVER_FLAG_RIOTSHIELD
shield_ent DisconnectPaths();
return shield_ent;
}
//******************************************************************
// *
// *
//******************************************************************
function watchRiotshieldDeploy() // self == player
{
self endon( "death" );
self endon( "disconnect" );
self endon( "start_riotshield_deploy" );
self waittill( "deploy_riotshield", deploy_attempt, weapon );
self SetPlacementHint( 1 );
placement_hint = false;
if ( deploy_attempt )
{
placement = self CanPlaceRiotshield( "deploy_riotshield" );
if ( placement["result"] /*&& riotshieldDistanceTest( placement["origin"] )*/)
{
self.hasDoneCombat = true;
zoffset = level.riotshield_placement_zoffset;
shield_ent = self spawnRiotshieldCover( placement["origin"] + (0,0,zoffset), placement["angles"] );
item_ent = DeployRiotShield( self, shield_ent );
primaries = self GetWeaponsListPrimaries();
/#
assert( isdefined( item_ent ));
assert( !isdefined( self.riotshieldRetrieveTrigger ));
assert( !isdefined( self.riotshieldEntity ));
if ( level.gameType != "shrp" )
{
assert( primaries.size > 0 );
}
#/
shield_ent clientfield::set( "riotshield_state", 1 );
shield_ent.reconModel clientfield::set( "riotshield_state", 1 );
if ( level.gameType != "shrp" )
{
if( self.lastNonShieldWeapon != level.weaponNone && self hasWeapon( self.lastNonShieldWeapon ) )
self SwitchToWeapon( self.lastNonShieldWeapon );
else
self SwitchToWeapon( primaries[0] );
}
if ( !self HasWeapon( level.weaponBaseMeleeHeld ))
{
self GiveWeapon( level.weaponBaseMeleeHeld );
self.riotshieldTakeWeapon = level.weaponBaseMeleeHeld;
}
self.riotshieldRetrieveTrigger = item_ent;
self.riotshieldEntity = shield_ent;
self thread watchDeployedRiotshieldEnts();
self thread deleteShieldOnTriggerDeath( self.riotshieldRetrieveTrigger );
self thread deleteShieldOnPlayerDeathOrDisconnect( shield_ent );
self.riotshieldEntity thread watchDeployedRiotshieldDamage();
level notify( "riotshield_planted", self );
}
else
{
placement_hint = true;
clip_max_ammo = weapon.clipSize;
self setWeaponAmmoClip( weapon, clip_max_ammo );
}
}
else
{
// tagTMR<NOTE>: just lowering the shield not trying to deploy
placement_hint = true;
}
if ( placement_hint )
{
self SetRiotshieldFailHint();
}
}
//******************************************************************
// *
// *
//******************************************************************
// tagTMR<NOTE>: distance check to keep riotshields from deploying too near each other
function riotshieldDistanceTest( origin )
{
/#
assert ( isdefined( origin ));
#/
min_dist_squared = GetDvarFloat( "riotshield_deploy_limit_radius" );
min_dist_squared *= min_dist_squared;
for ( i = 0; i < level.players.size; i++ )
{
if ( isdefined( level.players[i].riotshieldEntity ))
{
dist_squared = DistanceSquared( level.players[i].riotshieldEntity.origin, origin );
if ( min_dist_squared > dist_squared )
{
/#
println( "Shield placement denied! Failed distance check to other riotshields." );
#/
return false;
}
}
}
return true;
}
//******************************************************************
// *
// *
//******************************************************************
function watchDeployedRiotshieldEnts() // self == player
{
/#
assert( isdefined( self.riotshieldRetrieveTrigger ));
assert( isdefined( self.riotshieldEntity ));
#/
self waittill( "destroy_riotshield" );
if ( isdefined( self.riotshieldRetrieveTrigger ))
{
self.riotshieldRetrieveTrigger delete();
}
if ( isdefined( self.riotshieldEntity ))
{
if ( isdefined( self.riotshieldEntity.reconModel ))
{
self.riotshieldEntity.reconModel delete();
}
self.riotshieldEntity ConnectPaths();
self.riotshieldEntity delete();
}
}
//******************************************************************
// *
// *
//******************************************************************
function watchDeployedRiotshieldDamage() // self == riotshield script_model ent
{
self endon("death");
damageMax = GetDvarInt("riotshield_deployed_health");
self.damageTaken = 0;
while( true )
{
self.maxhealth = 100000;
self.health = self.maxhealth;
self waittill( "damage", damage, attacker, direction, point, type, tagName, modelName, partname, weapon, iDFlags );
if( !isdefined( attacker ) )
{
continue;
}
/#
assert( isdefined( self.owner ) && isdefined( self.owner.team ));
#/
if ( isplayer( attacker ) )
{
if ( (level.teamBased) && ( attacker.team == self.owner.team ) && ( attacker != self.owner))
{
continue;
}
}
if ( type == "MOD_MELEE" || type == "MOD_MELEE_ASSASSINATE" )
{
damage *= GetDvarfloat( "riotshield_melee_damage_scale" );
}
else if ( type == "MOD_PISTOL_BULLET" || type == "MOD_RIFLE_BULLET" )
{
damage *= GetDvarfloat( "riotshield_bullet_damage_scale" );
}
else if ( type == "MOD_GRENADE" || type == "MOD_GRENADE_SPLASH" || type == "MOD_EXPLOSIVE" || type == "MOD_EXPLOSIVE_SPLASH" || type == "MOD_PROJECTILE" || type == "MOD_PROJECTILE_SPLASH")
{
damage *= GetDvarfloat( "riotshield_explosive_damage_scale" );
}
else if ( type == "MOD_IMPACT" )
{
damage *= GetDvarFloat( "riotshield_projectile_damage_scale" );
}
else if ( type == "MOD_CRUSH" )
{
damage = damageMax;
}
self.damageTaken += damage;
if( self.damageTaken >= damageMax )
{
self thread damageThenDestroyRiotshield( attacker, weapon );
break;
}
}
}
//******************************************************************
// *
// *
//******************************************************************
function damageThenDestroyRiotshield( attacker, weapon ) // self == riotshield script_model ent
{
self notify( "damageThenDestroyRiotshield" );
self endon("death");
if ( isdefined( self.owner.riotshieldRetrieveTrigger ))
{
self.owner.riotshieldRetrieveTrigger delete();
}
if ( isdefined( self.reconModel ))
{
self.reconModel delete();
}
self ConnectPaths();
self.owner.riotshieldEntity = undefined;
self NotSolid();
self clientfield::set( "riotshield_state", 2 );
if (isdefined (attacker) && attacker != self.owner && isplayer( attacker ) )
{
scoreevents::processScoreEvent( "destroyed_shield", attacker, self.owner, weapon );
}
wait( GetDvarFloat( "riotshield_destroyed_cleanup_time" ));
self delete();
}
//******************************************************************
// *
// *
//******************************************************************
function deleteShieldOnTriggerDeath( shield_trigger ) // self == player
{
shield_trigger util::waittill_any( "trigger", "death" );
self notify( "destroy_riotshield" );
}
//******************************************************************
// *
// *
//******************************************************************
function deleteShieldOnPlayerDeathOrDisconnect( shield_ent ) // self == player
{
shield_ent endon( "death" );
shield_ent endon( "damageThenDestroyRiotshield" );
self util::waittill_any( "death", "disconnect", "remove_planted_weapons" );
shield_ent thread damageThenDestroyRiotshield();
}
//******************************************************************
// *
// *
//******************************************************************
function watchRiotshieldStuckEntityDeath( grenade, owner ) // self == entity stuck with nade
{
grenade endon( "death" );
self util::waittill_any( "damageThenDestroyRiotshield", "death", "disconnect", "weapon_change", "deploy_riotshield" );
grenade Detonate( owner );
}
function on_player_spawned()
{
self thread watch_riot_shield_use();
self thread begin_other_grenade_tracking();
}
function watch_riot_shield_use() // self == player
{
self endon( "death" );
self endon( "disconnect" );
// watcher for attaching the model to correct player bones
self thread trackRiotShield();
for ( ;; )
{
self waittill( "raise_riotshield" );
self thread startRiotshieldDeploy();
}
}
function begin_other_grenade_tracking()
{
self endon( "death" );
self endon( "disconnect" );
self notify( "riotshieldTrackingStart" );
self endon( "riotshieldTrackingStart" );
for (;;)
{
self waittill ( "grenade_fire", grenade, weapon, cookTime );
if ( grenade util::isHacked() )
{
continue;
}
switch ( weapon.name )
{
case "sticky_grenade":
case "proximity_grenade":
case "explosive_bolt":
grenade thread check_stuck_to_shield();
break;
}
}
}
function check_stuck_to_shield() // self == grenade
{
self endon( "death" );
self waittill( "stuck_to_shield", other, owner );
other watchRiotshieldStuckEntityDeath( self, owner );
}