/*-- Aim Manager Authors: Randrian Manages loading, aiming and shooting The weapon has to define an animations set which can have the following entries animation_set = { AimMode = AIM_Position, // The aiming mode see constants for explanations AnimationAim = "BowAimArms", // The animation that is used during aiming AnimationAim2 = "", // The animation the first is blended with (only for mode AIM_Weight) AimTime = nil, // Time for the aim animation cycle (only for mode AIM_Weight) AnimationLoad = "BowLoadArms",// The animation during loading LoadTime = 30, // The duration of the loading animation LoadTime2 = 5, // For a callback LoadTime2 frames after LoadShoot: DuringLoad AnimationShoot = nil, // The animation for shooting AnimationShoot2 = nil, // If not nil the shooting will blend according to angle (0° animation 1, 180° animation 2) AnimationShoot3 = nil, // If not nil blending between 3 actions (90° anination 1, 0° animation 2, 180° animation 3) ShootTime = 20, // The duration of shooting ShootTime2 = 5, // For a callback ShootTime2 frames after StartShoot: DuringShoot TurnType = 1, // Specify a special turn type (0: turn 120 degrees, 1: turn 180 degrees, 2: turn 220 degrees) see SetTurnType in clonk WalkSpeed = 84, // Reduce the walk speed during aiming WalkBack = 56, // Reduce the walk speed for walking backwards (speed 0 means no walking backwards at all) AimSpeed = 5, // the speed of aiming default 5° per frame AnimationReplacements = [ // Replace some animations with others during load/aim/shoot ["Walk", "BowWalk"], ["Walk_Position", 20], // Walk_Position changes the distance the clonk travels in one cycle of the animation ["Stand", "BowStand"], ["Jump", "BowJump"], ["KneelDown", "BowKneel"] ], // If an animation is nil there is no new animation played and the clonk just waits *Time frames }; The weapon gets the following callbacks GetAnimationSet(); // Has to return the animation set // The following Stop* Callbacks, have to return true if the clonk doesn't have to be reset (e.g. stating aiming after loading) FinishedLoading(object clonk); // When the loading animation is over (after LoadTime frames) FinishedAiming(object clonk, int angle); // When the clonk has finished loading and aiming at the desired position FinishedShooting(object clonk, int angle); // When the shooting animation is over (after ShootTime frames) DuringLoad(object clonk); // LoadTime2 frames after load start DuringShoot(object clonk, int angle); // ShootTime2 frames after shoot start // When the clonk has during aiming an action where he can't use his hands, the aiming is paused Reset(object clonk); // Callback when the clonk has been reseted The Weapon can use the following functions on the clonk: StartLoad(object weapon); // The weapon wants to start loading (e.g. on ControlUseStart) StartAim(object weapon); // The weapon wants to switch in aim mode (e.g. after loading on StopLoad) SetAimPosition(int angle); // The weapon specifies a new angle (e.g. on ControlUseHolding) StopAim(); // The weapon wants to shoot (e.g. on ControlUseStop) BUT: the stop is just scheduled! The clonk finished loading or aiming and then really stops aiming! StartShoot(object weapon); // The weapon wants to start the shoot animation (e.g. on StopAim) CancelAiming(); // The weapon wants to cancel the aiming (e.g. on ControlUseCancel) // When the clonk starts with load/aim/shoot (when StartLoad, StartAim or StartShoot is called) he uses the animation set // the means he replaces the animtione specified in AnimationReplacements and adjust the TurnType and Walkspeed (if these are not nil) --*/ local aim_set; local aim_weapon; local aim_animation_index; local aim_angle; local aim_stop; local aim_schedule_timer; local aim_schedule_call; local aim_schedule_timer2; local aim_schedule_call2; // Aim modes static const AIM_Position = 1; // The aim angle alters the position of the animation (0° menas 0% animation position, 180° menas 100% andimation position) static const AIM_Weight = 2; // The aim angle alters with blending between AnimationAim (for 0°) and AnimationAim2 (for 180°) func ReadyToAction() { return _inherited(...); } func SetHandAction() { return _inherited(...); } func ReplaceAction() { return _inherited(...); } func SetTurnType () { return _inherited(...); } func SetTurnForced() { return _inherited(...); } func SetBackwardsSpeed() { return _inherited(...); } func IsAiming() { return !!GetEffect("IntAim", this); } func FxIntAimCheckProcedureStart(target, effect, tmp) { if(tmp) return; } func FxIntAimCheckProcedureTimer() { // Care about the aim schedules if(aim_schedule_timer != nil) { aim_schedule_timer--; if(aim_schedule_timer == 0) { Call(aim_schedule_call); aim_schedule_call = nil; aim_schedule_timer = nil; } } if(aim_schedule_timer2 != nil) { aim_schedule_timer2--; if(aim_schedule_timer2 == 0) { Call(aim_schedule_call2); aim_schedule_call2 = nil; aim_schedule_timer2 = nil; } } // check procedure if(!ReadyToAction()) PauseAim(); } func FxIntAimCheckProcedureStop(target, effect, reason, tmp) { if(tmp) return; if(reason == 4) CancelAiming(); } func PauseAim() { if(!aim_weapon || aim_stop) return CancelAiming(); // reissue the CON_Use command to the weapon when ready this->PauseUse(aim_weapon); CancelAiming(); } public func StartLoad(object weapon) { // only if we aren't adjusted to this weapon already if(weapon != aim_weapon) { // Reset old if(aim_weapon != nil) aim_weapon->~Reset(); if(aim_set != nil) ResetHands(); // Remember new aim_weapon = weapon; aim_set = weapon->~GetAnimationSet(); // Applay the set ApplySet(aim_set); // Add effect to ensure procedure AddEffect("IntAimCheckProcedure", this, 1, 1, this); } if(aim_set["AnimationLoad"] != nil) PlayAnimation(aim_set["AnimationLoad"], CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, GetAnimationLength(aim_set["AnimationLoad"]), aim_set["LoadTime"], ANIM_Remove), Anim_Const(1000)); aim_schedule_timer = aim_set["LoadTime"]; aim_schedule_call = "StopLoad"; if(aim_set["LoadTime2"] != nil) { aim_schedule_timer2 = aim_set["LoadTime2"]; aim_schedule_call2 = "DuringLoad"; } } public func DuringLoad() { aim_weapon->~DuringLoad(this); } public func StopLoad() { if(!aim_weapon || !aim_weapon->~FinishedLoading(this)) // return 1 means the weapon goes on doing something (e.g. start aiming) then we don't reset ResetHands(); } public func StartAim(object weapon, int angle) { // only if we aren't adjusted to this weapon already if(weapon != aim_weapon) { // Reset old if(aim_weapon != nil) aim_weapon->~Reset(); if(aim_set != nil) ResetHands(); // Remember new aim_weapon = weapon; aim_set = weapon->~GetAnimationSet(); // Apply the set ApplySet(aim_set); // Add effect to ensure procedure AddEffect("IntAimCheckProcedure", this, 1, 1, this); } if(aim_set["AnimationAim"] != nil) { if(aim_set["AimMode"] == AIM_Position) aim_animation_index = PlayAnimation(aim_set["AnimationAim"], CLONK_ANIM_SLOT_Arms, Anim_Const(GetAnimationLength(aim_set["AnimationAim"])/2), Anim_Const(1000)); if(aim_set["AimMode"] == AIM_Weight) { aim_animation_index = PlayAnimation(aim_set["AnimationAim"], CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, GetAnimationLength(aim_set["AnimationAim"]), aim_set["AimTime"], ANIM_Loop), Anim_Const(1000)); aim_animation_index = PlayAnimation(aim_set["AnimationAim2"], CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, GetAnimationLength(aim_set["AnimationAim2"]), aim_set["AimTime"], ANIM_Loop), Anim_Const(1000), aim_animation_index); aim_animation_index++; SetAnimationWeight(aim_animation_index, Anim_Const(500)); } } // aim_angle = -90; // if(GetDir()) aim_angle = 90; AddEffect("IntAim", this, 1, 1, this); } func FxIntAimTimer(target, effect, time) { var angle, delta_angle, length; var speed = aim_set["AimSpeed"]; if(speed == nil) speed = 50; else speed *= 10; if(aim_angle < 0) SetTurnForced(DIR_Left); if(aim_angle > 0) SetTurnForced(DIR_Right); if(aim_set["AimMode"] == AIM_Position) { length = GetAnimationLength(aim_set["AnimationAim"]); angle = Abs(aim_angle)*10;//GetAnimationPosition(aim_animation_index)*1800/length; delta_angle = 0;//BoundBy(Abs(aim_angle*10)-angle, -speed, speed); SetAnimationPosition(aim_animation_index, Anim_Const( (angle+delta_angle)*length/1800 )); } if(aim_set["AimMode"] == AIM_Weight) { angle = Abs(aim_angle)*10;//GetAnimationWeight(aim_animation_index)*1800/1000; delta_angle = 0;//BoundBy(Abs(aim_angle*10)-angle, -speed, speed); SetAnimationWeight(aim_animation_index, Anim_Const( (angle+delta_angle)*1000/1800 )); } // We have reached the angle and we want to stop if(Abs(delta_angle) <= 5 && aim_stop == 1) { DoStopAim(); return -1; } } // returns the current aiming angle public func GetAimPosition() { return aim_angle; } public func SetAimPosition(int angle) { // Save angle aim_angle = angle; // Remove scheduled stop if aiming again aim_stop = 0; } public func StopAim() { // Schedule Stop aim_stop = 1; } private func DoStopAim() { // Return true means the weapon goes on doing something (e.g. start aiming) then we don't reset. if (!aim_weapon || !aim_weapon->~FinishedAiming(this, aim_angle)) ResetHands(); return; } public func StartShoot(object weapon) { // only if we aren't adjusted to this weapon already if(weapon != aim_weapon) { // Reset old if(aim_weapon != nil) aim_weapon->~Reset(); if(aim_set != nil) ResetHands(); // Remember new aim_weapon = weapon; aim_set = weapon->~GetAnimationSet(); // Applay the set ApplySet(aim_set); // Add effect to ensure procedure AddEffect("IntAimCheckProcedure", this, 1, 1, this); } if(aim_set["AnimationShoot"] != nil) { // Do we just have one animation? Then just play it if(aim_set["AnimationShoot2"] == nil) PlayAnimation(aim_set["AnimationShoot"], CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, GetAnimationLength(aim_set["AnimationShoot"]), aim_set["ShootTime"], ANIM_Remove), Anim_Const(1000)); // Well two animations blend betweend them (animtion 1 is 0° animation2 for 180°) else if(aim_set["AnimationShoot3"] == nil) { var iAim; iAim = PlayAnimation(aim_set["AnimationShoot"], CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, GetAnimationLength(aim_set["AnimationShoot"] ), aim_set["ShootTime"], ANIM_Remove), Anim_Const(1000)); iAim = PlayAnimation(aim_set["AnimationShoot2"], CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, GetAnimationLength(aim_set["AnimationShoot2"]), aim_set["ShootTime"], ANIM_Remove), Anim_Const(1000), iAim); SetAnimationWeight(iAim+1, Anim_Const(1000*Abs(aim_angle)/180)); } // Well then we'll have three to blend (animation 1 is 90°, animation 2 is 0°, animation 2 for 180°) else { var iAim; if(Abs(aim_angle) < 90) { iAim = PlayAnimation(aim_set["AnimationShoot2"], CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, GetAnimationLength(aim_set["AnimationShoot2"]), aim_set["ShootTime"], ANIM_Remove), Anim_Const(1000)); iAim = PlayAnimation(aim_set["AnimationShoot"], CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, GetAnimationLength(aim_set["AnimationShoot"] ), aim_set["ShootTime"], ANIM_Remove), Anim_Const(1000), iAim); SetAnimationWeight(iAim+1, Anim_Const(1000*Abs(aim_angle)/90)); } else { iAim = PlayAnimation(aim_set["AnimationShoot"], CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, GetAnimationLength(aim_set["AnimationShoot"] ), aim_set["ShootTime"], ANIM_Remove), Anim_Const(1000)); iAim = PlayAnimation(aim_set["AnimationShoot3"], CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, GetAnimationLength(aim_set["AnimationShoot3"]), aim_set["ShootTime"], ANIM_Remove), Anim_Const(1000), iAim); SetAnimationWeight(iAim+1, Anim_Const(1000*(Abs(aim_angle)-90)/90)); } } } aim_schedule_timer = aim_set["ShootTime"]; aim_schedule_call = "StopShoot"; if(aim_set["ShootTime2"] != nil) { aim_schedule_timer2 = aim_set["ShootTime2"]; aim_schedule_call2 = "DuringShoot"; } } public func DuringShoot() { if (aim_weapon) aim_weapon->~DuringShoot(this, aim_angle); } public func StopShoot() { if(aim_weapon == nil || !aim_weapon->~FinishedShooting(this, aim_angle)) // return 1 means the weapon goes on doing something (e.g. start aiming) then we don't reset ResetHands(); } public func CancelAiming() { ResetHands(); } public func ApplySet(set) { // Setting the hands as blocked, so that no other items are carried in the hands SetHandAction(1); if(set["TurnType"] != nil) SetTurnType(set["TurnType"], 1); if(set["AnimationReplacements"] != nil) for(var replace in set["AnimationReplacements"]) ReplaceAction(replace[0], replace[1]); if(set["WalkSpeed"] != nil) AddEffect("IntWalkSlow", this, 1, 0, this, nil, set["WalkSpeed"]); if(set["WalkBack"] != nil) SetBackwardsSpeed(set["WalkBack"]); } public func ResetHands() { if(!GetEffect("IntAimCheckProcedure", this)) return; if(aim_weapon != nil) { aim_weapon->~Reset(this); if(aim_set["AnimationReplacements"] != nil) for(var replace in aim_set["AnimationReplacements"]) ReplaceAction(replace[0], nil); } aim_stop = 0; aim_angle = -90+180*GetDir(); StopAnimation(GetRootAnimation(10)); RemoveEffect("IntWalkSlow", this); SetBackwardsSpeed(nil); RemoveEffect("IntAim", this); SetTurnForced(-1); SetTurnType(0, -1); SetHandAction(0); aim_weapon = nil; aim_set = nil; aim_schedule_call = nil; aim_schedule_timer = nil; aim_schedule_call2 = nil; aim_schedule_timer2 = nil; RemoveEffect("IntAimCheckProcedure", this); } /* +++++++++++ Slow walk +++++++++++ */ func FxIntWalkSlowStart(pTarget, effect, fTmp, iValue) { if(iValue == nil) iValue = 84; pTarget->PushActionSpeed("Walk", iValue); } func FxIntWalkSlowStop(pTarget, effect) { pTarget->PopActionSpeed("Walk"); }