989 lines
24 KiB
Plaintext
989 lines
24 KiB
Plaintext
#include maps\mp\_utility;
|
|
#include common_scripts\utility;
|
|
#include maps\mp\gametypes\_hud_util;
|
|
#include maps\mp\alien\_utility;
|
|
|
|
/*
|
|
Deployable box killstreaks: the player will be able to place a box in the world and teammates can grab items from it
|
|
this will be used on multiple killstreaks where you can place a box in the world with something in it
|
|
*/
|
|
|
|
BOX_TIMEOUT_UPDATE_INTERVAL = 1.0;
|
|
DEFAULT_USE_TIME = 3000;
|
|
BOX_DEFAULT_HEALTH = 999999; // so that boxes aren't killed in code
|
|
|
|
init()
|
|
{
|
|
if ( !IsDefined( level.boxSettings ) )
|
|
{
|
|
level.boxSettings = [];
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////
|
|
// MARKER FUNCTIONS
|
|
// 2012-06-21 wallace
|
|
// Stole an updated version from _uplink.gsc. Should probably unify all these funcs eventually
|
|
//////////////////////////////////////////////////
|
|
beginDeployableViaMarker( lifeId, boxType )
|
|
{
|
|
self thread watchDeployableMarkerCancel( boxType );
|
|
self thread watchDeployableMarkerPlacement( boxType, lifeId );
|
|
|
|
while ( true )
|
|
{
|
|
result = self waittill_any_return( "deployable_canceled", "deployable_deployed", "death", "disconnect" );
|
|
|
|
return ( result == "deployable_deployed" );
|
|
}
|
|
}
|
|
|
|
tryUseDeployable( lifeId, boxType ) // self == player
|
|
{
|
|
self thread watchDeployableMarkerCancel( boxType );
|
|
self thread watchDeployableMarkerPlacement( boxType, lifeId );
|
|
|
|
while ( true )
|
|
{
|
|
result = self waittill_any_return( "deployable_canceled", "deployable_deployed", "death", "disconnect" );
|
|
|
|
return ( result == "deployable_deployed" );
|
|
}
|
|
}
|
|
|
|
watchDeployableMarkerCancel( boxType )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
self endon( "deployable_deployed" );
|
|
|
|
boxConfig = level.boxSettings[ boxType ];
|
|
currentWeapon = self getCurrentWeapon();
|
|
|
|
while( currentWeapon == boxConfig.weaponInfo )
|
|
{
|
|
self waittill( "weapon_change", currentWeapon );
|
|
}
|
|
|
|
self notify( "deployable_canceled" );
|
|
}
|
|
|
|
watchDeployableMarkerPlacement( boxType, lifeId )
|
|
{
|
|
self endon( "spawned_player" ); // you shouldn't do endon( "death" ) here because this thread needs to run
|
|
self endon( "disconnect" );
|
|
self endon( "deployable_canceled" );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "grenade_fire", marker, weaponName );
|
|
|
|
if( isReallyAlive(self) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
marker Delete();
|
|
}
|
|
}
|
|
|
|
self notify( "deployable_deployed" );
|
|
|
|
marker.owner = self;
|
|
marker.weaponName = weaponName;
|
|
self.marker = marker;
|
|
|
|
marker PlaySoundToPlayer( level.boxSettings[ boxType ].deployedSfx, self );
|
|
|
|
marker thread markerActivate( lifeId, boxType, ::box_setActive );
|
|
}
|
|
|
|
override_box_moving_platform_death( data )
|
|
{
|
|
self notify( "death" ); // we're doing this here instead of letting the mover code just delete us so that we can run our necessary clean-up functionality (like removal of the objective marker from the minimap)
|
|
}
|
|
|
|
markerActivate( lifeId, boxType, usedCallback ) // self == marker
|
|
{
|
|
self notify( "markerActivate" );
|
|
self endon( "markerActivate" );
|
|
//self waittill( "explode", position );
|
|
self waittill( "missile_stuck" );
|
|
owner = self.owner;
|
|
position = self.origin;
|
|
|
|
if ( !isDefined( owner ) )
|
|
return;
|
|
|
|
box = createBoxForPlayer( boxType, position, owner );
|
|
|
|
// For moving platforms.
|
|
data = SpawnStruct();
|
|
data.linkParent = self GetLinkedParent();
|
|
|
|
//fixes wall hack exploit with linked items
|
|
if ( isDefined( data.linkParent ) && isDefined( data.linkParent.model ) && DeployableExclusion( data.linkParent.model ) )
|
|
{
|
|
box.origin = data.linkParent.origin;
|
|
|
|
grandParent = data.linkParent GetLinkedParent();
|
|
|
|
if ( isDefined( grandParent ) )
|
|
data.linkParent = grandParent;
|
|
else
|
|
data.linkParent = undefined;
|
|
}
|
|
|
|
data.deathOverrideCallback = ::override_box_moving_platform_death;
|
|
box thread maps\mp\_movers::handle_moving_platforms( data );
|
|
|
|
box.moving_platform = data.linkParent;
|
|
|
|
box SetOtherEnt(owner);
|
|
|
|
// ES - 2/24/14 - This waitframe is causing an issue where, when deployed on a moving platform, the "death" notification is sent instantly, but is never caught.
|
|
wait 0.05;
|
|
|
|
//self playSound( "sentry_gun_beep" );
|
|
box thread [[ usedCallback ]]();
|
|
|
|
self delete();
|
|
|
|
if( IsDefined(box) && (box touchingBadTrigger()) )
|
|
{
|
|
box notify( "death" );
|
|
}
|
|
}
|
|
|
|
DeployableExclusion( parentModel )
|
|
{
|
|
if ( parentModel == "weapon_alien_laser_drill" )
|
|
return true;
|
|
else if ( IsSubStr( parentModel, "crafting" ) )
|
|
return true;
|
|
else if ( IsSubStr( parentModel, "scorpion_body" ) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
isHoldingDeployableBox()
|
|
{
|
|
curWeap = self GetCurrentWeapon();
|
|
if ( IsDefined( curWeap ) )
|
|
{
|
|
foreach( deplBoxWeap in level.boxSettings )
|
|
{
|
|
if ( curWeap == deplBoxWeap.weaponInfo )
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
///////////////////////////////////////////////////
|
|
// END MARKER FUNCTIONS
|
|
//////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////
|
|
// BOX HANDLER FUNCTIONS
|
|
//////////////////////////////////////////////////
|
|
get_box_icon( resourceType, dpadName, upgrade_rank )
|
|
{
|
|
return level.alien_combat_resources[ resourceType][ dpadName ].upgrades[upgrade_rank].dpad_icon;
|
|
}
|
|
|
|
get_resource_type( dpadName )
|
|
{
|
|
if( !isDefined( dpadName ) )
|
|
return undefined;
|
|
|
|
foreach ( resource_type_name, resource_type in level.alien_combat_resources )
|
|
{
|
|
if ( IsDefined( resource_type[ dpadName ] ) )
|
|
{
|
|
return resource_type_name;
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
createBoxForPlayer( boxType, position, owner )
|
|
{
|
|
assertEx( isDefined( owner ), "createBoxForPlayer() called without owner specified" );
|
|
|
|
boxConfig = level.boxSettings[ boxType ];
|
|
|
|
box = Spawn( "script_model", position );
|
|
box setModel( boxConfig.modelBase );
|
|
box.health = BOX_DEFAULT_HEALTH;
|
|
box.maxHealth = boxConfig.maxHealth;
|
|
box.angles = owner.angles;
|
|
box.boxType = boxType;
|
|
box.owner = owner;
|
|
box.team = owner.team;
|
|
if ( IsDefined( boxConfig.dpadName ) )
|
|
{
|
|
box.dpadName = boxConfig.dpadName;
|
|
}
|
|
if ( IsDefined( boxConfig.maxUses ) )
|
|
{
|
|
box.usesRemaining = boxConfig.maxUses;
|
|
}
|
|
|
|
player = box.owner;
|
|
resource_type = get_resource_type( box.dpadName );
|
|
|
|
if ( is_combat_resource( resource_type ) )
|
|
{
|
|
box.upgrade_rank = player maps\mp\alien\_persistence::get_upgrade_level( resource_type );
|
|
box.icon_name = get_box_icon( resource_type, box.dpadName, box.upgrade_rank );
|
|
}
|
|
else
|
|
{
|
|
AssertEx( isDefined( boxConfig.icon_name ), "For non-combat-resource box, the .icon_name must be specified in the boxConfig struct" );
|
|
|
|
box.upgrade_rank = 0;
|
|
box.icon_name = boxConfig.icon_name;
|
|
}
|
|
|
|
// black box data tracking
|
|
level.alienBBData[ "team_item_deployed" ]++;
|
|
player maps\mp\alien\_persistence::eog_player_update_stat( "deployables", 1 );
|
|
|
|
/*
|
|
ownername = "";
|
|
if ( isdefined( owner.name ) )
|
|
ownername = owner.name;
|
|
|
|
itemname = boxType;
|
|
if ( isdefined( box.dpadName ) )
|
|
itemname = box.dpadName;
|
|
|
|
/#
|
|
if ( GetDvarInt( "alien_bbprint_debug" ) > 0 )
|
|
{
|
|
IPrintLnBold( "^8bbprint: aliendeployabledeployed \n" +
|
|
" itemname=" + itemname +
|
|
" itemlevel=" + box.upgrade_rank +
|
|
" itemx,y,z=" + position +
|
|
" ownername=" + ownername );
|
|
}
|
|
#/
|
|
|
|
bbprint( "aliendeployabledeployed",
|
|
"itemname %s itemlevel %s itemx %f itemy %f itemz %f ownername %s ",
|
|
itemname,
|
|
box.upgrade_rank,
|
|
position[0],
|
|
position[1],
|
|
position[2],
|
|
ownername );
|
|
*/
|
|
|
|
box box_setInactive();
|
|
box thread box_handleOwnerDisconnect();
|
|
box addBoxToLevelArray();
|
|
|
|
return box;
|
|
}
|
|
|
|
is_combat_resource( resource_type ) { return isDefined( resource_type ); }
|
|
|
|
box_setActive( skipOwnerUse ) // self == box
|
|
{
|
|
self setCursorHint( "HINT_NOICON" );
|
|
boxConfig = level.boxSettings[ self.boxType ];
|
|
self setHintString( boxConfig.hintString );
|
|
|
|
self.inUse = false;
|
|
|
|
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
|
|
Objective_Add( curObjID, "invisible", (0,0,0) );
|
|
Objective_Position( curObjID, self.origin );
|
|
Objective_State( curObjID, "active" );
|
|
|
|
if( isDefined( boxConfig.shaderName ) )
|
|
Objective_Icon( curObjID, boxConfig.shaderName );
|
|
|
|
self.objIdFriendly = curObjID;
|
|
|
|
// use the deployable on the owner once
|
|
if ( ( !IsDefined( skipOwnerUse ) || !skipOwnerUse ) && IsDefined( boxConfig.onuseCallback )
|
|
&& ( !IsDefined( boxconfig.canUseCallback ) || (self.owner [[ boxConfig.canUseCallback ]]() ) )
|
|
)
|
|
{
|
|
if( isReallyAlive( self.owner ) )
|
|
self.owner [[ boxConfig.onUseCallback ]]( self );
|
|
}
|
|
|
|
if ( level.teamBased )
|
|
{
|
|
Objective_Team( curObjID, self.team );
|
|
foreach ( player in level.players )
|
|
{
|
|
if ( self.team == player.team
|
|
&& (!IsDefined(boxConfig.canUseCallback) || player [[ boxConfig.canUseCallback ]](self) )
|
|
)
|
|
{
|
|
self box_SetIcon( player, boxConfig.streakName, boxConfig.headIconOffset );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Objective_Player( curObjID, self.owner GetEntityNumber() );
|
|
|
|
if( !IsDefined(boxConfig.canUseCallback) || self.owner [[ boxConfig.canUseCallback ]](self) )
|
|
{
|
|
self box_SetIcon( self.owner, boxConfig.streakName, boxConfig.headIconOffset );
|
|
}
|
|
}
|
|
|
|
self MakeUsable();
|
|
self.isUsable = true;
|
|
self SetCanDamage( true );
|
|
self thread box_handleDamage();
|
|
self thread box_handleDeath();
|
|
self thread box_timeOut();
|
|
|
|
self make_entity_sentient_mp( self.team, true );
|
|
|
|
if ( IsDefined( self.owner ) )
|
|
self.owner notify( "new_deployable_box", self );
|
|
|
|
if (level.teamBased)
|
|
{
|
|
foreach ( player in level.participants )
|
|
{
|
|
_box_setActiveHelper( player, self.team == player.team, boxConfig.canUseCallback );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach ( player in level.participants )
|
|
{
|
|
_box_setActiveHelper( player, IsDefined( self.owner ) && self.owner == player, boxConfig.canUseCallback );
|
|
}
|
|
}
|
|
|
|
if( ( !isdefined( self.air_dropped ) || !self.air_dropped ) && !isPlayingSolo() )
|
|
level thread teamPlayerCardSplash( boxConfig.splashName, self.owner, self.team );
|
|
|
|
self thread box_playerConnected();
|
|
self thread box_agentConnected();
|
|
}
|
|
|
|
_box_setActiveHelper( player, bActivate, canUseFunc )
|
|
{
|
|
if ( bActivate )
|
|
{
|
|
if ( !IsDefined( canUseFunc ) || player [[ canUseFunc ]](self) )
|
|
{
|
|
self box_enablePlayerUse( player );
|
|
}
|
|
else
|
|
{
|
|
self box_disablePlayerUse( player );
|
|
// if this player is already a juggernaut then when they die, let them use the box
|
|
self thread doubleDip( player );
|
|
}
|
|
self thread boxThink( player );
|
|
}
|
|
else
|
|
{
|
|
self box_disablePlayerUse( player );
|
|
}
|
|
}
|
|
|
|
box_playerConnected() // self == box
|
|
{
|
|
self endon( "death" );
|
|
|
|
// when new players connect they need a boxthink thread run on them
|
|
while( true )
|
|
{
|
|
level waittill( "connected", player );
|
|
self childthread box_waittill_player_spawn_and_add_box( player );
|
|
}
|
|
}
|
|
|
|
box_agentConnected() // self == box
|
|
{
|
|
self endon( "death" );
|
|
|
|
// when new agents connect they need a boxthink thread run on them
|
|
while( true )
|
|
{
|
|
level waittill( "spawned_agent_player", agent );
|
|
self box_addBoxForPlayer( agent );
|
|
}
|
|
}
|
|
|
|
box_waittill_player_spawn_and_add_box( player ) // self == box
|
|
{
|
|
player waittill( "spawned_player" );
|
|
if ( level.teamBased )
|
|
{
|
|
self box_addBoxForPlayer( player );
|
|
}
|
|
}
|
|
|
|
box_playerJoinedTeam( player ) // self == box
|
|
{
|
|
self endon( "death" );
|
|
player endon( "disconnect" );
|
|
|
|
// when new players connect they need a boxthink thread run on them
|
|
while( true )
|
|
{
|
|
player waittill( "joined_team" );
|
|
if ( level.teamBased )
|
|
{
|
|
self box_addBoxForPlayer( player );
|
|
}
|
|
}
|
|
}
|
|
|
|
box_addBoxForPlayer( player ) // self == box
|
|
{
|
|
if ( self.team == player.team )
|
|
{
|
|
self box_enablePlayerUse( player );
|
|
self thread boxThink( player );
|
|
self box_SetIcon( player, level.boxSettings[ self.boxType ].streakName, level.boxSettings[ self.boxType ].headIconOffset );
|
|
}
|
|
else
|
|
{
|
|
self box_disablePlayerUse( player );
|
|
self maps\mp\_entityheadIcons::setHeadIcon( player, "", (0,0,0) );
|
|
}
|
|
}
|
|
|
|
box_SetIcon( player, streakName, vOffset )
|
|
{
|
|
self maps\mp\_entityheadIcons::setHeadIcon( player, self.icon_name, (0, 0, vOffset), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
|
|
}
|
|
|
|
box_enablePlayerUse( player ) // self == box
|
|
{
|
|
if ( IsPlayer(player) )
|
|
self EnablePlayerUse( player );
|
|
|
|
self.disabled_use_for[player GetEntityNumber()] = false;
|
|
}
|
|
|
|
box_disablePlayerUse( player ) // self == box
|
|
{
|
|
if ( IsPlayer(player) )
|
|
self DisablePlayerUse( player );
|
|
|
|
self.disabled_use_for[player GetEntityNumber()] = true;
|
|
}
|
|
|
|
box_setInactive()
|
|
{
|
|
self makeUnusable();
|
|
self.isUsable = false;
|
|
self maps\mp\_entityheadIcons::setHeadIcon( "none", "", (0,0,0) );
|
|
if ( isDefined( self.objIdFriendly ) )
|
|
_objective_delete( self.objIdFriendly );
|
|
}
|
|
|
|
box_handleDamage() // self == box
|
|
{
|
|
boxConfig = level.boxSettings[ self.boxType ];
|
|
|
|
self maps\mp\gametypes\_damage::monitorDamage(
|
|
boxConfig.maxHealth,
|
|
boxConfig.damageFeedback,
|
|
::boxModifyDamage,
|
|
::boxHandleDeathDamage,
|
|
true // isKillstreak
|
|
);
|
|
}
|
|
|
|
boxModifyDamage( attacker, weapon, type, damage )
|
|
{
|
|
modifiedDamage = damage;
|
|
|
|
if( IsExplosiveDamageMOD( type ) )
|
|
{
|
|
modifiedDamage = damage * 1.5;
|
|
}
|
|
|
|
modifiedDamage = self maps\mp\gametypes\_damage::handleMeleeDamage( weapon, type, modifiedDamage );
|
|
modifiedDamage = self maps\mp\gametypes\_damage::handleMissileDamage( weapon, type, modifiedDamage );
|
|
modifiedDamage = self maps\mp\gametypes\_damage::handleAPDamage( weapon, type, modifiedDamage, attacker );
|
|
|
|
return modifiedDamage;
|
|
}
|
|
|
|
boxHandleDeathDamage( attacker, weapon, type, damage )
|
|
{
|
|
boxConfig = level.boxSettings[ self.boxType ];
|
|
self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, type, damage, boxConfig.xpPopup, boxConfig.voDestroyed );
|
|
}
|
|
|
|
box_handleDeath()
|
|
{
|
|
self waittill ( "death" );
|
|
|
|
// this handles cases of deletion
|
|
if ( !isDefined( self ) )
|
|
return;
|
|
|
|
self box_setInactive();
|
|
self removeBoxFromLevelArray();
|
|
|
|
boxConfig = level.boxSettings[ self.boxType ];
|
|
PlayFX( getfx( "deployablebox_crate_destroy" ), self.origin );
|
|
// 2013-03-08 wsh: whould probably validate all the used fields...
|
|
if ( IsDefined( boxConfig.deathDamageMax ) )
|
|
{
|
|
owner = undefined;
|
|
if ( IsDefined(self.owner) )
|
|
owner = self.owner;
|
|
|
|
// somewhat hacky:
|
|
// shift the origin of the damage because it'll collide with the box otherwise
|
|
// we could also apply the damage after we delete the item?
|
|
RadiusDamage( self.origin + (0, 0, boxConfig.headIconOffset),
|
|
boxConfig.deathDamageRadius,
|
|
boxConfig.deathDamageMax,
|
|
boxConfig.deathDamageMin,
|
|
owner,
|
|
"MOD_EXPLOSIVE",
|
|
boxConfig.deathWeaponInfo
|
|
);
|
|
}
|
|
|
|
wait( 0.1 );
|
|
|
|
self notify( "deleting" );
|
|
|
|
self delete();
|
|
}
|
|
|
|
box_handleOwnerDisconnect() // self == box
|
|
{
|
|
self endon ( "death" );
|
|
level endon ( "game_ended" );
|
|
|
|
self notify ( "box_handleOwner" );
|
|
self endon ( "box_handleOwner" );
|
|
|
|
old_owner = self.owner;
|
|
self.owner waittill( "killstreak_disowned" );
|
|
|
|
// special case for air dropped box to stay when fake owner leaves ( owner was randomly picked )
|
|
if ( isdefined( self.air_dropped ) && self.air_dropped )
|
|
{
|
|
// reassign owner to next avaliable player
|
|
foreach ( player in level.players )
|
|
{
|
|
if ( !isdefined( player ) || ( isdefined( old_owner ) && old_owner == player ) )
|
|
continue;
|
|
|
|
self.owner = player;
|
|
self thread box_handleOwnerDisconnect(); // recurse
|
|
return;
|
|
}
|
|
}
|
|
|
|
// removed if not air dropped or if no host player found (which shouldn't happen)
|
|
self notify( "death" );
|
|
}
|
|
|
|
boxThink( player )
|
|
{
|
|
self endon ( "death" );
|
|
|
|
self thread boxCaptureThink( player );
|
|
|
|
if ( !IsDefined(player.boxes) )
|
|
{
|
|
player.boxes = [];
|
|
}
|
|
player.boxes[player.boxes.size] = self;
|
|
|
|
boxConfig = level.boxSettings[ self.boxType ];
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill ( "captured", capturer );
|
|
|
|
if (capturer == player)
|
|
{
|
|
player PlayLocalSound( boxConfig.onUseSfx );
|
|
|
|
if ( IsDefined( boxConfig.onuseCallback ) )
|
|
{
|
|
player [[ boxConfig.onUseCallback ]]( self );
|
|
|
|
if ( maps\mp\alien\_utility::is_chaos_mode() )
|
|
maps\mp\alien\_chaos::update_pickup_deployable_box_event();
|
|
}
|
|
|
|
// if this is not the owner then give the owner some xp
|
|
if( IsDefined( self.owner ) && player != self.owner )
|
|
{
|
|
self.owner thread maps\mp\gametypes\_rank::xpEventPopup( boxConfig.event );
|
|
self.owner thread maps\mp\gametypes\_rank::giveRankXP( "support", boxConfig.useXP );
|
|
}
|
|
|
|
if ( IsDefined( self.usesRemaining ) )
|
|
{
|
|
self.usesRemaining--;
|
|
if ( self.usesRemaining == 0)
|
|
{
|
|
self box_leave();
|
|
break;
|
|
}
|
|
}
|
|
|
|
self maps\mp\_entityheadIcons::setHeadIcon( player, "", (0,0,0) );
|
|
self box_disablePlayerUse( player );
|
|
self thread doubleDip( player );
|
|
}
|
|
}
|
|
}
|
|
|
|
doubleDip( player ) // self == box
|
|
{
|
|
self endon( "death" );
|
|
player endon( "disconnect" );
|
|
|
|
// air dropped rewards can not be double dipped
|
|
if( isdefined( self.air_dropped ) && self.air_dropped )
|
|
return;
|
|
|
|
// once they die, let them take from the box again
|
|
player waittill( "death" );
|
|
|
|
if( level.teamBased )
|
|
{
|
|
if( self.team == player.team )
|
|
{
|
|
self box_SetIcon( player, level.boxSettings[ self.boxType ].streakName, level.boxSettings[ self.boxType ].headIconOffset );
|
|
self box_enablePlayerUse( player );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( IsDefined( self.owner ) && self.owner == player )
|
|
{
|
|
self box_SetIcon( player, level.boxSettings[ self.boxType ].streakName, level.boxSettings[ self.boxType ].headIconOffset );
|
|
self box_enablePlayerUse( player );
|
|
}
|
|
}
|
|
}
|
|
|
|
boxCaptureThink( player ) // self == box
|
|
{
|
|
while( isDefined( self ) )
|
|
{
|
|
self waittill( "trigger", tiggerer );
|
|
if ( is_aliens() )
|
|
{
|
|
if ( [[level.boxCaptureThink_alien_func]]( tiggerer ) )
|
|
continue;
|
|
}
|
|
if ( is_chaos_mode() )
|
|
{
|
|
switch ( self.boxType )
|
|
{
|
|
case "medic_skill":
|
|
case "specialist_skill":
|
|
case "tank_skill":
|
|
case "engineer_skill":
|
|
if( is_true( tiggerer.hasChaosClassSkill ) )
|
|
{
|
|
tiggerer maps\mp\_utility::setLowerMessage( "cant_use", &"ALIEN_CHAOS_CANT_PICKUP_BONUS", 3 );
|
|
continue;
|
|
}
|
|
else if ( is_true( tiggerer.chaosClassSkillInUse ) )
|
|
{
|
|
tiggerer maps\mp\_utility::setLowerMessage( "skill_in_use", &"ALIEN_CHAOS_SKILL_IN_USE", 3 );
|
|
continue;
|
|
}
|
|
break;
|
|
case "combo_freeze":
|
|
if( is_true( tiggerer.hasComboFreeze ) )
|
|
{
|
|
tiggerer maps\mp\_utility::setLowerMessage( "cant_use", &"ALIEN_CHAOS_CANT_PICKUP_BONUS", 3 );
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tiggerer == player
|
|
&& self useHoldThink( player, level.boxSettings[ self.boxType ].useTime )
|
|
)
|
|
{
|
|
self notify( "captured", player );
|
|
}
|
|
}
|
|
}
|
|
|
|
isFriendlyToBox( box )
|
|
{
|
|
return ( level.teamBased
|
|
&& self.team == box.team );
|
|
}
|
|
|
|
box_timeOut() // self == box
|
|
{
|
|
self endon( "death" );
|
|
level endon ( "game_ended" );
|
|
|
|
if ( box_should_leave_immediately() )
|
|
{
|
|
wait 0.05;
|
|
}
|
|
else
|
|
{
|
|
lifeSpan = level.boxSettings[ self.boxType ].lifeSpan;
|
|
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( lifeSpan );
|
|
}
|
|
|
|
self box_leave();
|
|
}
|
|
|
|
box_should_leave_immediately()
|
|
{
|
|
if ( ( self.boxtype == "deployable_ammo" && self.upgrade_rank == 4 ) || ( self.boxtype == "deployable_specialammo_comb" && self.upgrade_rank == 4 ) ) // stay to regen ammo
|
|
return false;
|
|
|
|
if ( maps\mp\alien\_utility::isPlayingSolo() && ( !isdefined( self.air_dropped ) || !self.air_dropped ) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
box_leave()
|
|
{
|
|
// TODO: get sound for this
|
|
//if ( isDefined( self.owner ) )
|
|
// self.owner thread leaderDialogOnPlayer( "sentry_gone" );
|
|
PlayFX( getfx( "deployablebox_crate_destroy" ), self.origin );
|
|
|
|
wait( 0.05 );
|
|
|
|
self notify( "death" );
|
|
}
|
|
|
|
deleteOnOwnerDeath( owner ) // self == box.friendlyModel or box.enemyModel, owner == box
|
|
{
|
|
wait ( 0.25 );
|
|
self linkTo( owner, "tag_origin", (0,0,0), (0,0,0) );
|
|
|
|
owner waittill ( "death" );
|
|
|
|
box_leave();
|
|
}
|
|
|
|
box_ModelTeamUpdater( showForTeam ) // self == box model (enemy or friendly)
|
|
{
|
|
self endon ( "death" );
|
|
|
|
self hide();
|
|
|
|
foreach ( player in level.players )
|
|
{
|
|
if ( player.team == showForTeam )
|
|
self showToPlayer( player );
|
|
}
|
|
|
|
for ( ;; )
|
|
{
|
|
level waittill ( "joined_team" );
|
|
|
|
self hide();
|
|
foreach ( player in level.players )
|
|
{
|
|
if ( player.team == showForTeam )
|
|
self showToPlayer( player );
|
|
}
|
|
}
|
|
}
|
|
|
|
useHoldThink( player, useTime )
|
|
{
|
|
if ( IsPlayer(player) )
|
|
player playerLinkTo( self );
|
|
else
|
|
player LinkTo( self );
|
|
player playerLinkedOffsetEnable();
|
|
|
|
player.boxParams = SpawnStruct();
|
|
player.boxParams.curProgress = 0;
|
|
player.boxParams.inUse = true;
|
|
player.boxParams.useRate = 0;
|
|
|
|
if ( isDefined( useTime ) )
|
|
{
|
|
player.boxParams.useTime = useTime;
|
|
}
|
|
else
|
|
{
|
|
player.boxParams.useTime = DEFAULT_USE_TIME;
|
|
}
|
|
|
|
//player _disableWeapon();
|
|
player disable_weapon_timeout( ( useTime + 0.05 ), "deployable_weapon_management" );
|
|
|
|
if ( IsPlayer(player) )
|
|
player thread personalUseBar( self );
|
|
|
|
result = useHoldThinkLoop( player );
|
|
assert ( isDefined( result ) );
|
|
|
|
if ( isAlive( player ) )
|
|
{
|
|
//player _enableWeapon();
|
|
player enable_weapon_wrapper( "deployable_weapon_management" );
|
|
player unlink();
|
|
}
|
|
|
|
if ( !isDefined( self ) )
|
|
return false;
|
|
|
|
player.boxParams.inUse = false;
|
|
player.boxParams.curProgress = 0;
|
|
|
|
return ( result );
|
|
}
|
|
|
|
personalUseBar( object ) // self == player
|
|
{
|
|
self endon( "disconnect" );
|
|
|
|
useBar = createPrimaryProgressBar( 0, 25 );
|
|
useBarText = createPrimaryProgressBarText( 0, 25 );
|
|
useBarText setText( level.boxSettings[ object.boxType ].capturingString );
|
|
|
|
lastRate = -1;
|
|
while ( isReallyAlive( self ) && isDefined( object ) && self.boxParams.inUse && object.isUsable && !level.gameEnded )
|
|
{
|
|
if ( lastRate != self.boxParams.useRate )
|
|
{
|
|
if( self.boxParams.curProgress > self.boxParams.useTime)
|
|
self.boxParams.curProgress = self.boxParams.useTime;
|
|
|
|
useBar updateBar( self.boxParams.curProgress / self.boxParams.useTime, (1000 / self.boxParams.useTime) * self.boxParams.useRate );
|
|
|
|
if ( !self.boxParams.useRate )
|
|
{
|
|
useBar hideElem();
|
|
useBarText hideElem();
|
|
}
|
|
else
|
|
{
|
|
useBar showElem();
|
|
useBarText showElem();
|
|
}
|
|
}
|
|
lastRate = self.boxParams.useRate;
|
|
wait ( 0.05 );
|
|
}
|
|
|
|
useBar destroyElem();
|
|
useBarText destroyElem();
|
|
}
|
|
|
|
useHoldThinkLoop( player )
|
|
{
|
|
while( !level.gameEnded && isDefined( self ) && isReallyAlive( player ) && player useButtonPressed() && player.boxParams.curProgress < player.boxParams.useTime )
|
|
{
|
|
player.boxParams.curProgress += (50 * player.boxParams.useRate);
|
|
|
|
if ( isDefined( player.objectiveScaler ) )
|
|
player.boxParams.useRate = 1 * player.objectiveScaler;
|
|
else
|
|
player.boxParams.useRate = 1;
|
|
|
|
if ( player.boxParams.curProgress >= player.boxParams.useTime )
|
|
return ( isReallyAlive( player ) );
|
|
|
|
wait 0.05;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
disableWhenJuggernaut() // self == box
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "death" );
|
|
|
|
while( true )
|
|
{
|
|
level waittill( "juggernaut_equipped", player );
|
|
self maps\mp\_entityheadIcons::setHeadIcon( player, "", (0,0,0) );
|
|
self box_disablePlayerUse( player );
|
|
self thread doubleDip( player );
|
|
}
|
|
}
|
|
|
|
addBoxToLevelArray() // self == box
|
|
{
|
|
// put the newly created box in the level array for the box type
|
|
level.deployable_box[ self.boxType ][ self GetEntityNumber() ] = self;
|
|
}
|
|
|
|
removeBoxFromLevelArray() // self == box
|
|
{
|
|
level.deployable_box[ self.boxType ][ self GetEntityNumber() ] = undefined;
|
|
}
|
|
|
|
|
|
default_canUseDeployable( boxEnt ) // self == player
|
|
{
|
|
if( ( isDefined( boxEnt ) && boxEnt.owner == self || self maps\mp\alien\_prestige::prestige_getNoDeployables() == 1.0 ) && !isdefined( boxEnt.air_dropped ) )
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
default_OnUseDeployable( boxent ) //self =a player
|
|
{
|
|
self thread maps\mp\alien\_persistence::deployablebox_used_track( boxEnt );
|
|
maps\mp\alien\_utility::deployable_box_onuse_message( boxent );
|
|
}
|
|
|
|
default_tryUseDeployable( lifeId, BOX_TYPE ) // self == player
|
|
{
|
|
result = self maps\mp\alien\_combat_resources::alien_beginDeployableViaMarker( lifeId, BOX_TYPE );
|
|
|
|
if( ( !IsDefined( result ) || !result ) )
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
init_deployable( BOX_TYPE, boxconfig )
|
|
{
|
|
if ( !IsDefined( level.boxSettings ) )
|
|
{
|
|
level.boxSettings = [];
|
|
}
|
|
|
|
level.boxSettings[ BOX_TYPE ] = boxConfig;
|
|
|
|
if ( !IsDefined( level.killStreakFuncs ) )
|
|
{
|
|
level.killStreakFuncs = [];
|
|
}
|
|
|
|
//level.killStreakFuncs[ BOX_TYPE ] = ::default_tryUseDeployable;
|
|
|
|
level.deployable_box[ BOX_TYPE ] = []; // storing each created box in their own array
|
|
} |