/* ================================================================ 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); }