void CCarrier::SpawnMonsters () { vec3f offset (105, 0, -58); // real distance needed is (sqrt (56*56*2) + sqrt(16*16*2)) or 101.8 anglef angles = Entity->State.GetAngles().ToVectors (); vec3f startpoint; G_ProjectSource (Entity->State.GetOrigin(), offset, angles, startpoint); // the +0.1 is because level.time is sometimes a little low FrameNumber mytime = ((Level.Frame + 1 - FrameCalc) / 5); vec3f spawnpoint; if (FindSpawnPoint (startpoint, flyer_mins, flyer_maxs, spawnpoint, 32)) { CMonsterEntity *ent = NULL; // the second flier should be a kamikaze flyer if (mytime != 2) ent = CreateMonster (spawnpoint, Entity->State.GetAngles(), "monster_flyer"); else ent = CreateMonster (spawnpoint, Entity->State.GetAngles(), "monster_kamikaze"); if (!ent) return; Entity->PlaySound (CHAN_BODY, Sounds[SOUND_SPAWN]); MonsterSlots--; ent->NextThink = Level.Frame; void (IMonster::*TheThink) () = ent->Monster->Think; (ent->Monster->*TheThink) (); ent->Monster->AIFlags |= AI_SPAWNED_CARRIER|AI_DO_NOT_COUNT|AI_IGNORE_SHOTS; ent->Monster->Commander = Entity; if ((Entity->Enemy->GetInUse()) && (entity_cast<IHurtableEntity>(*Entity->Enemy)->Health > 0)) { ent->Enemy = Entity->Enemy; ent->Monster->FoundTarget (); if (mytime == 1) { ent->Monster->Lefty = false; ent->Monster->AttackState = AS_SLIDING; ent->Monster->CurrentMove = &FlyerMoveAttack2; } else if (mytime == 2) { ent->Monster->Lefty = false; ent->Monster->AttackState = AS_STRAIGHT; ent->Monster->CurrentMove = &FlyerMoveKamikaze; ent->Monster->AIFlags |= AI_CHARGING; } else if (mytime == 3) { ent->Monster->Lefty = true; ent->Monster->AttackState = AS_SLIDING; ent->Monster->CurrentMove = &FlyerMoveAttack2; } } } }
void medic_cable_attack (edict_t *self) { vec3_t offset, start, end, f, r; trace_t tr; vec3_t dir, angles; float distance; if (!self->enemy->inuse) return; AngleVectors (self->s.angles, f, r, NULL); VectorCopy (medic_cable_offsets[self->s.frame - FRAME_attack42], offset); G_ProjectSource (self->s.origin, offset, f, r, start); // check for max distance VectorSubtract (start, self->enemy->s.origin, dir); distance = VectorLength(dir); if (distance > 256) return; // check for min/max pitch vectoangles (dir, angles); if (angles[0] < -180) angles[0] += 360; if (fabs(angles[0]) > 45) return; tr = gi.trace (start, NULL, NULL, self->enemy->s.origin, self, MASK_SHOT); if (tr.fraction != 1.0 && tr.ent != self->enemy) return; if (self->s.frame == FRAME_attack43) { gi.sound (self->enemy, CHAN_AUTO, sound_hook_hit, 1, ATTN_NORM, 0); self->enemy->monsterinfo.aiflags |= AI_RESURRECTING; } else if (self->s.frame == FRAME_attack50) { self->enemy->spawnflags = 0; self->enemy->monsterinfo.aiflags = 0; self->enemy->target = NULL; self->enemy->targetname = NULL; self->enemy->combattarget = NULL; self->enemy->deathtarget = NULL; self->enemy->owner = self; ED_CallSpawn (self->enemy); self->enemy->owner = NULL; if (self->enemy->think) { self->enemy->nextthink = level.time; self->enemy->think (self->enemy); } self->enemy->monsterinfo.aiflags |= AI_RESURRECTING; if (self->oldenemy && self->oldenemy->client) { self->enemy->enemy = self->oldenemy; FoundTarget (self->enemy); } } else { if (self->s.frame == FRAME_attack44) gi.sound (self, CHAN_WEAPON, sound_hook_heal, 1, ATTN_NORM, 0); } // adjust start for beam origin being in middle of a segment VectorMA (start, 8, f, start); // adjust end z for end spot since the monster is currently dead VectorCopy (self->enemy->s.origin, end); end[2] = self->enemy->absmin[2] + self->enemy->size[2] / 2; gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_MEDIC_CABLE_ATTACK); gi.WriteShort (self - g_edicts); gi.WritePosition (start); gi.WritePosition (end); gi.multicast (self->s.origin, MULTICAST_PVS); }
void GunnerGrenade (edict_t *self) { vec3_t start; vec3_t forward, right, up; vec3_t aim; int flash_number; float spread; float pitch = 0; // PMM vec3_t target; qboolean blindfire = false; if(!self->enemy || !self->enemy->inuse) //PGM return; //PGM // pmm if (self->monsterinfo.aiflags & AI_MANUAL_STEERING) blindfire = true; if (self->s.frame == FRAME_attak105) { spread = .02; flash_number = MZ2_GUNNER_GRENADE_1; } else if (self->s.frame == FRAME_attak108) { spread = .05; flash_number = MZ2_GUNNER_GRENADE_2; } else if (self->s.frame == FRAME_attak111) { spread = .08; flash_number = MZ2_GUNNER_GRENADE_3; } else // (self->s.frame == FRAME_attak114) { self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING; spread = .11; flash_number = MZ2_GUNNER_GRENADE_4; } // pmm // if we're shooting blind and we still can't see our enemy if ((blindfire) && (!visible(self, self->enemy))) { // and we have a valid blind_fire_target if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin)) return; VectorCopy (self->monsterinfo.blind_fire_target, target); } else VectorCopy (self->s.origin, target); // pmm AngleVectors (self->s.angles, forward, right, up); //PGM G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start); //PGM if(self->enemy) { float dist; VectorSubtract(target, self->s.origin, aim); dist = VectorLength(aim); // aim up if they're on the same level as me and far away. if((dist > 512) && (aim[2] < 64) && (aim[2] > -64)) { aim[2] += (dist - 512); } VectorNormalize (aim); pitch = aim[2]; if(pitch > 0.4) { pitch = 0.4; } else if(pitch < -0.5) pitch = -0.5; } //PGM VectorMA (forward, spread, right, aim); VectorMA (aim, pitch, up, aim); monster_fire_grenade (self, start, aim, 50, 600, flash_number); }
qboolean medic_checkattack (edict_t *self) { if (!(self->monsterinfo.aiflags & AI_MEDIC)) { if( medic_FindDeadMonster(self) ) return false; } if (self->monsterinfo.aiflags & AI_MEDIC) { float r; vec3_t forward, right, offset, start; trace_t tr; // if we have 5 seconds or less before a timeout, // look for a hint_path to the target if ( (self->timestamp < level.time + 5) && (self->monsterinfo.last_hint_time + 5 < level.time) ) { // check for hint_paths. self->monsterinfo.last_hint_time = level.time; if (hintcheck_monsterlost(self)) { if(developer->value) gi.dprintf("medic at %s using hint_paths to find %s\n", vtos(self->s.origin), self->enemy->classname); self->timestamp = level.time + MEDIC_TRY_TIME; return false; } } // if we ran out of time, give up if (self->timestamp < level.time) { //if(developer->value) // gi.dprintf("medic at %s timed out, abort heal\n",vtos(self->s.origin)); abortHeal (self, true); self->timestamp = 0; return false; } // if our target went away if ((!self->enemy) || (!self->enemy->inuse)) { abortHeal (self,false); return false; } // if target is embedded in a solid if (embedded(self->enemy)) { abortHeal (self,false); return false; } r = realrange(self,self->enemy); if (r > MEDIC_MAX_HEAL_DISTANCE+10) { self->monsterinfo.attack_state = AS_STRAIGHT; //abortHeal(self,false); return false; } else if(r < MEDIC_MIN_DISTANCE) { abortHeal(self,false); return false; } // Lazarus 1.6.2.3: if point-to-point vector from cable to // target is blocked by a solid AngleVectors (self->s.angles, forward, right, NULL); // Offset [8] has the largest displacement to the left... not a sure // thing but this one should be the most severe test. VectorCopy (medic_cable_offsets[8], offset); G_ProjectSource (self->s.origin, offset, forward, right, start); tr = gi.trace(start,NULL,NULL,self->enemy->s.origin,self,MASK_SHOT|MASK_WATER); if (tr.fraction < 1.0 && tr.ent != self->enemy) return false; medic_attack(self); return true; } // Lazarus: NEVER attack other monsters if ((self->enemy) && (self->enemy->svflags & SVF_MONSTER)) { self->enemy = self->oldenemy; self->oldenemy = NULL; if(self->enemy && self->enemy->inuse) { if(visible(self,self->enemy)) FoundTarget(self); else HuntTarget(self); } return false; } return M_CheckAttack (self); }
void medic_cable_attack (edict_t *self) { vec3_t offset, start, end, f, r; trace_t tr; vec3_t dir; float distance; if ((!self->enemy) || (!self->enemy->inuse) || (self->enemy->svflags & SVF_GIB)) { //gi.dprintf ("medic_cable_attack: aborting heal due to target being removed or gibbed\n"); abortHeal (self,false); return; } //Knightmare- don't heal insanes or actors or critters if (!strcmp (self->enemy->classname, "misc_insane") || !strcmp (self->enemy->classname, "misc_actor") || !strcmp (self->enemy->classname, "monster_mutant") || !strcmp (self->enemy->classname, "monster_flipper")) { //gi.dprintf ("medic_cable_attack: not healing insane or actor or critter\n"); abortHeal (self, true); return; } // Lazarus: check embeddment if (embedded(self->enemy)) { //gi.dprintf ("medic_cable_attack: dead monster embedded in solid, aborting heal\n"); abortHeal (self,false); return; } // see if our enemy has changed to a client, or our target has more than 0 health, // abort it .. we got switched to someone else due to damage if ((self->enemy->client) || (self->enemy->health > 0)) { //gi.dprintf ("medic_cable_attack: aborting heal due to target health > 0 or client\n"); abortHeal (self,false); return; } AngleVectors (self->s.angles, f, r, NULL); VectorCopy (medic_cable_offsets[self->s.frame - FRAME_attack42], offset); G_ProjectSource (self->s.origin, offset, f, r, start); // check for max distance // Lazarus: Not needed, done in checkattack // check for min distance VectorSubtract (self->enemy->s.origin, start, dir); distance = VectorLength(dir); if (distance < MEDIC_MIN_DISTANCE) { //gi.dprintf("medic_cable_attack: MEDIC_MIN_DISTANCE\n"); abortHeal (self,false); return; } // Lazarus: Check for enemy behind muzzle... don't do these guys, 'cause usually this // results in monster entanglement VectorNormalize(dir); if(DotProduct(dir,f) < 0.) { //gi.dprintf ("medic_cable_attack: aborting heal due to possible entanglment\n"); abortHeal (self,false); return; } // check for min/max pitch // Rogue takes this out... makes medic more likely to heal and // comments say "doesn't look bad when it fails"... we'll see /* vectoangles (dir, angles); if (angles[0] < -180) angles[0] += 360; if (fabs(angles[0]) > 45) return; */ tr = gi.trace (start, NULL, NULL, self->enemy->s.origin, self, MASK_SHOT); if (tr.fraction != 1.0 && tr.ent != self->enemy) { if (tr.ent == world) { // give up on second try if (self->monsterinfo.medicTries > 1) { abortHeal (self,true); return; } self->monsterinfo.medicTries++; cleanupHeal (self, 1); return; } abortHeal (self,false); return; } if (self->s.frame == FRAME_attack43) { gi.sound (self->enemy, CHAN_AUTO, sound_hook_hit, 1, ATTN_NORM, 0); self->enemy->monsterinfo.aiflags |= AI_RESURRECTING; M_SetEffects(self->enemy); } else if (self->s.frame == FRAME_attack50) { self->enemy->spawnflags &= SF_MONSTER_NOGIB; self->enemy->monsterinfo.aiflags = 0; self->enemy->target = NULL; self->enemy->targetname = NULL; self->enemy->combattarget = NULL; self->enemy->deathtarget = NULL; self->enemy->owner = self; // Lazarus: reset initially dead monsters to use the INVERSE of their // initial health, and force gib_health to default value if(self->enemy->max_health < 0) { self->enemy->max_health = -self->enemy->max_health; self->enemy->gib_health = 0; } self->enemy->health = self->enemy->max_health; self->enemy->takedamage = DAMAGE_AIM; self->enemy->flags &= ~FL_NO_KNOCKBACK; self->enemy->pain_debounce_time = 0; self->enemy->damage_debounce_time = 0; self->enemy->deadflag = DEAD_NO; if(self->enemy->s.effects & EF_FLIES) M_FliesOff(self->enemy); ED_CallSpawn (self->enemy); self->enemy->monsterinfo.healer = NULL; self->enemy->owner = NULL; // Knightmare- disable deadmonster_think if (self->enemy->postthink) self->enemy->postthink = NULL; if (self->enemy->think) { self->enemy->nextthink = level.time; self->enemy->think (self->enemy); } self->enemy->monsterinfo.aiflags &= ~AI_RESURRECTING; M_SetEffects(self->enemy); if (self->oldenemy && self->oldenemy->client) { self->enemy->enemy = self->oldenemy; FoundTarget (self->enemy); } else { // Lazarus: this should make oblivious monsters // find player again self->enemy->enemy = NULL; } } else { if (self->s.frame == FRAME_attack44) gi.sound (self, CHAN_WEAPON, sound_hook_heal, 1, ATTN_NORM, 0); } // adjust start for beam origin being in middle of a segment // Lazarus: This isn't right... this causes cable start point to be well above muzzle // when target is closeby. f should be vector from muzzle to target, not // the forward viewing direction. PLUS... 8 isn't right... the model is // actually 32 units long, so use 16.. fixed below. // VectorMA (start, 8, f, start); //Knightmare- if enemy went away, like after returning from another level, return if (!self->enemy) return; // adjust end z for end spot since the monster is currently dead VectorCopy (self->enemy->s.origin, end); end[2] = self->enemy->absmin[2] + self->enemy->size[2] / 2; // Lazarus fix VectorSubtract(end,start,f); VectorNormalize(f); VectorMA(start,16,f,start); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_MEDIC_CABLE_ATTACK); gi.WriteShort (self - g_edicts); gi.WritePosition (start); gi.WritePosition (end); gi.multicast (self->s.origin, MULTICAST_PVS); }
void CMaiden::Rocket () { if (!HasValidEnemy()) return; #if ROGUE_FEATURES vec3f start, dir, vec, target; bool blindfire = (AIFlags & AI_MANUAL_STEERING) ? true : false; anglef angles = Entity->State.GetAngles().ToVectors (); G_ProjectSource (Entity->State.GetOrigin(), MonsterFlashOffsets[MZ2_CHICK_ROCKET_1], angles, start); sint32 rocketSpeed = 500 + (100 * CvarList[CV_SKILL].Integer()); // PGM rock & roll.... :) target = (blindfire) ? BlindFireTarget : Entity->Enemy->State.GetOrigin(); if (blindfire) { vec = target; dir = vec - start; } // pmm // don't shoot at feet if they're above where i'm shooting from. else if(frand() < 0.33 || (start[2] < Entity->Enemy->GetAbsMin().Z)) { vec = target; vec.Z += Entity->Enemy->ViewHeight; dir = vec - start; } else { vec = target; vec.Z = Entity->Enemy->GetAbsMin().Z; dir = vec - start; } // Lead target (not when blindfiring) // 20, 35, 50, 65 chance of leading if((!blindfire) && ((frand() < (0.2 + ((3 - CvarList[CV_SKILL].Integer()) * 0.15))))) { vec = vec.MultiplyAngles (dir.Length() / rocketSpeed, entity_cast<IPhysicsEntity>(*Entity->Enemy)->Velocity); dir = vec - start; } dir.Normalize (); // pmm blindfire doesn't check target (done in checkattack) // paranoia, make sure we're not shooting a target right next to us CTrace trace (start, vec, Entity, CONTENTS_MASK_SHOT); if (blindfire) { // blindfire has different fail criteria for the trace if (!(trace.StartSolid || trace.AllSolid || (trace.Fraction < 0.5f))) MonsterFireRocket (start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1); else { // geez, this is bad. she's avoiding about 80% of her blindfires due to hitting things. // hunt around for a good shot // try shifting the target to the left a little (to help counter her large offset) vec = target; vec = vec.MultiplyAngles (-10, angles.Right); dir = vec - start; dir.NormalizeFast(); trace (start, vec, Entity, CONTENTS_MASK_SHOT); if (!(trace.StartSolid || trace.AllSolid || (trace.Fraction < 0.5))) MonsterFireRocket (start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1); else { // ok, that failed. try to the right vec = target; vec = vec.MultiplyAngles (10, angles.Right); dir = vec - start; dir.NormalizeFast(); trace (start, vec, Entity, CONTENTS_MASK_SHOT); if (!(trace.StartSolid || trace.AllSolid || (trace.Fraction < 0.5))) MonsterFireRocket (start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1); } } } else MonsterFireRocket (start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1); #else vec3f start, dir, vec; anglef angles = Entity->State.GetAngles().ToVectors (); G_ProjectSource (Entity->State.GetOrigin(), MonsterFlashOffsets[MZ2_CHICK_ROCKET_1], angles, start); vec = Entity->Enemy->State.GetOrigin(); vec.Z += Entity->Enemy->ViewHeight; dir = vec - start; dir.NormalizeFast (); MonsterFireRocket (start, dir, 50, 500, MZ2_CHICK_ROCKET_1); #endif }
void soldier_fire (edict_t *self, int flash_number) { vec3_t start; vec3_t forward, right, up; vec3_t aim; vec3_t dir; vec3_t end; float r, u; int flash_index; if ((self->s.skinnum % 6) < 2) flash_index = blaster_flash[flash_number]; else if ((self->s.skinnum % 6) < 4) flash_index = shotgun_flash[flash_number]; else flash_index = machinegun_flash[flash_number]; AngleVectors (self->s.angles, forward, right, NULL); G_ProjectSource (self->s.origin, monster_flash_offset[flash_index], forward, right, start); if (flash_number == 5 || flash_number == 6) { VectorCopy (forward, aim); } else { VectorCopy (self->enemy->s.origin, end); end[2] += self->enemy->viewheight; // Lazarus fog reduction of accuracy if(self->monsterinfo.visibility < FOG_CANSEEGOOD) { end[0] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); end[1] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); end[2] += crandom() * 320 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); } VectorSubtract (end, start, aim); // Lazarus: Accuracy is skill level dependent if(skill->value < 3) { vectoangles (aim, dir); AngleVectors (dir, forward, right, up); r = crandom()*(1000 - 333*skill->value); u = crandom()*(500 - 167*skill->value); VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); VectorSubtract (end, start, aim); } VectorNormalize (aim); } if ((self->s.skinnum % 6) <= 1) { // Lazarus: make bolt speed skill level dependent monster_fire_blaster (self, start, aim, 5, 600 + 100*skill->value, flash_index, EF_BLASTER, BLASTER_ORANGE); } else if ((self->s.skinnum % 6) <= 3) { monster_fire_shotgun (self, start, aim, 2, 1, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SHOTGUN_COUNT, flash_index); } else { if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) self->monsterinfo.pausetime = level.time + (3 + rand() % 8) * FRAMETIME; monster_fire_bullet (self, start, aim, 2, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_index); if (level.time >= self->monsterinfo.pausetime) self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; else self->monsterinfo.aiflags |= AI_HOLD_FRAME; } }
/////////////////////////////////////////////////////////////////////// // Checks for obstructions in front of bot // // This is a function I created origianlly for ACE that // tries to help steer the bot around obstructions. // // If the move is resolved here, this function returns true. /////////////////////////////////////////////////////////////////////// qboolean ACEMV_CheckEyes(edict_t *self, usercmd_t *ucmd) { vec3_t forward, right; vec3_t leftstart, rightstart,focalpoint; vec3_t upstart,upend; vec3_t dir,offset; trace_t traceRight,traceLeft,traceUp, traceFront; // for eyesight // Get current angle and set up "eyes" VectorCopy(self->s.angles,dir); AngleVectors (dir, forward, right, NULL); // Let them move to targets by walls if(!self->movetarget) VectorSet(offset,200,0,4); // focalpoint else VectorSet(offset,36,0,4); // focalpoint G_ProjectSource (self->s.origin, offset, forward, right, focalpoint); // Check from self to focalpoint // Ladder code VectorSet(offset,36,0,0); // set as high as possible G_ProjectSource (self->s.origin, offset, forward, right, upend); traceFront = gi.trace(self->s.origin, self->mins, self->maxs, upend, self, MASK_OPAQUE); if(traceFront.contents & 0x8000000) // using detail brush here cuz sometimes it does not pick up ladders...?? { ucmd->upmove = 400; ucmd->forwardmove = 400; return true; } // If this check fails we need to continue on with more detailed checks if(traceFront.fraction == 1) { ucmd->forwardmove = 400; return true; } VectorSet(offset, 0, 18, 4); G_ProjectSource (self->s.origin, offset, forward, right, leftstart); offset[1] -= 36; // want to make sure this is correct //VectorSet(offset, 0, -18, 4); G_ProjectSource (self->s.origin, offset, forward, right, rightstart); traceRight = gi.trace(rightstart, NULL, NULL, focalpoint, self, MASK_OPAQUE); traceLeft = gi.trace(leftstart, NULL, NULL, focalpoint, self, MASK_OPAQUE); // Wall checking code, this will degenerate progressivly so the least cost // check will be done first. // If open space move ok if(traceRight.fraction != 1 || traceLeft.fraction != 1 || strcmp(traceLeft.ent->classname,"func_door")!=0) { // Special uppoint logic to check for slopes/stairs/jumping etc. VectorSet(offset, 0, 18, 24); G_ProjectSource (self->s.origin, offset, forward, right, upstart); VectorSet(offset,0,0,200); // scan for height above head G_ProjectSource (self->s.origin, offset, forward, right, upend); traceUp = gi.trace(upstart, NULL, NULL, upend, self, MASK_OPAQUE); VectorSet(offset,200,0,200*traceUp.fraction-5); // set as high as possible G_ProjectSource (self->s.origin, offset, forward, right, upend); traceUp = gi.trace(upstart, NULL, NULL, upend, self, MASK_OPAQUE); // If the upper trace is not open, we need to turn. if(traceUp.fraction != 1) { if(traceRight.fraction > traceLeft.fraction) self->s.angles[YAW] += (1.0 - traceLeft.fraction) * 45.0; else self->s.angles[YAW] += -(1.0 - traceRight.fraction) * 45.0; ucmd->forwardmove = 400; return true; } } return false; }
void soldier_fire (edict_t *self, int flash_number) { vec3_t start; vec3_t forward, right, up; vec3_t aim; vec3_t dir; vec3_t end; float r, u; int flash_index; if (self->s.skinnum < 2) flash_index = blaster_flash[flash_number]; else if (self->s.skinnum < 4) flash_index = shotgun_flash[flash_number]; else flash_index = machinegun_flash[flash_number]; AngleVectors (self->s.angles, forward, right, NULL); G_ProjectSource (self->s.origin, monster_flash_offset[flash_index], forward, right, start); if (flash_number == 5 || flash_number == 6) { VectorCopy (forward, aim); } else { VectorCopy (self->enemy->s.origin, end); end[2] += self->enemy->viewheight; VectorSubtract (end, start, aim); vectoangles (aim, dir); AngleVectors (dir, forward, right, up); r = crandom()*1000; u = crandom()*500; VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); VectorSubtract (end, start, aim); VectorNormalize (aim); } if (self->s.skinnum <= 1) { monster_fire_blaster (self, start, aim, 5, 600, flash_index, EF_BLASTER); } else if (self->s.skinnum <= 3) { monster_fire_shotgun (self, start, aim, 2, 1, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SHOTGUN_COUNT, flash_index); } else { if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) self->monsterinfo.pausetime = level.time + (3 + rand() % 8) * FRAMETIME; monster_fire_bullet (self, start, aim, 2, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_index); if (level.time >= self->monsterinfo.pausetime) self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; else self->monsterinfo.aiflags |= AI_HOLD_FRAME; } }
void TankRocket (edict_t *self) { trace_t trace; vec3_t forward, right; vec3_t start; vec3_t dir; vec3_t vec; int flash_number; // Lazarus: Added skill level-dependent rocket speed similar to Rogue pack int rocketSpeed; // check if enemy went away if (!self->enemy || !self->enemy->inuse) return; if (self->s.frame == FRAME_attak324) flash_number = MZ2_TANK_ROCKET_1; else if (self->s.frame == FRAME_attak327) flash_number = MZ2_TANK_ROCKET_2; else // (self->s.frame == FRAME_attak330) flash_number = MZ2_TANK_ROCKET_3; AngleVectors (self->s.angles, forward, right, NULL); G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start); if((self->spawnflags & SF_MONSTER_SPECIAL)) rocketSpeed = 400; // Lazarus: Homing rockets are tougher if slow else rocketSpeed = 500 + (100 * skill->value); /* VectorCopy (self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract (vec, start, dir); VectorNormalize (dir); */ // Lazarus: Added homers VectorCopy (self->enemy->s.origin, vec); if(random() < 0.66 || (start[2] < self->enemy->absmin[2])) { // gi.dprintf("normal shot\n"); vec[2] += self->enemy->viewheight; } else { // gi.dprintf("shooting at feet!\n"); vec[2] = self->enemy->absmin[2]; } // Lazarus fog reduction of accuracy if(self->monsterinfo.visibility < FOG_CANSEEGOOD) { vec[0] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); vec[1] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); vec[2] += crandom() * 320 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); } VectorSubtract (vec, start, dir); // lead target, but not if using homers // 20, 35, 50, 65 chance of leading // Lazarus: Switched this around from Rogue code... it led target more often // for Easy, which seemed backwards if( (random() < (0.2 + skill->value * 0.15) ) && !(self->spawnflags & SF_MONSTER_SPECIAL)) { float dist; float time; dist = VectorLength (dir); time = dist/rocketSpeed; VectorMA(vec, time, self->enemy->velocity, vec); VectorSubtract(vec, start, dir); } VectorNormalize(dir); // paranoia, make sure we're not shooting a target right next to us trace = gi.trace(start, vec3_origin, vec3_origin, vec, self, MASK_SHOT); if(trace.ent == self->enemy || trace.ent == world) { if(trace.fraction > 0.5 || (trace.ent && trace.ent->client)) monster_fire_rocket (self, start, dir, 50, rocketSpeed, flash_number, (self->spawnflags & SF_MONSTER_SPECIAL ? self->enemy : NULL) ); } }