//----------------------------------------------------------------- static void GM_CreateExplosion( gentity_t *self, const int boltID, qboolean doSmall ) //doSmall = qfalse { if ( boltID >=0 ) { mdxaBone_t boltMatrix; vec3_t org, dir; trap->G2API_GetBoltMatrix( self->ghoul2, 0, boltID, &boltMatrix, self->r.currentAngles, self->r.currentOrigin, level.time, NULL, self->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, org ); BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, dir ); if ( doSmall ) { G_PlayEffectID( G_EffectIndex("env/small_explode2"), org, dir ); } else { G_PlayEffectID( G_EffectIndex("env/med_explode2"), org, dir ); } } }
/* ------------------------- void Droid_Spin( void ) ------------------------- */ void Droid_Spin( void ) { vec3_t dir = {0,0,1}; R2D2_TurnAnims(); // Head is gone, spin and spark if ( NPC->client->NPC_class == CLASS_R5D2 || NPC->client->NPC_class == CLASS_R2D2 ) { // No head? if (trap->G2API_GetSurfaceRenderStatus( NPC->ghoul2, 0, "head" )>0) { if (TIMER_Done(NPC,"smoke") && !TIMER_Done(NPC,"droidsmoketotal")) { TIMER_Set( NPC, "smoke", 100); G_PlayEffectID( G_EffectIndex("volumetric/droid_smoke") , NPC->r.currentOrigin,dir); } if (TIMER_Done(NPC,"droidspark")) { TIMER_Set( NPC, "droidspark", Q_irand(100,500)); G_PlayEffectID( G_EffectIndex("sparks/spark"), NPC->r.currentOrigin,dir); } ucmd.forwardmove = Q_irand( -64, 64); if (TIMER_Done(NPC,"roam")) { TIMER_Set( NPC, "roam", Q_irand( 250, 1000 ) ); NPCInfo->desiredYaw = Q_irand( 0, 360 ); // Go in random directions } } else { if (TIMER_Done(NPC,"roam")) { NPCInfo->localState = LSTATE_NONE; } else { NPCInfo->desiredYaw = AngleNormalize360(NPCInfo->desiredYaw + 40); // Spin around } } } else { if (TIMER_Done(NPC,"roam")) { NPCInfo->localState = LSTATE_NONE; } else { NPCInfo->desiredYaw = AngleNormalize360(NPCInfo->desiredYaw + 40); // Spin around } } NPC_UpdateAngles( qtrue, qtrue ); }
//------------------------------------------------------------------------------------------------------------ void auto_turret_die ( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) //------------------------------------------------------------------------------------------------------------ { vec3_t forward = { 0,0, 1 }, pos; // Turn off the thinking of the base & use it's targets g_entities[self->r.ownerNum].think = NULL; g_entities[self->r.ownerNum].use = NULL; // clear my data self->die = NULL; self->takedamage = qfalse; self->s.health = self->health = 0; self->s.maxhealth = self->maxHealth = 0; self->s.loopSound = 0; self->s.shouldtarget = qfalse; //self->s.owner = MAX_CLIENTS; //not owned by any client VectorCopy( self->r.currentOrigin, pos ); pos[2] += self->r.maxs[2]*0.5f; G_PlayEffect( EFFECT_EXPLOSION_TURRET, pos, forward ); G_PlayEffectID( G_EffectIndex( "turret/explode" ), pos, forward ); if ( self->splashDamage > 0 && self->splashRadius > 0 ) { G_RadiusDamage( self->r.currentOrigin, attacker, self->splashDamage, self->splashRadius, attacker, NULL, MOD_UNKNOWN ); } self->s.weapon = 0; // crosshair code uses this to mark crosshair red if ( self->s.modelindex2 ) { // switch to damage model if we should self->s.modelindex = self->s.modelindex2; if (self->target_ent && self->target_ent->s.modelindex2) { self->target_ent->s.modelindex = self->target_ent->s.modelindex2; } VectorCopy( self->r.currentAngles, self->s.apos.trBase ); VectorClear( self->s.apos.trDelta ); if ( self->target ) { G_UseTargets( self, attacker ); } } else { ObjectDie( self, inflictor, attacker, damage, meansOfDeath ); } }
/* ------------------------- Remote_Fire ------------------------- */ void Remote_Fire (void) { vec3_t delta1, enemy_org1, muzzle1; vec3_t angleToEnemy1; static vec3_t forward, vright, up; // static vec3_t muzzle; gentity_t *missile; CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 ); VectorCopy( NPC->r.currentOrigin, muzzle1 ); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, forward, vright, up); missile = CreateMissile( NPC->r.currentOrigin, forward, 1000, 10000, NPC, qfalse ); G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), NPC->r.currentOrigin, forward ); missile->classname = "briar"; missile->s.weapon = WP_BRYAR_PISTOL; missile->damage = 10; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BRYAR_PISTOL; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }
//------------------------------------ void Seeker_Fire( void ) { vec3_t dir, enemy_org, muzzle; gentity_t *missile; CalcEntitySpot( NPCS.NPC->enemy, SPOT_HEAD, enemy_org ); VectorSubtract( enemy_org, NPCS.NPC->r.currentOrigin, dir ); VectorNormalize( dir ); // move a bit forward in the direction we shall shoot in so that the bolt doesn't poke out the other side of the seeker VectorMA( NPCS.NPC->r.currentOrigin, 15, dir, muzzle ); missile = CreateMissile( muzzle, dir, 1000, 10000, NPCS.NPC, qfalse ); G_PlayEffectID( G_EffectIndex("blaster/muzzle_flash"), NPCS.NPC->r.currentOrigin, dir ); missile->classname = "blaster"; missile->s.weapon = WP_BLASTER; missile->damage = 5; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BLASTER; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; if ( NPCS.NPC->r.ownerNum < ENTITYNUM_NONE ) { missile->r.ownerNum = NPCS.NPC->r.ownerNum; } }
/* ------------------------- Mark1Dead_FireBlaster - Shoot the left weapon, the multi-blaster ------------------------- */ void Mark1Dead_FireBlaster (void) { vec3_t muzzle1,muzzle_dir; gentity_t *missile; mdxaBone_t boltMatrix; int bolt; bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash1"); trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, bolt, &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time, NULL, NPC->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 ); BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, muzzle_dir ); G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, muzzle_dir ); missile = CreateMissile( muzzle1, muzzle_dir, 1600, 10000, NPC, qfalse ); G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_fire")); missile->classname = "bryar_proj"; missile->s.weapon = WP_BRYAR_PISTOL; missile->damage = 1; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BRYAR_PISTOL; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }
/* ------------------------- ImperialProbe_FireBlaster ------------------------- */ void ImperialProbe_FireBlaster(void) { vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1; static vec3_t forward, vright, up; // static vec3_t muzzle; int genBolt1; gentity_t *missile; mdxaBone_t boltMatrix; genBolt1 = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash"); //FIXME: use {0, NPC->client->ps.legsYaw, 0} trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, genBolt1, &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time, NULL, NPC->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 ); G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, vec3_origin ); G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/probe/misc/fire" )); if (NPC->health) { CalcEntitySpot( NPC->enemy, SPOT_CHEST, enemy_org1 ); enemy_org1[0]+= Q_irand(0,10); enemy_org1[1]+= Q_irand(0,10); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, forward, vright, up); } else { AngleVectors (NPC->r.currentAngles, forward, vright, up); } missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC, qfalse ); missile->classname = "bryar_proj"; missile->s.weapon = WP_BRYAR_PISTOL; if ( g_spskill.integer <= 1 ) { missile->damage = 5; } else { missile->damage = 10; } missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_UNKNOWN; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }
void NPC_Mark2_Part_Explode( gentity_t *self, int bolt ) { if ( bolt >= 0 ) { mdxaBone_t boltMatrix; vector3 org, dir; trap->G2API_GetBoltMatrix( self->ghoul2, 0, bolt, &boltMatrix, &self->r.currentAngles, &self->r.currentOrigin, level.time, NULL, &self->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, &org ); BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, &dir ); G_PlayEffectID( G_EffectIndex( "env/med_explode2" ), &org, &dir ); G_PlayEffectID( G_EffectIndex( "blaster/smoke_bolton" ), &org, &dir ); } //G_PlayEffectID( G_EffectIndex("blaster/smoke_bolton"), self->playerModel, bolt, self->s.number); self->count++; // Count of pods blown off }
/* ------------------------- NPC_Mark1_Part_Explode ------------------------- */ void NPC_Mark1_Part_Explode( gentity_t *self, int bolt ) { if ( bolt >=0 ) { mdxaBone_t boltMatrix; vec3_t org, dir; trap_G2API_GetBoltMatrix( self->ghoul2, 0, bolt, &boltMatrix, self->r.currentAngles, self->r.currentOrigin, level.time, NULL, self->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, org ); BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, dir ); G_PlayEffectID( G_EffectIndex("env/med_explode2"), org, dir ); G_PlayEffectID( G_EffectIndex("blaster/smoke_bolton"), org, dir ); } }
void NPC_GM_StartLaser( void ) { if ( !NPC->lockCount ) {//haven't already started a laser attack //warm up for the beam attack TIMER_Set( NPC, "beamDelay", NPC->client->ps.torsoTimer ); TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer+3000 ); NPC->lockCount = 1; //turn on warmup effect G_PlayEffectID( G_EffectIndex("galak/beam_warmup"), NPC->r.currentOrigin, vec3_origin ); G_SoundOnEnt( NPC, CHAN_AUTO, "sound/weapons/galak/lasercharge.wav" ); } }
void G_MissileBounceEffect( gentity_t *ent, vector3 *org, vector3 *dir ) { //FIXME: have an EV_BOUNCE_MISSILE event that checks the s.weapon and does the appropriate effect switch ( ent->s.weapon ) { case WP_BOWCASTER: G_PlayEffectID( G_EffectIndex( "bowcaster/deflect" ), &ent->r.currentOrigin, dir ); break; case WP_BLASTER: case WP_BRYAR_PISTOL: G_PlayEffectID( G_EffectIndex( "blaster/deflect" ), &ent->r.currentOrigin, dir ); break; default: { gentity_t *te = G_TempEntity( org, EV_SABER_BLOCK ); VectorCopy( org, &te->s.origin ); VectorCopy( dir, &te->s.angles ); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum } break; } }
//---------------------------------------------------------------- static void turret_fire ( gentity_t *ent, vec3_t start, vec3_t dir ) //---------------------------------------------------------------- { vec3_t org; gentity_t *bolt; if ( (trap_PointContents( start, ent->s.number )&MASK_SHOT) ) { return; } VectorMA( start, -START_DIS, dir, org ); // dumb.... G_PlayEffectID( ent->genericValue13, org, dir ); bolt = G_Spawn(); //use a custom shot effect bolt->s.otherEntityNum2 = ent->genericValue14; //use a custom impact effect bolt->s.emplacedOwner = ent->genericValue15; bolt->classname = "turret_proj"; bolt->nextthink = level.time + 10000; bolt->think = G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_EMPLACED_GUN; bolt->r.ownerNum = ent->s.number; bolt->damage = ent->damage; bolt->alliedTeam = ent->alliedTeam; bolt->teamnodmg = ent->teamnodmg; //bolt->dflags = DAMAGE_NO_KNOCKBACK;// | DAMAGE_HEAVY_WEAP_CLASS; // Don't push them around, or else we are constantly re-aiming bolt->splashDamage = ent->damage; bolt->splashRadius = 100; bolt->methodOfDeath = MOD_TARGET_LASER; //[BugFix16] bolt->splashMethodOfDeath = MOD_TARGET_LASER; //[/BugFix16] bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; //bolt->trigger_formation = qfalse; // don't draw tail on first frame VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 ); VectorScale( bolt->r.maxs, -1, bolt->r.mins ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, ent->mass, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin); bolt->parent = ent; }
static int GLua_Sys_PlayEffect(lua_State *L) { vec3_t org, angs; GLuaVec_t *org2, *angs2; org2 = GLua_CheckVector(L,2); ConvertVec(org2, org); if (!lua_isnoneornil(L,3)) { angs2 = GLua_CheckVector(L,2); ConvertVec(angs2, angs); } else { VectorClear(angs); } G_PlayEffectID(luaL_checkint(L,1), org, angs); return 0; }
//lightning strike trigger lightning strike event void Do_Strike(gentity_t *ent) { trace_t localTrace; vec3_t strikeFrom; vec3_t strikePoint; vec3_t fxAng; //maybe allow custom fx direction at some point? VectorSet(fxAng, 90.0f, 0.0f, 0.0f); //choose a random point to strike within the bounds of the trigger strikePoint[0] = flrand(ent->r.absmin[0], ent->r.absmax[0]); strikePoint[1] = flrand(ent->r.absmin[1], ent->r.absmax[1]); //consider the bottom mins the ground level strikePoint[2] = ent->r.absmin[2]; //set the from point strikeFrom[0] = strikePoint[0]; strikeFrom[1] = strikePoint[1]; strikeFrom[2] = ent->r.absmax[2]-4.0f; //now trace for damaging stuff, and do the effect trap->Trace(&localTrace, strikeFrom, NULL, NULL, strikePoint, ent->s.number, MASK_PLAYERSOLID, 0, 0, 0); VectorCopy(localTrace.endpos, strikePoint); if (localTrace.startsolid || localTrace.allsolid) { //got a bad spot, think again next frame to try another strike ent->nextthink = level.time; return; } if (ent->radius) { //do a radius damage at the end pos G_RadiusDamage(strikePoint, ent, ent->damage, ent->radius, ent, NULL, MOD_SUICIDE); } else { //only damage individuals gentity_t *trHit = &g_entities[localTrace.entityNum]; if (trHit->inuse && trHit->takedamage) { //damage it then G_Damage(trHit, ent, ent, NULL, trHit->r.currentOrigin, ent->damage, 0, MOD_SUICIDE); } } G_PlayEffectID(ent->genericValue2, strikeFrom, fxAng); }
void NPC_GM_StartLaser( void ) { if ( !NPCS.NPC->lockCount ) {//haven't already started a laser attack //warm up for the beam attack #if 0 NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_RAISEWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); #endif TIMER_Set( NPCS.NPC, "beamDelay", NPCS.NPC->client->ps.torsoTimer ); TIMER_Set( NPCS.NPC, "attackDelay", NPCS.NPC->client->ps.torsoTimer+3000 ); NPCS.NPC->lockCount = 1; //turn on warmup effect G_PlayEffectID( G_EffectIndex("galak/beam_warmup"), NPCS.NPC->r.currentOrigin, vec3_origin ); G_SoundOnEnt( NPCS.NPC, CHAN_AUTO, "sound/weapons/galak/lasercharge.wav" ); } }
static void ATST_PlayEffect( gentity_t *self, const int boltID, const char *fx ) { if ( boltID >= 0 && fx && fx[0] ) { mdxaBone_t boltMatrix; vector3 org, dir; trap->G2API_GetBoltMatrix( self->ghoul2, 0, boltID, &boltMatrix, self->r.currentAngles, self->r.currentOrigin, level.time, NULL, self->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, org ); BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, dir ); G_PlayEffectID( G_EffectIndex( (char *)fx ), org, dir ); } }
//---------------------------------------------------------------- static void turret_fire ( gentity_t *ent, vec3_t start, vec3_t dir ) //---------------------------------------------------------------- { vec3_t org; gentity_t *bolt; if ( (trap_PointContents( start, ent->s.number )&MASK_SHOT) ) { return; } VectorMA( start, -START_DIS, dir, org ); // dumb.... G_PlayEffectID( ent->genericValue13, org, dir ); bolt = G_Spawn(); //use a custom shot effect bolt->s.otherEntityNum2 = ent->genericValue14; //use a custom impact effect bolt->s.emplacedOwner = ent->genericValue15; bolt->classname = "turret_proj"; bolt->nextthink = level.time + 10000; bolt->think = G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_EMPLACED_GUN; bolt->r.ownerNum = ent->s.number; bolt->damage = ent->damage; bolt->alliedTeam = ent->alliedTeam; bolt->teamnodmg = ent->teamnodmg; bolt->splashDamage = ent->damage; bolt->splashRadius = 100; bolt->methodOfDeath = MOD_TARGET_LASER; bolt->splashMethodOfDeath = MOD_TARGET_LASER; bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 ); VectorScale( bolt->r.maxs, -1, bolt->r.mins ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, ent->mass, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin); bolt->parent = ent; }
/* ------------------------- Mark2_FireBlaster ------------------------- */ void Mark2_FireBlaster(qboolean advance) { vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1; static vec3_t forward, vright, up; static vec3_t muzzle; gentity_t *missile; mdxaBone_t boltMatrix; int bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash"); trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, bolt, &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time, NULL, NPC->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 ); if (NPC->health) { CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 ); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, forward, vright, up); } else { AngleVectors (NPC->r.currentAngles, forward, vright, up); } G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, forward ); G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/mark2/misc/mark2_fire")); missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC, qfalse ); missile->classname = "bryar_proj"; missile->s.weapon = WP_BRYAR_PISTOL; missile->damage = 1; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BRYAR_PISTOL; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }
/* ------------------------- Mark1Dead_FireRocket - Shoot the left weapon, the multi-blaster ------------------------- */ void Mark1Dead_FireRocket (void) { mdxaBone_t boltMatrix; vec3_t muzzle1,muzzle_dir; gentity_t *missile; int damage = 50; int bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash5"); trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, bolt, &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time, NULL, NPC->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 ); BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, muzzle_dir ); G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, muzzle_dir ); G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_fire")); missile = CreateMissile( muzzle1, muzzle_dir, BOWCASTER_VELOCITY, 10000, NPC, qfalse ); missile->classname = "bowcaster_proj"; missile->s.weapon = WP_BOWCASTER; VectorSet( missile->r.maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE ); VectorScale( missile->r.maxs, -1, missile->r.mins ); missile->damage = damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; //missile->methodOfDeath = MOD_ENERGY; missile->methodOfDeath = MOD_ROCKET; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; missile->splashDamage = BOWCASTER_SPLASH_DAMAGE; missile->splashRadius = BOWCASTER_SPLASH_RADIUS; // we don't want it to bounce missile->bounceCount = 0; }
/* ------------------------- Remote_Fire ------------------------- */ void Remote_Fire (void) { vec3_t delta1, enemy_org1, muzzle1; vec3_t angleToEnemy1; //[SeekerItemNpc] //these dont need to be static, and vright and up arent used /*static*/ vec3_t forward;//, vright, up; /*static vec3_t muzzle;*/ //[/SeekerItemNpc] gentity_t *missile; CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 ); VectorCopy( NPC->r.currentOrigin, muzzle1 ); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); //[SeekerItemNpc] AngleVectors (angleToEnemy1, forward, NULL, NULL); //AngleVectors (angleToEnemy1, forward, vright, up); //[/SeekerItemNpc] missile = CreateMissile( NPC->r.currentOrigin, forward, 1000, 10000, NPC, qfalse ); G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), NPC->r.currentOrigin, forward ); missile->classname = "briar"; missile->s.weapon = WP_BRYAR_PISTOL; missile->damage = 10; missile->dflags = DAMAGE_DEATH_KNOCKBACK; //RAFIXME - impliment this MOD? //missile->methodOfDeath = MOD_ENERGY; missile->methodOfDeath = MOD_BRYAR_PISTOL; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }
//MP RULE - ALL PROCESSMOVECOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!! //If you really need to violate this rule for SP, then use ifdefs. //By BG-compatible, I mean no use of game-specific data - ONLY use //stuff available in the MP bgEntity (in SP, the bgEntity is #defined //as a gentity, but the MP-compatible access restrictions are based //on the bgEntity structure in the MP codebase) -rww // ProcessMoveCommands the Vehicle. static void ProcessMoveCommands( Vehicle_t *pVeh ) { /************************************************************************************/ /* BEGIN Here is where we move the vehicle (forward or back or whatever). BEGIN */ /************************************************************************************/ //Client sets ucmds and such for speed alterations float speedInc, speedIdleDec, speedIdle, speedIdleAccel, speedMin, speedMax; playerState_t *parentPS; playerState_t *pilotPS = NULL; int curTime; #ifdef _JK2MP parentPS = pVeh->m_pParentEntity->playerState; if (pVeh->m_pPilot) { pilotPS = pVeh->m_pPilot->playerState; } #else parentPS = &pVeh->m_pParentEntity->client->ps; if (pVeh->m_pPilot) { pilotPS = &pVeh->m_pPilot->client->ps; } #endif // If we're flying, make us accelerate at 40% (about half) acceleration rate, and restore the pitch // to origin (straight) position (at 5% increments). if ( pVeh->m_ulFlags & VEH_FLYING ) { speedInc = pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier * 0.4f; } #ifdef _JK2MP else if ( !parentPS->m_iVehicleNum ) #else else if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh ) ) #endif {//drifts to a stop speedInc = 0; //pVeh->m_ucmd.forwardmove = 127; } else { speedInc = pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier; } speedIdleDec = pVeh->m_pVehicleInfo->decelIdle * pVeh->m_fTimeModifier; #ifndef _JK2MP//SP curTime = level.time; #elif QAGAME//MP GAME curTime = level.time; #elif CGAME//MP CGAME //FIXME: pass in ucmd? Not sure if this is reliable... curTime = pm->cmd.serverTime; #endif if ( (pVeh->m_pPilot /*&& (pilotPS->weapon == WP_NONE || pilotPS->weapon == WP_MELEE )*/ && (pVeh->m_ucmd.buttons & BUTTON_ALT_ATTACK) && pVeh->m_pVehicleInfo->turboSpeed) /*|| (parentPS && parentPS->electrifyTime > curTime && pVeh->m_pVehicleInfo->turboSpeed)*/ //make them go! ) { if ( (parentPS && parentPS->electrifyTime > curTime) || (pVeh->m_pPilot->playerState && (pVeh->m_pPilot->playerState->weapon == WP_MELEE || (pVeh->m_pPilot->playerState->weapon == WP_SABER && BG_SabersOff( pVeh->m_pPilot->playerState ) ))) ) { if ((curTime - pVeh->m_iTurboTime)>pVeh->m_pVehicleInfo->turboRecharge) { pVeh->m_iTurboTime = (curTime + pVeh->m_pVehicleInfo->turboDuration); if (pVeh->m_pVehicleInfo->iTurboStartFX) { int i; for (i=0; (i<MAX_VEHICLE_EXHAUSTS && pVeh->m_iExhaustTag[i]!=-1); i++) { #ifdef QAGAME if (pVeh->m_pParentEntity && pVeh->m_pParentEntity->ghoul2 && pVeh->m_pParentEntity->playerState) { //fine, I'll use a tempent for this, but only because it's played only once at the start of a turbo. vec3_t boltOrg, boltDir; mdxaBone_t boltMatrix; VectorSet(boltDir, 0.0f, pVeh->m_pParentEntity->playerState->viewangles[YAW], 0.0f); trap_G2API_GetBoltMatrix(pVeh->m_pParentEntity->ghoul2, 0, pVeh->m_iExhaustTag[i], &boltMatrix, boltDir, pVeh->m_pParentEntity->playerState->origin, level.time, NULL, pVeh->m_pParentEntity->modelScale); BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltOrg); BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltDir); G_PlayEffectID(pVeh->m_pVehicleInfo->iTurboStartFX, boltOrg, boltDir); } #endif } } parentPS->speed = pVeh->m_pVehicleInfo->turboSpeed; // Instantly Jump To Turbo Speed } } } // Slide Breaking if (pVeh->m_ulFlags&VEH_SLIDEBREAKING) { if (pVeh->m_ucmd.forwardmove>=0 #ifndef _JK2MP || ((level.time - pVeh->m_pParentEntity->lastMoveTime)>500) #endif ) { pVeh->m_ulFlags &= ~VEH_SLIDEBREAKING; } parentPS->speed = 0; } else if ( (curTime > pVeh->m_iTurboTime) && !(pVeh->m_ulFlags&VEH_FLYING) && pVeh->m_ucmd.forwardmove<0 && fabs(pVeh->m_vOrientation[ROLL])>25.0f) { pVeh->m_ulFlags |= VEH_SLIDEBREAKING; } if ( curTime < pVeh->m_iTurboTime ) { speedMax = pVeh->m_pVehicleInfo->turboSpeed; if (parentPS) { parentPS->eFlags |= EF_JETPACK_ACTIVE; } } else { speedMax = pVeh->m_pVehicleInfo->speedMax; if (parentPS) { parentPS->eFlags &= ~EF_JETPACK_ACTIVE; } } speedIdle = pVeh->m_pVehicleInfo->speedIdle; speedIdleAccel = pVeh->m_pVehicleInfo->accelIdle * pVeh->m_fTimeModifier; speedMin = pVeh->m_pVehicleInfo->speedMin; if ( parentPS->speed || parentPS->groundEntityNum == ENTITYNUM_NONE || pVeh->m_ucmd.forwardmove || pVeh->m_ucmd.upmove > 0 ) { if ( pVeh->m_ucmd.forwardmove > 0 && speedInc ) { parentPS->speed += speedInc; } else if ( pVeh->m_ucmd.forwardmove < 0 ) { if ( parentPS->speed > speedIdle ) { parentPS->speed -= speedInc; } else if ( parentPS->speed > speedMin ) { parentPS->speed -= speedIdleDec; } } // No input, so coast to stop. else if ( parentPS->speed > 0.0f ) { parentPS->speed -= speedIdleDec; if ( parentPS->speed < 0.0f ) { parentPS->speed = 0.0f; } } else if ( parentPS->speed < 0.0f ) { parentPS->speed += speedIdleDec; if ( parentPS->speed > 0.0f ) { parentPS->speed = 0.0f; } } } else { if ( !pVeh->m_pVehicleInfo->strafePerc #ifdef _JK2MP || (0 && pVeh->m_pParentEntity->s.number < MAX_CLIENTS) ) #else || (!g_speederControlScheme->value && !pVeh->m_pParentEntity->s.number) ) #endif {//if in a strafe-capable vehicle, clear strafing unless using alternate control scheme //pVeh->m_ucmd.rightmove = 0; } } if ( parentPS->speed > speedMax ) { parentPS->speed = speedMax; } else if ( parentPS->speed < speedMin ) { parentPS->speed = speedMin; } if (parentPS && parentPS->electrifyTime > curTime) { parentPS->speed *= (pVeh->m_fTimeModifier/60.0f); } /********************************************************************************/ /* END Here is where we move the vehicle (forward or back or whatever). END */ /********************************************************************************/ }
void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; qboolean isKnockedSaber = qfalse; other = &g_entities[trace->entityNum]; // check for bounce if ( other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) && (g_tweakWeapons.integer & WT_ROCKET_MORTAR && ent->s.weapon == WP_REPEATER && ent->bounceCount == 50 && ent->setTime && ent->setTime > level.time - 300)) { //if its a direct hit and first 500ms of mortar, bounce off player. G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } else if ( !other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) { //only on the first bounce vv if (!(g_tweakWeapons.integer & WT_ROCKET_MORTAR && ent->s.weapon == WP_REPEATER && ent->bounceCount == 50 && ent->setTime && ent->setTime < level.time - 1000))//give this mortar a 1 second 'fuse' until its armed { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } } else if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF)) { //this is a knocked-away saber if (ent->bounceCount > 0 || ent->bounceCount == -5) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } isKnockedSaber = qtrue; } // I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags&(FL_BOUNCE_SHRAPNEL) ) ) || ((trace->surfaceFlags&SURF_FORCEFIELD)&&!ent->splashDamage&&!ent->splashRadius&&(ent->bounceCount > 0 || ent->bounceCount == -5)) ) { G_BounceMissile( ent, trace ); if ( ent->bounceCount < 1 ) { ent->flags &= ~FL_BOUNCE_SHRAPNEL; } //trap->Print("Shrapnel is still there\n"); return; } /* if ( !other->takedamage && ent->s.weapon == WP_THERMAL && !ent->alt_fire ) {//rolling thermal det - FIXME: make this an eFlag like bounce & stick!!! //G_BounceRollMissile( ent, trace ); if ( ent->owner && ent->owner->s.number == 0 ) { G_MissileAddAlerts( ent ); } //trap->linkentity( ent ); return; } */ if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if (otherOwner->takedamage && otherOwner->client && otherOwner->client->ps.duelInProgress && otherOwner->client->ps.duelIndex != ent->r.ownerNum) { goto killProj; } } else if (!isKnockedSaber) { if (other->takedamage && other->client && other->client->ps.duelInProgress && other->client->ps.duelIndex != ent->r.ownerNum) { goto killProj; } } if (other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY) { if (ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_ROCKET && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_ROCKET_HOMING && ent->methodOfDeath != MOD_THERMAL && ent->methodOfDeath != MOD_THERMAL_SPLASH && ent->methodOfDeath != MOD_TRIP_MINE_SPLASH && ent->methodOfDeath != MOD_TIMED_MINE_SPLASH && ent->methodOfDeath != MOD_DET_PACK_SPLASH && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && ent->methodOfDeath != MOD_SABER && ent->methodOfDeath != MOD_TURBLAST) { vec3_t fwd; if (trace) { VectorCopy(trace->plane.normal, fwd); } else { //oh well AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } G_DeflectMissile(other, ent, fwd); G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } } if ((other->flags & FL_SHIELDED) && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_EMPLACED_GUN && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_TURBLAST && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && !(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) ) { vec3_t fwd; if (other->client) { AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); } else { AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } G_DeflectMissile(other, ent, fwd); G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } if (other->takedamage && other->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && other->client->ps.saberBlockTime < level.time && !isKnockedSaber && WP_SaberCanBlock(other, ent->r.currentOrigin, 0, 0, qtrue, 0)) //loda fixme, add check for dimensions for blocking here? { //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked) vec3_t fwd; gentity_t *te; int otherDefLevel = other->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(ent->r.currentOrigin, te->s.origin); VectorCopy(trace->plane.normal, te->s.angles); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum /*if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove || other->client->pers.cmd.rightmove) */ if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if (otherDefLevel < 0) { otherDefLevel = 0; } } AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); if (otherDefLevel == FORCE_LEVEL_1) { //if def is only level 1, instead of deflecting the shot it should just die here } else if (otherDefLevel == FORCE_LEVEL_2) { G_DeflectMissile(other, ent, fwd); } else { G_ReflectMissile(other, ent, fwd); } other->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100)); //200; //For jedi AI other->client->ps.saberEventFlags |= SEF_DEFLECTED; if (otherDefLevel == FORCE_LEVEL_3) { other->client->ps.saberBlockTime = 0; //^_^ } if (otherDefLevel == FORCE_LEVEL_1) { goto killProj; } return; } else if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if (otherOwner->takedamage && otherOwner->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && (g_entities[ent->r.ownerNum].s.bolt1 == other->s.bolt1) &&//loda fixme, this stops missiles deflecting, but they still dont passthrough... ent->methodOfDeath != MOD_CONC_ALT /*&& otherOwner->client->ps.saberBlockTime < level.time*/) { //for now still deflect even if saberBlockTime >= level.time because it hit the actual saber vec3_t fwd; gentity_t *te; int otherDefLevel = otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; //in this case, deflect it even if we can't actually block it because it hit our saber //WP_SaberCanBlock(otherOwner, ent->r.currentOrigin, 0, 0, qtrue, 0); if (otherOwner->client && otherOwner->client->ps.weaponTime <= 0) { WP_SaberBlockNonRandom(otherOwner, ent->r.currentOrigin, qtrue); } te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(ent->r.currentOrigin, te->s.origin); VectorCopy(trace->plane.normal, te->s.angles); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum /*if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove || otherOwner->client->pers.cmd.rightmove)*/ if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if (otherDefLevel < 0) { otherDefLevel = 0; } } AngleVectors(otherOwner->client->ps.viewangles, fwd, NULL, NULL); if (otherDefLevel == FORCE_LEVEL_1) { //if def is only level 1, instead of deflecting the shot it should just die here } else if (otherDefLevel == FORCE_LEVEL_2) { G_DeflectMissile(otherOwner, ent, fwd); } else { G_ReflectMissile(otherOwner, ent, fwd); } otherOwner->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100));//200; //For jedi AI otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED; if (otherDefLevel == FORCE_LEVEL_3) { otherOwner->client->ps.saberBlockTime = 0; //^_^ } if (otherDefLevel == FORCE_LEVEL_1) { goto killProj; } return; } } // check for sticking if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK ) ) { laserTrapStick( ent, trace->endpos, trace->plane.normal ); G_AddEvent( ent, EV_MISSILE_STICK, 0 ); return; } //JAPRO - Serverside - Flag punting - Start if (g_allowFlagThrow.integer && !other->takedamage && other->s.eType == ET_ITEM) { vec3_t velocity; if (ent->s.weapon == WP_REPEATER && (ent->s.eFlags & EF_ALT_FIRING)) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 0.7f, other->s.pos.trDelta ); VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } else if (ent->s.weapon == WP_ROCKET_LAUNCHER && (ent->s.eFlags & EF_ALT_FIRING)) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 2.5f, other->s.pos.trDelta ); VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } else if (ent->s.weapon == WP_ROCKET_LAUNCHER) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 0.9f, other->s.pos.trDelta ); VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } else if (ent->s.weapon == WP_THERMAL) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 0.9f, other->s.pos.trDelta ); //tweak? VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } } //JAPRO - Serverside - Flag punting - End // impact damage if (other->takedamage && !isKnockedSaber) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; qboolean didDmg = qfalse; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } //damage falloff option, assumes bullet lifetime is 10,000 (default) if ((g_tweakWeapons.integer & WT_NO_SPREAD) && ((ent->s.weapon == WP_BLASTER && (ent->s.eFlags & EF_ALT_FIRING)) || (ent->s.weapon == WP_REPEATER && !(ent->s.eFlags & EF_ALT_FIRING)) )) { //If the weapon has spread, just reduce damage based on distance for nospread tweak. This should probably be accompanied with the damagenumber setting so you can keep track of your dmg.. float lifetime = (10000 - ent->nextthink + level.time) * 0.001; //float scale = powf(2, -lifetime); float scale = -1.5 * lifetime + 1; scale += 0.1f; //offset it a bit so super close shots dont get affected at all if (scale < 0.2f) scale = 0.2f; else if (scale > 1.0f) scale = 1.0f; ent->damage *= scale; //trap->SendServerCommand(-1, va("chat \"Missile has been alive for %.2f s new dmg is %i scale is %.2f\n\"", lifetime, ent->damage, scale)); } if (ent->s.weapon == WP_BOWCASTER || ent->s.weapon == WP_FLECHETTE || ent->s.weapon == WP_ROCKET_LAUNCHER) { if (ent->s.weapon == WP_FLECHETTE && (ent->s.eFlags & EF_ALT_FIRING)) { /* fix: there are rare situations where flechette did explode by timeout AND by impact in the very same frame, then here ent->think was set to G_FreeEntity, so the folowing think did invalidate this entity, BUT it would be reused later in this function for explosion event. This, then, would set ent->freeAfterEvent to qtrue, so event later, when reusing this entity by using G_InitEntity(), it would have this freeAfterEvent set AND this would in case of dropped item erase it from game immeadiately. THIS for example caused very rare flag dissappearing bug. */ if (ent->think == WP_flechette_alt_blow) ent->think(ent); } else { G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, /*ent->s.origin*/ent->r.currentOrigin, ent->damage, DAMAGE_HALF_ABSORB, ent->methodOfDeath); didDmg = qtrue; } } else { G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, /*ent->s.origin*/ent->r.currentOrigin, ent->damage, 0, ent->methodOfDeath); didDmg = qtrue; } if (didDmg && other && other->client) { //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever. class_t npc_class = other->client->NPC_class; // If we are a robot and we aren't currently doing the full body electricity... if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE || npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY ) { // special droid only behaviors if ( other->client->ps.electrifyTime < level.time + 100 ) { // ... do the effect for a split second for some more feedback other->client->ps.electrifyTime = level.time + 450; } //FIXME: throw some sparks off droids,too } } } if ( ent->s.weapon == WP_DEMP2 ) { //a hit with demp2 decloaks people, disables ships if ( other && other->client && other->client->NPC_class == CLASS_VEHICLE ) { //hit a vehicle if ( other->m_pVehicle //valid vehicle ent && other->m_pVehicle->m_pVehicleInfo//valid stats && (other->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER//always affect speeders ||(other->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && ent->classname && Q_stricmp("vehicle_proj", ent->classname ) == 0) )//only vehicle ion weapons affect a fighter in this manner && !FighterIsLanded( other->m_pVehicle , &other->client->ps )//not landed && !(other->spawnflags&2) )//and not suspended { //vehicles hit by "ion cannons" lose control if ( other->client->ps.electrifyTime > level.time ) { //add onto it //FIXME: extern the length of the "out of control" time? other->client->ps.electrifyTime += Q_irand(200,500); if ( other->client->ps.electrifyTime > level.time + 4000 ) { //cap it other->client->ps.electrifyTime = level.time + 4000; } } else { //start it //FIXME: extern the length of the "out of control" time? other->client->ps.electrifyTime = level.time + Q_irand(200,500); } } } else if ( other && other->client && other->client->ps.powerups[PW_CLOAKED] ) { Jedi_Decloak( other ); if ( ent->methodOfDeath == MOD_DEMP2_ALT ) { //direct hit with alt disables cloak forever //permanently disable the saboteur's cloak other->client->cloakToggleTime = Q3_INFINITE; } else { //temp disable other->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 ); } } } } #if _GRAPPLE//_GRAPPLE if (!strcmp(ent->classname, "laserTrap") && ent->s.weapon == WP_BRYAR_PISTOL) { //gentity_t *nent; vec3_t v; /* nent = G_Spawn(qtrue); nent->freeAfterEvent = qtrue; nent->s.weapon = WP_BRYAR_PISTOL;//WP_GRAPPLING_HOOK; -- idk what this is nent->s.saberInFlight = qtrue; nent->s.owner = ent->s.owner; */ ent->enemy = NULL; ent->s.otherEntityNum = -1; ent->s.groundEntityNum = -1; if ( other->s.eType == ET_MOVER || (other->client && !( other->s.eFlags & EF_DEAD ) ) ) { if ( other->client ) { //G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); //Event if (!ent->s.hasLookTarget) { G_PlayEffectID( G_EffectIndex("tusken/hit"), trace->endpos, trace->plane.normal ); } ent->s.hasLookTarget = qtrue; ent->enemy = other; other->s.otherEntityNum = ent->parent->s.number; v[0] = other->r.currentOrigin[0];// + (other->r.mins[0] + other->r.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1];// + (other->r.mins[1] + other->r.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5; SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth ent->s.otherEntityNum = ent->enemy->s.clientNum; other->s.otherEntityNum = ent->parent->s.clientNum; } else { if ( !strcmp(other->classname, "func_rotating") || !strcmp(other->classname, "func_pendulum") ) { Weapon_HookFree(ent); // don't work return; } ent->s.otherEntityNum = other->s.number; ent->s.groundEntityNum = other->s.number; VectorCopy(trace->endpos, v); //G_AddEvent( nent, EV_MISSILE_MISS, 0); //DirToByte( trace->plane.normal ) ); //Event if (!ent->s.hasLookTarget) { G_PlayEffectID( G_EffectIndex("tusken/hitwall"), trace->endpos, trace->plane.normal ); } ent->s.hasLookTarget = qtrue; } } else { VectorCopy(trace->endpos, v); //G_AddEvent( nent, EV_MISSILE_MISS, 0);//DirToByte( trace->plane.normal ) ); //Event if (!ent->s.hasLookTarget) { G_PlayEffectID( G_EffectIndex("tusken/hitwall"), trace->endpos, trace->plane.normal ); } ent->s.hasLookTarget = qtrue; } VectorCopy(trace->plane.normal, ent->s.angles); SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth // change over to a normal entity right at the point of impact //nent->s.eType = ET_GENERAL; ent->s.eType = ET_MISSILE; G_SetOrigin( ent, v ); //G_SetOrigin( nent, v ); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.lastHitLoc); VectorSubtract( ent->r.currentOrigin, ent->parent->client->ps.origin, v ); trap->LinkEntity( (sharedEntity_t *)ent ); //trap->LinkEntity( (sharedEntity_t *)nent ); return; } #endif killProj: // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if ( other->takedamage && other->client && !isKnockedSaber ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) { G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); } else if (ent->s.weapon != G2_MODEL_PART && !isKnockedSaber) { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } if (!isKnockedSaber) { ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; } SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); ent->takedamage = qfalse; // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent, ent->splashMethodOfDeath ) ) { if( !hitClient && g_entities[ent->r.ownerNum].client ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } if (ent->s.weapon == G2_MODEL_PART) { ent->freeAfterEvent = qfalse; //it will free itself } trap->LinkEntity( (sharedEntity_t *)ent ); }
void NPC_BSGM_Attack( void ) { //Don't do anything if we're hurt if ( NPCS.NPC->painDebounceTime > level.time ) { NPC_UpdateAngles( qtrue, qtrue ); return; } #if 0 //FIXME: if killed enemy, use victory anim if ( NPC->enemy && NPC->enemy->health <= 0 && !NPC->enemy->s.number ) {//my enemy is dead if ( NPC->client->ps.torsoAnim == BOTH_STAND2TO1 ) { if ( NPC->client->ps.torsoTimer <= 500 ) { G_AddVoiceEvent( NPC, Q_irand( EV_VICTORY1, EV_VICTORY3 ), 3000 ); NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); NPC->client->ps.legsTimer += 500; NPC->client->ps.torsoTimer += 500; } } else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1START ) { if ( NPC->client->ps.torsoTimer <= 500 ) { NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STARTGESTURE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); NPC->client->ps.legsTimer += 500; NPC->client->ps.torsoTimer += 500; } } else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STARTGESTURE ) { if ( NPC->client->ps.torsoTimer <= 500 ) { NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); NPC->client->ps.legsTimer += 500; NPC->client->ps.torsoTimer += 500; } } else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STOP ) { if ( NPC->client->ps.torsoTimer <= 500 ) { NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); NPC->client->ps.legsTimer = -1; NPC->client->ps.torsoTimer = -1; } } else if ( NPC->wait ) { if ( TIMER_Done( NPC, "gloatTime" ) ) { GM_StartGloat(); } else if ( DistanceHorizontalSquared( NPC->client->renderInfo.eyePoint, NPC->enemy->r.currentOrigin ) > 4096 && (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )//64 squared { NPCInfo->goalEntity = NPC->enemy; GM_Move(); } else {//got there GM_StartGloat(); } } NPC_FaceEnemy( qtrue ); NPC_UpdateAngles( qtrue, qtrue ); return; } #endif //If we don't have an enemy, just idle if ( NPC_CheckEnemyExt(qfalse) == qfalse || !NPCS.NPC->enemy ) { NPCS.NPC->enemy = NULL; NPC_BSGM_Patrol(); return; } enemyLOS4 = enemyCS4 = qfalse; move4 = qtrue; faceEnemy4 = qfalse; shoot4 = qfalse; hitAlly4 = qfalse; VectorClear( impactPos4 ); enemyDist4 = DistanceSquared( NPCS.NPC->r.currentOrigin, NPCS.NPC->enemy->r.currentOrigin ); //if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 || // NPC->client->ps.torsoAnim == BOTH_ATTACK5 ) if (0) { shoot4 = qfalse; if ( TIMER_Done( NPCS.NPC, "smackTime" ) && !NPCS.NPCInfo->blockedDebounceTime ) {//time to smack //recheck enemyDist4 and InFront if ( enemyDist4 < MELEE_DIST_SQUARED && InFront( NPCS.NPC->enemy->r.currentOrigin, NPCS.NPC->r.currentOrigin, NPCS.NPC->client->ps.viewangles, 0.3f ) ) { vec3_t smackDir; VectorSubtract( NPCS.NPC->enemy->r.currentOrigin, NPCS.NPC->r.currentOrigin, smackDir ); smackDir[2] += 30; VectorNormalize( smackDir ); //hurt them G_Sound( NPCS.NPC->enemy, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/skewerhit.wav" ) ); G_Damage( NPCS.NPC->enemy, NPCS.NPC, NPCS.NPC, smackDir, NPCS.NPC->r.currentOrigin, (g_npcspskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); if ( NPCS.NPC->client->ps.torsoAnim == BOTH_ATTACK4 ) {//smackdown int knockAnim = BOTH_KNOCKDOWN1; if ( BG_CrouchAnim( NPCS.NPC->enemy->client->ps.legsAnim ) ) {//knockdown from crouch knockAnim = BOTH_KNOCKDOWN4; } //throw them smackDir[2] = 1; VectorNormalize( smackDir ); G_Throw( NPCS.NPC->enemy, smackDir, 50 ); NPC_SetAnim( NPCS.NPC->enemy, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } else {//uppercut //throw them G_Throw( NPCS.NPC->enemy, smackDir, 100 ); //make them backflip NPC_SetAnim( NPCS.NPC->enemy, SETANIM_BOTH, BOTH_KNOCKDOWN5, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } //done with the damage NPCS.NPCInfo->blockedDebounceTime = 1; } } } else if ( NPCS.NPC->lockCount ) //already shooting laser {//sometimes use the laser beam attack, but only after he's taken down our generator shoot4 = qfalse; if ( NPCS.NPC->lockCount == 1 ) {//charging up if ( TIMER_Done( NPCS.NPC, "beamDelay" ) ) {//time to start the beam int laserAnim; //if ( Q_irand( 0, 1 ) ) if (1) { laserAnim = BOTH_ATTACK2; } /* else { laserAnim = BOTH_ATTACK7; } */ NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, laserAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); TIMER_Set( NPCS.NPC, "attackDelay", NPCS.NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) ); //turn on beam effect NPCS.NPC->lockCount = 2; G_PlayEffectID( G_EffectIndex("galak/trace_beam"), NPCS.NPC->r.currentOrigin, vec3_origin ); NPCS.NPC->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" ); if ( !NPCS.NPCInfo->coverTarg ) {//for moving looping sound at end of trace NPCS.NPCInfo->coverTarg = G_Spawn(); if ( NPCS.NPCInfo->coverTarg ) { G_SetOrigin( NPCS.NPCInfo->coverTarg, NPCS.NPC->client->renderInfo.muzzlePoint ); NPCS.NPCInfo->coverTarg->r.svFlags |= SVF_BROADCAST; NPCS.NPCInfo->coverTarg->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" ); } } } } else {//in the actual attack now if ( NPCS.NPC->client->ps.torsoTimer <= 0 ) {//attack done! NPCS.NPC->lockCount = 0; G_FreeEntity( NPCS.NPCInfo->coverTarg ); NPCS.NPC->s.loopSound = 0; #if 0 NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_DROPWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); #endif TIMER_Set( NPCS.NPC, "attackDelay", NPCS.NPC->client->ps.torsoTimer ); } else {//attack still going //do the trace and damage trace_t trace; vec3_t end, mins={-3,-3,-3}, maxs={3,3,3}; VectorMA( NPCS.NPC->client->renderInfo.muzzlePoint, 1024, NPCS.NPC->client->renderInfo.muzzleDir, end ); trap->Trace( &trace, NPCS.NPC->client->renderInfo.muzzlePoint, mins, maxs, end, NPCS.NPC->s.number, MASK_SHOT, qfalse, 0, 0 ); if ( trace.allsolid || trace.startsolid ) {//oops, in a wall if ( NPCS.NPCInfo->coverTarg ) { G_SetOrigin( NPCS.NPCInfo->coverTarg, NPCS.NPC->client->renderInfo.muzzlePoint ); } } else {//clear if ( trace.fraction < 1.0f ) {//hit something gentity_t *traceEnt = &g_entities[trace.entityNum]; if ( traceEnt && traceEnt->takedamage ) {//damage it G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) ); G_Damage( traceEnt, NPCS.NPC, NPCS.NPC, NPCS.NPC->client->renderInfo.muzzleDir, trace.endpos, 10, 0, MOD_UNKNOWN ); } } if ( NPCS.NPCInfo->coverTarg ) { G_SetOrigin( NPCS.NPCInfo->coverTarg, trace.endpos ); } if ( !Q_irand( 0, 5 ) ) { G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) ); } } } } } else {//Okay, we're not in a special attack, see if we should switch weapons or start a special attack /* if ( NPC->s.weapon == WP_REPEATER && !(NPCInfo->scriptFlags & SCF_ALT_FIRE)//using rapid-fire && NPC->enemy->s.weapon == WP_SABER //enemy using saber && NPC->client && (NPC->client->ps.saberEventFlags&SEF_DEFLECTED) && !Q_irand( 0, 50 ) ) {//he's deflecting my shots, switch to the laser or the lob fire for a while TIMER_Set( NPC, "noRapid", Q_irand( 2000, 6000 ) ); NPCInfo->scriptFlags |= SCF_ALT_FIRE; NPC->alt_fire = qtrue; if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH && (Q_irand( 0, 1 )||enemyDist4 < MAX_LOB_DIST_SQUARED) ) {//shield down, use laser NPC_GM_StartLaser(); } } else*/ if (// !NPC->client->ps.powerups[PW_GALAK_SHIELD] 1 //rwwFIXMEFIXME: just act like the shield is down til the effects and stuff are done && enemyDist4 < MELEE_DIST_SQUARED && InFront( NPCS.NPC->enemy->r.currentOrigin, NPCS.NPC->r.currentOrigin, NPCS.NPC->client->ps.viewangles, 0.3f ) && NPCS.NPC->enemy->localAnimIndex <= 1 )//within 80 and in front {//our shield is down, and enemy within 80, if very close, use melee attack to slap away if ( TIMER_Done( NPCS.NPC, "attackDelay" ) ) { //animate me int swingAnim = BOTH_ATTACK1; #if 0 if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH ) {//generator down, use random melee swingAnim = Q_irand( BOTH_ATTACK4, BOTH_ATTACK5 );//smackdown or uppercut } else {//always knock-away swingAnim = BOTH_ATTACK5;//uppercut } #endif //FIXME: swing sound NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, swingAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); TIMER_Set( NPCS.NPC, "attackDelay", NPCS.NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) ); //delay the hurt until the proper point in the anim TIMER_Set( NPCS.NPC, "smackTime", 600 ); NPCS.NPCInfo->blockedDebounceTime = 0; //FIXME: say something? } } else if ( !NPCS.NPC->lockCount && NPCS.NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH && TIMER_Done( NPCS.NPC, "attackDelay" ) && InFront( NPCS.NPC->enemy->r.currentOrigin, NPCS.NPC->r.currentOrigin, NPCS.NPC->client->ps.viewangles, 0.3f ) && ((!Q_irand( 0, 10*(2-g_npcspskill.integer))&& enemyDist4 > MIN_LOB_DIST_SQUARED&& enemyDist4 < MAX_LOB_DIST_SQUARED) ||(!TIMER_Done( NPCS.NPC, "noLob" )&&!TIMER_Done( NPCS.NPC, "noRapid" ))) && NPCS.NPC->enemy->s.weapon != WP_TURRET ) {//sometimes use the laser beam attack, but only after he's taken down our generator shoot4 = qfalse; NPC_GM_StartLaser(); } else if ( enemyDist4 < MIN_LOB_DIST_SQUARED && (NPCS.NPC->enemy->s.weapon != WP_TURRET || Q_stricmp( "PAS", NPCS.NPC->enemy->classname )) && TIMER_Done( NPCS.NPC, "noRapid" ) )//256 {//enemy within 256 if ( (NPCS.NPC->client->ps.weapon == WP_REPEATER) && (NPCS.NPCInfo->scriptFlags & SCF_ALT_FIRE) ) {//shooting an explosive, but enemy too close, switch to primary fire NPCS.NPCInfo->scriptFlags &= ~SCF_ALT_FIRE; NPCS.NPC->alt_fire = qfalse; //FIXME: use weap raise & lower anims NPC_ChangeWeapon( WP_REPEATER ); } } else if ( (enemyDist4 > MAX_LOB_DIST_SQUARED || (NPCS.NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPCS.NPC->enemy->classname ))) && TIMER_Done( NPCS.NPC, "noLob" ) )//448 {//enemy more than 448 away and we are ready to try lob fire again if ( (NPCS.NPC->client->ps.weapon == WP_REPEATER) && !(NPCS.NPCInfo->scriptFlags & SCF_ALT_FIRE) ) {//enemy far enough away to use lobby explosives NPCS.NPCInfo->scriptFlags |= SCF_ALT_FIRE; NPCS.NPC->alt_fire = qtrue; //FIXME: use weap raise & lower anims NPC_ChangeWeapon( WP_REPEATER ); } } } //can we see our target? if ( NPC_ClearLOS4( NPCS.NPC->enemy ) ) { NPCS.NPCInfo->enemyLastSeenTime = level.time;//used here for aim debouncing, not always a clear LOS enemyLOS4 = qtrue; if ( NPCS.NPC->client->ps.weapon == WP_NONE ) { enemyCS4 = qfalse;//not true, but should stop us from firing NPC_AimAdjust( -1 );//adjust aim worse longer we have no weapon } else {//can we shoot our target? if ( ((NPCS.NPC->client->ps.weapon == WP_REPEATER && (NPCS.NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist4 < MIN_LOB_DIST_SQUARED )//256 { enemyCS4 = qfalse;//not true, but should stop us from firing hitAlly4 = qtrue;//us! //FIXME: if too close, run away! } else { int hit = NPC_ShotEntity( NPCS.NPC->enemy, impactPos4 ); gentity_t *hitEnt = &g_entities[hit]; if ( hit == NPCS.NPC->enemy->s.number || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPCS.NPC->client->enemyTeam ) || ( hitEnt && hitEnt->takedamage ) ) {//can hit enemy or will hit glass or other breakable, so shoot anyway enemyCS4 = qtrue; NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy VectorCopy( NPCS.NPC->enemy->r.currentOrigin, NPCS.NPCInfo->enemyLastSeenLocation ); } else {//Hmm, have to get around this bastard NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPCS.NPC->client->playerTeam ) {//would hit an ally, don't fire!!! hitAlly4 = qtrue; } else {//Check and see where our shot *would* hit... if it's not close to the enemy (within 256?), then don't fire } } } } } else if ( trap->InPVS( NPCS.NPC->enemy->r.currentOrigin, NPCS.NPC->r.currentOrigin ) ) { int hit; gentity_t *hitEnt; if ( TIMER_Done( NPCS.NPC, "talkDebounce" ) && !Q_irand( 0, 10 ) ) { if ( NPCS.NPCInfo->enemyCheckDebounceTime < 8 ) { int speech = -1; switch( NPCS.NPCInfo->enemyCheckDebounceTime ) { case 0: case 1: case 2: speech = EV_CHASE1 + NPCS.NPCInfo->enemyCheckDebounceTime; break; case 3: case 4: case 5: speech = EV_COVER1 + NPCS.NPCInfo->enemyCheckDebounceTime-3; break; case 6: case 7: speech = EV_ESCAPING1 + NPCS.NPCInfo->enemyCheckDebounceTime-6; break; } NPCS.NPCInfo->enemyCheckDebounceTime++; if ( speech != -1 ) { G_AddVoiceEvent( NPCS.NPC, speech, Q_irand( 3000, 5000 ) ); TIMER_Set( NPCS.NPC, "talkDebounce", Q_irand( 5000, 7000 ) ); } } } NPCS.NPCInfo->enemyLastSeenTime = level.time; hit = NPC_ShotEntity( NPCS.NPC->enemy, impactPos4 ); hitEnt = &g_entities[hit]; if ( hit == NPCS.NPC->enemy->s.number || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPCS.NPC->client->enemyTeam ) || ( hitEnt && hitEnt->takedamage ) ) {//can hit enemy or will hit glass or other breakable, so shoot anyway enemyCS4 = qtrue; } else { faceEnemy4 = qtrue; NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy } } if ( enemyLOS4 ) { faceEnemy4 = qtrue; } else { if ( !NPCS.NPCInfo->goalEntity ) { NPCS.NPCInfo->goalEntity = NPCS.NPC->enemy; } if ( NPCS.NPCInfo->goalEntity == NPCS.NPC->enemy ) {//for now, always chase the enemy move4 = qtrue; } } if ( enemyCS4 ) { shoot4 = qtrue; //NPCInfo->enemyCheckDebounceTime = level.time;//actually used here as a last actual LOS } else { if ( !NPCS.NPCInfo->goalEntity ) { NPCS.NPCInfo->goalEntity = NPCS.NPC->enemy; } if ( NPCS.NPCInfo->goalEntity == NPCS.NPC->enemy ) {//for now, always chase the enemy move4 = qtrue; } } //Check for movement to take care of GM_CheckMoveState(); //See if we should override shooting decision with any special considerations GM_CheckFireState(); if ( NPCS.NPC->client->ps.weapon == WP_REPEATER && (NPCS.NPCInfo->scriptFlags&SCF_ALT_FIRE) && shoot4 && TIMER_Done( NPCS.NPC, "attackDelay" ) ) { vec3_t muzzle; vec3_t angles; vec3_t target; vec3_t velocity = {0,0,0}; vec3_t mins = {-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE}, maxs = {REPEATER_ALT_SIZE,REPEATER_ALT_SIZE,REPEATER_ALT_SIZE}; qboolean clearshot; CalcEntitySpot( NPCS.NPC, SPOT_WEAPON, muzzle ); VectorCopy( NPCS.NPC->enemy->r.currentOrigin, target ); target[0] += flrand( -5, 5 )+(Q_flrand(-1.0f, 1.0f)*(6-NPCS.NPCInfo->currentAim)*2); target[1] += flrand( -5, 5 )+(Q_flrand(-1.0f, 1.0f)*(6-NPCS.NPCInfo->currentAim)*2); target[2] += flrand( -5, 5 )+(Q_flrand(-1.0f, 1.0f)*(6-NPCS.NPCInfo->currentAim)*2); //Find the desired angles clearshot = WP_LobFire( NPCS.NPC, muzzle, target, mins, maxs, MASK_SHOT|CONTENTS_LIGHTSABER, velocity, qtrue, NPCS.NPC->s.number, NPCS.NPC->enemy->s.number, 300, 1100, 1500, qtrue ); if ( VectorCompare( vec3_origin, velocity ) || (!clearshot&&enemyLOS4&&enemyCS4) ) {//no clear lob shot and no lob shot that will hit something breakable if ( enemyLOS4 && enemyCS4 && TIMER_Done( NPCS.NPC, "noRapid" ) ) {//have a clear straight shot, so switch to primary NPCS.NPCInfo->scriptFlags &= ~SCF_ALT_FIRE; NPCS.NPC->alt_fire = qfalse; NPC_ChangeWeapon( WP_REPEATER ); //keep this weap for a bit TIMER_Set( NPCS.NPC, "noLob", Q_irand( 500, 1000 ) ); } else { shoot4 = qfalse; } } else { vectoangles( velocity, angles ); NPCS.NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); NPCS.NPCInfo->desiredPitch = AngleNormalize360( angles[PITCH] ); VectorCopy( velocity, NPCS.NPC->client->hiddenDir ); NPCS.NPC->client->hiddenDist = VectorNormalize ( NPCS.NPC->client->hiddenDir ); } } else if ( faceEnemy4 ) {//face the enemy NPC_FaceEnemy( qtrue ); } if ( !TIMER_Done( NPCS.NPC, "standTime" ) ) { move4 = qfalse; } if ( !(NPCS.NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) ) {//not supposed to chase my enemies if ( NPCS.NPCInfo->goalEntity == NPCS.NPC->enemy ) {//goal is my entity, so don't move move4 = qfalse; } } if ( move4 && !NPCS.NPC->lockCount ) {//move toward goal if ( NPCS.NPCInfo->goalEntity /*&& NPC->client->ps.legsAnim != BOTH_ALERT1 && NPC->client->ps.legsAnim != BOTH_ATTACK2 && NPC->client->ps.legsAnim != BOTH_ATTACK4 && NPC->client->ps.legsAnim != BOTH_ATTACK5 && NPC->client->ps.legsAnim != BOTH_ATTACK7*/ ) { move4 = GM_Move(); } else { move4 = qfalse; } } if ( !TIMER_Done( NPCS.NPC, "flee" ) ) {//running away faceEnemy4 = qfalse; } //FIXME: check scf_face_move_dir here? if ( !faceEnemy4 ) {//we want to face in the dir we're running if ( !move4 ) {//if we haven't moved, we should look in the direction we last looked? VectorCopy( NPCS.NPC->client->ps.viewangles, NPCS.NPCInfo->lastPathAngles ); } if ( move4 ) {//don't run away and shoot NPCS.NPCInfo->desiredYaw = NPCS.NPCInfo->lastPathAngles[YAW]; NPCS.NPCInfo->desiredPitch = 0; shoot4 = qfalse; } } NPC_UpdateAngles( qtrue, qtrue ); if ( NPCS.NPCInfo->scriptFlags & SCF_DONT_FIRE ) { shoot4 = qfalse; } if ( NPCS.NPC->enemy && NPCS.NPC->enemy->enemy ) { if ( NPCS.NPC->enemy->s.weapon == WP_SABER && NPCS.NPC->enemy->enemy->s.weapon == WP_SABER ) {//don't shoot at an enemy jedi who is fighting another jedi, for fear of injuring one or causing rogue blaster deflections (a la Obi Wan/Vader duel at end of ANH) shoot4 = qfalse; } } //FIXME: don't shoot right away! if ( shoot4 ) {//try to shoot if it's time if ( TIMER_Done( NPCS.NPC, "attackDelay" ) ) { if( !(NPCS.NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here { WeaponThink( qtrue ); } } } //also: if ( NPCS.NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPCS.NPC->enemy->classname ) ) {//crush turrets if ( G_BoundsOverlap( NPCS.NPC->r.absmin, NPCS.NPC->r.absmax, NPCS.NPC->enemy->r.absmin, NPCS.NPC->enemy->r.absmax ) ) {//have to do this test because placed turrets are not solid to NPCs (so they don't obstruct navigation) //if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 ) if (0) { #ifdef BASE_COMPAT NPCS.NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME; #endif G_Damage( NPCS.NPC->enemy, NPCS.NPC, NPCS.NPC, NULL, NPCS.NPC->r.currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); } else { G_Damage( NPCS.NPC->enemy, NPCS.NPC, NPCS.NPC, NULL, NPCS.NPC->r.currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); } } } else if ( NPCS.NPCInfo->touchedByPlayer != NULL && NPCS.NPCInfo->touchedByPlayer == NPCS.NPC->enemy ) {//touched enemy //if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 ) if (0) {//zap him! vec3_t smackDir; //animate me #if 0 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK6, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); #endif TIMER_Set( NPCS.NPC, "attackDelay", NPCS.NPC->client->ps.torsoTimer ); TIMER_Set( NPCS.NPC, "standTime", NPCS.NPC->client->ps.legsTimer ); //FIXME: debounce this? NPCS.NPCInfo->touchedByPlayer = NULL; //FIXME: some shield effect? #ifdef BASE_COMPAT NPCS.NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME; #endif VectorSubtract( NPCS.NPC->enemy->r.currentOrigin, NPCS.NPC->r.currentOrigin, smackDir ); smackDir[2] += 30; VectorNormalize( smackDir ); G_Damage( NPCS.NPC->enemy, NPCS.NPC, NPCS.NPC, smackDir, NPCS.NPC->r.currentOrigin, (g_npcspskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); //throw them G_Throw( NPCS.NPC->enemy, smackDir, 100 ); //NPC->enemy->s.powerups |= ( 1 << PW_SHOCKED ); if ( NPCS.NPC->enemy->client ) { // NPC->enemy->client->ps.powerups[PW_SHOCKED] = level.time + 1000; NPCS.NPC->enemy->client->ps.electrifyTime = level.time + 1000; } //stop any attacks NPCS.ucmd.buttons = 0; } } if ( NPCS.NPCInfo->movementSpeech < 3 && NPCS.NPCInfo->blockedSpeechDebounceTime <= level.time ) { if ( NPCS.NPC->enemy && NPCS.NPC->enemy->health > 0 && NPCS.NPC->enemy->painDebounceTime > level.time ) { if ( NPCS.NPC->enemy->health < 50 && NPCS.NPCInfo->movementSpeech == 2 ) { G_AddVoiceEvent( NPCS.NPC, EV_ANGER2, Q_irand( 2000, 4000 ) ); NPCS.NPCInfo->movementSpeech = 3; } else if ( NPCS.NPC->enemy->health < 75 && NPCS.NPCInfo->movementSpeech == 1 ) { G_AddVoiceEvent( NPCS.NPC, EV_ANGER1, Q_irand( 2000, 4000 ) ); NPCS.NPCInfo->movementSpeech = 2; } else if ( NPCS.NPC->enemy->health < 100 && NPCS.NPCInfo->movementSpeech == 0 ) { G_AddVoiceEvent( NPCS.NPC, EV_ANGER3, Q_irand( 2000, 4000 ) ); NPCS.NPCInfo->movementSpeech = 1; } } } }
static void Howler_Attack( float enemyDist, qboolean howl ) { int dmg = (NPCInfo->localState==LSTATE_BERZERK)?5:2; vec3_t boltOrg; vec3_t fwd; if ( !TIMER_Exists( NPC, "attacking" )) { int attackAnim = BOTH_GESTURE1; // Going to do an attack if ( NPC->enemy && NPC->enemy->client && PM_InKnockDown( &NPC->enemy->client->ps ) && enemyDist <= MIN_DISTANCE ) { attackAnim = BOTH_ATTACK2; } else if ( !Q_irand( 0, 4 ) || howl ) {//howl attack //G_SoundOnEnt( NPC, CHAN_VOICE, "sound/chars/howler/howl.mp3" ); } else if ( enemyDist > MIN_DISTANCE && Q_irand( 0, 1 ) ) {//lunge attack //jump foward vec3_t fwd, yawAng; VectorSet( yawAng, 0, NPC->client->ps.viewangles[YAW], 0 ); AngleVectors( yawAng, fwd, NULL, NULL ); VectorScale( fwd, (enemyDist*3.0f), NPC->client->ps.velocity ); NPC->client->ps.velocity[2] = 200; NPC->client->ps.groundEntityNum = ENTITYNUM_NONE; attackAnim = BOTH_ATTACK1; } else {//tongue attack attackAnim = BOTH_ATTACK2; } NPC_SetAnim( NPC, SETANIM_BOTH, attackAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART ); if ( NPCInfo->localState == LSTATE_BERZERK ) {//attack again right away TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer ); } else { TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer + Q_irand( 0, 1500 ) );//FIXME: base on skill TIMER_Set( NPC, "standing", -level.time ); TIMER_Set( NPC, "walking", -level.time ); TIMER_Set( NPC, "running", NPC->client->ps.legsTimer + 5000 ); } TIMER_Set( NPC, "attack_dmg", 200 ); // level two damage } // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks switch ( NPC->client->ps.legsAnim ) { case BOTH_ATTACK1: case BOTH_MELEE1: if ( NPC->client->ps.legsTimer > 650//more than 13 frames left && BG_AnimLength( NPC->localAnimIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsTimer >= 800 )//at least 16 frames into anim { Howler_TryDamage( dmg, qfalse, qfalse ); } break; case BOTH_ATTACK2: case BOTH_MELEE2: if ( NPC->client->ps.legsTimer > 350//more than 7 frames left && BG_AnimLength( NPC->localAnimIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsTimer >= 550 )//at least 11 frames into anim { Howler_TryDamage( dmg, qtrue, qfalse ); } break; case BOTH_GESTURE1: { if ( NPC->client->ps.legsTimer > 1800//more than 36 frames left && BG_AnimLength( NPC->localAnimIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsTimer >= 950 )//at least 19 frames into anim { Howler_Howl(); if ( !NPC->count ) { //RAFIXME - this probably won't work correctly. G_GetBoltPosition(NPC, NPC->NPC->genericBolt1, boltOrg, 0); AngleVectors( NPC->client->ps.viewangles, fwd, NULL, NULL ); G_PlayEffectID( G_EffectIndex( "howler/sonic" ), boltOrg, fwd); //G_PlayEffect( G_EffectIndex( "howler/sonic" ), NPC->ghoul2, NPC->NPC->genericBolt1, NPC->s.number, NPC->currentOrigin, 4750, qtrue ); G_SoundOnEnt( NPC, CHAN_VOICE, "sound/chars/howler/howl.mp3" ); NPC->count = 1; } } } break; default: //anims seem to get reset after a load, so just stop attacking and it will restart as needed. TIMER_Remove( NPC, "attacking" ); break; } // Just using this to remove the attacking flag at the right time TIMER_Done2( NPC, "attacking", qtrue ); }
//---------------------------------------------------------------- static void turretG2_fire ( gentity_t *ent, vec3_t start, vec3_t dir ) //---------------------------------------------------------------- { vec3_t org, ang; gentity_t *bolt; if ( (trap_PointContents( start, ent->s.number )&MASK_SHOT) ) { return; } VectorMA( start, -START_DIS, dir, org ); // dumb.... if ( ent->random ) { vectoangles( dir, ang ); ang[PITCH] += flrand( -ent->random, ent->random ); ang[YAW] += flrand( -ent->random, ent->random ); AngleVectors( ang, dir, NULL, NULL ); } vectoangles(dir, ang); if ( (ent->spawnflags&SPF_TURRETG2_TURBO) ) { //muzzle flash G_PlayEffectID( ent->genericValue13, org, ang ); WP_FireTurboLaserMissile( ent, start, dir ); if ( ent->alt_fire ) { TurboLaser_SetBoneAnim( ent, 2, 3 ); } else { TurboLaser_SetBoneAnim( ent, 0, 1 ); } } else { G_PlayEffectID( G_EffectIndex("blaster/muzzle_flash"), org, ang ); bolt = G_Spawn(); bolt->classname = "turret_proj"; bolt->nextthink = level.time + 10000; bolt->think = G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_BLASTER; bolt->r.ownerNum = ent->s.number; bolt->damage = ent->damage; bolt->alliedTeam = ent->alliedTeam; bolt->teamnodmg = ent->teamnodmg; bolt->dflags = (DAMAGE_NO_KNOCKBACK|DAMAGE_HEAVY_WEAP_CLASS); // Don't push them around, or else we are constantly re-aiming bolt->splashDamage = ent->splashDamage; bolt->splashRadius = ent->splashDamage; bolt->methodOfDeath = MOD_TARGET_LASER;//MOD_ENERGY; bolt->splashMethodOfDeath = MOD_TARGET_LASER;//MOD_ENERGY; bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; //bolt->trigger_formation = qfalse; // don't draw tail on first frame VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 ); VectorScale( bolt->r.maxs, -1, bolt->r.mins ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, ent->mass, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin); } }
/* ------------------------- Sentry_Fire ------------------------- */ void Sentry_Fire( void ) { vector3 muzzle; static vector3 forward, vright, up; gentity_t *missile; mdxaBone_t boltMatrix; int bolt; int which; NPC->flags &= ~FL_SHIELDED; if ( NPCInfo->localState == LSTATE_POWERING_UP ) { if ( TIMER_Done( NPC, "powerup" ) ) { NPCInfo->localState = LSTATE_ATTACKING; NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); } else { // can't do anything right now return; } } else if ( NPCInfo->localState == LSTATE_ACTIVE ) { NPCInfo->localState = LSTATE_POWERING_UP; G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/sentry/misc/sentry_shield_open" ) ); NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_POWERUP1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TIMER_Set( NPC, "powerup", 250 ); return; } else if ( NPCInfo->localState != LSTATE_ATTACKING ) { // bad because we are uninitialized NPCInfo->localState = LSTATE_ACTIVE; return; } // Which muzzle to fire from? which = NPCInfo->burstCount % 3; switch ( which ) { case 0: bolt = trap->G2API_AddBolt( NPC->ghoul2, 0, "*flash1" ); break; case 1: bolt = trap->G2API_AddBolt( NPC->ghoul2, 0, "*flash2" ); break; case 2: default: bolt = trap->G2API_AddBolt( NPC->ghoul2, 0, "*flash03" ); } trap->G2API_GetBoltMatrix( NPC->ghoul2, 0, bolt, &boltMatrix, &NPC->r.currentAngles, &NPC->r.currentOrigin, level.time, NULL, &NPC->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, &muzzle ); AngleVectors( &NPC->r.currentAngles, &forward, &vright, &up ); // G_Sound( NPC, G_SoundIndex("sound/chars/sentry/misc/shoot.wav")); G_PlayEffectID( G_EffectIndex( "bryar/muzzle_flash" ), &muzzle, &forward ); missile = CreateMissile( &muzzle, &forward, 1600, 10000, NPC, qfalse ); missile->classname = "bryar_proj"; missile->s.weapon = WP_BRYAR_PISTOL; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BRYAR_PISTOL; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; NPCInfo->burstCount++; NPC->attackDebounceTime = level.time + 50; missile->damage = 5; // now scale for difficulty if ( g_spSkill.integer == 0 ) { NPC->attackDebounceTime += 200; missile->damage = 1; } else if ( g_spSkill.integer == 1 ) { NPC->attackDebounceTime += 100; missile->damage = 3; } }
void GM_Dying( gentity_t *self ) { if ( level.time - self->s.time < 4000 ) {//FIXME: need a real effect //self->s.powerups |= ( 1 << PW_SHOCKED ); //self->client->ps.powerups[PW_SHOCKED] = level.time + 1000; self->client->ps.electrifyTime = level.time + 1000; if ( TIMER_Done( self, "dyingExplosion" ) ) { int newBolt; switch ( Q_irand( 1, 14 ) ) { // Find place to generate explosion case 1: if (!trap->G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "r_hand" )) {//r_hand still there GM_CreateExplosion( self, trap->G2API_AddBolt(self->ghoul2, 0, "*flasha"), qtrue ); NPC_SetSurfaceOnOff( self, "r_hand", TURN_OFF ); } else if (!trap->G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "r_arm_middle" )) {//r_arm_middle still there newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*r_arm_elbow" ); NPC_SetSurfaceOnOff( self, "r_arm_middle", TURN_OFF ); } break; case 2: //FIXME: do only once? if (!trap->G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_hand" )) {//l_hand still there GM_CreateExplosion( self, trap->G2API_AddBolt(self->ghoul2, 0, "*flashc"), qfalse ); NPC_SetSurfaceOnOff( self, "l_hand", TURN_OFF ); } else if (!trap->G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm_wrist" )) {//l_arm_wrist still there newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*l_arm_cap_l_hand" ); NPC_SetSurfaceOnOff( self, "l_arm_wrist", TURN_OFF ); } else if (!trap->G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm_middle" )) {//l_arm_middle still there newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*l_arm_cap_l_hand" ); NPC_SetSurfaceOnOff( self, "l_arm_middle", TURN_OFF ); } else if (!trap->G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm_augment" )) {//l_arm_augment still there newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*l_arm_elbow" ); NPC_SetSurfaceOnOff( self, "l_arm_augment", TURN_OFF ); } break; case 3: case 4: newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*hip_fr" ); GM_CreateExplosion( self, newBolt, qfalse ); break; case 5: case 6: newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*shldr_l" ); GM_CreateExplosion( self, newBolt, qfalse ); break; case 7: case 8: newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*uchest_r" ); GM_CreateExplosion( self, newBolt, qfalse ); break; case 9: case 10: GM_CreateExplosion( self, self->client->renderInfo.headBolt, qfalse ); break; case 11: newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*l_leg_knee" ); GM_CreateExplosion( self, newBolt, qtrue ); break; case 12: newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*r_leg_knee" ); GM_CreateExplosion( self, newBolt, qtrue ); break; case 13: newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*l_leg_foot" ); GM_CreateExplosion( self, newBolt, qtrue ); break; case 14: newBolt = trap->G2API_AddBolt( self->ghoul2, 0, "*r_leg_foot" ); GM_CreateExplosion( self, newBolt, qtrue ); break; } TIMER_Set( self, "dyingExplosion", Q_irand( 300, 1100 ) ); } } else {//one final, huge explosion G_PlayEffectID( G_EffectIndex("galak/explode"), self->r.currentOrigin, vec3_origin ); // G_PlayEffect( "small_chunks", self->r.currentOrigin ); // G_PlayEffect( "env/exp_trail_comp", self->r.currentOrigin, self->currentAngles ); self->nextthink = level.time + FRAMETIME; self->think = G_FreeEntity; } }
/* ------------------------- NPC_BSDroid_Pain ------------------------- */ void NPC_Droid_Pain(gentity_t *self, gentity_t *attacker, int damage) { gentity_t *other = attacker; int anim; int mod = gPainMOD; float pain_chance; VectorCopy( self->NPC->lastPathAngles, self->s.angles ); if ( self->client->NPC_class == CLASS_R5D2 ) { pain_chance = NPC_GetPainChance( self, damage ); // Put it in pain if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT || random() < pain_chance ) // Spin around in pain? Demp2 always does this { // Health is between 0-30 or was hit by a DEMP2 so pop his head if ( !self->s.m_iVehicleNum && ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) ) { if (!(self->spawnflags & 2)) // Doesn't have to ALWAYSDIE { if ((self->NPC->localState != LSTATE_SPINNING) && (!trap->G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "head" ))) { NPC_SetSurfaceOnOff( self, "head", TURN_OFF ); if ( self->client->ps.m_iVehicleNum ) { vec3_t up; AngleVectors( self->r.currentAngles, NULL, NULL, up ); G_PlayEffectID( G_EffectIndex("chunks/r5d2head_veh"), self->r.currentOrigin, up ); } else { G_PlayEffectID( G_EffectIndex("small_chunks") , self->r.currentOrigin, vec3_origin ); G_PlayEffectID( G_EffectIndex("chunks/r5d2head"), self->r.currentOrigin, vec3_origin ); } //self->s.powerups |= ( 1 << PW_SHOCKED ); //self->client->ps.powerups[PW_SHOCKED] = level.time + 3000; self->client->ps.electrifyTime = level.time + 3000; TIMER_Set( self, "droidsmoketotal", 5000); TIMER_Set( self, "droidspark", 100); self->NPC->localState = LSTATE_SPINNING; } } } // Just give him normal pain for a little while else { anim = self->client->ps.legsAnim; if ( anim == BOTH_STAND2 ) // On two legs? { anim = BOTH_PAIN1; } else // On three legs { anim = BOTH_PAIN2; } NPC_SetAnim( self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); // Spin around in pain self->NPC->localState = LSTATE_SPINNING; TIMER_Set( self, "roam", Q_irand(1000,2000)); } } } else if (self->client->NPC_class == CLASS_MOUSE) { if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) { self->NPC->localState = LSTATE_SPINNING; //self->s.powerups |= ( 1 << PW_SHOCKED ); //self->client->ps.powerups[PW_SHOCKED] = level.time + 3000; self->client->ps.electrifyTime = level.time + 3000; } else { self->NPC->localState = LSTATE_BACKINGUP; } self->NPC->scriptFlags &= ~SCF_LOOK_FOR_ENEMIES; } else if (self->client->NPC_class == CLASS_R2D2) { pain_chance = NPC_GetPainChance( self, damage ); if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT || random() < pain_chance ) // Spin around in pain? Demp2 always does this { // Health is between 0-30 or was hit by a DEMP2 so pop his head if ( !self->s.m_iVehicleNum && ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) ) { if (!(self->spawnflags & 2)) // Doesn't have to ALWAYSDIE { if ((self->NPC->localState != LSTATE_SPINNING) && (!trap->G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "head" ))) { NPC_SetSurfaceOnOff( self, "head", TURN_OFF ); if ( self->client->ps.m_iVehicleNum ) { vec3_t up; AngleVectors( self->r.currentAngles, NULL, NULL, up ); G_PlayEffectID( G_EffectIndex("chunks/r2d2head_veh"), self->r.currentOrigin, up ); } else { G_PlayEffectID( G_EffectIndex("small_chunks") , self->r.currentOrigin, vec3_origin ); G_PlayEffectID( G_EffectIndex("chunks/r2d2head"), self->r.currentOrigin, vec3_origin ); } //self->s.powerups |= ( 1 << PW_SHOCKED ); //self->client->ps.powerups[PW_SHOCKED] = level.time + 3000; self->client->ps.electrifyTime = level.time + 3000; TIMER_Set( self, "droidsmoketotal", 5000); TIMER_Set( self, "droidspark", 100); self->NPC->localState = LSTATE_SPINNING; } } } // Just give him normal pain for a little while else { anim = self->client->ps.legsAnim; if ( anim == BOTH_STAND2 ) // On two legs? { anim = BOTH_PAIN1; } else // On three legs { anim = BOTH_PAIN2; } NPC_SetAnim( self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); // Spin around in pain self->NPC->localState = LSTATE_SPINNING; TIMER_Set( self, "roam", Q_irand(1000,2000)); } } } else if ( self->client->NPC_class == CLASS_INTERROGATOR && ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) && other ) { vec3_t dir; VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, dir ); VectorNormalize( dir ); VectorMA( self->client->ps.velocity, 550, dir, self->client->ps.velocity ); self->client->ps.velocity[2] -= 127; } NPC_Pain( self, attacker, damage); }
//------------------------------------ qboolean Seeker_Fire( void ) { //[SeekerItemNpc] #if 1 vec3_t dir, enemy_org, muzzle; gentity_t *missile; trace_t tr; int randomnum = irand(1,5); if (randomnum == 1) { CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org ); } else if (randomnum == 2) { CalcEntitySpot( NPC->enemy,SPOT_WEAPON,enemy_org); } else if (randomnum == 3) { CalcEntitySpot(NPC->enemy,SPOT_ORIGIN,enemy_org); } else if (randomnum == 4) { CalcEntitySpot(NPC->enemy,SPOT_CHEST,enemy_org); } else { CalcEntitySpot(NPC->enemy,SPOT_LEGS,enemy_org); } enemy_org[0]+=irand(2,15); enemy_org[1]+=irand(2,15); //calculate everything based on our model offset VectorCopy(NPC->r.currentOrigin, muzzle); //correct for our model offset muzzle[2] -= 22; muzzle[2] -= randomnum; VectorSubtract( enemy_org, NPC->r.currentOrigin, dir ); VectorNormalize( dir ); // move a bit forward in the direction we shall shoot in so that the bolt doesn't poke out the other side of the seeker VectorMA( muzzle, 15, dir, muzzle ); trap_Trace(&tr, muzzle, NULL, NULL, enemy_org, NPC->s.number, MASK_PLAYERSOLID); //tr.fraction wont be 1.0f, since we hit our enemy. if(tr.entityNum != NPC->enemy->s.number) //if(tr.fraction >= 1.0f) //we cant reach our target return qfalse; //our player should get the kill, if any //FIXME: we have a special case here. we want the kill to be given to the activator, BUT we want the missile to NOT hurt the player //attempting to leave missile->parent == NPC, but missile->r.ownerNum to NPC->activator->s.number, no idea if it will work, just doing a guess. //if(NPC->activator) // missile = CreateMissile( muzzle, dir, 1000, 10000, NPC->activator, qfalse ); //else missile = CreateMissile( muzzle, dir, 1000, 10000, NPC, qfalse ); //missile = CreateMissile( muzzle, dir, 1000, 10000, NPC, qfalse ); G_PlayEffectID( G_EffectIndex("blaster/muzzle_flash"), muzzle, dir ); missile->classname = "blaster"; missile->s.weapon = WP_BLASTER; missile->damage = NPC->damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; //[SeekerItemNPC] //seeker items have different MOD if(NPC->client->leader //has leader && NPC->client->leader->client //leader is a client && NPC->client->leader->client->remote == NPC) //has us as their remote. { //yep, this is a player's seeker, use different MOD missile->methodOfDeath = MOD_SEEKER; } else { missile->methodOfDeath = MOD_BLASTER; } //missile->methodOfDeath = MOD_BLASTER; //[/SeekerItemNPC] missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; //[CoOp] /* Not in SP version of code. if ( NPC->r.ownerNum < ENTITYNUM_NONE ) { missile->r.ownerNum = NPC->r.ownerNum; } */ //[/CoOp] ////no, it isnt in SP version, but will it let the player get the kill but not let the seeker shoot itself? //if(NPC->activator) // missile->r.ownerNum = NPC->activator->s.number; //according to the old q3 source, if the missile has the activators ownerNum and the seeker also has that ownerNum, //the seeker shouldnt shoot itself. //wait... the silly thing wasnt shooting itself, but was out of ammo (and self destructed)! return qtrue; #else vec3_t dir, enemy_org, muzzle; gentity_t *missile; CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org ); VectorSubtract( enemy_org, NPC->r.currentOrigin, dir ); VectorNormalize( dir ); // move a bit forward in the direction we shall shoot in so that the bolt doesn't poke out the other side of the seeker VectorMA( NPC->r.currentOrigin, 15, dir, muzzle ); missile = CreateMissile( muzzle, dir, 1000, 10000, NPC, qfalse ); G_PlayEffectID( G_EffectIndex("blaster/muzzle_flash"), NPC->r.currentOrigin, dir ); missile->classname = "blaster"; missile->s.weapon = WP_BLASTER; missile->damage = 5; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BLASTER; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; //[CoOp] /* Not in SP version of code. if ( NPC->r.ownerNum < ENTITYNUM_NONE ) { missile->r.ownerNum = NPC->r.ownerNum; } */ //[/CoOp] #endif }