330 lines
7.2 KiB
C++
330 lines
7.2 KiB
C++
/*
|
|
================================================================
|
|
BOB MONSTER
|
|
================================================================
|
|
|
|
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 "bob.h"
|
|
#include "specialfx.h"
|
|
#include "player.h"
|
|
|
|
//Vector bobfieldmin (-64, -64, -64);
|
|
//Vector bobfieldmax (64, 64, 64);
|
|
|
|
#define BOB_CONCUSSION_RANGE 175
|
|
|
|
//===============================================================
|
|
// BOB
|
|
//===============================================================
|
|
|
|
Event EV_Bob_Concussion("fireconcussion");
|
|
|
|
CLASS_DECLARATION(Actor, Bob, "monster_bob");
|
|
|
|
ResponseDef Bob::Responses[] =
|
|
{
|
|
{&EV_Bob_Concussion, (Response)Bob::FireConcussion},
|
|
{&EV_Concussion_Effect, (Response)Bob::ConcussionEffect},
|
|
{&EV_Killed, (Response)Bob::Killed},
|
|
|
|
{NULL, NULL}
|
|
};
|
|
|
|
Bob::Bob()
|
|
{
|
|
concussiontime = level.time + 2;
|
|
}
|
|
|
|
void Bob::Prethink(void)
|
|
{
|
|
// do a check for something to concussion
|
|
if(concussiontime < level.time)
|
|
{
|
|
Entity *ent;
|
|
|
|
ent = NULL;
|
|
while(ent = findradius(ent, worldorigin, BOB_CONCUSSION_RANGE))
|
|
{
|
|
if(ent == this)
|
|
continue;
|
|
|
|
if (
|
|
(!ent->isSubclassOf(Projectile)) &&
|
|
(!(ent->isSubclassOf(Player) && ent->takedamage))
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(ent->isSubclassOf(Projectile)) // do an extra check for Projectiles
|
|
{
|
|
Entity *entowner;
|
|
|
|
entowner = ((Projectile *)ent)->Owner();
|
|
assert(entowner);
|
|
|
|
if(entowner->isSubclassOf(Bob))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// make sure that it's infront
|
|
if(InConcussionFOV(ent->centroid) && CanSeeFrom(worldorigin, ent))
|
|
{
|
|
FireConcussion(NULL);
|
|
concussiontime = level.time + 1.5;
|
|
break; // stop looking for a reason to fire
|
|
}
|
|
}
|
|
|
|
// if we didn't fire at anything, add a bit of a recheck delay
|
|
if(concussiontime < level.time)
|
|
{
|
|
concussiontime = level.time + 0.15;
|
|
}
|
|
}
|
|
|
|
//
|
|
// call our superclass
|
|
//
|
|
Actor::Prethink();
|
|
}
|
|
|
|
void Bob::Killed (Event *ev)
|
|
{
|
|
// make Bob go down in flames
|
|
edict->s.effects |= EF_DEATHFLAMES;
|
|
|
|
// call actual killed function
|
|
Actor::Killed(ev);
|
|
}
|
|
|
|
inline qboolean Bob::InConcussionFOV (Vector pos)
|
|
{
|
|
Vector delta;
|
|
float dot;
|
|
|
|
delta = pos - EyePosition();
|
|
if(!delta.x && !delta.y)
|
|
{
|
|
// special case for straight up and down
|
|
return true;
|
|
}
|
|
|
|
// give better vertical vision
|
|
delta.z = 0;
|
|
|
|
delta.normalize();
|
|
dot = DotProduct(orientation[0], delta.vec3());
|
|
|
|
return (dot > CONCUSSION_ARC);
|
|
}
|
|
|
|
void Bob::FireConcussion(Event *ev)
|
|
{
|
|
Vector pos;
|
|
ConcussionRing *ring;
|
|
|
|
lastfirepos = worldorigin;
|
|
lastfiredir = Vector(orientation[0]);
|
|
|
|
// apply kick back
|
|
velocity -= lastfiredir * CONCUSSION_KICKBACK * 0.25;
|
|
|
|
// make blast ring
|
|
pos = lastfirepos + lastfiredir*8;
|
|
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);
|
|
|
|
// make firing sound
|
|
sound("weapons/concussion/fire5b.wav", 1, CHAN_WEAPON);
|
|
|
|
blastcount = 0;
|
|
ProcessEvent(EV_Concussion_Effect);
|
|
}
|
|
|
|
void Bob::ConcussionEffect (Event *ev)
|
|
{
|
|
Entity *tmpent = NULL;
|
|
ConcussionRing *ring;
|
|
Vector targvec, vec;
|
|
Vector end;
|
|
trace_t trace;
|
|
float d, f, targdist;
|
|
|
|
if(health <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
tmpent = NULL;
|
|
while(tmpent = findradius(tmpent, lastfirepos, CONCUSSION_DIST * 0.75))
|
|
{
|
|
if(tmpent == this)
|
|
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;
|
|
|
|
// don't effect bob projectiles
|
|
if(tmpent->isSubclassOf(Projectile))
|
|
{
|
|
Entity *entowner;
|
|
|
|
entowner = ((Projectile *)tmpent)->Owner();
|
|
assert(entowner);
|
|
|
|
if(entowner->isSubclassOf(Bob))
|
|
continue;
|
|
}
|
|
|
|
targvec = tmpent->origin + (tmpent->mins + tmpent->maxs)*0.5;
|
|
|
|
trace = G_Trace(lastfirepos, Vector(0, 0, 0), Vector(0, 0, 0), targvec, this, 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(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 * 0.9) - (targdist * CONCUSSION_PUSH_DECAY);
|
|
// scale the force by the directional dot product
|
|
f *= d;
|
|
// scale the force by the mass of the target
|
|
if(tmpent->mass > 20)
|
|
f *= 1/(tmpent->mass*0.005);
|
|
else
|
|
f = 2000;
|
|
|
|
if(f < 0)
|
|
f = 0;
|
|
if(f > 2000)
|
|
f = 2000;
|
|
|
|
//apply force, but not to script objects and models
|
|
if((f > 0) && (!tmpent->isSubclassOf(ScriptSlave)))
|
|
{
|
|
tmpent->velocity += vec * f;
|
|
|
|
if(tmpent->velocity.length() > 1500)
|
|
{
|
|
tmpent->velocity.normalize();
|
|
tmpent->velocity *= 1500;
|
|
}
|
|
|
|
if(tmpent->velocity[2] <= 0)
|
|
tmpent->velocity[2] = 50;
|
|
else
|
|
tmpent->velocity[2] += 50;
|
|
|
|
// set info for wall splatting damage
|
|
tmpent->concussioner = this;
|
|
|
|
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, this, 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++;
|
|
}
|
|
}
|
|
|
|
//===============================================================
|
|
// BOB'S WEAPON
|
|
//===============================================================
|
|
|
|
CLASS_DECLARATION(Weapon, BobBow, "");
|
|
|
|
ResponseDef BobBow::Responses[] =
|
|
{
|
|
{&EV_Weapon_Shoot, (Response)BobBow::Shoot},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
BobBow::BobBow()
|
|
{
|
|
SetModels(NULL, "view_bobbow.def");
|
|
modelIndex("bobbolt.def");
|
|
modelIndex("sprites/cbshot.spr");
|
|
SetAmmo("BulletPulse", 10, 90);
|
|
SetType(WEAPON_2HANDED_HI);
|
|
|
|
SetMinRange(50);
|
|
SetProjectileSpeed(1000);
|
|
}
|
|
|
|
void BobBow::Shoot(Event *ev)
|
|
{
|
|
PBolt *bolt;
|
|
Vector pos, dir;
|
|
|
|
assert(owner);
|
|
if(!owner)
|
|
return;
|
|
|
|
GetMuzzlePosition( &pos, &dir, NULL, NULL);
|
|
|
|
bolt = new PBolt;
|
|
bolt->Setup(owner, pos, dir, -1);
|
|
|
|
NextAttack(1.8);
|
|
}
|