490 lines
11 KiB
C++
490 lines
11 KiB
C++
|
/*
|
||
|
================================================================
|
||
|
STINGER PACK WEAPON
|
||
|
================================================================
|
||
|
|
||
|
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 "explosion.h"
|
||
|
#include "rocketpack.h"
|
||
|
#include "worldspawn.h"
|
||
|
#include "surface.h"
|
||
|
#include "misc.h"
|
||
|
#include "actor.h"
|
||
|
|
||
|
#define STINGERROCKET_SPEED 1000
|
||
|
#define STINGERROCKET_RADIUS 120
|
||
|
|
||
|
//=================================================================
|
||
|
// anime rocket class
|
||
|
|
||
|
CLASS_DECLARATION( Projectile, StingerRocket, NULL );
|
||
|
|
||
|
Event EV_StingerRocket_Explode("explode");
|
||
|
Event EV_StingerRocket_Turn("turn");
|
||
|
|
||
|
ResponseDef StingerRocket::Responses[] =
|
||
|
{
|
||
|
{&EV_Touch, (Response)StingerRocket::Explode},
|
||
|
{&EV_StingerRocket_Explode, (Response)StingerRocket::Explode},
|
||
|
{&EV_StingerRocket_Turn, (Response)StingerRocket::Turn},
|
||
|
{NULL, NULL}
|
||
|
};
|
||
|
|
||
|
// also used to do velocity adjustments
|
||
|
EXPORT_FROM_DLL void StingerRocket::Turn (Event *ev)
|
||
|
{
|
||
|
Vector tmpvec, tmpvel;
|
||
|
int i, tmpof;
|
||
|
float tmplength;
|
||
|
|
||
|
tmpvec = targpos - origin;
|
||
|
tmplength = tmpvec.length();
|
||
|
// check for reaching current target position
|
||
|
if(tmplength < 90)
|
||
|
{
|
||
|
// advance primary destination position
|
||
|
destpos = destpos + movedir*300;
|
||
|
|
||
|
// get new target position
|
||
|
for(i = 0; i < 3; i++)
|
||
|
{
|
||
|
if(rand()%4)
|
||
|
offsets[i] *= -1;
|
||
|
tmpof = 8 + random()*32;
|
||
|
targpos[i] = destpos[i] + offsets[i]*(float)tmpof;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// set velocity towards target position if
|
||
|
// direction is different enough
|
||
|
tmpvec = targpos - origin;
|
||
|
tmpvec.normalize();
|
||
|
tmpvel = velocity;
|
||
|
tmpvel.normalize();
|
||
|
|
||
|
// make the stinger rockets do fewer corrections in deathamtch
|
||
|
if(deathmatch->value)
|
||
|
{
|
||
|
if(DotProduct(tmpvec.vec3(), tmpvel.vec3()) < 0.96)
|
||
|
{
|
||
|
velocity = tmpvel*2 + tmpvec*3;
|
||
|
velocity.normalize();
|
||
|
velocity *= speed;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
velocity = tmpvel*2 + tmpvec*3;
|
||
|
velocity.normalize();
|
||
|
velocity *= speed;
|
||
|
|
||
|
// constantly correct the rocket's angles in single player
|
||
|
angles = tmpvec.toAngles();
|
||
|
angles[PITCH] = -angles[PITCH];
|
||
|
setAngles(angles);
|
||
|
}
|
||
|
|
||
|
PostEvent(EV_StingerRocket_Turn, 0.1);
|
||
|
}
|
||
|
|
||
|
EXPORT_FROM_DLL void StingerRocket::Explode (Event *ev)
|
||
|
{
|
||
|
int damg;
|
||
|
Vector v;
|
||
|
Entity *other;
|
||
|
Vector norm;
|
||
|
Entity *owner;
|
||
|
|
||
|
other = ev->GetEntity(1);
|
||
|
assert(other);
|
||
|
|
||
|
if(other->isSubclassOf(Teleporter))
|
||
|
return;
|
||
|
|
||
|
if(other->entnum == this->owner)
|
||
|
return;
|
||
|
|
||
|
CancelEventsOfType(EV_StingerRocket_Turn);
|
||
|
|
||
|
stopsound(CHAN_VOICE);
|
||
|
|
||
|
setSolidType(SOLID_NOT);
|
||
|
hideModel();
|
||
|
if(HitSky())
|
||
|
{
|
||
|
PostEvent(EV_Remove, 0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
owner = G_GetEntity(this->owner);
|
||
|
|
||
|
damg = 70 + (int)G_Random(10);
|
||
|
|
||
|
if(!deathmatch->value && owner->isClient())
|
||
|
damg *= 1.5;
|
||
|
|
||
|
if (other->takedamage)
|
||
|
other->Damage(this, owner, damg, origin, velocity, level.impact_trace.plane.normal, 16, 0, MOD_STINGERROCKET, -1, -1, 1.0f );
|
||
|
|
||
|
SpawnBlastDamage( &level.impact_trace, damg, owner );
|
||
|
|
||
|
v = velocity;
|
||
|
v.normalize();
|
||
|
|
||
|
// don't do radius damage to the other, because all the damage
|
||
|
// was done in the impact
|
||
|
v = origin - v * 24;
|
||
|
CreateExplosion( v, damg, 0.75f, true, this, owner, other, MOD_STINGERSPLASH, 0.3);
|
||
|
PostEvent(EV_Remove, 0.1);
|
||
|
}
|
||
|
|
||
|
EXPORT_FROM_DLL void StingerRocket::Setup (Entity *owner, Vector pos, Vector firedest, Vector dir, Vector firedir)
|
||
|
{
|
||
|
Event *ev;
|
||
|
int i, tmpof;
|
||
|
|
||
|
this->owner = owner->entnum;
|
||
|
edict->owner = owner->edict;
|
||
|
|
||
|
setMoveType( MOVETYPE_FLYMISSILE );
|
||
|
setSolidType( SOLID_BBOX );
|
||
|
edict->clipmask = MASK_PROJECTILE;
|
||
|
|
||
|
angles = dir.toAngles();
|
||
|
angles[PITCH] = -angles[PITCH];
|
||
|
setAngles(angles);
|
||
|
|
||
|
speed = STINGERROCKET_SPEED;
|
||
|
velocity = dir * STINGERROCKET_SPEED;
|
||
|
|
||
|
PostEvent(EV_StingerRocket_Turn, 0.1);
|
||
|
|
||
|
// set missile duration
|
||
|
ev = new Event(EV_Remove);
|
||
|
ev->AddEntity(world);
|
||
|
PostEvent(ev, 20);
|
||
|
|
||
|
setModel( "stinger.def" );
|
||
|
edict->s.effects |= EF_ANIMEROCKET;
|
||
|
edict->s.effects |= EF_EVERYFRAME;
|
||
|
edict->s.angles[ROLL] = rand() % 360;
|
||
|
gravity = 0;
|
||
|
|
||
|
// give it a heat signature
|
||
|
edict->s.effects |= EF_WARM;
|
||
|
|
||
|
// setup movement stuff
|
||
|
movedir = firedir;
|
||
|
destpos = firedest;
|
||
|
for(i = 0; i < 3; i++)
|
||
|
{
|
||
|
if(rand()%2)
|
||
|
offsets[i] = 1;
|
||
|
else
|
||
|
offsets[i] = -1;
|
||
|
|
||
|
tmpof = 16 + random()*24;
|
||
|
targpos[i] = destpos[i] + offsets[i]*(float)tmpof;
|
||
|
}
|
||
|
|
||
|
// setup ambient thrust
|
||
|
ev = new Event(EV_RandomEntitySound);
|
||
|
ev->AddString("thrust");
|
||
|
ProcessEvent(ev);
|
||
|
|
||
|
// set the rocket's angles
|
||
|
angles = movedir.toAngles();
|
||
|
angles[PITCH] = -angles[PITCH];
|
||
|
setAngles(angles);
|
||
|
|
||
|
setSize("-1 -1 -1", "1 1 1");
|
||
|
setOrigin(pos);
|
||
|
worldorigin.copyTo(edict->s.old_origin);
|
||
|
}
|
||
|
|
||
|
//=================================================================
|
||
|
// stingerpack weapon class
|
||
|
|
||
|
CLASS_DECLARATION(Weapon, StingerPack, "weapon_stingerpack");
|
||
|
|
||
|
ResponseDef StingerPack::Responses[] =
|
||
|
{
|
||
|
{&EV_Weapon_Shoot, (Response)StingerPack::Shoot},
|
||
|
{NULL, NULL}
|
||
|
};
|
||
|
|
||
|
StingerPack::StingerPack()
|
||
|
{
|
||
|
SetModels("pack_w.def", "view_pack.def");
|
||
|
modelIndex("stinger.def");
|
||
|
SetAmmo("Rockets", 2, 10);
|
||
|
SetRank(78, 85);
|
||
|
SetType(WEAPON_1HANDED);
|
||
|
|
||
|
SetMinRange(STINGERROCKET_RADIUS);
|
||
|
SetProjectileSpeed(STINGERROCKET_SPEED);
|
||
|
|
||
|
rocketnum = 1;
|
||
|
}
|
||
|
|
||
|
void StingerPack::SecondaryUse (Event *ev)
|
||
|
{
|
||
|
// switch to the quantum
|
||
|
owner->useWeapon("QuantumDestabilizer");
|
||
|
}
|
||
|
|
||
|
void StingerPack::Shoot (Event *ev)
|
||
|
{
|
||
|
StingerRocket *rocket;
|
||
|
Vector pos;
|
||
|
Vector end;
|
||
|
trace_t trace;
|
||
|
Vector targ, dir;
|
||
|
Vector forward, right, up;
|
||
|
|
||
|
if(!owner)
|
||
|
return;
|
||
|
|
||
|
if(owner->isClient())
|
||
|
{
|
||
|
const gravityaxis_t &grav = gravity_axis[owner->gravaxis];
|
||
|
|
||
|
// get orientation according to gravityaxis
|
||
|
forward[grav.x] = owner->orientation[0][0];
|
||
|
forward[grav.y] = owner->orientation[0][1]*grav.sign;
|
||
|
forward[grav.z] = owner->orientation[0][2]*grav.sign;
|
||
|
right[grav.x] = owner->orientation[1][0];
|
||
|
right[grav.y] = owner->orientation[1][1]*grav.sign;
|
||
|
right[grav.z] = owner->orientation[1][2]*grav.sign;
|
||
|
up[grav.x] = 0;
|
||
|
up[grav.y] = 0;
|
||
|
up[grav.z] = 1*grav.sign;
|
||
|
|
||
|
// set firing position and direction
|
||
|
pos = owner->origin - forward*12 + up*owner->viewheight;
|
||
|
end = pos + forward*256;
|
||
|
dir = forward;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GetMuzzlePosition(&pos, &forward, &right);
|
||
|
up = Vector(0, 0, 1);
|
||
|
|
||
|
// set firing position and direction
|
||
|
pos = owner->origin - forward*12 + up*(owner->maxs.z*0.75);
|
||
|
if(owner->isSubclassOf(Actor))
|
||
|
{
|
||
|
if(((Actor *)owner.Ptr())->currentEnemy)
|
||
|
{
|
||
|
Vector targpos;
|
||
|
|
||
|
targpos = ((Actor *)owner.Ptr())->currentEnemy->centroid;
|
||
|
|
||
|
// lead target if not on easy skill
|
||
|
if(skill->value && G_Random() < 0.7)
|
||
|
{
|
||
|
switch(rocketnum)
|
||
|
{
|
||
|
case 1:
|
||
|
targpos += ((Actor *)owner.Ptr())->currentEnemy->velocity*0.25;
|
||
|
break;
|
||
|
case 2:
|
||
|
targpos += ((Actor *)owner.Ptr())->currentEnemy->velocity*0.5;
|
||
|
break;
|
||
|
case 3:
|
||
|
targpos += ((Actor *)owner.Ptr())->currentEnemy->velocity*0.75;
|
||
|
break;
|
||
|
case 4:
|
||
|
targpos += ((Actor *)owner.Ptr())->currentEnemy->velocity;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
forward = targpos - pos;
|
||
|
forward.normalize();
|
||
|
}
|
||
|
}
|
||
|
end = pos + forward*256;
|
||
|
dir = forward;
|
||
|
}
|
||
|
|
||
|
|
||
|
switch(rocketnum)
|
||
|
{
|
||
|
case 1:
|
||
|
pos += (right*12 - up*4);
|
||
|
dir += (right*0.4 + up*0.2);
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
pos += (right*6 + up*6);
|
||
|
dir += (right*0.2 + up*0.4);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
pos -= (right*6 - up*6);
|
||
|
dir -= (right*0.2 - up*0.4);
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
pos -= (right*12 + up*4);
|
||
|
dir -= (right*0.4 - up*0.2);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
dir.normalize();
|
||
|
|
||
|
//set starting position so that they won't go into walls
|
||
|
trace = G_Trace(owner->origin + up*owner->viewheight, Vector(-1, -1, -1), Vector(1, 1, 1), pos, owner, MASK_PROJECTILE, "StingerPack::Shoot");
|
||
|
pos = Vector(trace.endpos);
|
||
|
|
||
|
// set target location
|
||
|
trace = G_Trace( owner->origin + up*owner->viewheight, vec_zero, vec_zero, end, owner, MASK_PROJECTILE, "StingerPack::Shoot");
|
||
|
targ = Vector(trace.endpos);
|
||
|
|
||
|
rocket = new StingerRocket;
|
||
|
rocket->Setup(owner, pos, targ, dir, forward);
|
||
|
|
||
|
// force player to fire four at a time
|
||
|
if(rocketnum < 4)
|
||
|
PostEvent(EV_Weapon_Shoot, 0.1);
|
||
|
|
||
|
if(owner->isClient())
|
||
|
NextAttack(1.2);
|
||
|
else
|
||
|
NextAttack(2.0);
|
||
|
|
||
|
rocketnum++;
|
||
|
if(rocketnum > 4)
|
||
|
rocketnum = 1;
|
||
|
}
|
||
|
|
||
|
void StingerPack::DetachGun (void)
|
||
|
{
|
||
|
if(attached)
|
||
|
{
|
||
|
RandomGlobalSound("null_sound", 1, CHAN_WEAPONIDLE );
|
||
|
RandomGlobalSound("null_sound", 1, CHAN_WEAPON );
|
||
|
attached = false;
|
||
|
detach();
|
||
|
hideModel();
|
||
|
edict->s.gunmodelindex = 0;
|
||
|
edict->s.gunanim = 0;
|
||
|
edict->s.gunframe = 0;
|
||
|
edict->s.effects &= ~EF_SMOOTHANGLES;
|
||
|
edict->s.effects &= ~EF_WARM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void StingerPack::AttachGun (void)
|
||
|
{
|
||
|
int groupindex;
|
||
|
int tri_num;
|
||
|
Vector orient;
|
||
|
|
||
|
if ( !owner )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (attached)
|
||
|
DetachGun();
|
||
|
|
||
|
if ( gi.GetBoneInfo( owner->edict->s.modelindex, "pack", &groupindex, &tri_num, orient.vec3() ) )
|
||
|
{
|
||
|
attached = true;
|
||
|
attach( owner->entnum, groupindex, tri_num, orient );
|
||
|
showModel();
|
||
|
setOrigin( vec_zero );
|
||
|
edict->s.gunmodelindex = modelIndex( worldmodel.c_str() );
|
||
|
if ( edict->s.gunmodelindex )
|
||
|
{
|
||
|
edict->s.gunanim = gi.Anim_Random( edict->s.gunmodelindex, "idle" );
|
||
|
if ( edict->s.gunanim < 0 )
|
||
|
edict->s.gunanim = 0;
|
||
|
edict->s.gunframe = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
edict->s.gunanim = 0;
|
||
|
edict->s.gunframe = 0;
|
||
|
}
|
||
|
edict->s.effects |= EF_SMOOTHANGLES;
|
||
|
edict->s.effects |= EF_WARM;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( gi.GetBoneInfo( owner->edict->s.modelindex, "gun", &groupindex, &tri_num, orient.vec3() ) )
|
||
|
{
|
||
|
gi.dprintf( "attached Stinger Pack to gun bone\n" );
|
||
|
|
||
|
attached = true;
|
||
|
attach( owner->entnum, groupindex, tri_num, orient );
|
||
|
showModel();
|
||
|
setOrigin( vec_zero );
|
||
|
edict->s.gunmodelindex = modelIndex( worldmodel.c_str() );
|
||
|
if ( edict->s.gunmodelindex )
|
||
|
{
|
||
|
edict->s.gunanim = gi.Anim_Random( edict->s.gunmodelindex, "idle" );
|
||
|
if ( edict->s.gunanim < 0 )
|
||
|
edict->s.gunanim = 0;
|
||
|
edict->s.gunframe = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
edict->s.gunanim = 0;
|
||
|
edict->s.gunframe = 0;
|
||
|
}
|
||
|
edict->s.effects |= EF_SMOOTHANGLES;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gi.dprintf( "attach failed\n" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// >) >) >) >) >) >) >) >) >) >) >) >) >)
|
||
|
|
||
|
class EXPORT_FROM_DLL EvilStingerPack : public StingerPack
|
||
|
{
|
||
|
public:
|
||
|
CLASS_PROTOTYPE( EvilStingerPack );
|
||
|
|
||
|
EvilStingerPack();
|
||
|
virtual void Shoot( Event *ev );
|
||
|
};
|
||
|
|
||
|
CLASS_DECLARATION(StingerPack, EvilStingerPack, "weapon_evilstingerpack");
|
||
|
|
||
|
ResponseDef EvilStingerPack::Responses[] =
|
||
|
{
|
||
|
{&EV_Weapon_Shoot, (Response)EvilStingerPack::Shoot},
|
||
|
{NULL, NULL}
|
||
|
};
|
||
|
|
||
|
EvilStingerPack::EvilStingerPack()
|
||
|
{
|
||
|
SetMinRange(0);
|
||
|
}
|
||
|
|
||
|
void EvilStingerPack::Shoot (Event *ev)
|
||
|
{
|
||
|
StingerPack::Shoot(ev);
|
||
|
CancelEventsOfType(EV_Weapon_Shoot);
|
||
|
StingerPack::Shoot(ev);
|
||
|
CancelEventsOfType(EV_Weapon_Shoot);
|
||
|
StingerPack::Shoot(ev);
|
||
|
CancelEventsOfType(EV_Weapon_Shoot);
|
||
|
StingerPack::Shoot(ev);
|
||
|
CancelEventsOfType(EV_Weapon_Shoot);
|
||
|
NextAttack(0.1);
|
||
|
}
|