393 lines
9.4 KiB
C++
393 lines
9.4 KiB
C++
/*
|
|
================================================================
|
|
CONCUSSION GUN
|
|
================================================================
|
|
|
|
Copyright (C) 1998 by 2015, Inc.
|
|
All rights reserved.
|
|
|
|
This source is may not be distributed and/or modified without
|
|
expressly written permission by 2015, Inc.
|
|
*/
|
|
|
|
#include "g_local.h"
|
|
#include "concussion.h"
|
|
#include "worldspawn.h"
|
|
#include "player.h"
|
|
|
|
#define MAX_CONCUSSION_AMMO 100
|
|
#define CONCUSSION_REGEN_TIME_DM 0.5
|
|
#define CONCUSSION_REGEN_TIME 0.6
|
|
|
|
//==================================================================
|
|
|
|
CLASS_DECLARATION(Entity, ConcussionRing, NULL);
|
|
|
|
Event EV_ConcussionRing_Animate2("concussionring_animate2");
|
|
Event EV_ConcussionRing_Animate3("concussionring_animate3");
|
|
|
|
ResponseDef ConcussionRing::Responses[] =
|
|
{
|
|
{&EV_ConcussionRing_Animate2, (Response)ConcussionRing::Animate2},
|
|
{&EV_ConcussionRing_Animate3, (Response)ConcussionRing::Animate3},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
void ConcussionRing::Animate2(Event *ev)
|
|
{
|
|
edict->s.scale += 0.4;
|
|
edict->s.alpha *= 0.6;
|
|
edict->s.renderfx |= RF_TRANSLUCENT;
|
|
PostEvent(EV_ConcussionRing_Animate2, FRAMETIME);
|
|
}
|
|
|
|
void ConcussionRing::Animate3(Event *ev)
|
|
{
|
|
edict->s.scale += 0.1;
|
|
edict->s.alpha *= 0.5;
|
|
edict->s.renderfx |= RF_TRANSLUCENT;
|
|
PostEvent(EV_ConcussionRing_Animate3, FRAMETIME);
|
|
}
|
|
|
|
void ConcussionRing::Setup(Vector pos, Vector dir)
|
|
{
|
|
setMoveType(MOVETYPE_FLY);
|
|
setSolidType(SOLID_NOT);
|
|
setOrigin(pos);
|
|
worldorigin.copyTo(edict->s.old_origin);
|
|
|
|
angles = dir.toAngles();
|
|
angles[PITCH] = - angles[PITCH];
|
|
angles[ROLL] = rand() % 360;
|
|
avelocity[2] = crandom()*120;
|
|
setAngles(angles);
|
|
|
|
setModel("sprites/concussion.spr");
|
|
|
|
edict->s.renderfx |= RF_TRANSLUCENT;
|
|
}
|
|
|
|
//==================================================================
|
|
|
|
Event EV_Concussion_Effect("concussion_effect");
|
|
Event EV_Concussion_Regen("concussion_regen");
|
|
|
|
CLASS_DECLARATION(Weapon, ConcussionGun, "weapon_concussiongun");
|
|
|
|
ResponseDef ConcussionGun::Responses[] =
|
|
{
|
|
{&EV_Weapon_Shoot, (Response)ConcussionGun::Shoot},
|
|
{&EV_Concussion_Effect, (Response)ConcussionGun::BlastEffect},
|
|
{&EV_Concussion_Regen, (Response)ConcussionGun::BatteryRegen},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
ConcussionGun::ConcussionGun()
|
|
{
|
|
SetModels("concussion_w.def", "view_concussion.def");
|
|
modelIndex("sprites/concussion.spr");
|
|
SetAmmo("ConcussionBattery", 10, MAX_CONCUSSION_AMMO);
|
|
SetRank(75, 25);
|
|
SetType(WEAPON_2HANDED_LO);
|
|
|
|
SetMinRange(0);
|
|
SetProjectileSpeed(0);
|
|
}
|
|
|
|
void ConcussionGun::SecondaryUse (Event *ev)
|
|
{
|
|
// switch to the fists
|
|
owner->useWeapon("Fists");
|
|
}
|
|
|
|
void ConcussionGun::BatteryRegen(Event *ev)
|
|
{
|
|
// don't regen battery if owner is dead
|
|
if(!owner)
|
|
return;
|
|
|
|
// only do for players
|
|
if(!owner->isClient())
|
|
return;
|
|
|
|
// first make sure that we still have a living owner
|
|
if(!owner || owner->health < 1)
|
|
{
|
|
CancelEventsOfType(EV_Concussion_Regen);
|
|
return;
|
|
}
|
|
|
|
if(AmmoAvailable() < MAX_CONCUSSION_AMMO)
|
|
{
|
|
owner->giveItem( ammotype.c_str(), 2 );
|
|
|
|
// if still below max, then need to do it again
|
|
if(AmmoAvailable() < MAX_CONCUSSION_AMMO)
|
|
{
|
|
if(deathmatch->value)
|
|
PostEvent(EV_Concussion_Regen, CONCUSSION_REGEN_TIME_DM);
|
|
else
|
|
PostEvent(EV_Concussion_Regen, CONCUSSION_REGEN_TIME);
|
|
}
|
|
}
|
|
}
|
|
|
|
// start the concussion gun's firing sequence
|
|
void ConcussionGun::Shoot (Event *ev)
|
|
{
|
|
Entity *tmpent = NULL;
|
|
ConcussionRing *ring;
|
|
Vector targvec, vec;
|
|
Vector pos, dir, right;
|
|
Vector end;
|
|
trace_t trace;
|
|
float f;
|
|
|
|
assert(owner);
|
|
|
|
GetMuzzlePosition(&pos, &dir, &right);
|
|
end = pos - dir*40;
|
|
trace = G_Trace(pos, Vector(-1, -1, -1), Vector(1, 1, 1), end, owner, MASK_PLAYERSOLID, "ConcussionGun::Shoot");
|
|
pos = trace.endpos;
|
|
|
|
// do kick back
|
|
end = pos + (dir*CONCUSSION_DIST*0.5);
|
|
trace = G_Trace(pos, Vector(-4, -4, -4), Vector(4, 4, 4), end, owner, MASK_PLAYERSOLID, "ConcussionGun::Shoot");
|
|
f = (1 - trace.fraction)*CONCUSSION_KICKBACK;
|
|
if(f < 400)
|
|
f = 400;
|
|
// reduce upwards kickback in singleplayer
|
|
if(!deathmatch->value)
|
|
{
|
|
owner->velocity.x -= dir.x*f;
|
|
owner->velocity.y -= dir.y*f;
|
|
if(dir.z < 0)
|
|
owner->velocity.z -= dir.z*f*0.3;
|
|
else
|
|
owner->velocity.z -= dir.z*f*0.5;
|
|
}
|
|
else // in DM, let 'em fly
|
|
{
|
|
owner->velocity -= dir*f;
|
|
}
|
|
|
|
// store firing position & dir for later
|
|
lastfirepos = pos;
|
|
lastfiredir = dir;
|
|
|
|
// make a narrow moving ring at the start of the blast
|
|
pos += dir*2 + right*4;
|
|
ring = new ConcussionRing;
|
|
ring->Setup(pos, lastfiredir);
|
|
ring->velocity = lastfiredir*500;
|
|
ring->edict->s.scale = 0.05;
|
|
ring->edict->s.alpha = 0.75;
|
|
ring->PostEvent(EV_ConcussionRing_Animate2, FRAMETIME);
|
|
ring->PostEvent(EV_Remove, 1);
|
|
|
|
NextAttack(1);
|
|
|
|
blastcount = 0;
|
|
ProcessEvent(EV_Concussion_Effect);
|
|
|
|
// post the battery regenerating event
|
|
if(owner->isClient())
|
|
{
|
|
CancelEventsOfType(EV_Concussion_Regen);
|
|
PostEvent(EV_Concussion_Regen, CONCUSSION_REGEN_TIME);
|
|
}
|
|
}
|
|
|
|
void ConcussionGun::BlastEffect (Event *ev)
|
|
{
|
|
Entity *tmpent = NULL;
|
|
ConcussionRing *ring;
|
|
Vector targvec, vec;
|
|
Vector end;
|
|
trace_t trace;
|
|
float d, f, targdist;
|
|
|
|
if(!owner)
|
|
{
|
|
weaponstate = WEAPON_READY;
|
|
return;
|
|
}
|
|
|
|
tmpent = NULL;
|
|
while(tmpent = findradius(tmpent, lastfirepos, CONCUSSION_DIST))
|
|
{
|
|
if(tmpent == owner)
|
|
continue;
|
|
// only effect damagable things and projectiles
|
|
// don't effect BSP model entities
|
|
if(
|
|
((!tmpent->takedamage) &&
|
|
(!tmpent->isSubclassOf(Projectile))) ||
|
|
(tmpent->edict->solid == SOLID_BSP)
|
|
)
|
|
continue;
|
|
|
|
targvec = tmpent->origin + (tmpent->mins + tmpent->maxs)*0.5;
|
|
|
|
trace = G_Trace(lastfirepos, Vector(0, 0, 0), Vector(0, 0, 0), targvec, owner, MASK_SOLIDNONFENCE, "ConcussionGun::BlastEffect");
|
|
if(trace.fraction < 1)
|
|
continue;
|
|
|
|
vec = targvec - lastfirepos;
|
|
targdist = vec.normalize2();
|
|
|
|
d = DotProduct(vec, lastfiredir);
|
|
if(d < CONCUSSION_ARC)
|
|
continue;
|
|
|
|
if(!deathmatch->value) // don't make extra rings in DM, too much net traffic
|
|
{
|
|
if(tmpent->concussion_debounce < level.time)
|
|
{
|
|
// make a visible indication of a hit
|
|
end = targvec - lastfiredir*32;
|
|
ring = new ConcussionRing;
|
|
ring->Setup(end, lastfiredir);
|
|
ring->velocity = lastfiredir*50;
|
|
ring->edict->s.scale = 0.02;
|
|
ring->edict->s.alpha = 0.75;
|
|
ring->PostEvent(EV_ConcussionRing_Animate3, FRAMETIME);
|
|
ring->PostEvent(EV_Remove, 0.4);
|
|
}
|
|
|
|
tmpent->concussion_debounce = level.time + 0.3;
|
|
}
|
|
|
|
f = CONCUSSION_PUSH - (targdist * CONCUSSION_PUSH_DECAY);
|
|
// scale the force by the directional dot product
|
|
f *= d;
|
|
// scale the force by the mass of the target
|
|
f *= 1/(tmpent->mass*0.005);
|
|
if(f < 0)
|
|
f = 0;
|
|
|
|
//apply force, but not to script objects and models
|
|
if((f > 0) && (!tmpent->isSubclassOf(ScriptSlave)))
|
|
{
|
|
tmpent->velocity += vec * f;
|
|
|
|
if(!deathmatch->value)
|
|
{
|
|
if(tmpent->velocity[2] <= 0)
|
|
tmpent->velocity[2] = 50;
|
|
else
|
|
tmpent->velocity[2] += 50;
|
|
}
|
|
else
|
|
{
|
|
if(tmpent->velocity[2] <= 0)
|
|
tmpent->velocity[2] = 75;
|
|
else
|
|
tmpent->velocity[2] += 75;
|
|
}
|
|
|
|
// set info for wall splatting damage
|
|
tmpent->concussioner = owner;
|
|
|
|
if(tmpent->isSubclassOf(Player))
|
|
{
|
|
Player *player;
|
|
|
|
player = (Player *)tmpent;
|
|
player->concussion_timer = level.time + 1;
|
|
// jitter the poor sucker's view
|
|
f = f/CONCUSSION_PUSH;
|
|
player->SetAngleJitter(f*10, f*30, level.time);
|
|
player->SetOffsetJitter(f*5, f*20, level.time + 0.1);
|
|
}
|
|
}
|
|
|
|
// do some damage if applicable
|
|
f = CONCUSSION_DAMG - (targdist * CONCUSSION_DAMG_DECAY);
|
|
// scale the damage by the directional dot product
|
|
f *= d;
|
|
if(f < 0)
|
|
f = 0;
|
|
if(tmpent->takedamage)
|
|
tmpent->Damage(this, owner, f, tmpent->origin, lastfiredir, level.impact_trace.plane.normal, 32, 0, MOD_CONCUSSION, -1, -1, 1.0f);
|
|
}
|
|
|
|
|
|
// set effect counter as needed
|
|
if(blastcount < CONCUSSION_COUNT)
|
|
{
|
|
// do effect again
|
|
PostEvent(EV_Concussion_Effect, 0.1);
|
|
blastcount++;
|
|
}
|
|
}
|
|
|
|
// this stuff was added to prevent changing weapons
|
|
// when doesn't have enough ammo to fire
|
|
|
|
void ConcussionGun::Fire (void)
|
|
{
|
|
if ( !ReadyToFire() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!(((ammo_clip_size && ammo_in_clip >= ammorequired) || AmmoAvailable() >= ammorequired)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
UseAmmo( ammorequired );
|
|
|
|
weaponstate = WEAPON_FIRING;
|
|
|
|
CancelEventsOfType( EV_Weapon_DoneFiring );
|
|
// this is just a precaution that we can re-trigger
|
|
NextAttack( 5 );
|
|
|
|
RandomAnimate( "fire", EV_Weapon_DoneFiring );
|
|
|
|
last_attack_time = level.time;
|
|
}
|
|
|
|
qboolean ConcussionGun::HasAmmo (void)
|
|
{
|
|
if ( !owner )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// always return true from this
|
|
return true;
|
|
}
|
|
|
|
//==================================================================
|
|
// And yae, from yonder, hither weapon doth seth fourth evil things...
|
|
|
|
class EXPORT_FROM_DLL EvilConcussionGun : public ConcussionGun
|
|
{
|
|
public:
|
|
CLASS_PROTOTYPE( EvilConcussionGun );
|
|
|
|
EvilConcussionGun();
|
|
virtual void Shoot( Event *ev );
|
|
};
|
|
|
|
CLASS_DECLARATION(ConcussionGun, EvilConcussionGun, "weapon_evilconcussiongun");
|
|
|
|
ResponseDef EvilConcussionGun::Responses[] =
|
|
{
|
|
{&EV_Weapon_Shoot, (Response)EvilConcussionGun::Shoot},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
EvilConcussionGun::EvilConcussionGun()
|
|
{
|
|
}
|
|
|
|
void EvilConcussionGun::Shoot (Event *ev)
|
|
{
|
|
ConcussionGun::Shoot(ev);
|
|
NextAttack(0.5);
|
|
weaponstate = WEAPON_READY;
|
|
}
|