Пример #1
0
        // ============================================================================
        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);
        }
Пример #2
0
        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);
            }
        }
Пример #3
0
        /**
         * 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));
        }
Пример #4
0
        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);
                }
            }
        }
Пример #5
0
        /**
         * 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;
                    }
                }
            }
        }