sin-2015/rocketpack.cpp
1999-04-22 00:00:00 +00:00

489 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);
}