mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
1013 lines
28 KiB
C++
1013 lines
28 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2023 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
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "portableturret.h"
|
|
#include "g_spawn.h"
|
|
#include "g_phys.h"
|
|
#include "player.h"
|
|
|
|
Event EV_CarryableTurret_Drop
|
|
(
|
|
"dropturret",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Makes the carryable turret have itself dropped",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_CarryableTurret_TurretTik
|
|
(
|
|
"turrettik",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Tiki of the turret to place",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_CarryableTurret_Place
|
|
(
|
|
"placeturret",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Try to place the carryable turret",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_CarryableTurret_EnablePickup
|
|
(
|
|
"_internalenablepickup",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
EV_NORMAL
|
|
);
|
|
|
|
CLASS_DECLARATION(InventoryItem, CarryableTurret, NULL) {
|
|
{&EV_Touch, NULL },
|
|
{&EV_Item_Pickup, &CarryableTurret::EventPickup },
|
|
{&EV_CarryableTurret_Drop, &CarryableTurret::DropTurret },
|
|
{&EV_CarryableTurret_TurretTik, &CarryableTurret::EventTurretTik },
|
|
{&EV_CarryableTurret_Place, &CarryableTurret::EventPlaceTurret },
|
|
{&EV_CarryableTurret_EnablePickup, &CarryableTurret::EventEnablePickup},
|
|
{NULL, NULL }
|
|
};
|
|
|
|
CarryableTurret::CarryableTurret()
|
|
{
|
|
if (LoadingSavegame) {
|
|
return;
|
|
}
|
|
|
|
if (spawnflags & 1) {
|
|
Event *ev = new Event(EV_CarryableTurret_Place);
|
|
ev->AddInteger(1);
|
|
|
|
PostEvent(ev, EV_POSTSPAWN);
|
|
}
|
|
|
|
isPickable = true;
|
|
}
|
|
|
|
void CarryableTurret::EventPickup(Event *ev)
|
|
{
|
|
Entity *ent;
|
|
Sentient *sent;
|
|
Item *item;
|
|
Weapon *activeWeap;
|
|
|
|
if (!isPickable) {
|
|
return;
|
|
}
|
|
|
|
ent = ev->GetEntity(1);
|
|
if (!Pickupable(ent)) {
|
|
// can't be picked up by the entity
|
|
return;
|
|
}
|
|
|
|
sent = static_cast<Sentient *>(ent);
|
|
if (sent->HasItem(item_name)) {
|
|
// already in possession of the carryable turret
|
|
return;
|
|
}
|
|
|
|
item = ItemPickup(sent);
|
|
if (!item) {
|
|
// couldn't pick it up
|
|
return;
|
|
}
|
|
|
|
if (!item->GetOwner()) {
|
|
// invalid owner?!
|
|
return;
|
|
}
|
|
|
|
activeWeap = item->GetOwner()->GetActiveWeapon(WEAPON_MAIN);
|
|
if (activeWeap && activeWeap != item) {
|
|
activeWeap->DetachFromOwner();
|
|
}
|
|
|
|
// make the sentient use the carryable turret
|
|
item->GetOwner()->ChangeWeapon(static_cast<Weapon *>(item), WEAPON_MAIN);
|
|
}
|
|
|
|
qboolean CarryableTurret::Removable(void)
|
|
{
|
|
return qtrue;
|
|
}
|
|
|
|
void CarryableTurret::DetachFromOwner(void)
|
|
{
|
|
Weapon::DetachFromOwner();
|
|
|
|
PostEvent(EV_CarryableTurret_Drop, 0);
|
|
}
|
|
|
|
void CarryableTurret::AttachToHolster(weaponhand_t hand)
|
|
{
|
|
Weapon::AttachToHolster(hand);
|
|
|
|
PostEvent(EV_CarryableTurret_Drop, 0);
|
|
}
|
|
|
|
void CarryableTurret::DropTurret(Event *ev)
|
|
{
|
|
Weapon *activeWeap;
|
|
Sentient *currentOwner;
|
|
|
|
if (!owner) {
|
|
return;
|
|
}
|
|
|
|
currentOwner = owner;
|
|
activeWeap = currentOwner->GetActiveWeapon(WEAPON_MAIN);
|
|
Drop();
|
|
isPickable = false;
|
|
|
|
PostEvent(EV_CarryableTurret_EnablePickup, 1.5f);
|
|
|
|
if (activeWeap == this) {
|
|
currentOwner->edict->s.eFlags |= EF_UNARMED;
|
|
}
|
|
}
|
|
|
|
qboolean CarryableTurret::Drop(void)
|
|
{
|
|
qboolean result = Weapon::Drop();
|
|
if (result) {
|
|
spawnflags &= ~SPAWNFLAG_NOPC;
|
|
CancelEventsOfType(EV_Remove);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CarryableTurret::EventTurretTik(Event *ev)
|
|
{
|
|
turretModel = ev->GetString(1);
|
|
}
|
|
|
|
void CarryableTurret::EventPlaceTurret(Event *ev)
|
|
{
|
|
Event *newev;
|
|
Player *ownerP;
|
|
Entity *placer;
|
|
Vector turretAngles, targetPos;
|
|
Vector forward, right, up;
|
|
Vector endpos, endpos2;
|
|
Vector start, end;
|
|
Vector mins, maxs;
|
|
trace_t trace;
|
|
float groundAngle;
|
|
bool autoPlace = false;
|
|
|
|
if (ev->NumArgs() && ev->GetInteger(1)) {
|
|
autoPlace = true;
|
|
|
|
AngleVectors(angles, forward, NULL, NULL);
|
|
mins = Vector(-32, -32, 0);
|
|
maxs = Vector(32, 32, 32);
|
|
start = origin;
|
|
end = origin - forward * 55;
|
|
|
|
trace =
|
|
G_Trace(start, mins, maxs, end, this, MASK_CARRYABLETURRET, qtrue, "CarryableTurret::EventPlaceTurret1");
|
|
|
|
if (trace.startsolid || trace.fraction < 1 || trace.ent) {
|
|
gi.DPrintf(
|
|
"~*~*~ Not enough room to auto place portable turret at (%i %i %i)\n",
|
|
(int)origin[0],
|
|
(int)origin[1],
|
|
(int)origin[2]
|
|
);
|
|
angles = Vector(-90, 0, 0);
|
|
setAngles(angles);
|
|
return;
|
|
}
|
|
|
|
mins = Vector(-16, -16, 0);
|
|
maxs = Vector(16, 16, 8);
|
|
start = origin - forward * 55;
|
|
end = start;
|
|
end[2] -= 128;
|
|
|
|
trace =
|
|
G_Trace(start, mins, maxs, end, this, MASK_CARRYABLETURRET, qtrue, "CarryableTurret::EventPlaceTurret1");
|
|
|
|
mins = Vector(-16, -16, 0);
|
|
maxs = Vector(16, 16, 95);
|
|
start = trace.endpos;
|
|
|
|
trace =
|
|
G_Trace(start, mins, maxs, end, this, MASK_CARRYABLETURRET, qtrue, "CarryableTurret::EventPlaceTurret1");
|
|
|
|
if (trace.startsolid || trace.fraction < 1) {
|
|
gi.DPrintf(
|
|
"~*~*~ Not enough room to auto place portable turret at (%i %i %i)\n",
|
|
(int)origin[0],
|
|
(int)origin[1],
|
|
(int)origin[2]
|
|
);
|
|
angles = Vector(-90, 0, 0);
|
|
setAngles(angles);
|
|
return;
|
|
}
|
|
|
|
setOrigin(start);
|
|
angles[0] = 0;
|
|
angles[2] = 0;
|
|
setAngles(angles);
|
|
placer = this;
|
|
} else {
|
|
if (!owner) {
|
|
return;
|
|
}
|
|
|
|
placer = owner;
|
|
|
|
if (!placer->groundentity) {
|
|
return;
|
|
}
|
|
|
|
if (placer->IsSubclassOfPlayer()) {
|
|
ownerP = static_cast<Player *>(placer);
|
|
if (ownerP->client->ps.pm_flags & (PMF_NO_GRAVITY | PMF_VIEW_JUMP_START | PMF_VIEW_DUCK_RUN)) {
|
|
if (level.time > next_noammo_time) {
|
|
Sound(m_NoAmmoSound);
|
|
}
|
|
|
|
next_noammo_time = FireDelay(FIRE_PRIMARY) + level.time + level.frametime;
|
|
ownerP->HUDPrint(va("%s\n", gi.LV_ConvertString("You cannot place a turret right now.")));
|
|
return;
|
|
} else if (ownerP->client->ps.pm_flags & PMF_DUCKED) {
|
|
mins = Vector(-16, -16, 0);
|
|
maxs = Vector(16, 16, 95);
|
|
start = placer->origin;
|
|
|
|
trace = G_Trace(
|
|
start, mins, maxs, end, owner, MASK_PLAYERSOLID, qtrue, "CarryableTurret::EventPlaceTurret1"
|
|
);
|
|
|
|
if (trace.startsolid || trace.fraction < 1) {
|
|
if (level.time > next_noammo_time) {
|
|
Sound(m_NoAmmoSound);
|
|
}
|
|
|
|
next_noammo_time = FireDelay(FIRE_PRIMARY) + level.time + level.frametime;
|
|
ownerP->HUDPrint(va("%s\n", gi.LV_ConvertString("Not enough room to place the turret.")));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
turretAngles[0] = 0;
|
|
turretAngles[1] = placer->angles[1];
|
|
turretAngles[2] = 0;
|
|
AngleVectors(turretAngles, forward, right, NULL);
|
|
|
|
targetPos = placer->origin + forward * 55 + Vector(0, 0, 65.43f);
|
|
mins = Vector(-32, -32, -16);
|
|
maxs = Vector(32, 32, 8);
|
|
|
|
trace = G_Trace(
|
|
targetPos, mins, maxs, targetPos, placer, MASK_CARRYABLETURRET, qtrue, "CarryableTurret::EventPlaceTurret1"
|
|
);
|
|
if (trace.startsolid || trace.fraction < 1 || trace.ent) {
|
|
if (level.time > next_noammo_time) {
|
|
Sound(m_NoAmmoSound);
|
|
}
|
|
|
|
next_noammo_time = FireDelay(FIRE_PRIMARY) + level.time + level.frametime;
|
|
if (autoPlace) {
|
|
gi.DPrintf("Not enough room to place the turret.\n");
|
|
} else if (placer->IsSubclassOfPlayer()) {
|
|
ownerP->HUDPrint(va("%s\n", gi.LV_ConvertString("Not enough room to place the turret.")));
|
|
}
|
|
return;
|
|
}
|
|
|
|
mins = Vector(-4, -4, 0);
|
|
maxs = Vector(4, 4, 4);
|
|
start = placer->origin + Vector(0, 0, 16) + right * 12;
|
|
end = start + Vector(0, 0, -128);
|
|
|
|
trace = G_Trace(
|
|
start, mins, maxs, end, placer, CONTENTS_FENCE | CONTENTS_SOLID, qtrue, "CarryableTurret::EventPlaceTurret2"
|
|
);
|
|
endpos = trace.endpos;
|
|
|
|
start = placer->origin + Vector(0, 0, 16) - right * 12;
|
|
end = start + Vector(0, 0, -128);
|
|
|
|
trace = G_Trace(
|
|
start, mins, maxs, end, placer, CONTENTS_FENCE | CONTENTS_SOLID, qtrue, "CarryableTurret::EventPlaceTurret2"
|
|
);
|
|
endpos2 = trace.endpos;
|
|
|
|
groundAngle = RAD2DEG(atan2(endpos[2] - endpos2[2], 12));
|
|
if (fabs(groundAngle) > 40) {
|
|
if (level.time > next_noammo_time) {
|
|
Sound(m_NoAmmoSound);
|
|
}
|
|
|
|
next_noammo_time = FireDelay(FIRE_PRIMARY) + level.time + level.frametime;
|
|
if (autoPlace) {
|
|
gi.DPrintf(
|
|
"Ground is too uneven to place the turret. (Too much ground roll to the side. Ground is at %g, limit "
|
|
"is %g)\n",
|
|
fabs(groundAngle),
|
|
40.f
|
|
);
|
|
} else if (placer->IsSubclassOfPlayer()) {
|
|
ownerP = static_cast<Player *>(placer);
|
|
ownerP->HUDPrint(va("%s", gi.LV_ConvertString("Ground is too uneven to place the turret.")));
|
|
}
|
|
return;
|
|
}
|
|
mins = Vector(-2, -2, 0);
|
|
maxs = Vector(2, 2, 4);
|
|
start = targetPos + forward * 13;
|
|
end = start + Vector(0, 0, -129.43f);
|
|
|
|
trace = G_Trace(
|
|
start, mins, maxs, end, placer, CONTENTS_FENCE | CONTENTS_SOLID, qtrue, "CarryableTurret::EventPlaceTurret2"
|
|
);
|
|
|
|
groundAngle = RAD2DEG(atan2(placer->origin[2] - trace.endpos[2], 55));
|
|
if (fabs(groundAngle) > 30) {
|
|
if (level.time > next_noammo_time) {
|
|
Sound(m_NoAmmoSound);
|
|
}
|
|
|
|
next_noammo_time = FireDelay(FIRE_PRIMARY) + level.time + level.frametime;
|
|
if (autoPlace) {
|
|
gi.DPrintf(
|
|
"Ground is too uneven to place the turret. (Too much ground pitch. Ground is at %g, limit is %g)\n",
|
|
fabs(groundAngle),
|
|
30.f
|
|
);
|
|
} else if (placer->IsSubclassOfPlayer()) {
|
|
Player *ownerP = static_cast<Player *>(placer);
|
|
|
|
ownerP->HUDPrint(va("%s", gi.LV_ConvertString("Ground is too uneven to place the turret.")));
|
|
}
|
|
return;
|
|
}
|
|
|
|
turretAngles[0] = groundAngle;
|
|
AngleVectors(turretAngles, forward, NULL, up);
|
|
|
|
targetPos = placer->origin + forward * 55 + up * 65.43f;
|
|
if (!turretModel.length()) {
|
|
gi.DPrintf("WARNING: Carryable turret item '%s' doesn't have a turrettik specified\n", model.c_str());
|
|
return;
|
|
}
|
|
|
|
ClassDef *c;
|
|
PortableTurret *ent;
|
|
SpawnArgs spawnArgs;
|
|
|
|
spawnArgs.setArg("model", turretModel);
|
|
c = spawnArgs.getClassDef();
|
|
if (!c || !checkInheritance(Entity::classinfostatic(), c)) {
|
|
gi.DPrintf(
|
|
"WARNING: Carryable turret item '%s' can't find turrettik '%s'\n", model.c_str(), turretModel.c_str()
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (c != PortableTurret::classinfostatic()) {
|
|
gi.DPrintf(
|
|
"WARNING: Carryable turret item '%s' set to invalid turrettik '%s'\n", model.c_str(), turretModel.c_str()
|
|
);
|
|
return;
|
|
}
|
|
|
|
ent = static_cast<PortableTurret *>(c->newInstance());
|
|
ent->setModel(turretModel);
|
|
ent->setOrigin(placer->origin + forward * 32 + up * 65.43f);
|
|
ent->setAngles(turretAngles);
|
|
ent->ProcessInitCommands();
|
|
ent->SetGroundPitch(turretAngles[0]);
|
|
ent->SetOwnerPosition(placer->origin);
|
|
ent->CancelEventsOfType(EV_Item_DropToFloor);
|
|
|
|
if (autoPlace) {
|
|
newev = new Event(EV_Item_DropToFloor, 1);
|
|
newev->AddInteger(1);
|
|
ent->ProcessEvent(newev);
|
|
|
|
ent->setOrigin(vec_zero);
|
|
PostEvent(EV_Remove, level.frametime);
|
|
} else {
|
|
ent->ProcessEvent(EV_Item_DropToFloor);
|
|
|
|
DetachGun();
|
|
RemoveFromOwner();
|
|
setOrigin(vec_zero);
|
|
PostEvent(EV_Remove, level.frametime);
|
|
placer->velocity = vec_zero;
|
|
|
|
newev = new Event(EV_Use, 1);
|
|
newev->AddEntity(placer);
|
|
ent->ProcessEvent(newev);
|
|
}
|
|
}
|
|
|
|
void CarryableTurret::EventEnablePickup(Event *ev)
|
|
{
|
|
isPickable = true;
|
|
}
|
|
|
|
bool CarryableTurret::IsCarryableTurret() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void CarryableTurret::Archive(Archiver& arc)
|
|
{
|
|
Weapon::Archive(arc);
|
|
|
|
arc.ArchiveString(&turretModel);
|
|
arc.ArchiveBoolean(&isPickable);
|
|
}
|
|
|
|
CLASS_DECLARATION(TurretGun, PortableTurret, NULL) {
|
|
{&EV_Item_DropToFloor, &PortableTurret::PortablePlaceTurret},
|
|
{&EV_Weapon_SetAmmoClipSize, &PortableTurret::EventSetClipSize },
|
|
{NULL, NULL }
|
|
};
|
|
|
|
PortableTurret::PortableTurret()
|
|
{
|
|
groundPitch = 0.0;
|
|
nextUsableTime = 0.0;
|
|
packingUp = 0;
|
|
mustDetach = 0;
|
|
ammo_in_clip[0] = 0;
|
|
ammo_clip_size[0] = 0;
|
|
endReloadTime = 0.0;
|
|
}
|
|
|
|
PortableTurret::~PortableTurret()
|
|
{
|
|
if (baseEntity) {
|
|
baseEntity->ProcessEvent(EV_Remove);
|
|
baseEntity = NULL;
|
|
}
|
|
|
|
TurretGun::RemoveUserCamera();
|
|
}
|
|
|
|
void PortableTurret::SetGroundPitch(float pitch)
|
|
{
|
|
groundPitch = pitch;
|
|
}
|
|
|
|
void PortableTurret::SetOwnerPosition(const Vector& pos)
|
|
{
|
|
ownerPosition = pos + Vector(0, 0, 16);
|
|
}
|
|
|
|
void PortableTurret::PortablePlaceTurret(Event *ev)
|
|
{
|
|
Vector turretAngles;
|
|
Vector forward, up;
|
|
bool attachPlayer = false;
|
|
char baseModel[1024];
|
|
|
|
PlaceTurret(ev);
|
|
if (ev->NumArgs() && ev->GetInteger(1)) {
|
|
attachPlayer = true;
|
|
}
|
|
|
|
m_fPitchUpCap += groundPitch;
|
|
m_fPitchDownCap += groundPitch;
|
|
m_fMaxIdlePitch += groundPitch;
|
|
|
|
m_pUserCamera = new Camera();
|
|
currentModel = model;
|
|
|
|
COM_StripExtension(model.c_str(), baseModel, sizeof(baseModel));
|
|
strcat(baseModel, "_setup.tik");
|
|
setupModel = CanonicalTikiName(baseModel);
|
|
|
|
turretAngles[0] = groundPitch;
|
|
turretAngles[1] = m_fStartYaw;
|
|
turretAngles[2] = 0;
|
|
AngleVectors(turretAngles, forward, NULL, up);
|
|
|
|
cameraOrigin = origin + (forward * m_vViewOffset[0]) + (up * m_vViewOffset[2]);
|
|
|
|
if (attachPlayer) {
|
|
nextUsableTime = 0;
|
|
packingUp = false;
|
|
mustDetach = false;
|
|
|
|
m_pUserCamera->setOrigin(origin);
|
|
m_pUserCamera->setAngles(angles);
|
|
|
|
m_pUserCamera->SetPositionOffset(m_vViewOffset);
|
|
MakeBaseEntity();
|
|
|
|
endReloadTime = 0;
|
|
owner = NULL;
|
|
edict->r.ownerNum = ENTITYNUM_NONE;
|
|
|
|
m_fIdlePitchSpeed = 0;
|
|
m_iIdleHitCount = 0;
|
|
m_iFiring = 0;
|
|
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
|
|
ForceIdle();
|
|
} else {
|
|
nextUsableTime = level.time + 2;
|
|
packingUp = false;
|
|
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
|
|
model = setupModel;
|
|
if (setModel()) {
|
|
ForceIdle();
|
|
P_DeleteViewModel();
|
|
mustDetach = true;
|
|
|
|
m_pUserCamera->setOrigin(cameraOrigin);
|
|
m_pUserCamera->setAngles(turretAngles);
|
|
m_pUserCamera->SetPositionOffset(vec_zero);
|
|
} else {
|
|
Com_Printf("^~^~^PortableTurret::PortablePlaceTurret: Bad model name '%s'\n", setupModel.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void PortableTurret::MakeBaseEntity()
|
|
{
|
|
char baseModel[1024];
|
|
Vector turretAngles;
|
|
|
|
if (!baseEntity) {
|
|
COM_StripExtension(model, baseModel, sizeof(baseModel));
|
|
strcat(baseModel, "_base.tik");
|
|
baseEntity = new Entity();
|
|
}
|
|
|
|
baseEntity->takedamage = DAMAGE_NO;
|
|
baseEntity->setModel(baseModel);
|
|
baseEntity->setMoveType(MOVETYPE_NONE);
|
|
baseEntity->setSolidType(SOLID_NOT);
|
|
|
|
baseEntity->setSize(Vector(4, 4, 4), Vector(-4, -4, -50));
|
|
baseEntity->setOrigin(origin);
|
|
|
|
turretAngles[0] = groundPitch;
|
|
turretAngles[1] = angles[1];
|
|
turretAngles[2] = 0;
|
|
baseEntity->setAngles(turretAngles);
|
|
}
|
|
|
|
void PortableTurret::EventSetClipSize(Event *ev)
|
|
{
|
|
ammo_clip_size[FIRE_PRIMARY] = ev->GetFloat(1);
|
|
ammo_in_clip[FIRE_PRIMARY] = ammo_clip_size[FIRE_PRIMARY];
|
|
}
|
|
|
|
void PortableTurret::AbortTurretSetup()
|
|
{
|
|
Sentient *currentOwner;
|
|
bool isPackingUp = packingUp;
|
|
|
|
currentOwner = owner;
|
|
nextUsableTime = 0;
|
|
packingUp = false;
|
|
P_TurretEndUsed();
|
|
|
|
m_iFiring = 0;
|
|
|
|
if (isPackingUp) {
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
|
|
model = currentModel;
|
|
if (setModel()) {
|
|
ForceIdle();
|
|
MakeBaseEntity();
|
|
} else {
|
|
Com_Printf("^~^~^PortableTurret::P_ThinkActive: Bad model name '%s'\n", currentModel.c_str());
|
|
}
|
|
} else {
|
|
Item *item;
|
|
|
|
item = currentOwner->giveItem("weapons/mg42carryable.tik");
|
|
if (item) {
|
|
item->Drop();
|
|
} else {
|
|
gi.DPrintf("couldn't spawn carryable turret for player\n");
|
|
}
|
|
|
|
RemoveUserCamera();
|
|
hideModel();
|
|
setOrigin(vec_zero);
|
|
|
|
PostEvent(EV_Remove, level.frametime);
|
|
|
|
if (baseEntity) {
|
|
baseEntity->hideModel();
|
|
baseEntity->setOrigin(vec_zero);
|
|
baseEntity->PostEvent(EV_Remove, level.frametime);
|
|
baseEntity = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PortableTurret::P_UserAim(usercmd_t *cmd)
|
|
{
|
|
bool doDetach = false;
|
|
|
|
if (cmd->buttons & BUTTON_ATTACKRIGHT) {
|
|
if (!mustDetach) {
|
|
doDetach = true;
|
|
}
|
|
|
|
mustDetach = true;
|
|
} else {
|
|
mustDetach = false;
|
|
}
|
|
|
|
if (nextUsableTime > level.time) {
|
|
angles[0] = groundPitch;
|
|
angles[1] = m_fStartYaw;
|
|
|
|
setAngles(angles);
|
|
|
|
TurretGun::P_UserAim(cmd);
|
|
m_iFiring = 0;
|
|
flags |= FL_THINK;
|
|
} else if (doDetach) {
|
|
nextUsableTime = level.time + 2;
|
|
packingUp = true;
|
|
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
|
|
model = setupModel;
|
|
|
|
if (setModel()) {
|
|
SetWeaponAnim("packup");
|
|
|
|
P_DeleteViewModel();
|
|
|
|
if (baseEntity) {
|
|
baseEntity->ProcessEvent(EV_Remove);
|
|
baseEntity = NULL;
|
|
}
|
|
} else {
|
|
Com_Printf("^~^~^PortableTurret::PortablePlaceTurret: Bad model name '%s'\n", model.c_str());
|
|
}
|
|
} else {
|
|
TurretGun::P_UserAim(cmd);
|
|
}
|
|
}
|
|
|
|
void PortableTurret::P_TurretEndUsed()
|
|
{
|
|
if (nextUsableTime > level.time) {
|
|
AbortTurretSetup();
|
|
return;
|
|
}
|
|
|
|
endReloadTime = 0;
|
|
TurretGun::P_TurretEndUsed();
|
|
|
|
RemoveUserCamera();
|
|
ForceIdle();
|
|
}
|
|
|
|
void PortableTurret::P_TurretBeginUsed(Player *pEnt)
|
|
{
|
|
Event *ev;
|
|
Weapon *activeWeap;
|
|
|
|
TurretGun::P_TurretBeginUsed(pEnt);
|
|
|
|
activeWeap = pEnt->GetActiveWeapon(WEAPON_MAIN);
|
|
if (activeWeap && activeWeap->GetPutaway()) {
|
|
ev = new Event("deactivateweapon", 1);
|
|
ev->AddString("righthand");
|
|
pEnt->PostEvent(ev, 0);
|
|
}
|
|
|
|
ev = new Event("modheight", 1);
|
|
ev->AddString("stand");
|
|
pEnt->ProcessEvent(ev);
|
|
|
|
ev = new Event("moveposflags", 1);
|
|
ev->AddString("standing");
|
|
pEnt->ProcessEvent(ev);
|
|
}
|
|
|
|
void PortableTurret::P_ThinkActive()
|
|
{
|
|
Event *ev;
|
|
Weapon *activeWeap;
|
|
Entity *ent;
|
|
Player *ownerP = static_cast<Player *>(owner.Pointer());
|
|
|
|
if (!ownerP->IsZoomed()) {
|
|
ownerP->ToggleZoom(80);
|
|
}
|
|
|
|
owner->setOrigin(ownerPosition);
|
|
|
|
if (nextUsableTime != 0) {
|
|
if (nextUsableTime > level.time) {
|
|
float yawCap;
|
|
|
|
// cap the pitch
|
|
if (m_vUserViewAng[0] < m_fPitchUpCap) {
|
|
m_vUserViewAng[0] = m_fPitchUpCap;
|
|
} else if (m_vUserViewAng[0] > m_fPitchDownCap) {
|
|
m_vUserViewAng[0] = m_fPitchDownCap;
|
|
}
|
|
|
|
yawCap = AngleSubtract(m_vUserViewAng[1], m_fStartYaw);
|
|
if (yawCap > m_fMaxYawOffset) {
|
|
yawCap = m_fMaxYawOffset;
|
|
} else if (yawCap < -m_fMaxYawOffset) {
|
|
yawCap = -m_fMaxYawOffset;
|
|
}
|
|
|
|
m_vUserViewAng[1] = m_fStartYaw + yawCap;
|
|
// set the view angles
|
|
owner->SetViewAngles(m_vUserViewAng);
|
|
m_pUserCamera->setAngles(m_vUserViewAng);
|
|
|
|
return;
|
|
}
|
|
|
|
nextUsableTime = 0;
|
|
|
|
if (packingUp) {
|
|
SpawnArgs spawnArgs;
|
|
ClassDef *c;
|
|
|
|
ownerP->SetHolsteredByCode(false);
|
|
P_TurretEndUsed();
|
|
|
|
m_iFiring = 0;
|
|
|
|
spawnArgs.setArg("model", "weapons/mg42carryable.tik");
|
|
c = spawnArgs.getClassDef();
|
|
if (c && checkInheritance(Entity::classinfostatic(), c)) {
|
|
ent = static_cast<Entity *>(c->newInstance());
|
|
if (ent) {
|
|
activeWeap = ownerP->GetActiveWeapon(WEAPON_MAIN);
|
|
if (activeWeap) {
|
|
activeWeap->DetachFromOwner();
|
|
}
|
|
|
|
ent->setModel("weapons/mg42carryable.tik");
|
|
ent->setOrigin(vec_zero);
|
|
ent->ProcessInitCommands();
|
|
|
|
ev = new Event(EV_Item_Pickup, 1);
|
|
ev->AddEntity(ownerP);
|
|
ent->ProcessEvent(ev);
|
|
ent->PostEvent(EV_Remove, 0);
|
|
} else {
|
|
gi.DPrintf("couldn't spawn carryable turret for player\n");
|
|
}
|
|
|
|
// remove the carryable turret entity and the base
|
|
hideModel();
|
|
setOrigin(vec_zero);
|
|
PostEvent(EV_Remove, level.frametime);
|
|
|
|
if (baseEntity) {
|
|
baseEntity->hideModel();
|
|
baseEntity->setOrigin(vec_zero);
|
|
baseEntity->PostEvent(EV_Remove, level.frametime);
|
|
baseEntity = NULL;
|
|
}
|
|
} else {
|
|
gi.DPrintf("WARNING: couldn't spawn tiki '%s'\n", "weapons/mg42carryable.tik");
|
|
}
|
|
|
|
return;
|
|
}
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
StopWeaponAnim();
|
|
|
|
model = currentModel;
|
|
if (setModel()) {
|
|
ForceIdle();
|
|
MakeBaseEntity();
|
|
P_CreateViewModel();
|
|
ForceIdle();
|
|
} else {
|
|
Com_Printf("^~^~^PortableTurret::P_ThinkActive: Bad model name '%s'\n", currentModel.c_str());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (endReloadTime != 0) {
|
|
float yawCap;
|
|
Vector jittering;
|
|
|
|
m_iFiring = 0;
|
|
if (endReloadTime < level.time) {
|
|
ammo_in_clip[FIRE_PRIMARY] = ammo_clip_size[FIRE_PRIMARY];
|
|
endReloadTime = 0;
|
|
}
|
|
|
|
// cap the pitch
|
|
if (m_vUserViewAng[0] < m_fPitchUpCap) {
|
|
m_vUserViewAng[0] = m_fPitchUpCap;
|
|
} else if (m_vUserViewAng[0] > m_fPitchDownCap) {
|
|
m_vUserViewAng[0] = m_fPitchDownCap;
|
|
}
|
|
|
|
yawCap = AngleSubtract(m_vUserViewAng[1], m_fStartYaw);
|
|
if (yawCap > m_fMaxYawOffset) {
|
|
yawCap = m_fMaxYawOffset;
|
|
} else if (yawCap < -m_fMaxYawOffset) {
|
|
yawCap = -m_fMaxYawOffset;
|
|
}
|
|
|
|
m_vUserViewAng[1] = m_fStartYaw + yawCap;
|
|
// set the view angles
|
|
owner->SetViewAngles(m_vUserViewAng);
|
|
m_pUserCamera->setAngles(m_vUserViewAng);
|
|
} else {
|
|
if (ammo_clip_size[FIRE_PRIMARY] && ammo_in_clip[FIRE_PRIMARY] > 0) {
|
|
float yawCap;
|
|
Vector delta;
|
|
Vector targetAngles;
|
|
Vector jittering;
|
|
|
|
// cap the pitch
|
|
if (m_vUserViewAng[0] < m_fPitchUpCap) {
|
|
m_vUserViewAng[0] = m_fPitchUpCap;
|
|
} else if (m_vUserViewAng[0] > m_fPitchDownCap) {
|
|
m_vUserViewAng[0] = m_fPitchDownCap;
|
|
}
|
|
|
|
yawCap = AngleSubtract(m_vUserViewAng[1], m_fStartYaw);
|
|
if (yawCap > m_fMaxYawOffset) {
|
|
yawCap = m_fMaxYawOffset;
|
|
} else if (yawCap < -m_fMaxYawOffset) {
|
|
yawCap = -m_fMaxYawOffset;
|
|
}
|
|
|
|
m_vUserViewAng[1] = m_fStartYaw + yawCap;
|
|
// set the view angles
|
|
owner->SetViewAngles(m_vUserViewAng);
|
|
|
|
delta = owner->GunTarget() - origin;
|
|
targetAngles = delta.toAngles();
|
|
P_SetTargetAngles(targetAngles);
|
|
if (m_iFiring) {
|
|
m_iFiring = 4;
|
|
|
|
if (ReadyToFire(FIRE_PRIMARY)) {
|
|
Fire(FIRE_PRIMARY);
|
|
m_fCurrViewJitter = m_fViewJitter;
|
|
if (ammo_clip_size[FIRE_PRIMARY]) {
|
|
// decrease the ammo count
|
|
ammo_in_clip[FIRE_PRIMARY]--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// apply some jitter
|
|
jittering = m_vUserViewAng;
|
|
P_ApplyFiringViewJitter(jittering);
|
|
m_pUserCamera->setOrigin(origin);
|
|
m_pUserCamera->setAngles(jittering);
|
|
m_pUserCamera->SetPositionOffset(m_vViewOffset);
|
|
ownerP->client->ps.camera_flags |= CF_CAMERA_ANGLES_TURRETMODE;
|
|
} else {
|
|
m_iFiring = 0;
|
|
endReloadTime = level.time + 1.5;
|
|
|
|
angles[0] = GetGroundPitch();
|
|
angles[1] = GetStartYaw();
|
|
setAngles(angles);
|
|
|
|
SetWeaponAnim("reload");
|
|
}
|
|
}
|
|
}
|
|
|
|
void PortableTurret::P_TurretUsed(Player *player)
|
|
{
|
|
if (player != owner) {
|
|
for (Player *p = static_cast<Player *>(findradius(NULL, centroid, 50)); p;
|
|
p = static_cast<Player *>(findradius(p, centroid, 50))) {
|
|
if (!p->IsSubclassOfPlayer()) {
|
|
continue;
|
|
}
|
|
|
|
if (p != player && !p->IsSpectator() && !p->IsDead()) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
TurretGun::P_TurretUsed(player);
|
|
}
|
|
|
|
float PortableTurret::GetGroundPitch() const
|
|
{
|
|
return groundPitch;
|
|
}
|
|
|
|
float PortableTurret::GetStartYaw() const
|
|
{
|
|
return m_fStartYaw;
|
|
}
|
|
|
|
bool PortableTurret::IsReloading() const
|
|
{
|
|
return endReloadTime > level.time;
|
|
}
|
|
|
|
bool PortableTurret::IsSettingUp() const
|
|
{
|
|
return nextUsableTime > level.time && !packingUp;
|
|
}
|
|
|
|
bool PortableTurret::IsPackingUp() const
|
|
{
|
|
return nextUsableTime > level.time && packingUp;
|
|
}
|
|
|
|
void PortableTurret::Archive(Archiver& arc)
|
|
{
|
|
TurretGun::Archive(arc);
|
|
|
|
arc.ArchiveSafePointer(&baseEntity);
|
|
arc.ArchiveFloat(&groundPitch);
|
|
arc.ArchiveVector(&ownerPosition);
|
|
arc.ArchiveFloat(&nextUsableTime);
|
|
arc.ArchiveBoolean(&packingUp);
|
|
arc.ArchiveBoolean(&mustDetach);
|
|
arc.ArchiveString(¤tModel);
|
|
arc.ArchiveString(&setupModel);
|
|
arc.ArchiveVector(&cameraOrigin);
|
|
arc.ArchiveFloat(&endReloadTime);
|
|
}
|