#include maps\mp\_utility; #include maps\mp\gametypes\_hud_util; #include common_scripts\utility; #include maps\mp\alien\_utility; //======================================================= // onPlayerKilled //======================================================= onPlayerKilled(eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, killId) { if ( level.gameEnded == true ) return; if ( kill_trigger_event_was_processed() ) return; set_kill_trigger_event_processed( self, true ); maps\mp\alien\_laststand::Callback_PlayerLastStandAlien( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, getKillTriggerSpawnLoc() ); } kill_trigger_event_was_processed() { return is_true( self.kill_trigger_event_processed ); } set_kill_trigger_event_processed( player, value ) { self.kill_trigger_event_processed = value; } //======================================================= // onNormalDeath //======================================================= onNormalDeath( victim, attacker, lifeId ) { if ( game["state"] == "postgame" && game["teamScores"][attacker.team] > game["teamScores"][level.otherTeam[attacker.team]] ) attacker.finalKill = true; } CONST_DANGEROUS_RADIUS = 256; CONST_DANGEROUS_DURATION = 10; CONST_DANGEROUS_DURATION_TRAP = 3; CONST_KILLS_PER_TOKEN_AWARD = 300; //======================================================= // onAlienAgentKilled //======================================================= onAlienAgentKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration ) { // scene aliens suicide skips all regular alien dying logic if ( isdefined( sMeansOfDeath ) && sMeansOfDeath == "MOD_SUICIDE" && isdefined( self.scene ) && self.scene ) return; self.isActive = false; self.hasDied = false; self.owner = undefined; type = self.alien_type; pet_spawned = false; if ( !isDefined ( vDir ) ) vDir = anglesToForward( self.angles ); self maps\mp\alien\_alien_fx::disable_fx_on_death(); if ( sMeansOfDeath == "MOD_TRIGGER_HURT" ) return; // died by hurt trigger // Mark nearby nodes as dangerous - less for traps dangerous_duration = CONST_DANGEROUS_DURATION; if ( maps\mp\alien\_utility::is_trap( eInflictor ) ) { dangerous_duration = CONST_DANGEROUS_DURATION_TRAP; } level thread maps\mp\alien\_utility::mark_dangerous_nodes( self.origin, CONST_DANGEROUS_RADIUS, dangerous_duration ); isPetTrapKill = is_pettrap_kill( eInflictor ); //if killed with a special weapon then turn the alien into a pet if ( sWeapon == "alienthrowingknife_mp" && sMeansofDeath == "MOD_IMPACT" || isPetTrapKill || is_true( self.hypnoknifed ) ) { if ( self maps\mp\alien\_utility::can_hypno( eAttacker, isPetTrapKill ) ) { thread maps\mp\gametypes\aliens::spawnAllyPet( type, 1, self.origin, eAttacker, self.angles , isPetTrapKill ); pet_spawned = true; if ( type == "elite" && isPetTrapKill && isDefined( level.update_achievement_hypno_trap_func ) ) eAttacker [[level.update_achievement_hypno_trap_func]](); } //don't delete the pet trap! if ( !isPetTrapKill ) eInflictor delete(); } should_do_custom_death = false; if ( isDefined( level.custom_alien_death_func ) ) should_do_custom_death = self [[level.custom_alien_death_func]]( eInflictor, eAttacker,iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc ); if ( should_do_pipebomb_death( sWeapon ) ) { self thread do_pipebomb_death(); } else if ( self should_play_death() && sMeansOfDeath != "MOD_SUICIDE" && !pet_spawned && !should_do_custom_death ) play_death_anim_and_ragdoll( eInflictor, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc ); self on_alien_type_killed( pet_spawned ); self maps\mp\agents\alien\_alien_think::OnEnterAnimState( self.currentAnimState, "death" ); // we don't need to enter death so much as exit currentAnimState. //notify for dlc vo eAttacker notify("dlc_vo_notify", get_alien_type() + "_killed", eAttacker); switch ( get_alien_type() ) { case "mammoth": self PlaySoundOnMovingEnt( "queen_death" ); break; case "elite": self PlaySoundOnMovingEnt( "queen_death" ); break; case "minion": self PlaySoundOnMovingEnt( "alien_minion_explode" ); break; case "spitter": self PlaySoundOnMovingEnt( "spitter_death" ); break; default: self PlaySoundOnMovingEnt( "alien_death" ); break; } // chopper reward if ( isdefined( level.attack_heli ) && eAttacker == level.attack_heli ) { reward_point = self maps\mp\alien\_gamescore::get_reward_point_for_kill(); assertex( isdefined( eAttacker.reward_pool ) ); reward_unit = reward_point / eAttacker.reward_pool.size; // reset foreach ( player in eAttacker.reward_pool ) { if ( isdefined( player ) ) player.chopper_reward = 0; } // add foreach ( player in eAttacker.reward_pool ) { if ( isdefined( player ) ) player.chopper_reward += reward_unit; } // give foreach ( player in level.players ) { if ( isdefined( player ) && isdefined( player.chopper_reward ) ) maps\mp\alien\_gamescore::giveKillReward( player, int( player.chopper_reward ), "large" ); } } else { if ( Isdefined( eAttacker.pet ) && ( eAttacker.pet == 1 ) ) { maps\mp\alien\_gamescore::give_attacker_kill_rewards( eAttacker.owner ); } else { maps\mp\alien\_gamescore::give_attacker_kill_rewards( eAttacker, sHitLoc ); } // weaponstats tracking: register weapon shot hit eAttacker thread maps\mp\alien\_persistence::update_weaponstats_kills( sWeapon, 1 ); } //update any challenges related to aliens being killed maps\mp\alien\_challenge_function::update_alien_death_challenges( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration ); //update any achievements related to aliens being killed maps\mp\alien\_achievement::update_alien_kill_achievements( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration ); //update alien session stats related to aliens being killed maps\mp\alien\_persistence::update_alien_kill_sessionStats( eInflictor, eAttacker ); if ( is_chaos_mode() ) maps\mp\alien\_chaos::update_alien_killed_event( get_alien_type(), self.origin, eAttacker ); blackBox_alienKilled( eAttacker ); attacker_as_player = get_attacker_as_player( eAttacker ); if( IsDefined( attacker_as_player ) ) { record_player_kills( attacker_as_player ); check_award_token_for_kill( attacker_as_player ); } level notify( "alien_killed",self.origin, sMeansOfDeath, eAttacker ); } get_attacker_as_player( eAttacker ) { if( IsPlayer( eAttacker ) ) return eAttacker; if ( IsDefined( eAttacker.owner ) && IsPlayer( eAttacker.owner ) ) return eAttacker.owner; return undefined; } record_player_kills( player ) { player maps\mp\alien\_persistence::set_player_kills(); player maps\mp\alien\_persistence::eog_player_update_stat( "kills", 1 ); } check_award_token_for_kill( player ) { killCount = player maps\mp\alien\_persistence::get_player_kills(); if ( killCount % CONST_KILLS_PER_TOKEN_AWARD == 0 ) { player maps\mp\alien\_persistence::give_player_tokens( 1, true ); } } on_alien_type_killed( pet_spawned ) { switch ( self get_alien_type() ) { case "minion": level thread maps\mp\agents\alien\_alien_minion::minion_explode_on_death( self.origin ); break; case "spitter": maps\mp\agents\alien\_alien_spitter::spitter_death(); break; default: // Check for level specific overrides if( isDefined( level.dlc_alien_death_override_func )) self [[level.dlc_alien_death_override_func]]( pet_spawned ); break; } } should_play_death() { switch( get_alien_type() ) { case "minion": case "seeder": case "bomber": return false; default: return true; } } play_death_anim_and_ragdoll( eInflictor, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc ) { ORIENTED_DEATH_OFFSET = 24; APEX_TRAVERSAL_DEATH_OFFSET = 30; if ( GetDvarInt( "alien_easter_egg" ) > 0 || ( isdefined( level.easter_egg_lodge_sign_active ) && level.easter_egg_lodge_sign_active ) ) { PlayFX( level._effect[ "arcade_death" ], self.origin ); //self thread alien_toy_death(); } else { primary_animState = get_primary_death_anim_state(); if ( !is_normal_upright( AnglesToUp( self.angles ) ) ) move_away_from_surface( AnglesToUp( self.angles ), ORIENTED_DEATH_OFFSET ); if ( isDefined( self.apexTraversalDeathVector ) ) move_away_from_surface( self.apexTraversalDeathVector, APEX_TRAVERSAL_DEATH_OFFSET ); play_death_anim_and_ragdoll_internal( primary_animState, eInflictor, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc ); } } should_do_immediate_ragdoll( deathAnimState ) { if ( IsDefined( level.dlc_alien_should_immediate_ragdoll_on_death_override_func ) ) { should_immediate_ragdoll = [[level.dlc_alien_should_immediate_ragdoll_on_death_override_func]]( deathAnimState ); if ( IsDefined( should_immediate_ragdoll ) ) return should_immediate_ragdoll; } switch ( deathAnimState ) { case "jump": case "traverse": return true; default: return false; } } play_death_anim_and_ragdoll_internal( primary_animState, eInflictor, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc ) { if ( is_special_death( primary_animState ) ) { animState = "special_death"; animIndex = self maps\mp\agents\alien\_alien_anim_utils::getSpecialDeathAnimIndex( primary_animState ); } else { animState = self maps\mp\agents\alien\_alien_anim_utils::getDeathAnimState( ( primary_animState + "_death" ), iDamage ); animIndex = self maps\mp\agents\alien\_alien_anim_utils::getDeathAnimIndex( primary_animState , vDir, sHitLoc ); } do_immediate_ragdoll = should_do_immediate_ragdoll( primary_animState ); self ScrAgentSetPhysicsMode( get_death_anim_physics_mode( animState ) ); self SetAnimState( animState, animIndex ); self.body = get_clone_agent( animState, animIndex ); self thread handle_ragdoll( self.body, animState, do_immediate_ragdoll ); } move_away_from_surface( direction, offset_length ) { offsetLocation = self.origin + direction * offset_length; self SetOrigin( offsetLocation ); } get_primary_death_anim_state() { // special death case if ( isdefined( self.shocked ) && self.shocked ) //for electric fence, shock backwards anim return "electric_shock_death"; switch ( self.currentAnimState ) { case "scripted": { return "idle"; } case "move": { if ( self.trajectoryActive ) return "jump"; else return "run"; } case "idle": { return "idle"; } case "melee": { if ( self.trajectoryActive ) return "jump"; if ( self.melee_in_move_back || self.melee_in_posture ) return "idle"; else return "run"; } case "traverse": { if ( self.trajectoryActive ) return "jump"; else return "traverse"; } default: { AssertMsg( "currentAnimState: " + self.currentAnimState + " does not have a death anim mapping." ); } } } is_special_death( primary_animState ) { switch ( primary_animState ) { case "traverse": case "electric_shock_death": return true; default: return false; } } get_death_anim_physics_mode( anim_state ) { switch ( anim_state ) { case "electric_shock": // so alien doesn't get stuck on electric fence geo return "noclip"; default: return "gravity"; } } get_clone_agent( animState, animIndex ) { animEntry = self GetAnimEntry( animState, animIndex ); animLength = GetAnimLength( animEntry ); if ( AnimHasNotetrack( animEntry, "start_ragdoll" ) ) { notetracks = GetNotetrackTimes( animEntry, "start_ragdoll" ); assert( notetracks.size > 0 ); animLength *= notetracks[0]; } deathAnimDuration = int( animLength * 1000 ); // duration in milliseconds return ( self CloneAgent( deathAnimDuration ) ); } handle_ragdoll( corpse, animState, do_immediate_ragdoll ) { deathAnim = corpse getcorpseanim(); if ( !should_do_ragdoll( corpse, deathAnim ) ) return; if ( do_immediate_ragdoll ) { corpse startragdoll(); if ( corpse isRagdoll() ) //Immediate ragdoll succeed return; /# println( "Corpse failed immediate ragdoll at " + corpse.origin ); #/ } delayStartRagdoll( corpse, deathAnim ); if ( !isDefined( corpse ) ) return; // electric fence shock does physics to send aliens flying // TODO: Remove once death animation for shock_death does this if ( animState == "shock_death" ) { self notify( "in_ragdoll", corpse.origin ); } } delayStartRagdoll( corpse, deathAnim ) { totalAnimTime = getanimlength( deathAnim ); if ( animhasnotetrack( deathAnim, "start_ragdoll" ) ) { times = getnotetracktimes( deathAnim, "start_ragdoll" ); startFrac = times[ 0 ]; waitTime = startFrac * totalAnimTime; } else { waitTime = 0.2; } wait( waitTime ); if ( !isDefined( corpse ) ) // Corpse can be deleted during host migration { return; } else { corpse startragdoll(); if ( corpse isRagdoll() ) return; } /# println( "Corpse failed first ragdoll at " + corpse.origin ); #/ // Ragdoll failed, do a final attempt if ( waitTime < totalAnimTime ) { wait ( totalAnimTime - waitTime ); if ( !isDefined( corpse ) ) // Corpse can be deleted during host migration { return; } else { corpse startragdoll(); if ( corpse isRagdoll() ) return; } /# println( "Corpse failed second ragdoll at " + corpse.origin ); #/ } // If final attempt failed, delete the corpse if ( isDefined( corpse ) ) corpse delete(); } should_do_ragdoll( ent, deathAnim ) { if ( ent isRagDoll() ) return false; if ( animhasnotetrack( deathAnim, "ignore_ragdoll" ) ) return false; if ( IsDefined( level.noRagdollEnts ) && level.noRagdollEnts.size ) { foreach( noRag in level.noRagdollEnts ) { if ( distanceSquared( ent.origin, noRag.origin ) < 65536 ) //256^2 return false; } } return true; } blackBox_alienKilled( eAttacker ) { // black box data tracking if ( isPlayer( eAttacker ) || ( isDefined( eAttacker.pet ) && ( eAttacker.pet == 1 ) && isPlayer( eAttacker.petowner ) ) || ( IsDefined( eAttacker.owner ) && IsPlayer( eAttacker.owner ) ) ) { level.alienBBData[ "aliens_killed" ]++; } self notify( "alien_killed" ); // black box data tracking // =========================== blackbox print [START] =========================== // self is agent victim that died // attacker_is_agent attacker_is_agent = IsAgent( eAttacker ); // attacker_alive_time, attacker_agent_type, attacker_name if ( attacker_is_agent ) { attacker_alive_time = ( gettime() - eAttacker.birthtime ) / 1000; attacker_agent_type = "unknown agent"; attacker_name = "none"; if ( isdefined( eAttacker.agent_type ) ) { attacker_agent_type = eAttacker.agent_type; if ( isdefined( eAttacker.alien_type ) ) attacker_agent_type = eAttacker.alien_type; } } else { attacker_alive_time = 0; attacker_name = "none"; if ( isplayer( eAttacker ) ) { attacker_agent_type = "player"; if ( isdefined( eAttacker.name ) ) attacker_name = eAttacker.name; } else { attacker_agent_type = "nonagent"; } } // attacker origin attackerx = 0.0; attackery = 0.0; attackerz = 0.0; if ( isdefined( eAttacker ) && ( IsAgent( eAttacker ) || isPlayer( eAttacker ) ) ) { attackerx = eAttacker.origin[ 0 ]; attackery = eAttacker.origin[ 1 ]; attackerz = eAttacker.origin[ 2 ]; } victim_alive_time = 0; if ( isdefined( self.birthtime ) ) victim_alive_time = ( gettime() - self.birthtime ) / 1000; victim_spawn_origin = ( 0, 0, 0 ); if ( isdefined( self.spawnorigin ) ) victim_spawn_origin = self.spawnorigin; victim_dist_from_spawn = 0; if ( isdefined( self.spawnorigin ) ) victim_dist_from_spawn = distance( self.origin, self.spawnorigin ); victim_damage_done = 0; if ( isdefined( self.damage_done ) ) victim_damage_done = self.damage_done; victim_agent_type = "unknown agent"; if ( isdefined( self.agent_type ) ) { victim_agent_type = self.agent_type; if ( isdefined( self.alien_type ) ) victim_agent_type = self.alien_type; } current_enemy_population = 0; foreach ( agent in level.agentArray ) { if ( !IsDefined( agent.isActive ) || !agent.isActive ) continue; if ( isdefined( agent.team ) && agent.team == "axis" ) current_enemy_population++; } current_player_population = 0; if ( isdefined( level.players ) ) current_player_population = level.players.size; /# if ( GetDvarInt( "alien_bbprint_debug" ) > 0 ) { IPrintLnBold( "^8bbprint: alienkilled (1/2)\n" + " attackerisagent=" + attacker_is_agent + " attackeralivetime=" + attacker_alive_time + " attackeragenttype=" + attacker_agent_type + " attackername=" + attacker_name + " attackerx=" + eAttacker.origin[ 0 ] + " attackery=" + eAttacker.origin[ 1 ] + " attackerz=" + eAttacker.origin[ 2 ] + " victimalivetime=" + victim_alive_time ); IPrintLnBold( "^8bbprint: alienkilled (2/2)\n" + " victimspawnoriginx=" + victim_spawn_origin[ 0 ] + " victimspawnoriginy=" + victim_spawn_origin[ 1 ] + " victimspawnoriginz=" + victim_spawn_origin[ 2 ] + " victimdistfromspawn=" + victim_dist_from_spawn + " victimdamagedone=" + victim_damage_done + " victimagenttype=" + victim_agent_type + " currentenemypopulation=" + current_enemy_population + " currentplayerpopulation=" + current_player_population ); } #/ bbprint( "alienkilled", "attackerisagent %i attackeralivetime %f attackeragenttype %s attackername %s attackerx %f attackery %f attackerz %f victimalivetime %f victimspawnoriginx %f victimspawnoriginy %f victimspawnoriginz %f victimdistfromspawn %i victimdamagedone %i victimagenttype %s currentenemypopulation %i currentplayerpopulation %i ", attacker_is_agent, attacker_alive_time, attacker_agent_type, attacker_name, eAttacker.origin[ 0 ], eAttacker.origin[ 1 ], eAttacker.origin[ 2 ], victim_alive_time, victim_spawn_origin[ 0 ], victim_spawn_origin[ 1 ], victim_spawn_origin[ 2 ], victim_dist_from_spawn, victim_damage_done, victim_agent_type, current_enemy_population, current_player_population ); // =========================== [END] blackbox print =========================== } KILL_TRIGGER_SPAWN_STRUCT_TARGET_NAME = "respawn_edge"; kill_trigger_spawn_init() { level.killTriggerSpawnLocs = getstructArray( KILL_TRIGGER_SPAWN_STRUCT_TARGET_NAME, "targetname" ); } getKillTriggerSpawnLoc() { AssertEx( level.killTriggerSpawnLocs.size > 0, "Need to put script struct around kill triggers with KVP: 'targetname', '" + KILL_TRIGGER_SPAWN_STRUCT_TARGET_NAME + "'" ); return ( getClosest ( self.origin, level.killTriggerSpawnLocs ) ); } should_do_pipebomb_death( sWeapon ) { alientype = self get_alien_type(); //minions already explode, and the Rhino/Elite aliens shouldn't gib if ( alientype == "minion" || alientype == "elite" || alientype == "mammoth" ) return false; return ( isDefined( sWeapon ) && sWeapon == "iw6_aliendlc22_mp" ); //pipe bomb } do_pipebomb_death() { PlayFx( level._effect[ "alien_gib" ], self.origin + (0,0,32) ); } is_pettrap_kill( eInflictor ) { return ( isDefined( eInflictor ) && isDefined( eInflictor.is_pet_trap ) ); } general_alien_custom_death( eInflictor, eAttacker,iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc ) { is_player = IsDefined( eAttacker ) && isPlayer ( eAttacker ); if ( is_player && isDefined ( sWeapon ) && weapon_has_alien_attachment( sWeapon ) && sMeansOfDeath != "MOD_MELEE" && !is_true ( level.easter_egg_lodge_sign_active ) ) { PlayFx( level._effect[ "alien_ark_gib" ], self.origin + (0,0,32) ); return true; } else return false; }