nuclide/src/gs-entbase/server/func_tank.qc

383 lines
9.7 KiB
Plaintext

/*
* Copyright (c) 2016-2022 Vera Visions LLC.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*QUAKED func_tank (0 .5 .8) ? FNCTANK_ACTIVE x x x FNCTANK_DIRECTONLY FNCTANK_CONTROLLABLE
A mountable tank gun turret type entity. A player (or NPC) can interact with
it and shoot it. It's in the same family as the func_tankmortar entity, the
difference being that this shoots bullets and not mortar blasts.
-------- KEYS --------
"targetname" : Name
"yawrate" : The speed of the left/right movement of the gun.
"yawrange" : Range of left/right movement in degrees.
"pitchrate" : The speed of the up/down movement of the gun.
"pitchrange" : Range of up/down movement in degrees.
"barrel" : Distance from origin to barrel tip in units.
"barrely" : Horizontal distance origin to the center of the barrel tip.
"barrelz" : Vertical distance origin to the center of the barrel tip.
"firerate" : Number of bullets fired per second.
"bullet_damage" : Damage each fired bullet does.
"firespread" : Accuracy of the gun. 0 is best, 4 is worst.
"persistance" : Time in seconds for how long an NPC might continue shooting.
"minRange" : Minimum range the target can be at for an NPC to fire.
"maxRange" : Maximum range the target can be at for an NPC to fire.
"spritesmoke" : Sprite to spawn for 'smoke' when the entity is fired.
"spriteflash" : Sprite to spawn for a 'muzzleflash' when the entity is fired.
"spritescale" : Scale multiplier for both smoke and flash sprites.
"rotatesound" : Sound file to play in a loop while barrel is rotating.
-------- SPAWNFLAGS --------
FNCTANK_ACTIVE : Unimplemented.
FNCTANK_DIRECTONLY : Unimplemented.
FNCTANK_CONTROLLABLE : Unimplemented.
-------- NOTES --------
I don't like the sprite stuff tacked on at all because of the extra networking
involved and because it's so awfully GoldSrc specific.
Eventually I need to design a more generic routine that allows people to just
refer to materials with the appropriate blend-modes instead of hardcoding that
some random sprites needs to be treated additive.
-------- TRIVIA --------
This entity was introduced in Half-Life (1998).
*/
/* TODO: Implement these */
enumflags
{
FNCTANK_ACTIVE,
FNCTANK_UNUSED1,
FNCTANK_UNUSED2,
FNCTANK_UNUSED3,
FNCTANK_DIRECTONLY,
FNCTANK_CONTROLLABLE
};
class
func_tank:NSVehicle
{
/* attributes */
float m_flYawRate; /* TODO */
float m_flYawRange; /* TODO */
float m_flPitchRate; /* TODO */
float m_flPitchRange; /* TODO */
vector m_vecTipPos;
float m_flFireRate;
int m_iDamage;
vector m_vecSpread; /* TODO: Needs checking */
string m_strSpriteSmoke;
string m_strSpriteFlash;
float m_flSpriteScale;
string m_strSndRotate; /* TODO */
float m_flPersistance; /* TODO */
float m_flMinRange; /* TODO */
float m_flMaxRange; /* TODO */
float m_flFireTime;
public:
void func_tank(void);
virtual void Spawned(void);
virtual void Save(float);
virtual void Restore(string,string);
virtual void customphysics(void);
virtual void PlayerInput(void);
virtual void Respawn(void);
virtual void SpriteSmoke(vector);
virtual void SpriteFlash(vector);
virtual void SpawnKey(string,string);
};
void
func_tank::func_tank(void)
{
m_flYawRate = 0.0f;
m_flYawRange = 0.0f;
m_flPitchRate = 0.0f;
m_flPitchRange = 0.0f;
m_vecTipPos = [0.0f, 0.0f, 0.0f];
m_flFireRate = 0.0f;
m_iDamage = 0i;
m_vecSpread = [0.0f, 0.0f, 0.0f];
m_strSpriteSmoke = __NULL__;
m_strSpriteFlash = __NULL__;
m_flSpriteScale = 0.0f;
m_strSndRotate = __NULL__;
m_flPersistance = 0.0f;
m_flMinRange = 0.0f;
m_flMaxRange = 0.0f;
m_flFireTime = 0.0f;
m_iVehicleFlags |= VHF_FROZEN | VHF_NOATTACK;
}
void
func_tank::Save(float handle)
{
super::Save(handle);
SaveInt(handle, "m_iDamage", m_iDamage);
SaveFloat(handle, "m_flYawRate", m_flYawRate);
SaveFloat(handle, "m_flYawRange", m_flYawRange);
SaveFloat(handle, "m_flPitchRate", m_flPitchRate);
SaveFloat(handle, "m_flPitchRange", m_flPitchRange);
SaveFloat(handle, "m_flFireRate", m_flFireRate);
SaveFloat(handle, "m_flSpriteScale", m_flSpriteScale);
SaveFloat(handle, "m_flPersistance", m_flPersistance);
SaveFloat(handle, "m_flMinRange", m_flMinRange);
SaveFloat(handle, "m_flMaxRange", m_flMaxRange);
SaveString(handle, "m_strSpriteSmoke", m_strSpriteSmoke);
SaveString(handle, "m_strSpriteFlash", m_strSpriteFlash);
SaveString(handle, "m_strSndRotate", m_strSndRotate);
SaveVector(handle, "m_vecTipPos", m_vecTipPos);
SaveVector(handle, "m_vecSpread", m_vecSpread);
}
void
func_tank::Restore(string strKey, string strValue)
{
switch (strKey) {
case "m_iDamage":
m_iDamage = ReadInt(strValue);
break;
case "m_flYawRate":
m_flYawRate = ReadFloat(strValue);
break;
case "m_flYawRange":
m_flYawRange = ReadFloat(strValue);
break;
case "m_flPitchRate":
m_flPitchRate = ReadFloat(strValue);
break;
case "m_flPitchRange":
m_flPitchRange = ReadFloat(strValue);
break;
case "m_flFireRate":
m_flFireRate = ReadFloat(strValue);
break;
case "m_flSpriteScale":
m_flSpriteScale = ReadFloat(strValue);
break;
case "m_flPersistance":
m_flPersistance = ReadFloat(strValue);
break;
case "m_flMinRange":
m_flMinRange = ReadFloat(strValue);
break;
case "m_flMaxRange":
m_flMaxRange = ReadFloat(strValue);
break;
case "m_strSpriteSmoke":
m_strSpriteSmoke = ReadString(strValue);
break;
case "m_strSpriteFlash":
m_strSpriteFlash = ReadString(strValue);
break;
case "m_strSndRotate":
m_strSndRotate = ReadString(strValue);
break;
case "m_vecTipPos":
m_vecTipPos = ReadVector(strValue);
break;
case "m_vecSpread":
m_vecSpread = ReadVector(strValue);
break;
default:
super::Restore(strKey, strValue);
}
}
void
func_tank::SpawnKey(string strKey, string strValue)
{
switch (strKey) {
case "yawrate":
m_flYawRate = stof(strValue);
break;
case "yawrange":
m_flYawRange = stof(strValue);
break;
case "pitchrate":
m_flPitchRate = stof(strValue);
break;
case "pitchrange":
m_flPitchRange = stof(strValue);
break;
case "barrel":
m_vecTipPos[0] = stof(strValue);
break;
case "barrely":
m_vecTipPos[1] = stof(strValue);
break;
case "barrelz":
m_vecTipPos[2] = stof(strValue);
break;
case "firerate":
m_flFireRate = 1.0f / stof(strValue);
break;
case "bullet_damage":
m_iDamage = stoi(strValue);
break;
case "firespread":
m_vecSpread = [0.25, 0.25, 0] * stof(strValue);
break;
case "persistance":
m_flPersistance = stof(strValue);
break;
case "minRange":
m_flMinRange = stof(strValue);
break;
case "maxRange":
m_flMaxRange = stof(strValue);
break;
case "spritesmoke":
m_strSpriteSmoke = strValue;
break;
case "spriteflash":
m_strSpriteFlash = strValue;
break;
case "spritescale":
m_flSpriteScale = stof(strValue);
break;
case "rotatesound":
m_strSndRotate = strValue;
break;
default:
super::SpawnKey(strKey, strValue);
}
}
void
func_tank::Spawned(void)
{
super::Spawned();
if (m_strSpriteFlash)
precache_model(m_strSpriteFlash);
if (m_strSpriteSmoke)
precache_model(m_strSpriteSmoke);
}
void
func_tank::Respawn(void)
{
SetMovetype(MOVETYPE_PUSH);
SetSolid(SOLID_BSP);
SetModel(GetSpawnModel());
SetOrigin(GetSpawnOrigin());
SetAngles(GetSpawnAngles());
if (m_eDriver)
PlayerLeave((NSClientPlayer)m_eDriver);
}
void
func_tank::SpriteSmoke(vector org)
{
static void Die(void) {
remove(self);
}
if (!m_strSpriteSmoke)
return;
NSRenderableEntity smoke = spawn(NSRenderableEntity);
smoke.SetModel(m_strSpriteSmoke);
smoke.SetOrigin(org);
smoke.think = Die;
smoke.nextthink = time + 0.1f;
smoke.scale = m_flSpriteScale;
smoke.SetRenderMode(RM_ADDITIVE);
smoke.SetRenderColor([1,1,1]);
smoke.SetRenderAmt(1.0f);
}
void
func_tank::SpriteFlash(vector org)
{
static void Die(void) {
remove(self);
}
if (!m_strSpriteFlash)
return;
NSRenderableEntity flash = spawn(NSRenderableEntity);
flash.SetModel(m_strSpriteFlash);
flash.SetOrigin(org);
flash.think = Die;
flash.nextthink = time + 0.1f;
flash.scale = m_flSpriteScale;
flash.SetRenderMode(RM_ADDITIVE);
flash.SetRenderColor([1,1,1]);
flash.SetRenderAmt(1.0f);
}
void
func_tank::customphysics(void)
{
if (m_eDriver && m_eDriver.health <= 0)
PlayerLeave((NSClientPlayer)m_eDriver);
if (m_eDriver) {
vector endorg;
makevectors(m_eDriver.v_angle);
endorg = v_forward * 4086;
angles = vectoangles(endorg - origin);
PlayerUpdateFlags();
if (vlen(m_eDriver.origin - origin) > 128)
PlayerLeave((NSClientPlayer)m_eDriver);
}
}
void
func_tank_shootsingle(entity shooter, vector org, vector spread, int damg)
{
entity oldself = self;
self = shooter;
TraceAttack_FireBullets(1, org, damg, spread, 0);
self = oldself;
}
void
func_tank::PlayerInput(void)
{
if (m_flFireTime < time)
if (input_buttons & INPUT_BUTTON0) {
vector spos;
/* barrel tip offset */
makevectors(angles);
spos = origin + v_forward * m_vecTipPos[0];
spos += v_right * m_vecTipPos[1];
spos += v_up * m_vecTipPos[2];
UseTargets(this, TRIG_ON, m_flDelay);
func_tank_shootsingle(m_eDriver, spos, m_vecSpread, m_iDamage);
m_flFireTime = time + m_flFireRate;
SpriteSmoke(spos);
SpriteFlash(spos);
/* disable actual weapon fire */
input_buttons = 0;
}
input_buttons &= ~INPUT_BUTTON0;
}