2016-03-27 11:49:47 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
Copyright (C) 2015 the OpenMoHAA team
|
|
|
|
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
|
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
// vehicleturret.cpp: Vehicle Turret.
|
|
|
|
//
|
|
|
|
|
2023-04-29 21:56:38 +02:00
|
|
|
#include "g_phys.h"
|
2016-03-27 11:49:47 +02:00
|
|
|
#include "vehicleturret.h"
|
|
|
|
#include "player.h"
|
|
|
|
#include "explosion.h"
|
|
|
|
#include "gibs.h"
|
2023-04-29 21:56:38 +02:00
|
|
|
#include "scriptexception.h"
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
static const Vector g_vUserMins(-16, -16, 0);
|
|
|
|
static const Vector g_vUserMaxs(16, 16, 96);
|
2023-08-11 03:09:10 +02:00
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
static constexpr float MAX_TANDEM_YAW_OFFSET = 0;
|
|
|
|
static constexpr float MAX_VT_PITCHCAP_OFFSET = 0;
|
2023-09-24 17:13:34 +02:00
|
|
|
static constexpr float MAX_VT_YAW_OFFSET = 0;
|
2023-09-23 21:42:40 +02:00
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
Event EV_VehicleTurretGun_SetBaseEntity
|
|
|
|
(
|
|
|
|
"setbaseentity",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"e",
|
|
|
|
"base_entity",
|
|
|
|
"Sets the base entity to take its orientation from.",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_RemoveOnDeath
|
|
|
|
(
|
2023-08-11 03:09:10 +02:00
|
|
|
"removeondeath",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"i",
|
|
|
|
"removeondeath",
|
2023-09-19 21:02:09 +02:00
|
|
|
"If set to a non-zero value, vehicles will not be removed when they die",
|
|
|
|
EV_NORMAL
|
2023-08-11 03:09:10 +02:00
|
|
|
);
|
2023-09-19 21:02:09 +02:00
|
|
|
Event EV_VehicleTurretGun_SetCollisionEntity
|
|
|
|
(
|
|
|
|
"setcollisionentity",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"e",
|
|
|
|
"entity",
|
|
|
|
"Sets the Collision Entity.",
|
|
|
|
EV_NORMAL
|
2023-08-11 03:09:10 +02:00
|
|
|
);
|
2023-09-19 21:02:09 +02:00
|
|
|
Event EV_VehicleTurretGun_Lock
|
|
|
|
(
|
|
|
|
"lock",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"The turret cannot be used.",
|
|
|
|
EV_NORMAL
|
2023-08-11 03:09:10 +02:00
|
|
|
);
|
2023-09-19 21:02:09 +02:00
|
|
|
Event EV_VehicleTurretGun_Unlock
|
|
|
|
(
|
|
|
|
"unlock",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"The turret can be used.",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_SoundSet
|
|
|
|
(
|
|
|
|
"SoundSet",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"s",
|
|
|
|
"soundset",
|
|
|
|
"Sets the Sound Set to use.",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_CollisionEntitySetter
|
|
|
|
(
|
|
|
|
"collisionent",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"e",
|
|
|
|
"entity",
|
|
|
|
"Sets the Collision Entity",
|
|
|
|
EV_SETTER
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_CollisionEntityGetter
|
|
|
|
(
|
|
|
|
"collisionent",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Gets the Collision Entity",
|
|
|
|
EV_GETTER
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_WarmupDelay
|
|
|
|
(
|
|
|
|
"warmupdelay",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"f",
|
|
|
|
"value",
|
|
|
|
"Set the warmup delay before use after mounting the weapon.",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_FireWarmupDelay
|
|
|
|
(
|
|
|
|
"firewarmupdelay",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"f",
|
|
|
|
"value",
|
|
|
|
"Set the warmup delay before use after mounting the weapon.",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_ReloadShots
|
|
|
|
(
|
|
|
|
"reloadshots",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"i",
|
|
|
|
"value",
|
|
|
|
"Set the number of shots fired before forcing a reload",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_ReloadDelay
|
|
|
|
(
|
|
|
|
"reloaddelay",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"f",
|
|
|
|
"value",
|
|
|
|
"Set a delay that implies a reload. Will also play a sound",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_AimOffset
|
|
|
|
(
|
|
|
|
"aimoffset",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"v",
|
|
|
|
"value",
|
|
|
|
"Adjust aiming angles by offset.",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_AimTolerance
|
|
|
|
(
|
|
|
|
"aimtolerance",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"v",
|
|
|
|
"caps",
|
|
|
|
"Sets a tolerance for the angles.",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_SetTargetEntity
|
|
|
|
(
|
|
|
|
"settargetentity",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"e",
|
|
|
|
"ent",
|
|
|
|
"Set the entity to point the turret at visually.",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
Event EV_VehicleTurretGun_PlayReloadSound
|
|
|
|
(
|
|
|
|
"playreloadsound",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Play the turret reload sound.",
|
|
|
|
EV_NORMAL
|
2023-08-11 03:09:10 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
CLASS_DECLARATION(TurretGun, VehicleTurretGun, NULL) {
|
|
|
|
{&EV_Item_DropToFloor, &VehicleTurretGun::PlaceTurret },
|
|
|
|
{&EV_Item_Pickup, NULL },
|
|
|
|
{&EV_Touch, NULL },
|
|
|
|
{&EV_Use, &VehicleTurretGun::TurretUsed },
|
|
|
|
{&EV_VehicleTurretGun_SetBaseEntity, &VehicleTurretGun::SetBaseEntity },
|
|
|
|
{&EV_VehicleTurretGun_RemoveOnDeath, &VehicleTurretGun::EventRemoveOnDeath },
|
|
|
|
{&EV_VehicleTurretGun_SetCollisionEntity, &VehicleTurretGun::EventSetCollisionModel},
|
|
|
|
{&EV_Damage, &VehicleTurretGun::EventDamage },
|
|
|
|
{&EV_Killed, &VehicleTurretGun::EventKilled },
|
|
|
|
{&EV_VehicleTurretGun_Lock, &VehicleTurretGun::EventLock },
|
|
|
|
{&EV_VehicleTurretGun_Unlock, &VehicleTurretGun::EventUnlock },
|
|
|
|
{&EV_VehicleTurretGun_SoundSet, &VehicleTurretGun::SetSoundSet },
|
|
|
|
{&EV_VehicleTurretGun_CollisionEntitySetter, &VehicleTurretGun::EventSetCollisionModel},
|
|
|
|
{&EV_VehicleTurretGun_CollisionEntityGetter, &VehicleTurretGun::EventGetCollisionModel},
|
2023-10-02 21:13:48 +02:00
|
|
|
{&EV_VehicleTurretGun_WarmupDelay, &VehicleTurretGun::SetWarmupDelay },
|
|
|
|
{&EV_VehicleTurretGun_FireWarmupDelay, &VehicleTurretGun::SetFireWarmupDelay },
|
|
|
|
{&EV_VehicleTurretGun_ReloadShots, &VehicleTurretGun::SetReloadShots },
|
|
|
|
{&EV_VehicleTurretGun_ReloadDelay, &VehicleTurretGun::SetReloadDelay },
|
|
|
|
{&EV_VehicleTurretGun_AimOffset, &VehicleTurretGun::SetAimOffset },
|
|
|
|
{&EV_VehicleTurretGun_AimTolerance, &VehicleTurretGun::SetAimTolerance },
|
|
|
|
{&EV_VehicleTurretGun_SetTargetEntity, &VehicleTurretGun::SetTargetEntity },
|
|
|
|
{&EV_VehicleTurretGun_PlayReloadSound, &VehicleTurretGun::PlayReloadSound },
|
2023-08-11 03:09:10 +02:00
|
|
|
{NULL, NULL }
|
2016-03-27 11:49:47 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
VehicleTurretGun::VehicleTurretGun()
|
|
|
|
{
|
2023-11-16 23:11:48 +01:00
|
|
|
entflags |= ECF_VEHICLETURRET;
|
2023-08-11 03:09:10 +02:00
|
|
|
|
2023-09-23 22:34:08 +02:00
|
|
|
AddWaitTill(STRING_DEATH);
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
if (LoadingSavegame) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
edict->s.eType = ET_MODELANIM;
|
|
|
|
setRespawn(false);
|
2023-09-23 22:34:08 +02:00
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
respondto = TRIGGER_PLAYERS | TRIGGER_MONSTERS;
|
|
|
|
edict->clipmask = MASK_VEHICLETURRET;
|
2023-09-23 22:34:08 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
m_bUsable = true;
|
|
|
|
m_bPlayerUsable = true;
|
|
|
|
m_bRestable = false;
|
|
|
|
m_fIdlePitchSpeed = 0;
|
2023-09-23 22:34:08 +02:00
|
|
|
m_iIdleHitCount = 0;
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
takedamage = DAMAGE_NO;
|
|
|
|
health = 100.0f;
|
|
|
|
max_health = 100.0f;
|
2023-09-23 22:34:08 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
setSize(Vector(-16, -16, 0), Vector(16, 16, 32));
|
2023-09-24 01:13:03 +02:00
|
|
|
|
|
|
|
m_fPitchUpCap = -45;
|
|
|
|
m_fPitchDownCap = 45;
|
|
|
|
m_fMaxYawOffset = 180;
|
|
|
|
m_fTurnSpeed = 160;
|
|
|
|
m_fAIPitchSpeed = 48;
|
|
|
|
m_fUserDistance = 64.0f;
|
2023-09-23 22:34:08 +02:00
|
|
|
m_vIdleCheckOffset.setXYZ(-56, 0, 0);
|
|
|
|
|
|
|
|
m_fMinBurstTime = 0;
|
|
|
|
m_fMaxBurstTime = 0;
|
|
|
|
m_fMinBurstDelay = 0;
|
|
|
|
m_fMaxBurstDelay = 0;
|
|
|
|
|
|
|
|
AxisClear(m_mBaseOrientation);
|
|
|
|
m_fFireToggleTime = 0;
|
|
|
|
m_iFiring = 0;
|
|
|
|
m_bBOIsSet = false;
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
m_pBaseEntity = NULL;
|
|
|
|
m_vLastBaseAngles = vec_zero;
|
|
|
|
m_vBaseAngles = vec_zero;
|
2023-09-23 22:34:08 +02:00
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
m_vBarrelPos = origin;
|
|
|
|
m_vLastBarrelPos = origin;
|
2023-09-23 22:34:08 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
m_bUseRemoteControl = false;
|
|
|
|
m_pVehicleOwner = NULL;
|
2023-09-23 22:34:08 +02:00
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
m_bRemoveOnDeath = true;
|
|
|
|
m_eSoundState = STT_OFF;
|
|
|
|
m_fNextSoundState = level.time;
|
2023-09-23 22:34:08 +02:00
|
|
|
|
|
|
|
m_fWarmupDelay = 0;
|
|
|
|
m_fFireWarmupDelay = 0;
|
|
|
|
m_fTargetReloadTime = 0;
|
|
|
|
m_fWarmupTimeRemaining = 0;
|
|
|
|
m_fReloadDelay = 0;
|
|
|
|
m_fReloadTimeRemaining = 0;
|
|
|
|
|
|
|
|
m_iReloadShots = 1;
|
2023-09-24 01:13:03 +02:00
|
|
|
m_iAmmo = 1;
|
2023-09-23 22:34:08 +02:00
|
|
|
|
|
|
|
ammo_in_clip[FIRE_PRIMARY] = m_iAmmo;
|
|
|
|
ammo_clip_size[FIRE_PRIMARY] = m_iReloadShots;
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
m_vAimOffset[0] = 0;
|
|
|
|
m_vAimOffset[1] = 0;
|
|
|
|
m_vAimOffset[2] = 0;
|
2023-09-23 22:34:08 +02:00
|
|
|
m_vAimTolerance[0] = 20;
|
|
|
|
m_vAimTolerance[1] = 20;
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
m_bLocked = true;
|
|
|
|
m_bLockedAim = false;
|
|
|
|
m_sSoundSet = "";
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
VehicleTurretGun::~VehicleTurretGun()
|
|
|
|
{
|
2023-08-11 03:09:10 +02:00
|
|
|
Unregister(STRING_ONTARGET);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
if (m_pCollisionEntity) {
|
|
|
|
m_pCollisionEntity->PostEvent(EV_Remove, 0);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-11-16 23:11:48 +01:00
|
|
|
entflags &= ~ECF_VEHICLETURRET;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::Think(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
Sentient *sentOwner;
|
|
|
|
float yawOffset, pitchOffset;
|
|
|
|
|
|
|
|
if (m_pCollisionEntity) {
|
|
|
|
m_pCollisionEntity->NotSolid();
|
|
|
|
}
|
|
|
|
|
|
|
|
sentOwner = GetSentientOwner();
|
|
|
|
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER && m_pRemoteOwner && m_pRemoteOwner->isSubclassOf(Player)) {
|
|
|
|
// always render the turret
|
|
|
|
edict->s.renderfx |= RF_DEPTHHACK;
|
|
|
|
} else {
|
|
|
|
edict->s.renderfx &= ~RF_DEPTHHACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateTimers(yawOffset, pitchOffset);
|
|
|
|
|
|
|
|
if (owner) {
|
|
|
|
UpdateAndMoveOwner();
|
|
|
|
UpdateCaps(yawOffset, pitchOffset);
|
|
|
|
} else if (m_bUseRemoteControl) {
|
|
|
|
UpdateRemoteControl();
|
|
|
|
UpdateCaps(yawOffset, pitchOffset);
|
|
|
|
} else if (aim_target) {
|
|
|
|
UpdateAimTarget();
|
|
|
|
UpdateCaps(yawOffset, pitchOffset);
|
|
|
|
} else if (m_bRestable) {
|
|
|
|
IdleToRestPosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateOrientation(false);
|
|
|
|
UpdateSound();
|
|
|
|
UpdateFireControl();
|
|
|
|
|
|
|
|
if (sentOwner) {
|
|
|
|
G_TouchTriggers(sentOwner);
|
|
|
|
UpdateOwner(sentOwner);
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateCollisionEntity();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
void VehicleTurretGun::P_UserAim(usercmd_t *ucmd)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
Vehicle *pVehicle;
|
|
|
|
int slot;
|
|
|
|
int newSlot;
|
|
|
|
|
|
|
|
TurretGun::P_UserAim(ucmd);
|
|
|
|
|
|
|
|
if (ucmd->buttons & BUTTON_ATTACKRIGHT) {
|
|
|
|
m_bLockedAim = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_bLockedAim) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_iFiring) {
|
|
|
|
m_bLockedAim = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_pVehicleOwner->IsSubclassOfVehicle()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pVehicle = static_cast<Vehicle *>(m_pVehicleOwner.Pointer());
|
|
|
|
slot = pVehicle->FindTurretSlotByEntity(this);
|
|
|
|
newSlot = slot + 1;
|
|
|
|
if (newSlot == pVehicle->numTurrets) {
|
|
|
|
newSlot = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newSlot != slot) {
|
|
|
|
Entity *slotEnt;
|
|
|
|
|
|
|
|
slotEnt = pVehicle->QueryTurretSlotEntity(newSlot);
|
2024-02-19 23:41:57 +01:00
|
|
|
// slotEnt check:
|
|
|
|
// Added in OPM
|
|
|
|
if (slotEnt && slotEnt->IsSubclassOfVehicleTurretGun()) {
|
2023-09-24 01:13:03 +02:00
|
|
|
VehicleTurretGun *existing;
|
|
|
|
Vector newAng;
|
|
|
|
bool wasThisLocked, wasExistingLocked;
|
|
|
|
|
|
|
|
existing = static_cast<VehicleTurretGun *>(slotEnt);
|
|
|
|
|
|
|
|
wasThisLocked = m_bLocked;
|
|
|
|
wasExistingLocked = existing->m_bLocked;
|
|
|
|
m_bLocked = false;
|
|
|
|
existing->m_bLocked = false;
|
|
|
|
|
|
|
|
newAng = existing->m_vUserViewAng;
|
|
|
|
m_vUserLastCmdAng = vec_zero;
|
|
|
|
|
|
|
|
pVehicle->AttachTurretSlot(newSlot, owner, vec_zero, NULL);
|
|
|
|
existing->m_vUserViewAng = newAng;
|
|
|
|
|
|
|
|
owner = NULL;
|
|
|
|
edict->r.ownerNum = ENTITYNUM_NONE;
|
|
|
|
|
|
|
|
if (wasThisLocked) {
|
|
|
|
m_bLocked = true;
|
|
|
|
}
|
|
|
|
if (wasExistingLocked) {
|
|
|
|
existing->m_bLocked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
existing->m_bLockedAim = true;
|
|
|
|
m_bLockedAim = false;
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::TurretBeginUsed(Sentient *pEnt)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-11 03:09:10 +02:00
|
|
|
if (m_pVehicleOwner) {
|
|
|
|
Vehicle *pVehicle = (Vehicle *)m_pVehicleOwner.Pointer();
|
|
|
|
int slot = pVehicle->FindTurretSlotByEntity(this);
|
|
|
|
|
|
|
|
pVehicle->UpdateTurretSlot(slot);
|
|
|
|
m_vBaseAngles = m_pVehicleOwner->angles;
|
|
|
|
}
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
owner = pEnt;
|
2023-08-11 03:09:10 +02:00
|
|
|
edict->r.ownerNum = pEnt->entnum;
|
|
|
|
m_bHadOwner = true;
|
|
|
|
|
|
|
|
Sound(sPickupSound);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
owner->SetViewAngles(m_vBaseAngles);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
m_vUserViewAng = m_vBaseAngles;
|
|
|
|
m_vUserViewAng[0] = AngleNormalize180(m_vUserViewAng[0]);
|
2023-09-24 01:13:03 +02:00
|
|
|
m_vUserLastCmdAng.setXYZ(0, 0, 0);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
setAngles(m_vBaseAngles);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
if (owner->IsSubclassOfPlayer()) {
|
|
|
|
Player *player = (Player *)owner.Pointer();
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
player->EnterTurret(this);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
if (!m_pUserCamera) {
|
|
|
|
m_pUserCamera = new Camera;
|
|
|
|
}
|
2023-02-01 00:28:40 +01:00
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
player->SetCamera(m_pUserCamera, 0.5);
|
2023-08-11 03:09:10 +02:00
|
|
|
m_pUserCamera->setAngles(m_vBaseAngles);
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateOwner(owner);
|
|
|
|
flags &= ~FL_THINK;
|
|
|
|
current_attachToTag = "";
|
|
|
|
ForceIdle();
|
2023-09-10 23:55:08 +02:00
|
|
|
P_CreateViewModel();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::TurretEndUsed(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-11 03:09:10 +02:00
|
|
|
if (owner->IsSubclassOfPlayer()) {
|
|
|
|
Player *player = (Player *)owner.Pointer();
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
RemoveUserCamera();
|
2023-08-11 03:09:10 +02:00
|
|
|
player->ExitTurret();
|
2023-09-10 23:55:08 +02:00
|
|
|
P_DeleteViewModel();
|
2023-08-11 03:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
owner = NULL;
|
|
|
|
edict->r.ownerNum = ENTITYNUM_NONE;
|
|
|
|
m_fIdlePitchSpeed = 0;
|
|
|
|
m_iIdleHitCount = 0;
|
|
|
|
m_iFiring = 0;
|
2023-09-24 01:13:03 +02:00
|
|
|
m_vTargetAngles = m_vLocalAngles;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::TurretUsed(Sentient *pEnt)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
if (!owner) {
|
2023-08-11 03:09:10 +02:00
|
|
|
TurretBeginUsed(pEnt);
|
2023-09-24 01:13:03 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (owner == pEnt) {
|
|
|
|
TurretEndUsed();
|
2023-08-11 03:09:10 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::TurretUsed(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-11 03:09:10 +02:00
|
|
|
Sentient *pEnt = (Sentient *)ev->GetEntity(1);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
if (!pEnt || !pEnt->IsSubclassOfSentient()) {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
if (!m_bUsable || m_bLocked) {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
if (pEnt->IsSubclassOfPlayer() && !m_bPlayerUsable) {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
TurretUsed(pEnt);
|
|
|
|
}
|
|
|
|
|
2023-10-01 21:15:37 +02:00
|
|
|
void VehicleTurretGun::SetBaseOrientation(const vec3_t borientation[3], const vec3_t bangles)
|
2023-09-19 21:02:09 +02:00
|
|
|
{
|
|
|
|
m_bBOIsSet = true;
|
|
|
|
|
|
|
|
VectorCopy(borientation[0], m_mBaseOrientation[0]);
|
|
|
|
VectorCopy(borientation[1], m_mBaseOrientation[1]);
|
|
|
|
VectorCopy(borientation[2], m_mBaseOrientation[2]);
|
|
|
|
m_vLastBaseAngles = m_vBaseAngles;
|
|
|
|
if (bangles) {
|
|
|
|
m_vBaseAngles = bangles;
|
|
|
|
} else {
|
|
|
|
MatrixToEulerAngles(borientation, m_vBaseAngles);
|
|
|
|
}
|
|
|
|
|
|
|
|
flags |= FL_THINK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetBaseEntity(Entity *e)
|
|
|
|
{
|
|
|
|
m_pBaseEntity = e;
|
|
|
|
m_vLastBaseAngles = m_vBaseAngles;
|
|
|
|
|
|
|
|
if (e) {
|
|
|
|
m_vBaseAngles = e->angles;
|
|
|
|
} else {
|
|
|
|
m_vBaseAngles = vec_zero;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags |= FL_THINK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetBaseEntity(Event *ev)
|
|
|
|
{
|
|
|
|
SetBaseEntity(ev->GetEntity(1));
|
2023-09-24 01:13:03 +02:00
|
|
|
|
|
|
|
flags |= FL_THINK;
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
void VehicleTurretGun::RemoteControl(usercmd_t *ucmd, Sentient *owner)
|
|
|
|
{
|
|
|
|
Vector vNewCmdAng;
|
2023-09-19 21:02:09 +02:00
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
if (!ucmd || !owner) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vNewCmdAng = Vector(SHORT2ANGLE(ucmd->angles[0]), SHORT2ANGLE(ucmd->angles[1]), SHORT2ANGLE(ucmd->angles[2]));
|
|
|
|
|
|
|
|
if (vNewCmdAng[0] || vNewCmdAng[1] || vNewCmdAng[2]) {
|
|
|
|
m_vUserViewAng[0] += AngleSubtract(vNewCmdAng[0], m_vUserLastCmdAng[0]);
|
|
|
|
m_vUserViewAng[1] += AngleSubtract(vNewCmdAng[1], m_vUserLastCmdAng[1]);
|
|
|
|
m_vUserViewAng[2] += AngleSubtract(vNewCmdAng[2], m_vUserLastCmdAng[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_vUserLastCmdAng = vNewCmdAng;
|
|
|
|
|
|
|
|
if (!m_bUseRemoteControl) {
|
|
|
|
m_bUseRemoteControl = true;
|
|
|
|
m_pRemoteOwner = owner;
|
|
|
|
m_vUserViewAng = m_vLocalAngles;
|
|
|
|
|
|
|
|
GetRemoteOwner()->SetViewAngles(m_vUserViewAng + m_vBaseAngles);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ucmd->buttons & (BUTTON_ATTACKLEFT | BUTTON_ATTACKRIGHT)) {
|
|
|
|
if (!m_iFiring) {
|
|
|
|
m_iFiring = 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_iFiring = 0;
|
|
|
|
m_fTargetReloadTime = 0;
|
|
|
|
flags |= FL_THINK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::UpdateOrientation(bool bCollisionCheck)
|
|
|
|
{
|
|
|
|
Entity *ent;
|
|
|
|
Vector localAngles;
|
|
|
|
vec3_t mat[3];
|
|
|
|
|
|
|
|
if (m_pBaseEntity) {
|
|
|
|
ent = m_pBaseEntity;
|
|
|
|
} else if (edict->s.parent) {
|
|
|
|
ent = G_GetEntity(edict->s.parent);
|
|
|
|
} else {
|
|
|
|
ent = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_iPitchBone = gi.Tag_NumForName(edict->tiki, "pitch");
|
|
|
|
m_iBarrelTag = gi.Tag_NumForName(edict->tiki, GetTagBarrel());
|
|
|
|
m_iEyeBone = gi.Tag_NumForName(edict->tiki, "eyebone");
|
|
|
|
|
|
|
|
if (m_iBarrelTag >= 0) {
|
|
|
|
orientation_t barrel_or;
|
|
|
|
|
|
|
|
GetTagPositionAndOrientation(m_iBarrelTag, &barrel_or);
|
|
|
|
m_vLastBarrelPos = m_vBarrelPos;
|
|
|
|
m_vBarrelPos = barrel_or.origin;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bCollisionCheck && m_iBarrelTag >= 0) {
|
|
|
|
trace_t trace;
|
|
|
|
// check if the new barrel position is ok
|
|
|
|
trace = G_Trace(
|
|
|
|
m_vLastBarrelPos,
|
|
|
|
Vector(8, 8, 8),
|
|
|
|
Vector(-8, -8, -8),
|
|
|
|
m_vBarrelPos,
|
|
|
|
this,
|
|
|
|
edict->clipmask,
|
|
|
|
qfalse,
|
|
|
|
"VehicleTurretGun::Think.BarrelCheck"
|
|
|
|
);
|
|
|
|
|
|
|
|
if (trace.fraction == 1 || trace.startsolid || trace.allsolid) {
|
|
|
|
CollisionCorrect(&trace);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
localAngles = m_vLocalAngles;
|
|
|
|
|
|
|
|
if (m_iPitchBone >= 0) {
|
|
|
|
vec3_t controllerAngles = {localAngles[0], 0, 0};
|
|
|
|
SetControllerAngles(0, controllerAngles);
|
|
|
|
localAngles[0] = 0;
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
AnglesToAxis(localAngles, mat);
|
|
|
|
|
|
|
|
if (m_bBOIsSet) {
|
|
|
|
vec3_t BOmat[3];
|
|
|
|
|
|
|
|
MatrixMultiply(mat, m_mBaseOrientation, BOmat);
|
|
|
|
MatrixToEulerAngles(BOmat, angles);
|
|
|
|
setAngles(angles);
|
|
|
|
} else {
|
|
|
|
setAngles(localAngles);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::CollisionCorrect(trace_t *pTr)
|
|
|
|
{
|
|
|
|
float planedot;
|
|
|
|
|
|
|
|
if (pTr->plane.normal == vec_zero) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pTr->plane.normal[2]) {
|
|
|
|
if (pTr->plane.normal[2] > 0) {
|
|
|
|
m_vLocalAngles[0] -= m_fAIPitchSpeed * level.frametime;
|
|
|
|
} else {
|
|
|
|
m_vLocalAngles[0] += m_fAIPitchSpeed * level.frametime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
planedot = DotProduct(pTr->plane.normal, orientation[1]);
|
|
|
|
if (fabs(planedot) > 0.25f) {
|
|
|
|
if (planedot > 0) {
|
|
|
|
m_vLocalAngles[1] += m_fTurnSpeed * level.frametime;
|
|
|
|
} else {
|
|
|
|
m_vLocalAngles[1] -= m_fTurnSpeed * level.frametime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::EventKilled(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-11 03:09:10 +02:00
|
|
|
Entity *ent;
|
|
|
|
Entity *attacker;
|
|
|
|
|
|
|
|
deadflag = DEAD_DEAD;
|
|
|
|
Unregister(STRING_DEATH);
|
|
|
|
|
|
|
|
if (!m_bRemoveOnDeath) {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
takedamage = DAMAGE_NO;
|
|
|
|
setSolidType(SOLID_NOT);
|
|
|
|
hideModel();
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
attacker = ev->GetEntity(1);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
if (flags & FL_DIE_EXPLODE) {
|
|
|
|
CreateExplosion(origin, 150 * edict->s.scale, this, this, this);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
if (flags & FL_DIE_GIBS) {
|
|
|
|
setSolidType(SOLID_NOT);
|
|
|
|
hideModel();
|
|
|
|
CreateGibs(this, -150, edict->s.scale, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*killtarget) {
|
|
|
|
ent = NULL;
|
|
|
|
while ((ent = (Entity *)G_FindTarget(ent, killtarget)) != NULL) {
|
|
|
|
ent->PostEvent(EV_Remove, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*target) {
|
|
|
|
ent = NULL;
|
|
|
|
while ((ent = (Entity *)G_FindTarget(ent, target)) != NULL) {
|
|
|
|
ent->ProcessEvent(EV_Activate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PostEvent(EV_Remove, 0);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::EventDamage(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
if (g_gametype->integer == GT_TOW && !dmManager.RoundActive()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
if (owner) {
|
2023-09-07 18:21:03 +02:00
|
|
|
owner->ProcessEvent(*ev);
|
2023-08-11 03:09:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pVehicleOwner) {
|
2023-09-07 18:21:03 +02:00
|
|
|
m_pVehicleOwner->ProcessEvent(*ev);
|
2023-08-11 03:09:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_bUseRemoteControl) {
|
2023-09-24 01:13:03 +02:00
|
|
|
meansOfDeath_t mod = static_cast<meansOfDeath_t>(ev->GetInteger(9));
|
|
|
|
switch (mod) {
|
|
|
|
case MOD_BULLET:
|
|
|
|
case MOD_BASH:
|
|
|
|
case MOD_FAST_BULLET:
|
|
|
|
case MOD_VEHICLE:
|
|
|
|
case MOD_SHOTGUN:
|
|
|
|
return;
|
2023-09-24 23:37:03 +02:00
|
|
|
default:
|
|
|
|
break;
|
2023-09-24 01:13:03 +02:00
|
|
|
}
|
|
|
|
|
2024-03-02 15:29:08 +01:00
|
|
|
Entity::DamageEvent(ev);
|
2023-08-11 03:09:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
if (m_pRemoteOwner && m_pRemoteOwner->IsSubclassOfSentient()) {
|
|
|
|
Sentient *sent = static_cast<Sentient *>(m_pRemoteOwner.Pointer());
|
2023-08-11 03:09:10 +02:00
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
if (sent->GetVehicle()) {
|
|
|
|
sent->GetVehicle()->ProcessEvent(*ev);
|
2023-08-11 03:09:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
void VehicleTurretGun::SetVehicleOwner(Entity *e)
|
|
|
|
{
|
|
|
|
m_pVehicleOwner = e;
|
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::EventRemoveOnDeath(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-11 03:09:10 +02:00
|
|
|
m_bRemoveOnDeath = ev->GetBoolean(1);
|
|
|
|
}
|
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
void VehicleTurretGun::UpdateSound(void)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
float fPitchDiff, fYawDiff, fDiff;
|
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
if (level.time < m_fNextSoundState) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
fYawDiff = fabs(AngleSubtract(m_vTargetAngles[1], m_vLocalAngles[1]));
|
|
|
|
fPitchDiff = fabs(AngleSubtract(m_vTargetAngles[0], m_vLocalAngles[0]));
|
|
|
|
if (fYawDiff > fPitchDiff) {
|
|
|
|
fDiff = fYawDiff;
|
|
|
|
} else {
|
|
|
|
fDiff = fPitchDiff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!owner && !m_bUseRemoteControl && !aim_target && !m_bRestable) {
|
|
|
|
fDiff = 0;
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
|
|
|
|
switch (m_eSoundState) {
|
2023-09-24 23:37:03 +02:00
|
|
|
case STT_OFF:
|
2023-09-19 21:02:09 +02:00
|
|
|
StopLoopSound();
|
|
|
|
m_fNextSoundState = level.time;
|
|
|
|
if (fabs(fDiff) > 0.5f) {
|
2023-09-24 01:13:03 +02:00
|
|
|
m_eSoundState = STT_OFF_TRANS_MOVING;
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
case STT_OFF_TRANS_MOVING:
|
2023-09-19 21:02:09 +02:00
|
|
|
m_fNextSoundState = level.time;
|
2024-11-10 19:29:33 +01:00
|
|
|
m_eSoundState = STT_MOVING;
|
|
|
|
Sound(m_sSoundSet + "snd_move_start");
|
2023-09-19 21:02:09 +02:00
|
|
|
break;
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
case STT_MOVING:
|
2023-09-19 21:02:09 +02:00
|
|
|
m_fNextSoundState = level.time;
|
2023-09-24 01:13:03 +02:00
|
|
|
if (fDiff < 0.5) {
|
|
|
|
m_eSoundState = STT_MOVING_TRANS_OFF;
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
LoopSound(m_sSoundSet + "snd_move");
|
|
|
|
break;
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
case STT_MOVING_TRANS_OFF:
|
2023-09-19 21:02:09 +02:00
|
|
|
m_fNextSoundState = level.time;
|
2023-09-24 01:13:03 +02:00
|
|
|
m_eSoundState = STT_OFF;
|
2024-11-10 19:29:33 +01:00
|
|
|
StopLoopSound();
|
2023-09-24 01:13:03 +02:00
|
|
|
Sound(m_sSoundSet + "snd_move_stop");
|
2023-09-19 21:02:09 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2023-09-24 01:13:03 +02:00
|
|
|
m_fNextSoundState = level.time;
|
|
|
|
m_eSoundState = STT_OFF;
|
2023-09-19 21:02:09 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::EventSetCollisionModel(Event *ev)
|
|
|
|
{
|
|
|
|
Entity *pColEnt = ev->GetEntity(1);
|
|
|
|
|
|
|
|
if (!pColEnt) {
|
|
|
|
ScriptError("Trying to set a collision model with a NULL entity.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pCollisionEntity) {
|
2023-09-24 01:13:03 +02:00
|
|
|
m_pCollisionEntity->PostEvent(EV_Remove, 0);
|
2023-08-11 03:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m_pCollisionEntity = new VehicleCollisionEntity(this);
|
2023-09-24 01:13:03 +02:00
|
|
|
if (m_pCollisionEntity) {
|
|
|
|
m_pCollisionEntity->setModel(pColEnt->model);
|
|
|
|
m_pCollisionEntity->setOrigin(origin);
|
|
|
|
m_pCollisionEntity->setAngles(angles);
|
2023-08-11 03:09:10 +02:00
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
if (m_pCollisionEntity->model.length() && *m_pCollisionEntity->model == '*') {
|
|
|
|
UpdateCollisionEntity();
|
|
|
|
m_pCollisionEntity->DisconnectPaths();
|
|
|
|
} else {
|
|
|
|
// Re-post the event with the correct time
|
|
|
|
m_pCollisionEntity->CancelEventsOfType(EV_Remove);
|
|
|
|
m_pCollisionEntity->PostEvent(EV_Remove, EV_VEHICLE);
|
|
|
|
m_pCollisionEntity = NULL;
|
2023-08-11 03:09:10 +02:00
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
ScriptError("Model for Entity not of a valid type. Must be B-Model.");
|
|
|
|
}
|
2023-08-11 03:09:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::EventLock(Event *ev)
|
|
|
|
{
|
|
|
|
Lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::EventUnlock(Event *ev)
|
|
|
|
{
|
|
|
|
UnLock();
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::SetSoundSet(Event *ev)
|
|
|
|
{
|
|
|
|
m_sSoundSet = ev->GetString(1);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
void VehicleTurretGun::UpdateOwner(Sentient *pOwner)
|
|
|
|
{
|
2023-09-24 17:13:34 +02:00
|
|
|
Vector jitter;
|
|
|
|
Vector position;
|
|
|
|
Vector forward, left, up;
|
|
|
|
orientation_t bone_or;
|
|
|
|
|
|
|
|
if (m_iEyeBone >= 0) {
|
|
|
|
GetRawTag(m_iEyeBone, &bone_or);
|
|
|
|
angles.AngleVectorsLeft(&forward, &left, &up);
|
|
|
|
|
|
|
|
position = origin + bone_or.origin[0] * forward + bone_or.origin[1] * left + bone_or.origin[2] * up;
|
|
|
|
} else {
|
|
|
|
(m_vBaseAngles + m_vLocalAngles).AngleVectorsLeft(&forward, &left, &up);
|
|
|
|
|
|
|
|
position = origin + forward * -16 + up * 5;
|
|
|
|
|
|
|
|
if (m_iPitchBone >= 0) {
|
|
|
|
GetRawTag(m_iPitchBone, &bone_or);
|
|
|
|
angles.AngleVectorsLeft(&forward, &left, &up);
|
|
|
|
|
|
|
|
position += bone_or.origin[0] * forward + bone_or.origin[1] * left + bone_or.origin[2] * up;
|
|
|
|
} else {
|
|
|
|
angles.AngleVectorsLeft(&forward, NULL, &up);
|
|
|
|
position += up * 40;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
jitter = m_vUserViewAng + m_vBaseAngles;
|
|
|
|
P_ApplyFiringViewJitter(jitter);
|
|
|
|
|
|
|
|
if (!m_pUserCamera && pOwner->IsSubclassOfPlayer()) {
|
|
|
|
Player *player = static_cast<Player *>(pOwner);
|
|
|
|
|
|
|
|
m_pUserCamera = new Camera();
|
|
|
|
player->SetCamera(m_pUserCamera, 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (owner) {
|
2023-11-13 00:35:55 +01:00
|
|
|
jitter[1] = m_vUserViewAng[1];
|
2023-09-24 17:13:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m_pUserCamera->setOrigin(position);
|
|
|
|
m_pUserCamera->setAngles(jitter);
|
|
|
|
|
|
|
|
if (GetTag("tag_seat", &bone_or)) {
|
|
|
|
vec3_t ang;
|
|
|
|
|
|
|
|
pOwner->setOrigin(bone_or.origin);
|
|
|
|
MatrixToEulerAngles(bone_or.axis, ang);
|
|
|
|
pOwner->setAngles(ang);
|
|
|
|
} else {
|
|
|
|
// no seat
|
|
|
|
pOwner->setOrigin(origin);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pOwner->IsSubclassOfPlayer()) {
|
|
|
|
Player *player = static_cast<Player *>(pOwner);
|
|
|
|
|
|
|
|
if (!player->IsZoomed()) {
|
|
|
|
player->ToggleZoom(80);
|
|
|
|
}
|
|
|
|
|
|
|
|
player->client->ps.camera_flags |= CF_CAMERA_ANGLES_TURRETMODE;
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
2023-09-24 01:13:03 +02:00
|
|
|
void VehicleTurretGun::TurretHasBeenMounted()
|
2023-08-11 03:09:10 +02:00
|
|
|
{
|
2024-02-12 15:12:55 +01:00
|
|
|
m_fLastFireTime = (level.time + m_fWarmupDelay) - fire_delay[FIRE_PRIMARY];
|
2023-09-24 01:13:03 +02:00
|
|
|
if (m_fWarmupDelay > 0.25) {
|
|
|
|
Sound(m_sSoundSet + "snd_warmup");
|
|
|
|
}
|
|
|
|
|
|
|
|
m_fWarmupTimeRemaining = m_fWarmupDelay;
|
2023-08-11 03:09:10 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
void VehicleTurretGun::PlaceTurret(Event *ev)
|
|
|
|
{
|
|
|
|
setSolidType(SOLID_BBOX);
|
2023-10-01 21:15:37 +02:00
|
|
|
setContents(CONTENTS_UNKNOWN2);
|
2023-09-19 21:02:09 +02:00
|
|
|
setMoveType(MOVETYPE_NONE);
|
|
|
|
showModel();
|
|
|
|
groundentity = NULL;
|
|
|
|
m_fStartYaw = angles[1];
|
|
|
|
flags |= FL_THINK;
|
|
|
|
m_vLastBaseAngles = angles;
|
|
|
|
m_vBaseAngles = angles;
|
|
|
|
|
|
|
|
if (m_vBaseAngles.length() != 0.0f) {
|
|
|
|
AnglesToAxis(m_vBaseAngles, m_mBaseOrientation);
|
|
|
|
m_bBOIsSet = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_iPitchBone = gi.Tag_NumForName(edict->tiki, "pitch");
|
|
|
|
SetControllerTag(0, m_iPitchBone);
|
2023-10-01 21:15:37 +02:00
|
|
|
m_iBarrelTag = gi.Tag_NumForName(edict->tiki, GetTagBarrel());
|
2023-09-19 21:02:09 +02:00
|
|
|
m_iEyeBone = gi.Tag_NumForName(edict->tiki, "eyebone");
|
|
|
|
|
2023-10-01 21:15:37 +02:00
|
|
|
if (m_pRemoteOwner) {
|
|
|
|
UpdateOwner(m_pRemoteOwner);
|
|
|
|
} else if (owner) {
|
|
|
|
UpdateOwner(owner);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::EventTurnSpeed(Event *ev)
|
|
|
|
{
|
|
|
|
AI_TurnSpeed(ev->GetFloat(1));
|
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
bool VehicleTurretGun::UseRemoteControl(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-11 03:09:10 +02:00
|
|
|
return m_bUseRemoteControl;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
qboolean VehicleTurretGun::ReadyToFire(firemode_t mode, qboolean playsound)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
if (!use_no_ammo) {
|
|
|
|
return Weapon::ReadyToFire(mode, playsound);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_fReloadDelay <= 0) {
|
|
|
|
return Weapon::ReadyToFire(mode, playsound);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_fReloadTimeRemaining <= 0) {
|
|
|
|
return Weapon::ReadyToFire(mode, playsound);
|
|
|
|
}
|
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::AdjustReloadStatus()
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
if (!use_no_ammo) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_fReloadDelay <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_iAmmo--;
|
|
|
|
ammo_in_clip[FIRE_PRIMARY] = m_iAmmo;
|
|
|
|
if (!m_iAmmo) {
|
|
|
|
m_iAmmo = m_iReloadShots;
|
|
|
|
m_fReloadTimeRemaining = m_fReloadDelay;
|
|
|
|
|
2024-09-02 19:30:39 +02:00
|
|
|
if (g_target_game <= target_game_e::TG_MOHTA) {
|
|
|
|
//
|
|
|
|
// Below 2.30 (Spearhead), the sound is played immediately.
|
|
|
|
// 2.30 has it wrong with the Nebelwerfer.
|
|
|
|
//
|
|
|
|
ProcessEvent(EV_VehicleTurretGun_PlayReloadSound);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Changed in 2.30
|
|
|
|
// Play the reload sound once finished reloading
|
2023-09-24 01:13:03 +02:00
|
|
|
PostEvent(EV_VehicleTurretGun_PlayReloadSound, m_fReloadDelay * 0.5);
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::GetMuzzlePosition(vec3_t position, vec3_t vBarrelPos, vec3_t forward, vec3_t right, vec3_t up)
|
|
|
|
{
|
2023-09-24 17:13:34 +02:00
|
|
|
Vector delta;
|
|
|
|
Vector aim_angles;
|
|
|
|
Sentient *viewer;
|
|
|
|
orientation_t barrel_or;
|
|
|
|
vec3_t weap_axis[3];
|
|
|
|
float mat[3][3];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
viewer = owner;
|
|
|
|
if (!owner) {
|
|
|
|
viewer = GetRemoteOwner();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!viewer) {
|
|
|
|
if (forward || right || up) {
|
|
|
|
if (!m_bUseRemoteControl && aim_target) {
|
|
|
|
Vector dir;
|
|
|
|
Vector vAng;
|
|
|
|
Vector vFwd;
|
|
|
|
|
|
|
|
AngleVectors(angles, vFwd, right, up);
|
|
|
|
delta = aim_target->origin;
|
|
|
|
|
|
|
|
if (aim_target->HasVehicle()) {
|
|
|
|
delta.z -= 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
dir = delta - origin;
|
|
|
|
VectorNormalize(dir);
|
|
|
|
dir.x = vFwd.x;
|
|
|
|
dir.y = vFwd.y;
|
|
|
|
VectorNormalize(dir);
|
|
|
|
VectorToAngles(dir, vAng);
|
|
|
|
AngleVectors(vAng, forward, right, up);
|
|
|
|
} else {
|
|
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy(origin, position);
|
|
|
|
|
|
|
|
if (GetRawTag(GetTagBarrel(), &barrel_or)) {
|
|
|
|
AnglesToAxis(angles, weap_axis);
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
VectorMA(position, barrel_or.origin[i], weap_axis[i], position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vBarrelPos) {
|
|
|
|
VectorCopy(position, vBarrelPos);
|
|
|
|
}
|
|
|
|
} else if (viewer->IsSubclassOfPlayer()) {
|
|
|
|
VectorCopy(origin, position);
|
|
|
|
|
|
|
|
if (GetRawTag(GetTagBarrel(), &barrel_or)) {
|
|
|
|
AnglesToAxis(angles, weap_axis);
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
VectorMA(position, barrel_or.origin[i], weap_axis[i], position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vBarrelPos) {
|
|
|
|
VectorCopy(position, vBarrelPos);
|
|
|
|
}
|
|
|
|
|
2024-02-26 19:21:12 +01:00
|
|
|
delta = viewer->GunTarget(false, position) - position;
|
2023-09-24 17:13:34 +02:00
|
|
|
aim_angles = delta.toAngles();
|
|
|
|
|
|
|
|
if (IsSubclassOfVehicleTurretGun()) {
|
|
|
|
vec3_t ang;
|
|
|
|
|
|
|
|
MatrixMultiply(barrel_or.axis, weap_axis, mat);
|
|
|
|
vectoangles(mat[0], ang);
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
float diff;
|
|
|
|
|
|
|
|
diff = abs(AngleSubtract(aim_angles[i], ang[i]));
|
|
|
|
if (diff > m_vAimTolerance[i]) {
|
|
|
|
aim_angles[i] = m_vAimOffset[i] + ang[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (forward || right || up) {
|
|
|
|
AngleVectors(aim_angles, forward, right, up);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
TurretGun::GetMuzzlePosition(position, vBarrelPos, forward, right, up);
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::ApplyFireKickback(const Vector& org, float kickback)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
Vehicle *pVehicle;
|
|
|
|
|
|
|
|
if (!m_pVehicleOwner || !m_pVehicleOwner->IsSubclassOfVehicle()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pVehicle = static_cast<Vehicle *>(m_pVehicleOwner.Pointer());
|
|
|
|
pVehicle->m_fForwardForce += org.x * kickback;
|
|
|
|
pVehicle->m_fLeftForce += org.y * kickback;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetRemoteOwner(Sentient *e)
|
|
|
|
{
|
|
|
|
m_bUseRemoteControl = true;
|
|
|
|
m_pRemoteOwner = e;
|
2023-10-02 00:16:12 +02:00
|
|
|
flags |= FL_THINK;
|
2023-09-24 01:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::EventGetCollisionModel(Event *ev)
|
|
|
|
{
|
|
|
|
ev->AddEntity(m_pCollisionEntity);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
float VehicleTurretGun::FireDelay(firemode_t mode)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
return fire_delay[mode];
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetWarmupDelay(Event *ev)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
m_fWarmupDelay = ev->GetFloat(1);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetFireWarmupDelay(Event *ev)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
m_fFireWarmupDelay = ev->GetFloat(1);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetReloadDelay(Event *ev)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
m_fReloadDelay = ev->GetFloat(1);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetReloadShots(Event *ev)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
m_iReloadShots = ev->GetInteger(1);
|
|
|
|
m_iAmmo = m_iReloadShots;
|
|
|
|
ammo_in_clip[FIRE_PRIMARY] = m_iAmmo;
|
|
|
|
ammo_clip_size[FIRE_PRIMARY] = m_iAmmo;
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetAimOffset(Event *ev)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
m_vAimOffset = ev->GetVector(1);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetAimTolerance(Event *ev)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
m_vAimTolerance = ev->GetVector(1);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetTargetEntity(Event *ev)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
SetTargetEntity(ev->GetEntity(1));
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::PlayReloadSound(Event *ev)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
Sound(m_sSoundSet + "snd_reload", CHAN_AUTO);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::SetTargetEntity(Entity *ent)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
Vector delta;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!ent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (owner || m_pRemoteOwner) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
delta = ent->origin - origin;
|
|
|
|
delta.normalize();
|
|
|
|
|
|
|
|
vectoangles(delta, m_vLocalAngles);
|
|
|
|
m_vLocalAngles -= m_vBaseAngles;
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
if (m_vLocalAngles[i] > 180) {
|
|
|
|
m_vLocalAngles[i] -= 360;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_vLocalAngles[i] < -180) {
|
|
|
|
m_vLocalAngles[i] += 360;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-06 22:19:24 +02:00
|
|
|
m_vLocalAngles[0] = Q_clamp_float(m_vLocalAngles[0], m_fPitchUpCap, m_fPitchDownCap);
|
|
|
|
m_vLocalAngles[1] = Q_clamp_float(m_vLocalAngles[1], -(m_fStartYaw + m_fMaxYawOffset), m_fStartYaw + m_fMaxYawOffset);
|
2023-09-24 01:13:03 +02:00
|
|
|
|
|
|
|
UpdateOrientation(false);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::UpdateAndMoveOwner()
|
|
|
|
{
|
2023-09-24 17:13:34 +02:00
|
|
|
Vector delta;
|
|
|
|
Vector dir;
|
|
|
|
Vector targetAngles;
|
|
|
|
Entity *parentEnt;
|
|
|
|
float fDiff;
|
|
|
|
|
|
|
|
parentEnt = GetParent();
|
|
|
|
|
2023-10-06 22:19:24 +02:00
|
|
|
m_vUserViewAng[0] = Q_clamp_float(m_vUserViewAng[0], m_fPitchUpCap - MAX_VT_PITCHCAP_OFFSET, m_fPitchDownCap + MAX_VT_PITCHCAP_OFFSET);
|
2023-09-24 17:13:34 +02:00
|
|
|
|
|
|
|
fDiff = AngleSubtract(m_vUserViewAng[1], m_fStartYaw);
|
2023-10-06 22:19:24 +02:00
|
|
|
fDiff = Q_clamp_float(fDiff, -(m_fMaxYawOffset + MAX_VT_YAW_OFFSET), m_fMaxYawOffset + MAX_VT_YAW_OFFSET);
|
2023-11-13 00:35:55 +01:00
|
|
|
m_vUserViewAng[1] = m_fStartYaw + fDiff;
|
2023-09-24 17:13:34 +02:00
|
|
|
|
|
|
|
owner->SetViewAngles(m_vUserViewAng + m_vBaseAngles);
|
2023-11-13 00:35:55 +01:00
|
|
|
delta = owner->GunTarget(true) - origin;
|
2023-09-24 17:13:34 +02:00
|
|
|
|
|
|
|
if (m_bBOIsSet) {
|
|
|
|
dir[0] = m_mBaseOrientation[0] * delta;
|
|
|
|
dir[1] = m_mBaseOrientation[1] * delta;
|
|
|
|
dir[2] = m_mBaseOrientation[2] * delta;
|
|
|
|
} else if (parentEnt) {
|
|
|
|
vec3_t mat[3];
|
|
|
|
|
|
|
|
AnglesToAxis(parentEnt->angles, mat);
|
|
|
|
dir[0] = mat[0] * delta;
|
|
|
|
dir[1] = mat[1] * delta;
|
|
|
|
dir[2] = mat[2] * delta;
|
|
|
|
} else {
|
|
|
|
dir = delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorNormalize(dir);
|
|
|
|
vectoangles(dir, m_vTargetAngles);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::UpdateTimers(float& yawTimer, float& pitchTimer)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
if (m_fReloadTimeRemaining > 0) {
|
|
|
|
m_fReloadTimeRemaining -= level.frametime;
|
|
|
|
if (m_fReloadTimeRemaining <= 0) {
|
|
|
|
ammo_in_clip[FIRE_PRIMARY] = m_iAmmo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_fWarmupTimeRemaining > 0) {
|
|
|
|
m_fWarmupTimeRemaining -= level.frametime;
|
|
|
|
yawTimer = level.frametime * (m_fTurnSpeed * ((m_fWarmupDelay - m_fWarmupTimeRemaining) / m_fWarmupDelay));
|
|
|
|
pitchTimer = level.frametime * (m_fAIPitchSpeed * ((m_fWarmupDelay - m_fWarmupTimeRemaining) / m_fWarmupDelay));
|
2023-10-02 00:16:12 +02:00
|
|
|
} else {
|
2023-10-02 21:13:48 +02:00
|
|
|
yawTimer = m_fTurnSpeed * level.frametime;
|
2023-10-02 00:16:12 +02:00
|
|
|
pitchTimer = m_fAIPitchSpeed * level.frametime;
|
2023-09-24 01:13:03 +02:00
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::UpdateCaps(float maxYawOffset, float maxPitchOffset)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
float fDiff;
|
|
|
|
|
|
|
|
if (m_vTargetAngles[0] > 180) {
|
|
|
|
m_vTargetAngles[0] -= 360;
|
|
|
|
} else if (m_vTargetAngles[0] < -180) {
|
|
|
|
m_vTargetAngles[0] += 360;
|
|
|
|
}
|
|
|
|
|
2023-10-06 22:19:24 +02:00
|
|
|
m_vTargetAngles[0] = Q_clamp_float(m_vTargetAngles[0], m_fPitchUpCap, m_fPitchDownCap);
|
2023-09-24 01:13:03 +02:00
|
|
|
|
|
|
|
if (m_fAIPitchSpeed > 1000) {
|
|
|
|
m_vLocalAngles[0] = m_vTargetAngles[0];
|
|
|
|
} else {
|
|
|
|
fDiff = AngleSubtract(m_vTargetAngles[0], m_vLocalAngles[0]);
|
|
|
|
if (fabs(fDiff) >= maxPitchOffset) {
|
|
|
|
if (fDiff > 0) {
|
|
|
|
m_vLocalAngles[0] += maxPitchOffset;
|
|
|
|
} else {
|
|
|
|
m_vLocalAngles[0] -= maxPitchOffset;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_vLocalAngles[0] = m_vTargetAngles[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fDiff = AngleSubtract(m_vTargetAngles[1], m_fStartYaw);
|
2023-10-06 22:19:24 +02:00
|
|
|
fDiff = Q_clamp_float(fDiff, -m_fMaxYawOffset, m_fMaxYawOffset);
|
2023-09-24 01:13:03 +02:00
|
|
|
|
|
|
|
if (m_fTurnSpeed > 1000) {
|
|
|
|
m_vLocalAngles[1] = m_vTargetAngles[1];
|
|
|
|
} else {
|
|
|
|
m_vTargetAngles[1] = m_fStartYaw + fDiff;
|
|
|
|
|
|
|
|
fDiff = AngleSubtract(m_vTargetAngles[1], m_vLocalAngles[1]);
|
|
|
|
if (fabs(fDiff) < 2) {
|
|
|
|
Unregister(STRING_ONTARGET);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fabs(fDiff) >= maxYawOffset) {
|
|
|
|
if (fDiff > 0) {
|
|
|
|
m_vLocalAngles[1] += maxYawOffset;
|
|
|
|
} else {
|
|
|
|
m_vLocalAngles[1] -= maxYawOffset;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_vLocalAngles[1] = m_vTargetAngles[1];
|
|
|
|
}
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::IdleToRestPosition()
|
|
|
|
{
|
2023-09-24 17:13:34 +02:00
|
|
|
Vector vDir, vNewAngles, vEnd;
|
|
|
|
trace_t trace;
|
|
|
|
|
|
|
|
if (angles[0] > 180) {
|
|
|
|
angles[0] -= 360;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (angles[0] < -80) {
|
|
|
|
m_fIdlePitchSpeed = 0;
|
|
|
|
m_iIdleHitCount = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_iIdleHitCount > 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_fIdlePitchSpeed -= level.frametime * 300;
|
|
|
|
|
|
|
|
vNewAngles = Vector(angles[0] + level.frametime * m_fIdlePitchSpeed, angles[1], angles[2]);
|
|
|
|
vNewAngles.AngleVectorsLeft(&vDir);
|
|
|
|
vEnd = origin + vDir * m_vIdleCheckOffset[0];
|
|
|
|
|
|
|
|
trace = G_Trace(origin, vec_zero, vec_zero, vEnd, this, edict->clipmask, false, "TurretGun::Think idle");
|
|
|
|
|
|
|
|
if (trace.fraction == 1) {
|
|
|
|
setAngles(vNewAngles);
|
|
|
|
m_iIdleHitCount = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int iTry;
|
|
|
|
for (iTry = 3; iTry > 0; iTry--) {
|
|
|
|
vNewAngles[0] = angles[0] + level.frametime * m_fIdlePitchSpeed * iTry * 0.25f;
|
|
|
|
if (vNewAngles[0] < m_fMaxIdlePitch) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
vNewAngles.AngleVectorsLeft(&vDir);
|
|
|
|
vEnd = origin + vDir * m_vIdleCheckOffset[0];
|
|
|
|
|
|
|
|
trace = G_Trace(origin, vec_zero, vec_zero, vEnd, this, edict->clipmask, false, "TurretGun::Think idle");
|
|
|
|
|
|
|
|
if (trace.fraction == 1) {
|
|
|
|
setAngles(vNewAngles);
|
|
|
|
|
|
|
|
m_iIdleHitCount = 0;
|
|
|
|
m_fIdlePitchSpeed *= 0.25f * iTry;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!iTry) {
|
|
|
|
m_fIdlePitchSpeed = 0;
|
|
|
|
|
|
|
|
Entity *ent = G_GetEntity(trace.entityNum);
|
|
|
|
|
|
|
|
if (ent && ent == world) {
|
|
|
|
m_iIdleHitCount++;
|
|
|
|
} else {
|
|
|
|
m_iIdleHitCount = 0;
|
|
|
|
}
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::UpdateFireControl()
|
|
|
|
{
|
2023-09-24 17:13:34 +02:00
|
|
|
if (!m_iFiring) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_iFiring == 1) {
|
|
|
|
if (m_fFireWarmupDelay > 0) {
|
|
|
|
Sound(m_sSoundSet + "snd_fire_warmup");
|
|
|
|
m_fTargetReloadTime = level.time + m_fFireWarmupDelay;
|
|
|
|
m_iFiring = 2;
|
|
|
|
} else {
|
|
|
|
m_iFiring = 3;
|
|
|
|
}
|
|
|
|
} else if (m_iFiring == 2) {
|
|
|
|
if (level.time >= m_fTargetReloadTime) {
|
|
|
|
m_iFiring = 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_iFiring <= 2) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_fMaxBurstDelay) {
|
|
|
|
m_iFiring = 4;
|
|
|
|
|
|
|
|
if (ReadyToFire(FIRE_PRIMARY, false)) {
|
|
|
|
Fire(FIRE_PRIMARY);
|
|
|
|
assert(!m_pVehicleOwner || (m_pVehicleOwner && m_pVehicleOwner->IsSubclassOfVehicle()));
|
|
|
|
|
|
|
|
m_fCurrViewJitter = m_fViewJitter;
|
|
|
|
AdjustReloadStatus();
|
|
|
|
|
|
|
|
if (m_pVehicleOwner && m_pVehicleOwner->IsSubclassOfVehicleTank()) {
|
|
|
|
VehicleTank *tank = static_cast<VehicleTank *>(m_pVehicleOwner.Pointer());
|
|
|
|
tank->KickSuspension(Vector(orientation[1]) * -1, 6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (m_iFiring == 4) {
|
|
|
|
if (ReadyToFire(FIRE_PRIMARY, false)) {
|
|
|
|
Fire(FIRE_PRIMARY);
|
|
|
|
assert(!m_pVehicleOwner || (m_pVehicleOwner && m_pVehicleOwner->IsSubclassOfVehicle()));
|
|
|
|
|
|
|
|
m_fCurrViewJitter = m_fViewJitter;
|
|
|
|
AdjustReloadStatus();
|
|
|
|
|
|
|
|
if (m_pVehicleOwner && m_pVehicleOwner->IsSubclassOfVehicleTank()) {
|
|
|
|
VehicleTank *tank = static_cast<VehicleTank *>(m_pVehicleOwner.Pointer());
|
|
|
|
tank->KickSuspension(Vector(orientation[1]) * -1, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (level.time > m_fFireToggleTime) {
|
|
|
|
m_iFiring = 1;
|
|
|
|
m_fFireToggleTime = level.time + m_fMinBurstDelay + (m_fMaxBurstDelay - m_fMinBurstDelay) * random();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (level.time > m_fFireToggleTime) {
|
|
|
|
m_iFiring = 4;
|
|
|
|
m_fFireToggleTime = level.time + m_fMinBurstDelay + (m_fMaxBurstDelay - m_fMinBurstDelay) * random();
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::UpdateCollisionEntity()
|
|
|
|
{
|
2023-09-24 17:13:34 +02:00
|
|
|
Sentient *sentOwner;
|
|
|
|
Vector amove;
|
|
|
|
Vector newAngles;
|
|
|
|
solid_t oldsolid;
|
|
|
|
|
|
|
|
if (!m_pCollisionEntity) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
setSolidType(SOLID_NOT);
|
|
|
|
|
|
|
|
sentOwner = GetSentientOwner();
|
|
|
|
if (sentOwner) {
|
|
|
|
oldsolid = sentOwner->edict->solid;
|
|
|
|
sentOwner->setSolidType(SOLID_NOT);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_pCollisionEntity->Solid();
|
|
|
|
if (m_iPitchBone < 0) {
|
|
|
|
newAngles = angles;
|
|
|
|
} else if (m_bBOIsSet) {
|
|
|
|
vec3_t axis[3];
|
|
|
|
vec3_t mat[3];
|
|
|
|
|
2024-02-12 15:02:49 +01:00
|
|
|
AnglesToAxis(m_vLocalAngles, axis);
|
2023-09-24 17:13:34 +02:00
|
|
|
MatrixMultiply(axis, m_mBaseOrientation, mat);
|
|
|
|
MatrixToEulerAngles(mat, newAngles);
|
|
|
|
} else {
|
|
|
|
newAngles = m_vLocalAngles;
|
|
|
|
}
|
|
|
|
|
|
|
|
amove[0] = angledist(newAngles[0] - m_pCollisionEntity->angles[0]);
|
|
|
|
amove[1] = angledist(newAngles[1] - m_pCollisionEntity->angles[1]);
|
|
|
|
amove[2] = angledist(newAngles[2] - m_pCollisionEntity->angles[2]);
|
|
|
|
|
|
|
|
G_PushMove(m_pCollisionEntity, origin - m_pCollisionEntity->origin, amove);
|
|
|
|
|
|
|
|
m_pCollisionEntity->setOrigin(origin);
|
|
|
|
m_pCollisionEntity->setAngles(newAngles);
|
|
|
|
m_pCollisionEntity->velocity = velocity;
|
|
|
|
m_pCollisionEntity->avelocity = avelocity;
|
|
|
|
|
|
|
|
if (sentOwner) {
|
|
|
|
sentOwner->setSolidType(static_cast<solid_t>(oldsolid));
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::RestrictPitch()
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-06 22:19:24 +02:00
|
|
|
m_vUserViewAng[0] = Q_clamp_float(m_vUserViewAng[0], m_fPitchUpCap - MAX_VT_PITCHCAP_OFFSET, m_fPitchDownCap + MAX_VT_PITCHCAP_OFFSET);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::RestrictYaw()
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
float fDiff;
|
|
|
|
|
2023-10-02 00:16:12 +02:00
|
|
|
fDiff = AngleSubtract(m_vUserViewAng[1], m_fStartYaw);
|
2023-10-06 22:19:24 +02:00
|
|
|
fDiff = Q_clamp_float(fDiff, -(m_fMaxYawOffset + MAX_VT_YAW_OFFSET), m_fMaxYawOffset + MAX_VT_YAW_OFFSET);
|
2023-09-24 01:13:03 +02:00
|
|
|
|
2023-10-02 00:16:12 +02:00
|
|
|
m_vUserViewAng[1] = m_fStartYaw + fDiff;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::UpdateRemoteControl()
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-24 17:13:34 +02:00
|
|
|
Sentient *sentOwner;
|
|
|
|
Vector position;
|
|
|
|
Vector forward, left, up;
|
|
|
|
Vector start, end;
|
|
|
|
Vector delta;
|
|
|
|
Vector transformed;
|
|
|
|
trace_t trace;
|
|
|
|
|
|
|
|
RestrictPitch();
|
|
|
|
RestrictYaw();
|
|
|
|
|
|
|
|
sentOwner = GetSentientOwner();
|
|
|
|
if (!sentOwner) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sentOwner->SetViewAngles(m_vUserViewAng + m_vBaseAngles);
|
|
|
|
|
|
|
|
if (m_iEyeBone < 0) {
|
|
|
|
(m_vUserViewAng + m_vBaseAngles).AngleVectors(&forward);
|
|
|
|
end = m_pRemoteOwner->origin + forward * 16384;
|
|
|
|
|
|
|
|
trace =
|
|
|
|
G_Trace(m_pRemoteOwner->origin, vec_zero, vec_zero, end, this, MASK_TRANSPARENT, qfalse, "VehicleTurret");
|
|
|
|
|
|
|
|
position = trace.endpos;
|
|
|
|
} else {
|
|
|
|
orientation_t eye_or;
|
|
|
|
|
|
|
|
GetRawTag(m_iEyeBone, &eye_or);
|
|
|
|
angles.AngleVectorsLeft(&forward, &left, &up);
|
|
|
|
|
|
|
|
start = origin + forward * eye_or.origin[0] + left * eye_or.origin[1] + up * eye_or.origin[2];
|
|
|
|
(m_vUserViewAng + m_vBaseAngles).AngleVectorsLeft(&forward);
|
|
|
|
|
|
|
|
end = start + forward * 8192;
|
|
|
|
|
|
|
|
trace = G_Trace(start, vec_zero, vec_zero, end, this, MASK_TRANSPARENT, qfalse, "VehicleTurret");
|
|
|
|
|
|
|
|
position = trace.endpos;
|
|
|
|
}
|
|
|
|
|
|
|
|
delta = position - origin;
|
|
|
|
|
|
|
|
if (m_bBOIsSet) {
|
|
|
|
transformed[0] = m_mBaseOrientation[0] * delta;
|
|
|
|
transformed[1] = m_mBaseOrientation[1] * delta;
|
|
|
|
transformed[2] = m_mBaseOrientation[2] * delta;
|
|
|
|
} else if (GetParent()) {
|
|
|
|
vec3_t mat[3];
|
|
|
|
|
|
|
|
AnglesToAxis(GetParent()->angles, mat);
|
|
|
|
transformed[0] = mat[0] * delta;
|
|
|
|
transformed[1] = mat[1] * delta;
|
|
|
|
transformed[2] = mat[2] * delta;
|
|
|
|
} else {
|
|
|
|
transformed = delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorNormalize(transformed);
|
|
|
|
vectoangles(transformed, m_vTargetAngles);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::UpdateAimTarget()
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
Vector delta;
|
|
|
|
Vector transformed;
|
|
|
|
|
|
|
|
delta = aim_target->centroid - centroid;
|
|
|
|
if (m_bBOIsSet) {
|
|
|
|
transformed[0] = m_mBaseOrientation[0] * delta;
|
|
|
|
transformed[1] = m_mBaseOrientation[1] * delta;
|
|
|
|
transformed[2] = m_mBaseOrientation[2] * delta;
|
|
|
|
} else if (GetParent()) {
|
|
|
|
vec3_t mat[3];
|
|
|
|
|
|
|
|
AnglesToAxis(GetParent()->angles, mat);
|
|
|
|
transformed[0] = mat[0] * delta;
|
|
|
|
transformed[1] = mat[1] * delta;
|
|
|
|
transformed[2] = mat[2] * delta;
|
|
|
|
} else {
|
|
|
|
transformed = delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorNormalize(transformed);
|
|
|
|
vectoangles(transformed, m_vTargetAngles);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
Entity *VehicleTurretGun::GetParent() const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
if (m_pBaseEntity) {
|
|
|
|
return m_pBaseEntity;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (edict->s.parent) {
|
|
|
|
return G_GetEntity(edict->s.parent);
|
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
return NULL;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
SentientPtr VehicleTurretGun::GetRemoteOwner(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-11 03:09:10 +02:00
|
|
|
return m_pRemoteOwner;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
SentientPtr VehicleTurretGun::GetSentientOwner()
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-10-06 22:19:24 +02:00
|
|
|
if (IsRemoteControlled()) {
|
|
|
|
return GetRemoteOwner();
|
|
|
|
} else {
|
|
|
|
return owner;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::EndRemoteControl()
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
m_bUseRemoteControl = false;
|
|
|
|
m_pRemoteOwner = NULL;
|
|
|
|
m_fIdlePitchSpeed = 0;
|
|
|
|
m_iIdleHitCount = 0;
|
|
|
|
m_iFiring = 0;
|
|
|
|
m_vTargetAngles = m_vLocalAngles;
|
2023-10-13 21:16:37 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// Added in OPM.
|
|
|
|
// The camera must be removed after ending the remote control.
|
|
|
|
RemoveUserCamera();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
float VehicleTurretGun::GetWarmupFraction() const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
float frac;
|
|
|
|
|
|
|
|
if (!m_fTargetReloadTime) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-10-02 21:13:48 +02:00
|
|
|
frac = (m_fTargetReloadTime - level.time) / m_fFireWarmupDelay;
|
|
|
|
// Clamp between [0,1]
|
|
|
|
frac = Q_clamp_float(frac, 0, 1);
|
2023-09-24 01:13:03 +02:00
|
|
|
|
|
|
|
return 1.0 - frac;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
2023-08-11 03:09:10 +02:00
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
bool VehicleTurretGun::IsRemoteControlled()
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
return m_bUseRemoteControl;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VehicleTurretGun::isLocked(void)
|
|
|
|
{
|
|
|
|
return m_bLocked;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::Lock(void)
|
|
|
|
{
|
|
|
|
m_bLocked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGun::UnLock(void)
|
|
|
|
{
|
|
|
|
m_bLocked = false;
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
2023-08-11 03:09:10 +02:00
|
|
|
void VehicleTurretGun::Archive(Archiver& arc)
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
TurretGun::Archive(arc);
|
|
|
|
|
|
|
|
arc.ArchiveVector(&m_vTargetAngles);
|
|
|
|
arc.ArchiveVector(&m_vLocalAngles);
|
|
|
|
arc.ArchiveVec3(m_mBaseOrientation[0]);
|
|
|
|
arc.ArchiveVec3(m_mBaseOrientation[1]);
|
|
|
|
arc.ArchiveVec3(m_mBaseOrientation[2]);
|
|
|
|
arc.ArchiveInteger(&m_iPitchBone);
|
|
|
|
arc.ArchiveVector(&m_vBaseAngles);
|
|
|
|
arc.ArchiveVector(&m_vLastBaseAngles);
|
|
|
|
arc.ArchiveSafePointer(&m_pBaseEntity);
|
|
|
|
arc.ArchiveBool(&m_bBOIsSet);
|
|
|
|
arc.ArchiveBool(&m_bUseRemoteControl);
|
|
|
|
arc.ArchiveSafePointer(&m_pRemoteOwner);
|
|
|
|
arc.ArchiveInteger(&m_iBarrelTag);
|
|
|
|
arc.ArchiveInteger(&m_iEyeBone);
|
|
|
|
arc.ArchiveVector(&m_vBarrelPos);
|
|
|
|
arc.ArchiveVector(&m_vLastBarrelPos);
|
|
|
|
arc.ArchiveFloat(&m_fWarmupTimeRemaining);
|
|
|
|
arc.ArchiveFloat(&m_fWarmupDelay);
|
|
|
|
arc.ArchiveFloat(&m_fFireWarmupDelay);
|
|
|
|
arc.ArchiveFloat(&m_fTargetReloadTime);
|
|
|
|
arc.ArchiveInteger(&m_iReloadShots);
|
|
|
|
arc.ArchiveInteger(&m_iAmmo);
|
|
|
|
arc.ArchiveFloat(&m_fReloadDelay);
|
|
|
|
arc.ArchiveFloat(&m_fReloadTimeRemaining);
|
|
|
|
arc.ArchiveVector(&m_vAimOffset);
|
|
|
|
arc.ArchiveVector(&m_vAimTolerance);
|
|
|
|
arc.ArchiveSafePointer(&m_pVehicleOwner);
|
|
|
|
arc.ArchiveBool(&m_bRemoveOnDeath);
|
|
|
|
ArchiveEnum(m_eSoundState, SOUND_STATE_TURRET);
|
|
|
|
arc.ArchiveFloat(&m_fNextSoundState);
|
|
|
|
arc.ArchiveBool(&m_bLocked);
|
|
|
|
arc.ArchiveString(&m_sSoundSet);
|
|
|
|
arc.ArchiveSafePointer(&m_pCollisionEntity);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
EntityPtr VehicleTurretGun::GetVehicle() const
|
|
|
|
{
|
2023-09-24 01:13:03 +02:00
|
|
|
return m_pVehicleOwner;
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
2023-09-26 20:06:27 +02:00
|
|
|
SentientPtr VehicleTurretGun::GetRawRemoteOwner() const
|
|
|
|
{
|
|
|
|
return m_pRemoteOwner;
|
|
|
|
}
|
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
Event EV_VTGP_LinkTurret
|
|
|
|
(
|
|
|
|
"linkturret",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"s",
|
|
|
|
"name",
|
|
|
|
"Sets the next turret in the link.",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
2024-11-11 14:03:24 +01:00
|
|
|
|
|
|
|
// Added in 2.30
|
2023-09-19 21:02:09 +02:00
|
|
|
Event EV_VTGP_SetSwitchThread
|
|
|
|
(
|
|
|
|
"setswitchthread",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"s",
|
|
|
|
"thread",
|
|
|
|
"Set the thread to execute when turret is switched",
|
|
|
|
EV_NORMAL
|
|
|
|
);
|
|
|
|
|
|
|
|
CLASS_DECLARATION(VehicleTurretGun, VehicleTurretGunTandem, "VehicleTurretGunTandem") {
|
|
|
|
{&EV_VTGP_LinkTurret, &VehicleTurretGunTandem::EventLinkTurret },
|
2024-11-11 14:03:24 +01:00
|
|
|
|
|
|
|
// Added in 2.30
|
2023-09-19 21:02:09 +02:00
|
|
|
{&EV_VTGP_SetSwitchThread, &VehicleTurretGunTandem::EventSetSwitchThread},
|
|
|
|
{NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
VehicleTurretGunTandem::VehicleTurretGunTandem()
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
m_Slot.ent = NULL;
|
|
|
|
m_Slot.flags = SLOT_FREE;
|
|
|
|
m_Slot.boneindex = -1;
|
|
|
|
m_Slot.enter_boneindex = -1;
|
2023-09-19 21:02:09 +02:00
|
|
|
|
2023-09-23 21:42:40 +02:00
|
|
|
m_PrimaryTurret = NULL;
|
2023-10-06 19:47:59 +02:00
|
|
|
m_HeadTurret = this;
|
2023-09-23 21:42:40 +02:00
|
|
|
m_ActiveTurret = NULL;
|
|
|
|
|
|
|
|
m_fSwitchTimeRemaining = 0;
|
|
|
|
// 1 second switch delay
|
|
|
|
m_fSwitchDelay = 1;
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
2023-10-06 19:55:36 +02:00
|
|
|
VehicleTurretGunTandem::~VehicleTurretGunTandem()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Added in 2.30
|
|
|
|
// Also remove linked turrets
|
|
|
|
//
|
|
|
|
if (m_Slot.ent) {
|
|
|
|
m_Slot.ent->PostEvent(EV_Remove, 0);
|
|
|
|
}
|
|
|
|
}
|
2023-09-23 21:42:40 +02:00
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
void VehicleTurretGunTandem::EventLinkTurret(Event *ev)
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
VehicleTurretGunTandem *linkedTurret;
|
|
|
|
|
|
|
|
linkedTurret = new VehicleTurretGunTandem();
|
|
|
|
linkedTurret->SetBaseOrientation(orientation, NULL);
|
|
|
|
linkedTurret->setModel(ev->GetString(1));
|
|
|
|
|
|
|
|
AttachLinkedTurret(linkedTurret);
|
|
|
|
UpdateLinkedTurret();
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::AttachLinkedTurret(Entity *ent)
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
if (!ent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
OpenSlotsByModel();
|
|
|
|
m_Slot.ent = ent;
|
|
|
|
m_Slot.flags = SLOT_BUSY;
|
|
|
|
ent->takedamage = DAMAGE_NO;
|
|
|
|
ent->PostEvent(EV_BecomeNonSolid, level.frametime);
|
|
|
|
|
|
|
|
// process every end of frame
|
|
|
|
flags |= FL_POSTTHINK;
|
|
|
|
|
|
|
|
m_Slot.ent->setAngles(angles);
|
|
|
|
|
|
|
|
if (m_PrimaryTurret) {
|
|
|
|
VehicleTurretGunTandem *pTurret = static_cast<VehicleTurretGunTandem *>(m_Slot.ent.Pointer());
|
|
|
|
pTurret->SetPrimaryTurret(m_PrimaryTurret);
|
|
|
|
} else {
|
|
|
|
VehicleTurretGunTandem *pTurret = static_cast<VehicleTurretGunTandem *>(m_Slot.ent.Pointer());
|
|
|
|
pTurret->SetPrimaryTurret(this);
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::UpdateLinkedTurret()
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
VehicleTurretGun *pTurret;
|
|
|
|
orientation_t tag_or;
|
|
|
|
|
|
|
|
if (!(m_Slot.flags & SLOT_BUSY) || !m_Slot.ent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m_Slot.boneindex == -1) {
|
|
|
|
m_Slot.ent->setOrigin(origin);
|
|
|
|
m_Slot.ent->avelocity = avelocity;
|
|
|
|
m_Slot.ent->velocity = velocity;
|
|
|
|
|
|
|
|
pTurret = static_cast<VehicleTurretGun *>(m_Slot.ent.Pointer());
|
|
|
|
pTurret->SetBaseOrientation(orientation, NULL);
|
|
|
|
} else {
|
|
|
|
GetTag(m_Slot.boneindex, &tag_or);
|
|
|
|
|
|
|
|
m_Slot.ent->setOrigin(tag_or.origin);
|
|
|
|
m_Slot.ent->avelocity = avelocity;
|
|
|
|
m_Slot.ent->velocity = velocity;
|
|
|
|
|
|
|
|
pTurret = static_cast<VehicleTurretGun *>(m_Slot.ent.Pointer());
|
|
|
|
pTurret->SetBaseOrientation(tag_or.axis, NULL);
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::OpenSlotsByModel()
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
int tagnum;
|
|
|
|
|
|
|
|
tagnum = gi.Tag_NumForName(edict->tiki, "turret0");
|
|
|
|
if (tagnum >= 0) {
|
|
|
|
m_Slot.boneindex = tagnum;
|
|
|
|
m_Slot.ent = NULL;
|
|
|
|
m_Slot.flags = SLOT_FREE;
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::Think()
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
if (m_PrimaryTurret) {
|
|
|
|
ThinkSecondary();
|
|
|
|
} else {
|
|
|
|
ThinkPrimary();
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool VehicleTurretGunTandem::IsRemoteControlled()
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
if (m_PrimaryTurret) {
|
|
|
|
return m_PrimaryTurret->m_bUseRemoteControl;
|
|
|
|
} else {
|
|
|
|
return m_bUseRemoteControl;
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SentientPtr VehicleTurretGunTandem::GetRemoteOwner()
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
if (m_PrimaryTurret) {
|
|
|
|
return m_PrimaryTurret->m_pRemoteOwner;
|
|
|
|
} else {
|
|
|
|
return m_pRemoteOwner;
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
2023-10-13 21:16:56 +02:00
|
|
|
void VehicleTurretGunTandem::EndRemoteControl()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Added in OPM.
|
|
|
|
// This cleanups remove control stuff from the main turret and linked turrets.
|
|
|
|
VehicleTurretGun::EndRemoteControl();
|
|
|
|
|
|
|
|
if (m_HeadTurret && m_HeadTurret != this) {
|
|
|
|
return m_HeadTurret->EndRemoteControl();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-19 21:02:09 +02:00
|
|
|
void VehicleTurretGunTandem::ThinkSecondary()
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
float yawOffset, pitchOffset;
|
|
|
|
Sentient *sentOwner;
|
|
|
|
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER && m_PrimaryTurret->edict->s.renderfx & RF_DEPTHHACK) {
|
|
|
|
edict->s.renderfx |= RF_DEPTHHACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pCollisionEntity) {
|
|
|
|
m_pCollisionEntity->NotSolid();
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateTimers(yawOffset, pitchOffset);
|
|
|
|
|
|
|
|
if (IsRemoteControlled() && IsActiveTurret()) {
|
|
|
|
UpdateRemoteControl();
|
|
|
|
UpdateCaps(yawOffset, pitchOffset);
|
|
|
|
} else if (m_bRestable) {
|
|
|
|
IdleToRestPosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateOrientation(false);
|
|
|
|
UpdateSound();
|
|
|
|
UpdateFireControl();
|
|
|
|
|
|
|
|
sentOwner = GetSentientOwner();
|
|
|
|
if (IsActiveTurret() && sentOwner) {
|
|
|
|
G_TouchTriggers(sentOwner);
|
|
|
|
UpdateOwner(sentOwner);
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateCollisionEntity();
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::ThinkPrimary()
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
float yawOffset, pitchOffset;
|
|
|
|
Sentient *sentOwner;
|
|
|
|
|
|
|
|
if (m_fSwitchTimeRemaining > 0) {
|
|
|
|
m_fSwitchTimeRemaining -= level.frametime;
|
|
|
|
}
|
|
|
|
|
2023-10-06 19:47:59 +02:00
|
|
|
if (m_ActiveTurret && m_ActiveTurret != m_HeadTurret) {
|
|
|
|
m_fSwitchTimeRemaining = m_fSwitchDelay;
|
|
|
|
m_HeadTurret->m_pUserCamera->PostEvent(EV_Remove, 0);
|
|
|
|
m_HeadTurret->m_pUserCamera = NULL;
|
2023-09-23 21:42:40 +02:00
|
|
|
|
2023-10-06 19:47:59 +02:00
|
|
|
// switch to the active turret
|
|
|
|
m_HeadTurret = m_ActiveTurret;
|
|
|
|
m_ActiveTurret = NULL;
|
2023-09-23 21:42:40 +02:00
|
|
|
|
2023-10-06 19:47:59 +02:00
|
|
|
// clear angles
|
|
|
|
m_vUserLastCmdAng = vec_zero;
|
|
|
|
m_HeadTurret->m_vUserLastCmdAng = vec_zero;
|
|
|
|
m_vTargetAngles = m_vLocalAngles;
|
2023-09-23 21:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pCollisionEntity) {
|
|
|
|
m_pCollisionEntity->NotSolid();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER && m_pRemoteOwner && m_pRemoteOwner->isSubclassOf(Player)) {
|
|
|
|
// always render the turret
|
|
|
|
edict->s.renderfx |= RF_DEPTHHACK;
|
|
|
|
} else {
|
|
|
|
edict->s.renderfx &= ~RF_DEPTHHACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateTimers(yawOffset, pitchOffset);
|
|
|
|
|
|
|
|
if (IsRemoteControlled()) {
|
|
|
|
UpdateRemoteControl();
|
|
|
|
UpdateCaps(yawOffset, pitchOffset);
|
|
|
|
} else if (m_bRestable) {
|
|
|
|
IdleToRestPosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateOrientation(false);
|
|
|
|
UpdateSound();
|
|
|
|
UpdateFireControl();
|
|
|
|
|
|
|
|
sentOwner = GetSentientOwner();
|
|
|
|
if (IsActiveTurret() && sentOwner) {
|
|
|
|
G_TouchTriggers(sentOwner);
|
|
|
|
UpdateOwner(sentOwner);
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateCollisionEntity();
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::SetPrimaryTurret(VehicleTurretGunTandem *pTurret)
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
m_PrimaryTurret = pTurret;
|
|
|
|
if (m_PrimaryTurret) {
|
|
|
|
m_HeadTurret = NULL;
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::RemoteControl(usercmd_t *ucmd, Sentient *owner)
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
Vector vNewCmdAng;
|
|
|
|
|
|
|
|
if (!ucmd || !owner) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_HeadTurret && m_HeadTurret != this) {
|
|
|
|
m_HeadTurret->RemoteControlSecondary(ucmd, owner);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vNewCmdAng = Vector(SHORT2ANGLE(ucmd->angles[0]), SHORT2ANGLE(ucmd->angles[1]), SHORT2ANGLE(ucmd->angles[2]));
|
|
|
|
|
2024-08-24 00:38:06 +02:00
|
|
|
if (m_vUserLastCmdAng[0] || m_vUserLastCmdAng[1] || m_vUserLastCmdAng[2]) {
|
2023-09-23 21:42:40 +02:00
|
|
|
m_vUserViewAng[0] += AngleSubtract(vNewCmdAng[0], m_vUserLastCmdAng[0]);
|
|
|
|
m_vUserViewAng[1] += AngleSubtract(vNewCmdAng[1], m_vUserLastCmdAng[1]);
|
|
|
|
m_vUserViewAng[2] += AngleSubtract(vNewCmdAng[2], m_vUserLastCmdAng[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_vUserLastCmdAng = vNewCmdAng;
|
|
|
|
|
|
|
|
RemoteControlFire(ucmd, owner);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::RemoteControlSecondary(usercmd_t *ucmd, Sentient *owner)
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
Vector vNewCmdAng;
|
|
|
|
|
|
|
|
if (!ucmd || !owner) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vNewCmdAng = Vector(SHORT2ANGLE(ucmd->angles[0]), SHORT2ANGLE(ucmd->angles[1]), SHORT2ANGLE(ucmd->angles[2]));
|
|
|
|
|
2024-08-24 00:38:06 +02:00
|
|
|
if (m_vUserLastCmdAng[0] || m_vUserLastCmdAng[1] || m_vUserLastCmdAng[2]) {
|
2023-09-23 21:42:40 +02:00
|
|
|
m_vUserViewAng[0] += AngleSubtract(vNewCmdAng[0], m_vUserLastCmdAng[0]);
|
|
|
|
m_vUserViewAng[1] += AngleSubtract(vNewCmdAng[1], m_vUserLastCmdAng[1]);
|
|
|
|
m_vUserViewAng[2] += AngleSubtract(vNewCmdAng[2], m_vUserLastCmdAng[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_vUserLastCmdAng = vNewCmdAng;
|
|
|
|
|
|
|
|
RemoteControlFire(ucmd, owner);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::RemoteControlFire(usercmd_t *ucmd, Sentient *owner)
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
if (ucmd->buttons & BUTTON_ATTACKLEFT) {
|
|
|
|
if (!m_iFiring) {
|
|
|
|
m_iFiring = 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ucmd->buttons & BUTTON_ATTACKRIGHT) {
|
|
|
|
SwitchToLinkedTurret();
|
|
|
|
}
|
|
|
|
m_iFiring = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags |= FL_THINK;
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::EventSetSwitchThread(Event *ev)
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
if (ev->IsFromScript()) {
|
|
|
|
m_SwitchLabel.SetThread(ev->GetValue(1));
|
|
|
|
} else {
|
|
|
|
m_SwitchLabel.Set(ev->GetString(1));
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::SwitchToLinkedTurret()
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
VehicleTurretGunTandem *pTurret;
|
|
|
|
|
|
|
|
if (GetPrimaryTurret()->m_fSwitchTimeRemaining > 0) {
|
2023-10-06 19:47:59 +02:00
|
|
|
return;
|
|
|
|
}
|
2023-09-23 21:42:40 +02:00
|
|
|
|
2023-10-06 19:47:59 +02:00
|
|
|
if (m_Slot.ent) {
|
|
|
|
pTurret = static_cast<VehicleTurretGunTandem*>(m_Slot.ent.Pointer());
|
2024-11-11 14:03:24 +01:00
|
|
|
// Added in 2.30
|
|
|
|
m_SwitchLabel.Execute(this, pTurret, NULL);
|
2023-10-06 19:47:59 +02:00
|
|
|
} else {
|
|
|
|
pTurret = m_PrimaryTurret;
|
2024-11-11 14:03:24 +01:00
|
|
|
// Added in 2.30
|
|
|
|
m_PrimaryTurret->m_SwitchLabel.Execute(this, pTurret, NULL);
|
2023-09-23 21:42:40 +02:00
|
|
|
}
|
2023-10-06 19:47:59 +02:00
|
|
|
|
|
|
|
SetActiveTurret(pTurret);
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::SetActiveTurret(VehicleTurretGunTandem *pTurret)
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
if (m_PrimaryTurret) {
|
|
|
|
if (!m_PrimaryTurret->m_ActiveTurret) {
|
|
|
|
m_PrimaryTurret->m_ActiveTurret = pTurret;
|
|
|
|
}
|
|
|
|
} else if (!m_ActiveTurret) {
|
|
|
|
m_ActiveTurret = pTurret;
|
|
|
|
}
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::RestrictYaw()
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
float delta;
|
|
|
|
|
|
|
|
delta = AngleSubtract(m_vUserViewAng[1], m_fStartYaw);
|
2023-10-06 22:19:24 +02:00
|
|
|
delta = Q_clamp_float(delta, -(m_fMaxYawOffset + MAX_VT_YAW_OFFSET), m_fMaxYawOffset + MAX_VT_YAW_OFFSET);
|
2023-09-23 21:42:40 +02:00
|
|
|
|
|
|
|
m_vUserViewAng[1] = m_fStartYaw + delta;
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VehicleTurretGunTandem::Archive(Archiver& arc)
|
|
|
|
{
|
2023-09-23 21:42:40 +02:00
|
|
|
VehicleTurretGun::Archive(arc);
|
|
|
|
|
|
|
|
m_Slot.Archive(arc);
|
|
|
|
arc.ArchiveSafePointer(&m_PrimaryTurret);
|
|
|
|
arc.ArchiveSafePointer(&m_HeadTurret);
|
|
|
|
arc.ArchiveSafePointer(&m_ActiveTurret);
|
|
|
|
arc.ArchiveFloat(&m_fSwitchTimeRemaining);
|
|
|
|
arc.ArchiveFloat(&m_fSwitchDelay);
|
2024-11-11 14:05:14 +01:00
|
|
|
// Added in 2.30
|
|
|
|
m_SwitchLabel.Archive(arc);
|
2023-09-23 21:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
VehicleTurretGunTandem *VehicleTurretGunTandem::GetPrimaryTurret()
|
|
|
|
{
|
|
|
|
if (m_PrimaryTurret) {
|
|
|
|
return m_PrimaryTurret;
|
|
|
|
} else {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VehicleTurretGunTandem::IsActiveTurret() const
|
|
|
|
{
|
|
|
|
if (!m_PrimaryTurret) {
|
2023-10-06 19:47:59 +02:00
|
|
|
return m_HeadTurret == this;
|
2023-09-23 21:42:40 +02:00
|
|
|
}
|
|
|
|
|
2023-10-06 19:47:59 +02:00
|
|
|
return m_PrimaryTurret->m_HeadTurret == this;
|
2023-09-19 21:02:09 +02:00
|
|
|
}
|