// ============================================================================ public static bool monster_start(edict_t self) { if (GameBase.deathmatch.value != 0) { GameUtil.G_FreeEdict(self); return(false); } if ((self.spawnflags & 4) != 0 && 0 == (self.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) { self.spawnflags &= ~4; self.spawnflags |= 1; // gi.dprintf("fixed spawnflags on %s at %s\n", self.classname, // vtos(self.s.origin)); } if (0 == (self.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) { GameBase.level.total_monsters++; } self.nextthink = GameBase.level.time + Defines.FRAMETIME; self.svflags |= Defines.SVF_MONSTER; self.s.renderfx |= Defines.RF_FRAMELERP; self.takedamage = Defines.DAMAGE_AIM; self.air_finished = GameBase.level.time + 12; self.use = GameUtil.monster_use; self.max_health = self.health; self.clipmask = Defines.MASK_MONSTERSOLID; self.s.skinnum = 0; self.deadflag = Defines.DEAD_NO; self.svflags &= ~Defines.SVF_DEADMONSTER; if (null == self.monsterinfo.checkattack) { self.monsterinfo.checkattack = GameUtil.M_CheckAttack; } Math3D.VectorCopy(self.s.origin, self.s.old_origin); if (GameBase.st.item != null && GameBase.st.item.Length > 0) { self.item = GameItems.FindItemByClassname(GameBase.st.item); if (self.item == null) { GameBase.gi.dprintf("monster_start:" + self.classname + " at " + Lib.vtos(self.s.origin) + " has bad item: " + GameBase.st.item + "\n"); } } // randomize what frame they start on if (self.monsterinfo.currentmove != null) { self.s.frame = self.monsterinfo.currentmove.firstframe + Lib.rand() % (self.monsterinfo.currentmove.lastframe - self.monsterinfo.currentmove.firstframe + 1); } return(true); }
public static void T_Damage( edict_t targ, edict_t inflictor, edict_t attacker, float[] dir, float[] point, float[] normal, int damage, int knockback, int dflags, int mod ) { gclient_t client; int take; int save; int asave; int psave; int te_sparks; if (targ.takedamage == 0) { return; } // friendly fire avoidance // if enabled you can't hurt teammates (but you can hurt yourself) // knockback still occurs if (targ != attacker && ((GameBase.deathmatch.value != 0 && 0 != ((int)GameBase.dmflags.value & (Defines.DF_MODELTEAMS | Defines.DF_SKINTEAMS))) || GameBase.coop.value != 0)) { if (GameUtil.OnSameTeam(targ, attacker)) { if (((int)GameBase.dmflags.value & Defines.DF_NO_FRIENDLY_FIRE) != 0) { damage = 0; } else { mod |= Defines.MOD_FRIENDLY_FIRE; } } } GameBase.meansOfDeath = mod; // easy mode takes half damage if (GameBase.skill.value == 0 && GameBase.deathmatch.value == 0 && targ.client != null) { damage /= 2; if (damage == 0) { damage = 1; } } client = targ.client; if ((dflags & Defines.DAMAGE_BULLET) != 0) { te_sparks = Defines.TE_BULLET_SPARKS; } else { te_sparks = Defines.TE_SPARKS; } Math3D.VectorNormalize(dir); // bonus damage for suprising a monster if (0 == (dflags & Defines.DAMAGE_RADIUS) && (targ.svflags & Defines.SVF_MONSTER) != 0 && attacker.client != null && targ.enemy == null && targ.health > 0) { damage *= 2; } if ((targ.flags & Defines.FL_NO_KNOCKBACK) != 0) { knockback = 0; } // figure momentum add if (0 == (dflags & Defines.DAMAGE_NO_KNOCKBACK)) { if (knockback != 0 && targ.movetype != Defines.MOVETYPE_NONE && targ.movetype != Defines.MOVETYPE_BOUNCE && targ.movetype != Defines.MOVETYPE_PUSH && targ.movetype != Defines.MOVETYPE_STOP) { float[] kvel = { 0, 0, 0 }; float mass; if (targ.mass < 50) { mass = 50; } else { mass = targ.mass; } if (targ.client != null && attacker == targ) { Math3D.VectorScale(dir, 1600.0f * (float)knockback / mass, kvel); } // the rocket jump hack... else { Math3D.VectorScale(dir, 500.0f * (float)knockback / mass, kvel); } Math3D.VectorAdd(targ.velocity, kvel, targ.velocity); } } take = damage; save = 0; // check for godmode if ((targ.flags & Defines.FL_GODMODE) != 0 && 0 == (dflags & Defines.DAMAGE_NO_PROTECTION)) { take = 0; save = damage; GameCombat.SpawnDamage(te_sparks, point, normal, save); } // check for invincibility if (client != null && client.invincible_framenum > GameBase.level.framenum && 0 == (dflags & Defines.DAMAGE_NO_PROTECTION)) { if (targ.pain_debounce_time < GameBase.level.time) { GameBase.gi.sound(targ, Defines.CHAN_ITEM, GameBase.gi.soundindex("items/protect4.wav"), 1, Defines.ATTN_NORM, 0); targ.pain_debounce_time = GameBase.level.time + 2; } take = 0; save = damage; } psave = GameCombat.CheckPowerArmor(targ, point, normal, take, dflags); take -= psave; asave = GameCombat.CheckArmor(targ, point, normal, take, te_sparks, dflags); take -= asave; // treat cheat/powerup savings the same as armor asave += save; // team damage avoidance if (0 == (dflags & Defines.DAMAGE_NO_PROTECTION) && GameCombat.CheckTeamDamage(targ, attacker)) { return; } // do the damage if (take != 0) { if (0 != (targ.svflags & Defines.SVF_MONSTER) || client != null) { GameCombat.SpawnDamage(Defines.TE_BLOOD, point, normal, take); } else { GameCombat.SpawnDamage(te_sparks, point, normal, take); } targ.health = targ.health - take; if (targ.health <= 0) { if ((targ.svflags & Defines.SVF_MONSTER) != 0 || client != null) { targ.flags |= Defines.FL_NO_KNOCKBACK; } GameCombat.Killed(targ, inflictor, attacker, take, point); return; } } if ((targ.svflags & Defines.SVF_MONSTER) != 0) { GameCombat.M_ReactToDamage(targ, attacker); if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED) && take != 0) { targ.pain.pain(targ, attacker, knockback, take); // nightmare mode monsters don't go into pain frames often if (GameBase.skill.value == 3) { targ.pain_debounce_time = GameBase.level.time + 5; } } } else if (client != null) { if ((targ.flags & Defines.FL_GODMODE) == 0 && take != 0) { targ.pain.pain(targ, attacker, knockback, take); } } else if (take != 0) { if (targ.pain != null) { targ.pain.pain(targ, attacker, knockback, take); } } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if (client != null) { client.damage_parmor += psave; client.damage_armor += asave; client.damage_blood += take; client.damage_knockback += knockback; Math3D.VectorCopy(point, client.damage_from); } }
/** * Decides if we're going to attack or do something else used by ai_run and * ai_stand. * * .enemy Will be world if not currently angry at anyone. * * .movetarget The next path spot to walk toward. If .enemy, ignore * .movetarget. When an enemy is killed, the monster will try to return to * it's path. * * .hunt_time Set to time + something when the player is in sight, but * movement straight for him is blocked. This causes the monster to use wall * following code for movement direction instead of sighting on the player. * * .ideal_yaw A yaw angle of the intended direction, which will be turned * towards at up to 45 deg / state. If the enemy is in view and hunt_time is * not active, this will be the exact line towards the enemy. * * .pausetime A monster will leave it's stand state and head towards it's * .movetarget when time > .pausetime. * * walkmove(angle, speed) primitive is all or nothing */ public static bool ai_checkattack(edict_t self, float dist) { float[] temp = { 0, 0, 0 }; bool hesDeadJim; // this causes monsters to run blindly to the combat point w/o firing if (self.goalentity != null) { if ((self.monsterinfo.aiflags & Defines.AI_COMBAT_POINT) != 0) { return(false); } if ((self.monsterinfo.aiflags & Defines.AI_SOUND_TARGET) != 0) { if (GameBase.level.time - self.enemy.teleport_time > 5.0) { if (self.goalentity == self.enemy) { if (self.movetarget != null) { self.goalentity = self.movetarget; } else { self.goalentity = null; } } self.monsterinfo.aiflags &= ~Defines.AI_SOUND_TARGET; if ((self.monsterinfo.aiflags & Defines.AI_TEMP_STAND_GROUND) != 0) { self.monsterinfo.aiflags &= ~(Defines.AI_STAND_GROUND | Defines.AI_TEMP_STAND_GROUND); } } else { self.show_hostile = (int)GameBase.level.time + 1; return(false); } } } GameAI.enemy_vis = false; // see if the enemy is dead hesDeadJim = false; if (null == self.enemy || !self.enemy.inuse) { hesDeadJim = true; } else if ((self.monsterinfo.aiflags & Defines.AI_MEDIC) != 0) { if (self.enemy.health > 0) { hesDeadJim = true; self.monsterinfo.aiflags &= ~Defines.AI_MEDIC; } } else { if ((self.monsterinfo.aiflags & Defines.AI_BRUTAL) != 0) { if (self.enemy.health <= -80) { hesDeadJim = true; } } else { if (self.enemy.health <= 0) { hesDeadJim = true; } } } if (hesDeadJim) { self.enemy = null; // FIXME: look all around for other targets if (self.oldenemy != null && self.oldenemy.health > 0) { self.enemy = self.oldenemy; self.oldenemy = null; GameAI.HuntTarget(self); } else { if (self.movetarget != null) { self.goalentity = self.movetarget; self.monsterinfo.walk.think(self); } else { // we need the pausetime otherwise the stand code // will just revert to walking with no target and // the monsters will wonder around aimlessly trying // to hunt the world entity self.monsterinfo.pausetime = GameBase.level.time + 100000000; self.monsterinfo.stand.think(self); } return(true); } } self.show_hostile = (int)GameBase.level.time + 1; // wake up other // monsters check knowledge of enemy GameAI.enemy_vis = GameUtil.visible(self, self.enemy); if (GameAI.enemy_vis) { self.monsterinfo.search_time = GameBase.level.time + 5; Math3D.VectorCopy(self.enemy.s.origin, self.monsterinfo.last_sighting); } GameAI.enemy_infront = GameUtil.infront(self, self.enemy); GameAI.enemy_range = GameUtil.range(self, self.enemy); Math3D.VectorSubtract(self.enemy.s.origin, self.s.origin, temp); GameAI.enemy_yaw = Math3D.vectoyaw(temp); // JDC self.ideal_yaw = enemy_yaw; if (self.monsterinfo.attack_state == Defines.AS_MISSILE) { GameAI.ai_run_missile(self); return(true); } if (self.monsterinfo.attack_state == Defines.AS_MELEE) { GameAI.ai_run_melee(self); return(true); } // if enemy is not currently visible, we will never attack if (!GameAI.enemy_vis) { return(false); } return(self.monsterinfo.checkattack.think(self)); }
public static void M_ReactToDamage(edict_t targ, edict_t attacker) { if (null != attacker.client && 0 != (attacker.svflags & Defines.SVF_MONSTER)) { return; } if (attacker == targ || attacker == targ.enemy) { return; } // if we are a good guy monster and our attacker is a player // or another good guy, do not get mad at them if (0 != (targ.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) { if (attacker.client != null || (attacker.monsterinfo.aiflags & Defines.AI_GOOD_GUY) != 0) { return; } } // we now know that we are not both good guys // if attacker is a client, get mad at them because he's good and we're // not if (attacker.client != null) { targ.monsterinfo.aiflags &= ~Defines.AI_SOUND_TARGET; // this can only happen in coop (both new and old enemies are // clients) // only switch if can't see the current enemy if (targ.enemy != null && targ.enemy.client != null) { if (GameUtil.visible(targ, targ.enemy)) { targ.oldenemy = attacker; return; } targ.oldenemy = targ.enemy; } targ.enemy = attacker; if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED)) { GameUtil.FoundTarget(targ); } return; } // it's the same base (walk/swim/fly) type and a different classname and // it's not a tank // (they spray too much), get mad at them if ((targ.flags & (Defines.FL_FLY | Defines.FL_SWIM)) == (attacker.flags & (Defines.FL_FLY | Defines.FL_SWIM)) && !targ.classname.Equals(attacker.classname) && !attacker.classname.Equals("monster_tank") && !attacker.classname.Equals("monster_supertank") && !attacker.classname.Equals("monster_makron") && !attacker.classname.Equals("monster_jorg")) { if (targ.enemy != null && targ.enemy.client != null) { targ.oldenemy = targ.enemy; } targ.enemy = attacker; if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED)) { GameUtil.FoundTarget(targ); } } // if they *meant* to shoot us, then shoot back else if (attacker.enemy == targ) { if (targ.enemy != null && targ.enemy.client != null) { targ.oldenemy = targ.enemy; } targ.enemy = attacker; if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED)) { GameUtil.FoundTarget(targ); } } // otherwise get mad at whoever they are mad at (help our buddy) unless // it is us! else if (attacker.enemy != null && attacker.enemy != targ) { if (targ.enemy != null && targ.enemy.client != null) { targ.oldenemy = targ.enemy; } targ.enemy = attacker.enemy; if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED)) { GameUtil.FoundTarget(targ); } } }
/** * Use the targets. * * The global "activator" should be set to the entity that initiated the * firing. * * If self.delay is set, a DelayedUse entity will be created that will * actually do the SUB_UseTargets after that many seconds have passed. * * Centerprints any self.message to the activator. * * Search for (string)targetname in all entities that match * (string)self.target and call their .use function */ public static void G_UseTargets(edict_t ent, edict_t activator) { edict_t t; GameUtil.checkClassname(ent); // check for a delay if (ent.delay != 0) { // create a temp object to fire at a later time t = GameUtil.G_Spawn(); t.classname = "DelayedUse"; t.nextthink = GameBase.level.time + ent.delay; t.think = GameUtil.Think_Delay; t.activator = activator; if (activator == null) { GameBase.gi.dprintf("Think_Delay with no activator\n"); } t.message = ent.message; t.target = ent.target; t.killtarget = ent.killtarget; return; } // print the message if (ent.message != null && (activator.svflags & Defines.SVF_MONSTER) == 0) { GameBase.gi.centerprintf(activator, "" + ent.message); if (ent.noise_index != 0) { GameBase.gi.sound(activator, Defines.CHAN_AUTO, ent.noise_index, 1, Defines.ATTN_NORM, 0); } else { GameBase.gi.sound(activator, Defines.CHAN_AUTO, GameBase.gi.soundindex("misc/talk1.wav"), 1, Defines.ATTN_NORM, 0); } } // kill killtargets EdictIterator edit = null; if (ent.killtarget != null) { while ((edit = GameBase.G_Find(edit, GameBase.findByTarget, ent.killtarget)) != null) { t = edit.o; GameUtil.G_FreeEdict(t); if (!ent.inuse) { GameBase.gi.dprintf("entity was removed while using killtargets\n"); return; } } } // fire targets if (ent.target != null) { edit = null; while ((edit = GameBase.G_Find(edit, GameBase.findByTarget, ent.target)) != null) { t = edit.o; // doors fire area portals in a specific way if (Lib.Q_stricmp("func_areaportal", t.classname) == 0 && (Lib.Q_stricmp("func_door", ent.classname) == 0 || Lib.Q_stricmp("func_door_rotating", ent.classname) == 0)) { continue; } if (t == ent) { GameBase.gi.dprintf("WARNING: Entity used itself.\n"); } else { if (t.use != null) { t.use.use(t, ent, activator); } } if (!ent.inuse) { GameBase.gi.dprintf("entity was removed while using targets\n"); return; } } } }