#include maps\_utility; #include common_scripts\utility; #include animscripts\Utility; #include animscripts\Debug; #include maps\_anim; #using_animtree ("generic_human"); precacheReviveModels() { if ( IsDefined( level.reviveFeature ) && !level.reviveFeature ) return; precacheModel( "char_rus_bandages1" ); } revive_main() { anim.nextReviveSequenceTime = GetTime() + RandomIntRange(0, 20000); anim.nextReviveSequencePlayerTimeMin = 15000; anim.nextReviveSequencePlayerTimeMax = 30000; anim.nextReviveSequenceTimeMin = 5000; anim.nextReviveSequenceTimeMax = 10000; anim.bleederBleedOutTimeMin = 5000; anim.bleederBleedOutTimeMax = 10000; anim.reviverPingDist = 512; anim.reviverIgnorePlayerDistSq = 200*200; anim.reviveSequencePlayerVisibleDistSq = 1000*1000; anim.revive = []; anim.bleed["stand"]["fall"] = %ai_revive_wounded_onback_fall_stand; anim.bleed["crouch"]["fall"] = %ai_revive_wounded_onback_fall_crouch; anim.bleed["prone"]["fall"] = %ai_dying_crawl_2_back_revive; anim.bleed["bleed_loop"] = %ai_revive_wounded_onback_loop; anim.bleed["left"]["being_revived"] = %ai_revive_wounded_onback_left_revive; anim.bleed["left"]["get_up"] = %ai_revive_wounded_onback_left_get_up; anim.revive["left"]["reviving"] = %ai_revive_reviver_onback_left_revive; anim.revive["left"]["get_up"] = %ai_revive_reviver_onback_left_get_up; } revive_init() { self.a.revivedOnce = false; self.a.reviveMe = false; self.a.falling = false; self.a.bleeding = false; self.a.isReviver = false; self.a.isReviving = false; self.a.canBleed = true; self.a.canRevive = true; self.a.reviver = undefined; self.a.bleeder = undefined; self.a.oldRevivers = []; } tryGoingDown(damage, hitloc) { Assert(IsDefined(level.reviveFeature), "level.reviveFeature is not initialized."); if(!level.reviveFeature) return false; if(!GetDvarInt( #"scr_aiReviveFeature")) { if(!shouldReviveSequenceHappen()) return false; } if( !IsAI( self ) || !IsAlive(self) || (self.isdog) || (self.team != "axis") || (self.a.canBleed == false) || (self.a.pose == "back") ) return false; if( self.a.pose == "prone" ) return false; if( IsDefined( self.a.usingTurret ) ) return false; if(!GetDvarInt( #"scr_aiReviveFeature")) { friendlies = GetAIArray(self.team); alive_nearby_ai = get_array_of_closest( self.origin, friendlies, undefined, undefined, anim.reviverPingDist ); if( alive_nearby_ai.size <= 2 ) return false; } if( (self.a.isreviver == false ) && (self.a.revivedOnce == false) && (self.a.reviveMe == false) && isAIDamageFatal(damage) && !damageLocation(hitloc) ) { if( !IsReviveOnSafeTerrain() ) return false; Assert(IsAlive(self) == true, "AI is already dead or in extended death and cant be put down."); self.a.reviveMe = true; self thread killMeOnScriptInterrupt(); self.health = self.health + damage + RandomIntRange(60,100); return true; } return false; } killMeOnScriptInterrupt() { self endon( "death" ); self waittill( "killanimscript" ); self waittill( "killanimscript" ); if( self isBleeder() && IsAlive(self) ) { self DoDamage( self.health + 200, self.origin ); } } revive_strat() { self endon("death"); self endon("killanimscript"); self thread bleeder_bleed_to_death(); self thread fall_down_to_bleed(); self waittill("ready_after_revived"); } reviver_selection_think(team) { self endon("death"); self endon("revived"); self endon("reevaluate_reviver"); self endon("killanimscript"); Assert(IsAlive(self) == true, "AI is already dead or in extended death and cant be put down."); if( self.a.revivedOnce != true ) { self waittill_players_goes_away(); reviver_found = false; while(!reviver_found) { friendlies = GetAIArray( self.team ); if( self.a.oldRevivers.size > 0 ) ai = get_array_of_closest( self.origin, friendlies, self.a.oldRevivers, undefined, anim.reviverPingDist ); else ai = get_array_of_closest( self.origin, friendlies, undefined, undefined, anim.reviverPingDist ); for( i = 0; i < ai.size; i++ ) { current_ai = ai[i]; if( isAIOldReviver( current_ai ) || ( current_ai.a.canRevive == false ) ) continue; if( IsDefined( current_ai.ignoreall ) && current_ai.ignoreall == true ) continue; if( ( current_ai != self ) && IsDefined( current_ai.a.revivedOnce ) && ( current_ai.a.reviveMe == false ) && ( current_ai.a.isReviver == false ) ) { AssertEx(IsDefined(self.predictedRevivePoint), "Predicted revive point is not calculated."); if( findpath( current_ai.origin, self.predictedRevivePoint ) ) { reviver_found = true; self.a.oldRevivers[self.a.oldRevivers.size] = current_ai; self.a.reviver = current_ai; current_ai.a.bleeder = self; self thread revive_process(); break; } } } if( !reviver_found ) wait(2); } } } revive_process() { self endon("death"); self.a.reviver thread reviver_think(); self thread handle_bleeder_death(); self.a.reviver thread handle_reviver_death(); } waittill_players_goes_away() { while( IsAlive(self) ) { player = get_closest_player(self.origin); if( DistanceSquared( player.origin, self.origin) > anim.reviverIgnorePlayerDistSq ) break; wait(1); } } handle_reviver_death() { self endon("revive_complete"); self endon("bleeder_death"); self waittill_any( "death" ); self free_reviver("death"); wait(0.05); if( IsDefined( self.a.bleeder ) && IsAlive(self.a.bleeder)) { if( self.a.revivedOnce ) { self.a.bleeder thread bleeder_getup(); } else { self .a.bleeder play_bleed_loop(); self.a.bleeder thread reviver_selection_think(self.a.bleeder.team); } } } handle_reviver_goal_change() { self endon("revive_complete"); self endon("bleeder_death"); self endon("reevaluate_reviver"); self endon("reviving_bleeder"); self waittill_any("goalradius_changed"); self free_reviver("goalradius_changed"); wait(0.05); if( IsDefined( self.a.bleeder ) && IsAlive(self.a.bleeder) ) { if( self.a.revivedOnce ) self.a.bleeder thread bleeder_getup(); else self.a.bleeder thread reviver_selection_think(self.a.bleeder.team); } } handle_bleeder_death(reviver) { self endon("revived"); self endon("reevaluate_reviver"); self waittill("death"); self.a.reviver notify("bleeder_death"); self.a.reviver free_reviver("bleeder_death"); } reviver_think() { self endon("death"); self endon("bleeder_death"); self endon("reevaluate_reviver"); self.a.isReviver = true; while(!self.a.bleeder.a.bleeding) { wait(0.05); } self.ReviveOldgoalradius = self.goalradius; self.goalradius = 4; self thread handle_reviver_goal_change(); revive_anim = getReviverReviveAnim(); approach_angle = GetStartAngles( self.a.bleeder.origin, self.a.bleeder.angles, revive_anim ); revive_point = getRevivePoint(self.a.bleeder); self SetGoalPos(revive_point, approach_angle); self waittill("goal"); self reviver_revive(revive_anim, revive_point); } free_reviver(reason) { self endon("death"); if( IsDefined(self) ) { self.a.isReviver = false; self.a.isReviving = false; self.ignoreme = false; if( IsDefined(reason) && ( (reason == "death") || (reason == "goalradius_changed" ) ) ) { self notify("reevaluate_reviver"); self.a.bleeder notify("reevaluate_reviver"); } if( IsDefined(self.ReviveOldgoalradius) ) self.goalradius = self.ReviveOldgoalradius; if( IsAlive(self) && IsDefined(reason) && reason != "death" ) { self notify( "killanimscript" ); waittillframeend; self thread call_overloaded_func( "animscripts\combat", "main" ); } } } free_bleeder() { self endon("death"); if (IsDefined(self)) { self.a.reviveMe = false; self.a.bleeding = false; self.a.falling = false; self.health = RandomIntRange(90, 120); self notify("ready_after_revived"); if( IsDefined( self.a.special ) && self.a.special == "bleeder_death" ) self.a.special = "none"; if( IsAlive(self) ) { self notify( "killanimscript" ); waittillframeend; self thread call_overloaded_func( "animscripts\combat", "main" ); } } } reviver_revive(revive_anim, revive_point) { self endon("bleeder_death"); self.a.isReviving = true; bleeder = self.a.bleeder; bleeder notify("being_revived"); self notify("reviving_bleeder"); self.ignoreme = true; resetReviveSequenceTimer(); bleeder SetFlaggedAnimKnob( "being_revived", getBleederReviveAnim(), 1, 0.1, 1 ); self AnimScripted( "revive", bleeder.origin, bleeder.angles, revive_anim, "normal", %body, 1 ); self animscripts\shared::DoNoteTracks("revive", ::handleReviverNotetracks); self notify("revive_complete"); self revive_getup_process(); } revive_getup_process() { self.a.bleeder thread bleeder_getup(); self animcustom( ::reviver_getup ); } bleeder_getup() { self SetFlaggedAnimKnobAllRestart( "bleeder_get_up", getBleederGetUpAnim(), %body ,1, 0.1, 1.0 ); self animscripts\shared::DoNoteTracks( "bleeder_get_up" ); self free_bleeder(); } reviver_getup() { self SetFlaggedAnimKnobAllRestart( "reviver_get_up", getReviverGetUpAnim(), %body, 1, 0.1, 1.0 ); self animscripts\shared::DoNoteTracks( "reviver_get_up" ); self free_reviver("revive_complete"); } bleeder_bleed_to_death() { self endon("revived"); self.bleedOutTime = RandomIntRange(anim.bleederBleedOutTimeMin,anim.bleederBleedOutTimeMax); wait( self.bleedOutTime/1000 ); if( IsDefined(self) && IsAlive(self) ) self DoDamage( self.health + 200, self.origin ); } fall_down_to_bleed() { self endon("death"); self endon("revived"); self endon("being_revived"); self endon("killanimscript"); self SetAnimKnobAll( %revive, %body, 1, 0.1, 1 ); transAnim = getTransitionAnim(); self.a.falling = true; self SetFlaggedAnimKnob( "fall_transition", transAnim, 1, 0.1, 1.5 ); self thread handleBleederNotetracks("fall_transition"); self animscripts\shared::DoNoteTracks( "fall_transition"); Assert( self.a.pose == "back", "Fall transition animation is missing the anim_pose = back notetrack." ); self ClearAnim(transAnim, 0.2); self.a.bleeding = true; self play_bleed_loop(); } play_bleed_loop() { self SetFlaggedAnimKnobAllRestart( "bleeding", getBleedLoopAnim(), %body ,1, 0.1, 1.0 ); } handleBleederNotetracks(anim_name) { self endon("death"); for (;;) { self waittill(anim_name, note); switch ( note ) { case "reviver_selection": { self thread reviver_selection_think(self.team); break; } case "anim_pose = \"back\"": { self.a.special = "bleeder_death"; break; } } if(note == "end") { break; } } } handleReviverNotetracks(note) { switch ( note ) { case "attach_bandage": { bleeder = self.a.bleeder; bleeder Attach("char_rus_bandages1"); bleeder.a.revivedOnce = true; bleeder notify("revived"); break; } } } getTransitionAnim() { assert( self.a.pose == "stand" || self.a.pose == "crouch" || self.a.pose == "prone" ); transitionAnim = anim.bleed[self.a.pose]["fall"]; Assert(animHasNoteTrack( transitionAnim, "anim_pose = \"back\"" )); Assert(animHasNoteTrack( transitionAnim, "reviver_selection" )); return transitionAnim; } getBleedLoopAnim() { bleedLoopAnim = anim.bleed["bleed_loop"]; return bleedLoopAnim; } getBleederReviveAnim() { reviveAnim = anim.bleed["left"]["being_revived"]; return reviveAnim; } getReviverReviveAnim() { reviveAnim = anim.revive["left"]["reviving"]; Assert(animHasNoteTrack( reviveAnim, "anim_pose = \"crouch\"" )); Assert(animHasNoteTrack( reviveAnim, "attach_bandage" )); return reviveAnim; } getBleederGetUpAnim() { bleederGetUpAnim = anim.bleed["left"]["get_up"]; Assert(animHasNoteTrack( bleederGetUpAnim, "anim_pose = \"stand\"" )); return bleederGetUpAnim; } getReviverGetUpAnim() { reviverGetUpAnim = anim.revive["left"]["get_up"]; Assert(animHasNoteTrack( reviverGetUpAnim, "anim_pose = \"stand\"" )); return reviverGetUpAnim; } damageLocation(hitloc) { if( hitloc == "helmet" || hitloc == "head" ) return true; return false; } isAIOldReviver(ai) { if( self.a.oldRevivers.size > 0 ) { for( i = 0; i < self.a.oldRevivers.size; i++ ) { if( IsDefined( self.a.oldRevivers[i] ) && self.a.oldRevivers[i] == ai ) return true; } } return false; } getRevivePoint(bleeder) { revive_point = GetStartOrigin(bleeder.origin, bleeder.angles, getReviverReviveAnim() ); return revive_point; } IsReviveOnSafeTerrain() { self endon("death"); groundPos = physicstrace( self.origin, self.origin - ( 0, 0, 10000 ) ); bleederDistanceFromGround = distance( self.origin, groundPos ); if( ( bleederDistanceFromGround > 2 ) || ( bleederDistanceFromGround < 0 ) ) return false; angleDelta = getAngleDelta(anim.bleed[self.a.pose]["fall"], 0, 1); finalYaw = self.angles[1] + angleDelta; finalAngles = ( self.angles[0], finalYaw, self.angles[2] ); moveDelta = GetMoveDelta( anim.bleed[self.a.pose]["fall"], 0, 1 ); endPoint = self localToWorldCoords( moveDelta ); if( !self mayMoveToPoint( endPoint ) ) return false; self.predictedRevivePoint = getPredictedRevivePoint( endPoint, finalAngles ); groundPos = physicstrace( self.predictedRevivePoint, self.predictedRevivePoint - ( 0, 0, 10000 ) ); revivePointDistanceFromGround = distance( self.predictedRevivePoint, groundPos ); if( revivePointDistanceFromGround < 0 || revivePointDistanceFromGround > 15 ) return false; diff = abs( bleederDistanceFromGround - revivePointDistanceFromGround ); if( diff > 15 ) return false; return true; } getPredictedRevivePoint( endPoint, finalAngles ) { revive_point = GetStartOrigin(endPoint, finalAngles, getReviverReviveAnim()); return revive_point; } isAIDamageFatal(damage) { Assert(IsAlive(self) == true, "AI is already dead or in extended death and cant be put down."); health = self.health - damage; if( health <= 0 ) return true; return false; } shouldBleed() { self endon("death"); Assert(IsAlive(self) == true, "AI is already dead or in extended death and cant be put down."); Assert(IsAlive(self.a.revivedOnce) == false, "AI is already revived/bleeded once."); if( ( self.a.reviveMe == true ) && ( self.a.bleeding == false ) && ( self.a.falling == false ) ) return true; return false; } isBleedingOrFalling() { Assert(IsAlive(self) == true, "AI is already dead or in extended death and cant be put down."); if( (self.a.bleeding == true) || (self.a.falling == true) ) return true; return false; } shouldReviveSequenceHappen() { if ( GetTime() < anim.nextReviveSequenceTime ) { return false; } if( ( randomint(100) > 40 ) ) return false; return true; } resetReviveSequenceTimer() { players = GetPlayers(); anybody_nearby = 0; for (i=0;i