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