///////////////////////////////////////////////////////////////////////////// // // File : knockback_manager.skrit // Author(s): Witness (Lisa Hui) // Purpose : "knocks" back and stuns a single target // //---------------------------------------------------------------------------- // Version: 1.0 Date: August 28, 2004 //---------------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////////// property float stun_duration$ = 1.0 doc = ""; property string stun_effect_script$ = "" doc = ""; property string stun_effect_params$ = "" doc = ""; property float drift_speed$ = 9.0 doc = ""; property float drift_distance_min$ = 1.0 doc = ""; property float drift_distance_max$ = 2.0 doc = ""; property string drift_effect_script$ = "" doc = ""; property string drift_effect_params$ = "" doc = ""; owner = GoSkritComponent; ////////////////////////////////////////////////////////////////////////////// // GLOBALS ////////////////////////////////////////////////////////////////////////////// Goid user$, target$; ////////////////////////////////////////////////////////////////////////////// // STATES ////////////////////////////////////////////////////////////////////////////// startup State Wait$ { event OnGoHandleMessage$( eWorldEvent e$, WorldMessage msg$ ) { //Report.Screenf("knockback_manager heard: %s", ToString( e$ )); if ( e$ == WE_REQ_ACTIVATE ) { user$ = msg$.SendFrom; target$ = MakeGoid( msg$.Data1 ); if ( !user$.IsValid || !target$.IsValid ) { return; } target$.Go.Actor.SAddGenericState( "knockback", "Stunned", stun_duration$, user$, owner.Goid, 1.0 ); //Report.Screenf("knockback_manager - watching target messages"); GoDb.StartWatching( owner.Goid, target$ ); if ( drift_distance_max$ == 0.0 ) { SetState Stunned$; return; } SetState Drifting$; } } } State Drifting$ { float orig_speed$; bool drifted$; SFxSID drift_effect_id$ = 0; event OnEnterState$ { //Report.Screen("knockback_manager - OnEnterState$ - Drifting$"); //Report.Screenf("---> calculating KNOCKBACK"); drifted$ = false; SiegePos pos$ = target$.Go.Mind.TempPos1; if ( !AIQuery.FindSpotRelativeToSource( user$.Go, target$.Go, false, drift_distance_min$, drift_distance_max$, 0.0, 0.1, 0.3, pos$, false ) ) { //Report.Screenf("---> could not find knockback position"); SetState Stunned$; return; } //create point to check LOS, as none of the other IsLosClear functions seem to work... GoCloneReq req$ = MakeGoCloneReq( "point" ); req$.StartingPos = pos$; Goid point$ = GoDb.SCloneGo( req$ ); //check for line of sight if ( !AIQuery.IsLosClear( target$.Go, point$.Go ) ) { //Report.Screen("---> knockback is blocked, look for a closer position..."); if ( !AIQuery.FindClearLosPoint( target$.Go, point$.Go, AIQuery.GetDistance( target$.Go.Placement.Position, pos$ ), 0.1, pos$ ) ) { //Report.Screen("---> couldn't find clear LOS knockback destination"); SetState Stunned$; return; } } //dispose of point GoDb.RSMarkForDeletion( point$, true, false, false ); //change speed and become rigid for drifting orig_speed$ = target$.Go.Body.GetAvgMoveVelocity(); target$.Go.Body.SetAvgMoveVelocity( drift_speed$ ); target$.Go.Aspect.SSetLockMeshFlag( true ); drifted$ = true; //play drift sfx if ( drift_effect_script$ != "" ) { drift_effect_id$ = SiegeFx.SRunScript( "knockback_drift", target$, user$, "", owner.Goid, WE_REQ_CAST ); } //run drift sequence MCPManager.Flush( target$ ); eReqRetCode ret$ = MCPManager.MakeRequest( target$, PL_APPROACH, pos$, 0.0, //can stop upon achieving this distance from pos REQFLAG_FACEREVERSE ); //Report.Screenf( "PL_APPROACH returned [%s]", ToString(ret$) ); } event OnGoHandleMessage$( eWorldEvent e$, WorldMessage msg$ ) { if ( e$ == WE_REQ_ACTIVATE ) { if ( target$ != MakeGoid( msg$.Data1 ) ) { return; } user$ = msg$.SendFrom; if ( !user$.IsValid ) { return; } target$.Go.Actor.SRemoveGenericState( "knockback" ); target$.Go.Actor.SAddGenericState( "knockback", "Stunned", stun_duration$, user$, owner.Goid, 1.0 ); } } event OnExitState$ { if ( drifted$ ) { //if ( drift_effect_id$ != 0 ) { // SiegeFx.SStopScript( drift_effect_id$ ); //} target$.Go.Body.SetAvgMoveVelocity( orig_speed$ ); target$.Go.Aspect.SSetLockMeshFlag( false ); } } transition { //-> Exiting$ : OnGoHandleCCMessage( WE_MCP_INVALIDATED ); //-> Stunned$ : OnGoHandleCCMessage( WE_MCP_CHORE_CHANGING ); -> Exiting$ : OnGoHandleCCMessage( WE_KILLED ); -> Stunned$ : OnGoHandleCCMessage( WE_MCP_SECTION_COMPLETED ); //should be done moving at this point -> Stunned$ : OnGoHandleCCMessage( WE_ANIM_DONE ); // if anim is finished playing, target is NOT in a walk loop (safety check) -> Stunned$ : OnGoHandleCCMessage( WE_MCP_DEPENDANCY_BROKEN ); //path blocked -> Stunned$ : OnGoHandleCCMessage( WE_DAMAGED ) = { //stop drifting immediately if hit MCPManager.Flush( target$ ); } } } State Stunned$ { SFxSID stun_effect_id$ = 0; event OnEnterState$ { //Report.Screen("knockback_manager - OnEnterState$ - Stunned$"); if ( !target$.IsValid ) { SetState Exiting$; return; } eLifeState state$ = target$.Go.LifeState; if ( !IsConscious( state$ ) || !IsAlive( state$ ) ) { //dead or unconscious targets don't need stunning SetState Exiting$; return; } //reset state timer target$.Go.Actor.SRemoveGenericState( "knockback" ); target$.Go.Actor.SAddGenericState( "knockback", "Stunned", stun_duration$, user$, owner.Goid, 1.0 ); //briefly suspend thoughts MCPManager.MakeRequest( target$, PL_FACE ); target$.Go.Mind.Clear(); MCPManager.Flush( target$ ); //run stun sfx if ( stun_effect_script$ != "" ) { stun_effect_id$ = SiegeFx.SRunScript( stun_effect_script$, target$, user$, stun_effect_params$, owner.Goid, WE_REQ_ACTIVATE ); } this.CreateTimer( 1, stun_duration$ ); } event OnGoHandleMessage$( eWorldEvent e$, WorldMessage msg$ ) { if ( e$ == WE_REQ_ACTIVATE ) { if ( target$ != MakeGoid( msg$.Data1 ) ) { return; } user$ = msg$.SendFrom; if ( !user$.IsValid ) { return; } if ( drift_distance_max$ == 0.0 ) { SetState Stunned$; return; } target$.Go.Actor.SRemoveGenericState( "knockback" ); target$.Go.Actor.SAddGenericState( "knockback", "Stunned", stun_duration$, user$, owner.Goid, 1.0 ); SetState Drifting$; } } event OnExitState$ { //stop the effect if ( stun_effect_id$ != 0 ) { SiegeFx.SStopScript( stun_effect_id$ ); } //rouse the target... target$.Go.Mind.SDoJob( MakeJobReq( JAT_BRAIN, JQ_BRAIN, QP_CLEAR, AO_REFLEX ) ); } transition { -> Exiting$ : OnTimer( 1 ); -> Exiting$ : OnGoHandleCCMessage( WE_KILLED ); } } State Exiting$ { event OnEnterState$ { //Report.Screen("knockback_manager - OnEnterState$ - Exiting$"); if ( target$.IsValid ) { target$.Go.Actor.SRemoveGenericState( "knockback" ); GoDb.StopWatching( owner.Goid, target$ ); } GoDb.RSMarkForDeletion( owner.Goid, true, false, false ); } }