2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
6 ////////////////////////////////////////
7 // Player Gun Animation
37 #include <plyrmode.h> // for gun wobble
64 #include <dpccam.h> // for zoom
81 // Include these absolutely last
84 DEFINE_LG_GUID(IID_IPlayerGun
, 0x18d);
86 // where should I really get these from?
87 #define min(a,b) (((a) < (b)) ? (a) : (b))
88 #define max(a,b) (((a) > (b)) ? (a) : (b))
93 kPlayerGunOff
, kPlayerGunLowered
, kPlayerGunRaising
, kPlayerGunRaised
,
94 kPlayerGunLowering
, kPlayerGunPoweringUp
, kPlayerGunFiring
, kPlayerGunPoweringDown
,
95 kPlayerGunResetting
, kPlayerGunStartingLoad
, kPlayerGunFinishingLoad
, kPlayerGunLoading
,
98 enum ePlayerGunSwingState
100 kPlayerGunNotSwing
, kPlayerGunSwingStart
, kPlayerGunSwingLeft
, kPlayerGunSwingRight
,
104 kPlayerGunTriggerPulled
= 0x0001, kPlayerGunTriggerReleased
= 0x0002, kPlayerGunOverloadStarted
= 0x0004
108 const float kRandMax
= 32767;
111 public cCTDelegating
<IPlayerGun
>,
112 public cCTAggregateMemberControl
<kCTU_Default
>
115 cPlayerGun(IUnknown
* pOuter
):
119 MI_INIT_AGGREGATION_1(pOuter
, IPlayerGun
, kPriorityNormal
, NULL
);
122 STDMETHOD(Init
)(THIS
)
125 m_state
= kPlayerGunOff
;
130 STDMETHOD(GameInit
)(THIS
)
132 m_pObjSys
= AppGetObj(IObjectSystem
);
135 STDMETHOD(GameTerm
)(THIS
)
137 SafeRelease(m_pObjSys
);
141 STDMETHOD(Set
)(THIS_ ObjID gunID
);
142 STDMETHOD(SetRaised
)(THIS_ ObjID gunID
);
143 STDMETHOD_(ObjID
, Get
)(THIS
)
147 STDMETHOD_(ObjID
, GetHand
)(THIS
)
151 STDMETHOD(Off
)(THIS
);
152 STDMETHOD(Frame
)(tSimTime deltaTime
);
153 STDMETHOD(Raise
)(THIS
);
154 STDMETHOD(Lower
)(THIS
);
155 STDMETHOD(Unequip
) (THIS_ ObjID oldGun
, ObjID newGun
, BOOL spew
, BOOL sednToInv
);
156 STDMETHOD(ReleaseTrigger
)(THIS
);
157 STDMETHOD(PullTrigger
)(THIS
);
158 STDMETHOD(Load
)(void);
159 STDMETHOD_(BOOL
, IsReloading
)(THIS
) const
161 return((m_state
== kPlayerGunStartingLoad
) ||
162 (m_state
== kPlayerGunLoading
) ||
163 (m_state
== kPlayerGunFinishingLoad
));
165 STDMETHOD_(BOOL
, IsTriggerPulled
)(THIS
) const
167 return(m_flags
&kPlayerGunTriggerPulled
);
169 STDMETHOD_ (BOOL
, MatchesCurrentAmmo
)(THIS_ ObjID clipID
)
171 if (m_gunID
== OBJ_NULL
)
173 return(GetProjectile(m_gunID
) == GetProjectileFromClip(clipID
, m_gunID
));
178 ObjID m_handID
; // ID of the object we are using to represent the gun in front of the player
179 ObjID m_gunID
; // Actual gun object
180 ePlayerGunState m_state
;
182 ePlayerGunSwingState m_swingState
;
183 tSimTime m_swingStartTime
;
184 tSimTime m_lastFireTime
;
185 tSimTime m_stateChangeTime
; // time at which we last changed state
186 tSimTime m_burstNum
; // how many shots we've fired in current burst
189 mxs_ang m_pitchTarget
; // where we want pitch to be
190 mxs_ang m_pitchRate
; // rate at which we're going there
192 float m_bobTarget
; // where we want bob to be
193 float m_kickBack
; // current kick pack offset
194 IObjectSystem
*m_pObjSys
;
195 sGunAnimsState m_animState
; // anim state
197 // cached property data
198 sGunAnim
*m_pGunAnim
; // anim controls from property
199 sPlayerGunDesc
*m_pPlayerGunDesc
;
200 eWeaponSkills m_weaponType
;
201 ObjID m_unequippingGun
;
203 BOOL m_unequippingSpew
;
204 BOOL m_unequippingSendToInv
;
206 // internal functions
208 void SetState(ePlayerGunState state
);
209 void UpdateState(void);
210 void SetControls(void);
211 void SetSwingState(ePlayerGunSwingState state
);
212 void UpdateObjPos(void);
213 void IntegrateControls(tSimTime delta
);
217 BOOL
Animating(void);
218 void SetJoint(int jointNum
, float jointPos
);
219 void StartJointAnimation(const sGunJointAnim
&jointAnim
);
220 void StartAnimation(void);
221 void EndAnimation(void);
222 BOOL
AnimateJoint(const sGunJointAnim
&jointAnim
);
223 BOOL
AnimateParameter(float *pValue
, sGunAnimState
*pState
, const sGunParamAnim
¶ms
);
225 mxs_ang
CalcKickAngle(mxs_ang theta
, int flags
);
226 void BoundAbs(mxs_ang
&pTheta
, mxs_ang bound
);
227 void BoundAbs(float &pTheta
, float bound
);
228 void ApplyKick(mxs_ang pitch
, mxs_ang heading
, sGunKick
*pGunKick
, float pct
);
229 void ApplyJolt(mxs_ang pitch
, mxs_ang heading
, float joltBack
, float pct
);
230 mxs_ang
CalcRandAngle(void) const;
231 BOOL
TestForBreakage(void);
232 void StartFiringSequence(void);
233 void LaunchProjectile(mxs_ang pitch
, mxs_ang heading
);
235 void LinkProjectile(ObjID projID
);
236 void ExplodeProjectile(void);
238 BOOL
ShouldWobble(void);
241 //////////////////////////////////////////
242 // Decide if gun should wobble
244 BOOL
cPlayerGun::ShouldWobble(void)
246 if (((g_pPlayerMode
->GetMode() == kPM_Stand
) || (g_pPlayerMode
->GetMode() == kPM_Crouch
)) && PhysObjOnGround(PlayerObject()))
249 PhysGetVelocity(PlayerObject(), &velocity
);
250 mxs_real velocity_mag
= mx_mag_vec(&velocity
);
252 return(velocity_mag
>GetGunAnimParams()->m_wobbleSpeed
);
257 ////////////////////////////////////////
258 // Set the current gun
260 STDMETHODIMP
cPlayerGun::Set(ObjID gunID
)
264 // interpret Set to OBJ_NULL as Off
265 if (gunID
== OBJ_NULL
)
270 if (!PlayerGunDescGet(gunID
, &m_pPlayerGunDesc
))
272 Warning(("No player gun description for obj %d\n", gunID
));
277 if (!g_pWeaponTypeProperty
->Get(gunID
, (int*)&m_weaponType
))
279 Warning(("No weapon type for obj %d\n", gunID
));
280 // default to conventional
281 m_weaponType
= kWeaponConventional
;
284 // For DC, all weapons "conventional"
285 m_weaponType
= kWeaponConventional
;
289 // play reload sound (== activate sound)
290 cTagSet
eventTags("Event Reload");
291 int schemaHandle
= ESndPlay(&eventTags
, gunID
, OBJ_NULL
);
293 // tell ghost we are using the gun.... (ack, why do we have to do this twice?)
294 GhostSetWeapon(PlayerObject(),gunID
,FALSE
);
299 ////////////////////////////////////////
300 // Set the current gun
302 STDMETHODIMP
cPlayerGun::SetRaised(ObjID gunID
)
306 // interpret Set to OBJ_NULL as Off
307 if (gunID
== OBJ_NULL
)
312 if (!PlayerGunDescGet(gunID
, &m_pPlayerGunDesc
))
314 Warning(("No player gun description for obj %d\n", gunID
));
319 if (!g_pWeaponTypeProperty
->Get(gunID
, (int*)&m_weaponType
))
321 Warning(("No weapon type for obj %d\n", gunID
));
322 // default to conventional
323 m_weaponType
= kWeaponConventional
;
326 // For DC, all weapons "conventional"
327 m_weaponType
= kWeaponConventional
;
329 m_pitch
= GetGunAnimParams()->m_raisedPitch
;
330 SetState(kPlayerGunRaised
);
333 // tell ghost we are using the gun.... (ack, why do we have to do this twice?)
334 GhostSetWeapon(PlayerObject(),gunID
,FALSE
);
339 ////////////////////////////////////////
342 void cPlayerGun::Create(void)
344 Label
*pHandModelName
;
345 float zeroJoints
[MAX_REND_JOINTS
] = {0, 0, 0, 0, 0, 0};
346 sGunAnimParams
*pAnimParams
= GetGunAnimParams();
348 Assert_(m_handID
==OBJ_NULL
);
349 Assert_(m_gunID
!=OBJ_NULL
);
351 // create the gun model object
352 m_handID
= m_pObjSys
->BeginCreate(ROOT_ARCHETYPE
, kObjectConcrete
);
353 m_pObjSys
->NameObject(m_handID
, "Player Gun");
354 if (PlayerGunDescGetHandModel(m_gunID
, &pHandModelName
))
355 ObjSetModelName(m_handID
, (char*)pHandModelName
);
356 m_pObjSys
->SetObjTransience(m_handID
, TRUE
);
357 ObjSetHasRefs(m_handID
, FALSE
);
358 ObjSetJointPos(m_handID
, zeroJoints
);
359 m_pObjSys
->EndCreate(m_handID
);
362 m_animState
.m_flags
= 0;
366 m_pitch
= pAnimParams
->m_loweredPitch
;
367 m_pitchTarget
= pAnimParams
->m_raisedPitch
;
368 m_pitchRate
= pAnimParams
->m_raisePitchRate
;
372 ////////////////////////////////////////
375 STDMETHODIMP
cPlayerGun::Raise(void)
377 SetState(kPlayerGunRaising
);
381 ////////////////////////////////////////
383 STDMETHODIMP
cPlayerGun::Lower(void)
385 SetState(kPlayerGunLowering
);
389 ////////////////////////////////////////
391 STDMETHODIMP
cPlayerGun::Unequip(ObjID oldGun
, ObjID newGun
, BOOL spew
, BOOL sendToInv
)
393 // @TODO: make this work
394 m_unequippingGun
= oldGun
;
396 m_unequippingSpew
= spew
;
397 m_unequippingSendToInv
= sendToInv
; // FALSE if from drag/drop
402 ////////////////////////////////////////
404 void cPlayerGun::SetSwingState(ePlayerGunSwingState state
)
406 m_swingState
= state
;
407 m_swingStartTime
= GetSimTime();
410 ////////////////////////////////////////
412 void cPlayerGun::SetState(ePlayerGunState state
)
416 case kPlayerGunRaised
:
417 SetSwingState(kPlayerGunNotSwing
);
422 m_stateChangeTime
= GetSimTime();
426 case kPlayerGunLowered
:
428 if (m_unequippingGun
)
430 AutoAppIPtr(DPCPlayer
);
431 pDPCPlayer
->Equip(PlayerObject(), kEquipWeapon
,
432 m_nextGun
,m_unequippingSpew
);
433 if (m_unequippingSendToInv
) {
434 DPCInvAddObj(PlayerObject(), m_unequippingGun
, 1);
436 m_unequippingGun
= m_nextGun
= OBJ_NULL
;
439 case kPlayerGunRaised
:
443 float zoom
= BaseGunDescGetZoom(m_gunID
);
444 ZoomTarget(zoom
, 2.0);
448 case kPlayerGunPoweringUp
:
449 case kPlayerGunPoweringDown
:
450 // Post firing animation
451 if (g_gunAnimPostProperty
->Get(m_gunID
, &m_pGunAnim
))
454 case kPlayerGunResetting
:
455 // Post firing animation
456 if (g_gunAnimPostProperty
->Get(m_gunID
, &m_pGunAnim
))
459 case kPlayerGunStartingLoad
:
460 m_pitchTarget
= m_pPlayerGunDesc
->m_reloadPitch
;
461 m_pitchRate
= m_pPlayerGunDesc
->m_reloadRate
;
463 case kPlayerGunFinishingLoad
:
464 m_pitchTarget
= GetGunAnimParams()->m_raisedPitch
;
465 m_pitchRate
= m_pPlayerGunDesc
->m_reloadRate
;
470 ////////////////////////////////////////
472 inline void cPlayerGun::SetJoint(int jointNum
, float jointPos
)
477 pJoints
= ObjJointPos(m_handID
);
478 pJoints
[jointNum
] = jointPos
;
481 ////////////////////////////////////////
483 void cPlayerGun::StartJointAnimation(const sGunJointAnim
&jointAnim
)
485 SetJoint(jointAnim
.m_num
, jointAnim
.m_params
.m_target2
);
488 ////////////////////////////////////////
490 inline BOOL
cPlayerGun::Animating(void)
492 return(m_animState
.m_flags
)&kGunAnimating
;
495 ////////////////////////////////////////
497 void cPlayerGun::StartAnimation(void)
499 Assert_(m_pGunAnim
!= NULL
);
500 m_animState
.m_startTime
= GetSimTime();
501 StartJointAnimation(m_pGunAnim
->m_jointAnim
);
502 m_animState
.m_flags
= kGunAnimating
;
503 m_animState
.m_jointAnimState
.m_flags
= 0;
506 void cPlayerGun::EndAnimation(void)
508 m_animState
.m_flags
&= ~kGunAnimating
;
512 ////////////////////////////////////////
513 // Integrate a given parameter value towards a target, specified in the anim params
514 // Sets values in the gun joint state structure
515 BOOL
cPlayerGun::AnimateParameter(float *pValue
, sGunAnimState
*pState
, const sGunParamAnim
¶ms
)
523 if ((pState
->m_flags
)&kGunAnimReversed
)
525 rate
= params
.m_rate2
/1000;
526 target
= params
.m_target2
;
527 elapsedTime
= GetSimTime()-pState
->m_reverseTime
;
528 value
= params
.m_target1
+rate
*elapsedTime
;
532 rate
= params
.m_rate1
/1000;
533 target
= params
.m_target1
;
534 elapsedTime
= GetSimTime()-m_animState
.m_startTime
;
535 value
= params
.m_target2
+rate
*elapsedTime
;
537 if (((rate
<0) && (value
<=target
)) || ((rate
>0) && (value
>=target
)))
540 // check if we want to go to target 2 or not
541 if (!(params
.m_flags
&kGunAnimTarget2
) || ((pState
->m_flags
)&kGunAnimReversed
))
545 pState
->m_flags
|= kGunAnimReversed
;
546 pState
->m_reverseTime
= GetSimTime();
554 ////////////////////////////////////////
555 // Returns TRUE when animation completed
557 BOOL
cPlayerGun::AnimateJoint(const sGunJointAnim
&jointAnim
)
561 pJoints
= ObjJointPos(m_handID
);
562 if (AnimateParameter(&pJoints
[0], &m_animState
.m_jointAnimState
, m_pGunAnim
->m_jointAnim
.m_params
))
567 ////////////////////////////////////////
568 // Enact animation & detect end
570 void cPlayerGun::Animate(void)
572 Assert_(m_pGunAnim
!= NULL
);
573 // currently we only have one animation to do!
574 if (AnimateJoint(m_pGunAnim
->m_jointAnim
))
578 ////////////////////////////////////////
580 mxs_ang
cPlayerGun::CalcKickAngle(mxs_ang theta
, int flags
)
582 AutoAppIPtr(DPCPlayer
);
583 // adjust for agility
584 // MAX_STAT_VAL better be more than 1...
585 theta
= mxs_ang(float(theta
)*(float(MAX_STAT_VAL
-pDPCPlayer
->GetStat(kStatAgility
))/float(MAX_STAT_VAL
-1)));
586 if ((flags
&kPlayerGunKickUp
) && (flags
&kPlayerGunKickDown
))
587 if (Rand()>(kRandMax
/2))
588 flags
= kPlayerGunKickUp
;
590 flags
= kPlayerGunKickDown
;
591 if (flags
&kPlayerGunKickUp
)
592 return theta
*(0.5+float(Rand())/(2*kRandMax
));
593 else if (flags
&kPlayerGunKickDown
)
594 return theta
*(-0.5-float(Rand())/(2*kRandMax
));
599 ////////////////////////////////////////
601 void cPlayerGun::ApplyJolt(mxs_ang pitch
, mxs_ang heading
, float joltBack
, float pct
)
609 PhysGetSubModRotation(PlayerObject(), PLAYER_HEAD
, &ang
);
611 // Apply heading jolt
612 ang
.tz
+= heading
*pct
;
614 PhysicsListenerLock
= TRUE
;
615 PhysSetSubModRotation(PlayerObject(), PLAYER_HEAD
, &ang
);
616 PhysicsListenerLock
= FALSE
;
620 PhysGetVelocity(PlayerObject(), &vel
);
621 CameraGetLocation(PlayerCamera(), &pos
, &ang
);
622 mx_ang2mat(&mat
, &ang
);
623 mx_scale_addeq_vec(&vel
, &mat
.vec
[0], joltBack
);
624 PhysSetVelocity(PlayerObject(), &vel
);
627 ////////////////////////////////////////
629 void cPlayerGun::BoundAbs(mxs_ang
&theta
, mxs_ang bound
)
632 theta
= max(theta
, bound
);
634 theta
= min(theta
, bound
);
637 ////////////////////////////////////////
639 void cPlayerGun::BoundAbs(float &theta
, float bound
)
642 theta
= max(theta
, bound
);
644 theta
= min(theta
, bound
);
647 ////////////////////////////////////////
649 void cPlayerGun::ApplyKick(mxs_ang pitch
, mxs_ang heading
, sGunKick
*pGunKick
, float pct
)
651 m_pitch
+= pitch
*pct
;
652 BoundAbs(m_pitch
, pGunKick
->m_kickPitchMax
);
653 m_pitchRate
= pGunKick
->m_kickAngularReturnRate
;
654 m_heading
+= heading
*pct
;
655 m_kickBack
+= pGunKick
->m_kickBack
*pct
;
656 BoundAbs(m_kickBack
, pGunKick
->m_kickBackMax
);
660 ////////////////////////////////////////
662 inline mxs_ang
cPlayerGun::CalcRandAngle(void) const
664 AutoAppIPtr(DPCPlayer
);
665 mxs_ang randAngle
= GetSkillParams()->m_inaccuracy
*(MAX_SKILL_VAL
-pDPCPlayer
->GetWeaponSkill(m_weaponType
));
668 if ((m_pPlayerGunDesc->m_handedness == kPlayerGunTwoHanded) &&
669 pDPCPlayer->HasTrait(PlayerObject(), kTraitRifleman))
671 ConfigSpew("PlayerShotSpew", ("Rifleman: Adjusting inaccuracy from %x to %x\n", randAngle, mxs_ang(randAngle*kTraitRiflemanKickFrac)));
672 randAngle *= kTraitRiflemanKickFrac;
674 else if ((m_pPlayerGunDesc->m_handedness == kPlayerGunOneHanded) &&
675 pDPCPlayer->HasTrait(PlayerObject(), kTraitOneHand))
677 ConfigSpew("PlayerShotSpew", ("OneHanded: Adjusting inaccuracy from %x to %x\n", randAngle, mxs_ang(randAngle*kTraitOneHandKickFrac)));
678 randAngle *= kTraitOneHandKickFrac;
684 /////////////////////////////////////////////////////////
685 // Check to see if gun breaks. TRUE iff gun breaks.
686 // Should only be called with state normal.
689 BOOL
cPlayerGun::TestForBreakage(void)
691 sGunReliability
*pGunReliability
;
692 BOOL gunBreaks
= FALSE
;
694 if (ObjGetObjState(m_gunID
) != kObjStateNormal
)
696 if (g_pGunReliabilityProperty
->Get(m_gunID
, &pGunReliability
))
698 AutoAppIPtr(DPCPlayer
);
699 float condition
= GunGetCondition(m_gunID
);
700 if (condition
< pGunReliability
->m_threshBreak
)
702 float minBreak
= pGunReliability
->m_minBreak
/100;
703 float maxBreak
= pGunReliability
->m_maxBreak
/100;
704 int weapSkill
= pDPCPlayer
->GetWeaponSkill(m_weaponType
);
705 float breakChance
= (minBreak
+(1-(condition
-pGunReliability
->m_threshBreak
)/100)*(maxBreak
-minBreak
))*(1-(GetSkillParams()->m_breakModifier
*weapSkill
));
707 if ((float(Rand())/kRandMax
)<breakChance
)
710 ObjSetObjState(m_gunID
, kObjStateBroken
);
712 // Spoken "broken" alert
713 SchemaPlay((Label
*)"bb04",NULL
);
715 cTagSet
tagSet("Event Break");
716 ESndPlay(&tagSet
, m_gunID
, OBJ_NULL
);
719 float degradeRate
= pGunReliability
->m_degradeRate
;
721 if (config_get_float("gun_degrade_rate", °radeMod
))
723 degradeRate
*= degradeMod
;
726 // maintenance affecting degredation rate removed in favor
727 // of the new "wrench" system. -- Xemu, 2/15/99
728 int maintSkill = pDPCPlayer->GetTechSkill(kTechMaintenance);
730 degradeRate *= (1.0-(1.0/(7.0-float(maintSkill))));
732 if (condition
>degradeRate
)
734 ConfigSpew("DegradeSpew", ("Gun %d degrades by %g to %g\n", m_gunID
, degradeRate
, condition
-degradeRate
));
735 condition
-= degradeRate
;
739 ConfigSpew("DegradeSpew", ("Gun %d degrades to zero\n", m_gunID
));
743 GunSetCondition(m_gunID
, condition
);
745 // Implement degrading of gun's special powers
746 float degradeRate2
= pGunReliability
->m_silenceDegradeRate
;
749 float silenceVal
= GunGetSilencing(m_gunID
);
750 if (silenceVal
>degradeRate2
)
752 ConfigSpew("DegradeSpew", ("Gun %d alt-degrades by %g to %g\n", m_gunID
, degradeRate2
, silenceVal
-degradeRate2
));
753 silenceVal
-= degradeRate2
;
757 ConfigSpew("DegradeSpew", ("Gun %d alt-degrades to zero\n", m_gunID
));
760 GunSetSilencing(m_gunID
, silenceVal
);
767 ////////////////////////////////////////
769 void cPlayerGun::LaunchProjectile(mxs_ang pitch
, mxs_ang heading
)
773 sBaseGunDesc
*pBaseGunDesc
;
775 if (!BaseGunDescGet(m_gunID
, &pBaseGunDesc
))
777 Warning(("No base gun description for obj %d\n", m_gunID
));
782 // @TODO: cache proj ID
783 if ((projID
= GetProjectile(m_gunID
)) != OBJ_NULL
)
786 mxs_ang randAngle
= CalcRandAngle();
787 AutoAppIPtr(DPCPlayer
);
789 mxs_ang cursor_pitch
, cursor_heading
;
790 cursor_pitch
= cursor_heading
= 0;
793 float dx
, dy
, MCH
, MCP
;
797 mouse_get_xy(&mpx
, &mpy
);
800 dx
= -1.0 * (float)(mpx
- (scm
.w
/ 2)) / (float)(scm
.w
/ 2);
801 dy
= -1.0 * (float)(mpy
- (scm
.h
/ 2)) / (float)(scm
.h
/ 2);
805 config_get_float("cursor_heading",&MCH
);
806 config_get_float("cursor_pitch",&MCP
);
808 cursor_heading
= (mxs_ang
)(dx
* MCH
);
809 cursor_pitch
= (mxs_ang
)(dy
* MCP
);
810 //mprintf("cursor deviation = %x, %x\n",cursor_heading, cursor_pitch);
814 float stimModifier
= pBaseGunDesc
->m_stimModifier
;
816 stimModifier
*= pDPCPlayer
->GetStimMultiplier(m_gunID
);
818 if (pDPCPlayer
->HasTrait(PlayerObject(), kTraitSharpshooter
))
819 stimModifier
*= GetTraitParams()->m_lethalMult
;
821 // Launch projectiles
822 if (GunStateGetAmmo(m_gunID
)>=pBaseGunDesc
->m_ammoUsage
)
824 sLaunchParams launchParams
= g_defaultLaunchParams
;
826 ConfigSpew("PlayerShotSpew", ("Launching projectiles, objID %d\n", projID
));
827 // setup valid loc (fall back proj start location) as player head position...
828 launchParams
.flags
= kLaunchTellAI
|kLaunchLocOverride
|kLaunchPitchOverride
;
829 // setup remainder of launch parameters
830 launchParams
.speedMult
= pBaseGunDesc
->m_speedModifier
;
831 launchParams
.intensityMult
= stimModifier
;
832 // VHotGetOffset(&launchParams.offset, m_handID, 0);
833 // mx_addeq_vec(&launchParams.offset, &m_pPlayerGunDesc->m_fireOffset);
834 launchParams
.loc
= PlayerCamera()->pos
;
835 launchParams
.pitch
= PlayerCamera()->ang
.ty
-pitch
- cursor_pitch
;
836 launchParams
.heading
= heading
+ cursor_heading
;
837 launchParams
.error
= randAngle
;
838 // actually launch the damn things
839 launchedID
= GunLaunchProjectile(PlayerObject(), projID
, &launchParams
);
841 GunStateSetAmmo(m_gunID
, GunStateGetAmmo(m_gunID
)-pBaseGunDesc
->m_ammoUsage
);
843 // Spoken out-of-ammo alert
844 if (GunStateGetAmmo(m_gunID
) == 0)
846 SchemaPlay((Label
*)"bb08",NULL
);
850 // link the projectile if we are delayed burst
851 if ((m_pPlayerGunDesc
->m_flags
)&kPlayerGunDelayedExplosion
)
852 LinkProjectile(launchedID
);
855 cTagSet
tagSet("Event Shoot");
856 // @TODO: make this the real weapon mode
857 tagSet
.Add(cTag("WeaponMode", int(0)));
859 float condition
= GunGetSilencing(m_gunID
);
860 tagSet
.Add(cTag("DegradeValue", int(condition
/10)));
861 // Don't network the sound for all players
862 sSchemaCallParams params
= g_sDefaultSchemaCallParams
;
863 params
.flags
|= SCH_NO_NETWORK
;
864 ObjPos
*pHandPos
= ObjPosGet(m_handID
);
865 AutoAppIPtr(NetManager
);
867 if (pNetManager
->Networking())
868 ESndPlayLoc(&tagSet
, m_gunID
, projID
, &(pHandPos
->loc
.vec
), ¶ms
);
870 ESndPlay(&tagSet
, m_gunID
, projID
);
872 // DPCHACK Porges -- this sort of abuses the sound card but is
873 // here so gun fire (as opposed to bullet impact) will propagate
874 // to AIs. Probably wants another routine to prop the sound w/o
875 // playing it out loud or something.
876 ESndPlayLoc(&tagSet
, m_gunID
, projID
, &(pHandPos
->loc
.vec
), ¶ms
);
881 if ((m_flags
&kPlayerGunTriggerPulled
) &&
882 ((m_burstNum
<pBaseGunDesc
->m_burst
) || (pBaseGunDesc
->m_burst
<0)) &&
883 (GunStateGetAmmo(m_gunID
)>=pBaseGunDesc
->m_ammoUsage
))
884 SetState(kPlayerGunResetting
);
886 SetState(kPlayerGunPoweringDown
);
889 ///////////////////////////////////////////
891 void cPlayerGun::Fire(void)
893 ITraitManager
*pTraitMan
= AppGetObj(ITraitManager
);
894 cMxsVector fireOffset
;
896 GunKickGetSafe(m_gunID
, &pGunKick
);
898 mxs_ang pitch
= CalcKickAngle(pGunKick
->m_kickPitch
, m_pPlayerGunDesc
->m_flags
);
899 mxs_ang heading
= CalcKickAngle(pGunKick
->m_kickHeading
, (m_pPlayerGunDesc
->m_flags
) >> 2);
900 mxs_ang joltPitch
= CalcKickAngle(pGunKick
->m_joltPitch
, (m_pPlayerGunDesc
->m_flags
) >> 4);
901 mxs_ang joltHeading
= CalcKickAngle(pGunKick
->m_joltHeading
, (m_pPlayerGunDesc
->m_flags
) >> 6);
904 ApplyKick(pitch
, heading
, pGunKick
, 1);
906 ApplyJolt(-joltPitch
, joltHeading
, pGunKick
->m_joltBack
, 1);
909 // note - kick disabled for actual shot effect for the moment (probably for all time)
910 // so just pass in zero for pitch & heading adj
911 LaunchProjectile(0, 0);
913 // create gun flash(es)
914 AutoAppIPtr(TraitManager
);
915 CreateGunFlashes(pTraitManager
->GetArchetype(m_gunID
), m_handID
);
918 post-shot no longer used...
919 // post-shot kickback
920 ApplyKick(pitch, heading, pGunKick, 1-pGunKick->m_preKickPct);
922 ApplyJolt(-joltPitch, joltHeading, pGunKick->m_joltBack, 1-pGunKick->m_preKickPct);
925 m_lastFireTime
= GetSimTime();
927 SafeRelease(pTraitMan
);
928 GhostNotify(PlayerObject(),kGhostStFiring
);
931 ////////////////////////////////////////
933 void cPlayerGun::UpdateState(void)
935 sGunAnimParams
*pAnimParams
= GetGunAnimParams();
939 case kPlayerGunPoweringUp
:
940 SetState(kPlayerGunFiring
);
943 case kPlayerGunFiring
:
944 if (ObjGetObjState(m_gunID
) == kObjStateNormal
)
946 if (!TestForBreakage())
950 SetState(kPlayerGunPoweringDown
);
952 case kPlayerGunPoweringDown
:
954 sBaseGunDesc
*pBaseGunDesc
;
955 BaseGunDescGetSafe(m_gunID
, &pBaseGunDesc
);
956 if (GetSimTime()>=(m_lastFireTime
+pBaseGunDesc
->m_shotInterval
))
959 cTagSet eventTags("Event Reset");
960 int schemaHandle = ESndPlay(&eventTags, m_gunID, OBJ_NULL);
962 SetState(kPlayerGunRaised
);
966 case kPlayerGunResetting
:
968 // @TODO: query anims here?
969 sBaseGunDesc
*pBaseGunDesc
;
970 BaseGunDescGetSafe(m_gunID
, &pBaseGunDesc
);
971 if (GetSimTime()>=(m_lastFireTime
+pBaseGunDesc
->m_burstInterval
))
972 SetState(kPlayerGunFiring
);
975 case kPlayerGunRaising
:
976 if (m_pitch
== pAnimParams
->m_raisedPitch
)
977 SetState(kPlayerGunRaised
);
979 case kPlayerGunLowering
:
980 if (m_pitch
== pAnimParams
->m_loweredPitch
)
981 SetState(kPlayerGunLowered
);
983 case kPlayerGunRaised
:
984 if ((m_swingState
== kPlayerGunNotSwing
) && ShouldWobble())
985 SetSwingState(kPlayerGunSwingStart
);
986 else if (!ShouldWobble())
987 SetSwingState(kPlayerGunNotSwing
);
989 case kPlayerGunFinishingLoad
:
990 if (m_pitch
== pAnimParams
->m_raisedPitch
)
991 SetState(kPlayerGunRaised
);
993 case kPlayerGunStartingLoad
:
994 if (m_pitch
== m_pPlayerGunDesc
->m_reloadPitch
)
995 SetState(kPlayerGunLoading
);
997 case kPlayerGunLoading
:
999 sBaseGunDesc
*pBaseGunDesc
;
1000 BaseGunDescGetSafe(m_gunID
, &pBaseGunDesc
);
1001 if (GetSimTime()>(m_stateChangeTime
+pBaseGunDesc
->m_reloadTime
))
1003 GunLoadSound(m_gunID
);
1004 SetState(kPlayerGunFinishingLoad
);
1011 ////////////////////////////////////////
1013 float Integrate(float val
, float targ
, float rate
, tSimTime delta
)
1015 float diff
= val
-targ
;
1016 float move
= rate
*delta
/1000;
1030 ////////////////////////////////////////
1032 mxs_ang
IntegrateAng(mxs_ang val
, mxs_ang targ
, mxs_ang rate
, tSimTime delta
)
1036 mxs_ang move
= fix_mul(rate
, fix_from_float(float(delta
)/1000.0));
1037 mxs_ang diff
= targ
-val
;
1047 if (move
>mxs_ang(-diff
))
1056 ////////////////////////////////////////
1058 void cPlayerGun::SetControls(void)
1060 sGunAnimParams
*pAnimParams
= GetGunAnimParams();
1064 case kPlayerGunRaised
:
1065 if (m_swingState
!= kPlayerGunNotSwing
)
1067 int delta
= GetSimTime()-m_swingStartTime
;
1068 switch (m_swingState
)
1070 case kPlayerGunSwingStart
:
1071 if (delta
>(pAnimParams
->m_swingPeriod
)/2)
1072 SetSwingState(kPlayerGunSwingRight
);
1073 delta
= GetSimTime()-m_swingStartTime
;
1075 case kPlayerGunSwingLeft
:
1076 if (delta
>pAnimParams
->m_swingPeriod
)
1077 SetSwingState(kPlayerGunSwingRight
);
1078 delta
= GetSimTime()-m_swingStartTime
;
1080 case kPlayerGunSwingRight
:
1081 if (delta
>pAnimParams
->m_swingPeriod
)
1082 SetSwingState(kPlayerGunSwingLeft
);
1083 delta
= GetSimTime()-m_swingStartTime
;
1086 switch (m_swingState
)
1088 case kPlayerGunSwingStart
:
1089 m_heading
= fix_mul(pAnimParams
->m_swingAmplitude
, fix_from_float(float(delta
)/(float(pAnimParams
->m_swingPeriod
))));
1091 case kPlayerGunSwingLeft
:
1092 m_heading
= fix_mul(pAnimParams
->m_swingAmplitude
, fix_from_float((float(delta
)-float((pAnimParams
->m_swingPeriod
)/2))/float(pAnimParams
->m_swingPeriod
)));
1094 case kPlayerGunSwingRight
:
1095 m_heading
= fix_mul(pAnimParams
->m_swingAmplitude
, fix_from_float((float((pAnimParams
->m_swingPeriod
)/2)-float(delta
))/float(pAnimParams
->m_swingPeriod
)));
1098 if (m_bob
==m_bobTarget
)
1100 m_bobTarget
= (float(Rand())/float(RAND_MAX
))*pAnimParams
->m_bobAmplitude
;
1101 ConfigSpew("BobSpew", ("Setting bob target to %g\n", m_bobTarget
));
1105 case kPlayerGunRaising
:
1106 m_pitchTarget
= pAnimParams
->m_raisedPitch
;
1107 m_pitchRate
= pAnimParams
->m_raisePitchRate
;
1109 case kPlayerGunLowering
:
1110 m_pitchTarget
= pAnimParams
->m_loweredPitch
;
1111 m_pitchRate
= pAnimParams
->m_raisePitchRate
;
1116 ////////////////////////////////////////
1118 void cPlayerGun::IntegrateControls(tSimTime delta
)
1121 sGunAnimParams
*pAnimParams
= GetGunAnimParams();
1124 m_heading
= IntegrateAng(m_heading
, 0, pAnimParams
->m_swingReturn
, delta
);
1127 m_pitch
= IntegrateAng(m_pitch
, m_pitchTarget
, m_pitchRate
, delta
);
1130 ConfigSpew("BobSpew", ("Bob target is %g, delta %d, rate %g\n", m_bob
, delta
, pAnimParams
->m_bobRate
));
1131 ConfigSpew("BobSpew", ("Setting bob from %g", m_bob
));
1132 if (m_bob
<m_bobTarget
)
1133 m_bob
= min(m_bob
+pAnimParams
->m_bobRate
*delta
/1000, m_bobTarget
);
1134 else if (m_bob
>m_bobTarget
)
1135 m_bob
= max(m_bob
-pAnimParams
->m_bobRate
*delta
/1000, m_bobTarget
);
1136 ConfigSpew("BobSpew", (" to %g\n", m_bob
));
1139 GunKickGetSafe(m_gunID
, &pGunKick
);
1140 m_kickBack
= Integrate(m_kickBack
, 0, pGunKick
->m_kickBackReturnRate
, delta
);
1143 ////////////////////////////////////////
1145 void cPlayerGun::UpdateObjPos(void)
1148 mxs_angvec camFacing
;
1150 mxs_vector offset
, kickBackVec
;
1152 if (m_handID
== OBJ_NULL
)
1155 // Get base location/facing
1156 CameraGetLocation(PlayerCamera(), &camPos
, &camFacing
);
1157 // turn player facing into matrix
1158 mx_ang2mat(&m1
, &camFacing
);
1160 // apply heading adjustment
1161 mx_rot_z_mat(&m2
, &m1
, m_heading
);
1162 // apply pitch adjustment
1163 mx_rot_y_mat(&m1
, &m2
, -m_pitch
);
1165 // calc facing vector
1166 mx_mat2ang(&camFacing
, &m1
);
1169 mx_copy_vec(&offset
, &(m_pPlayerGunDesc
->m_posOffset
));
1170 kickBackVec
.x
= m_kickBack
;
1173 mx_addeq_vec(&offset
, &kickBackVec
);
1174 mx_mat_muleq_vec(&m1
, &offset
);
1175 mx_addeq_vec(&camPos
, &offset
);
1180 // update actual obj pos
1181 ObjPosUpdate(m_handID
, &camPos
, &camFacing
);
1184 ////////////////////////////////////////
1186 STDMETHODIMP
cPlayerGun::Off(void)
1188 // destroy old model
1189 if (m_handID
!= OBJ_NULL
)
1191 m_pObjSys
->Destroy(m_handID
);
1192 m_handID
= OBJ_NULL
;
1193 SetState(kPlayerGunOff
);
1197 GhostSetWeapon(PlayerObject(),m_gunID
,FALSE
);
1201 ////////////////////////////////////////
1203 STDMETHODIMP
cPlayerGun::Frame(tSimTime timeDelta
)
1205 if (m_state
!= kPlayerGunOff
)
1213 // UpdateState can kill the model
1214 if (m_handID
== OBJ_NULL
)
1218 IntegrateControls(timeDelta
);
1224 ////////////////////////////////////////
1226 inline void cPlayerGun::StartFiringSequence(void)
1228 SetState(kPlayerGunPoweringUp
);
1232 ////////////////////////////////////////
1234 // LinkProjectile: used by delayed blast weapons.
1235 // We are over-loading the projectile link here, but what the hell.
1236 // No one else should be linking projectiles to the player... or this will cause trouble
1238 void cPlayerGun::LinkProjectile(ObjID projID
)
1240 AutoAppIPtr(LinkManager
);
1242 pLinkManager
->Add(PlayerObject(), projID
, g_pProjectileLinks
->GetID());
1245 ////////////////////////////////////////
1247 // Explode all linked projectiles.
1249 void cPlayerGun::ExplodeProjectile(void)
1251 cAutoLinkQuery
query(g_pProjectileLinks
, PlayerObject(), LINKOBJ_WILDCARD
);
1252 AutoAppIPtr(DamageModel
);
1253 AutoAppIPtr(LinkManager
);
1256 while (!query
->Done())
1258 projID
= query
.GetDest();
1259 pLinkManager
->Remove(query
->ID());
1260 pDamageModel
->SlayObject(projID
, PlayerObject());
1265 ////////////////////////////////////////
1267 STDMETHODIMP
cPlayerGun::PullTrigger(void)
1269 Assert_(!(m_flags
&kPlayerGunTriggerPulled
));
1270 m_flags
|= kPlayerGunTriggerPulled
;
1274 case kPlayerGunRaised
:
1275 if (ObjGetObjState(m_gunID
) == kObjStateNormal
)
1277 sBaseGunDesc
*pBaseGunDesc
;
1278 BaseGunDescGetSafe(m_gunID
, &pBaseGunDesc
);
1279 if (GunStateGetAmmo(m_gunID
)>=pBaseGunDesc
->m_ammoUsage
)
1281 StartFiringSequence();
1286 // play out of ammo sound
1287 ESndPlay(&cTagSet("Event OutofAmmo"), m_gunID
, OBJ_NULL
);
1291 // @TODO: play broken noise here
1293 ESndPlay(&cTagSet("Event Broken"), m_gunID
, OBJ_NULL
);
1302 ////////////////////////////////////////
1304 STDMETHODIMP
cPlayerGun::ReleaseTrigger(void)
1306 m_flags
&= ~kPlayerGunTriggerPulled
;
1307 if ((m_pPlayerGunDesc
->m_flags
)&kPlayerGunDelayedExplosion
)
1309 ExplodeProjectile();
1314 ////////////////////////////////////////
1316 STDMETHODIMP
cPlayerGun::Load(void)
1318 SetState(kPlayerGunStartingLoad
);
1322 ////////////////////////////////////////
1324 void DPCPlayerGunCreate(void)
1326 AutoAppIPtr(Unknown
);
1327 new cPlayerGun(pUnknown
);