]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/monsters/sv_monsters.qc
Merge branch 'master' into TimePath/modules
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / monsters / sv_monsters.qc
index bba248b8d4f42de6df35de02b31bd52b9f5def1d..a4f7e6acaace738772fd366d81f499b57a4cdf7c 100644 (file)
@@ -1,29 +1,27 @@
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-    #include <lib/warpzone/common.qh>
-    #include "../constants.qh"
-    #include "../teams.qh"
-    #include "../util.qh"
-    #include "all.qh"
-    #include "sv_monsters.qh"
-       #include "../physics/movelib.qh"
-    #include "../weapons/all.qh"
-    #include <server/autocvars.qh>
-    #include <server/defs.qh>
-    #include "../deathtypes/all.qh"
-    #include <server/mutators/all.qh>
-       #include <server/steerlib.qh>
-       #include "../turrets/sv_turrets.qh"
-       #include "../turrets/util.qh"
-    #include "../vehicles/all.qh"
-    #include <server/campaign.qh>
-    #include <server/command/common.qh>
-    #include <server/command/cmd.qh>
-       #include "../triggers/triggers.qh"
-    #include <lib/csqcmodel/sv_model.qh>
-    #include <server/round_handler.qh>
-#endif
+#include "sv_monsters.qh"
+
+#include <server/g_subs.qh>
+#include <lib/warpzone/common.qh>
+#include "../constants.qh"
+#include "../teams.qh"
+#include "../util.qh"
+#include "all.qh"
+#include "../physics/movelib.qh"
+#include "../weapons/_mod.qh"
+#include <server/autocvars.qh>
+#include <server/defs.qh>
+#include "../deathtypes/all.qh"
+#include <server/mutators/_mod.qh>
+#include <server/steerlib.qh>
+#include "../turrets/sv_turrets.qh"
+#include "../turrets/util.qh"
+#include "../vehicles/all.qh"
+#include <server/campaign.qh>
+#include <server/command/_mod.qh>
+#include "../triggers/triggers.qh"
+#include <lib/csqcmodel/sv_model.qh>
+#include <server/round_handler.qh>
+#include <server/weapons/_mod.qh>
 
 void monsters_setstatus(entity this)
 {
@@ -31,7 +29,7 @@ void monsters_setstatus(entity this)
        this.stat_monsters_killed = monsters_killed;
 }
 
-void monster_dropitem(entity this)
+void monster_dropitem(entity this, entity attacker)
 {
        if(!this.candrop || !this.monster_loot)
                return;
@@ -42,15 +40,15 @@ void monster_dropitem(entity this)
 
        e.monster_loot = this.monster_loot;
 
-       MUTATOR_CALLHOOK(MonsterDropItem, e);
-       e = other;
+       MUTATOR_CALLHOOK(MonsterDropItem, this, e, attacker);
+       e = M_ARGV(1, entity);
 
        if(e && e.monster_loot)
        {
                e.noalign = true;
-               WITH(entity, self, e, e.monster_loot(e));
+               e.monster_loot(e);
                e.gravity = 1;
-               e.movetype = MOVETYPE_TOSS;
+               set_movetype(e, MOVETYPE_TOSS);
                e.reset = SUB_Remove;
                setorigin(e, org);
                e.velocity = randomvec() * 175 + '0 0 325';
@@ -94,6 +92,7 @@ bool Monster_ValidTarget(entity this, entity targ)
        || (SAME_TEAM(targ, this))
        || (STAT(FROZEN, targ))
        || (targ.alpha != 0 && targ.alpha < 0.5)
+       || (MUTATOR_CALLHOOK(MonsterValidTarget, this, targ))
        )
        {
                // if any of the above checks fail, target is not valid
@@ -123,29 +122,26 @@ entity Monster_FindTarget(entity mon)
 {
        if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return mon.enemy; } // Handled by a mutator
 
-       entity head, closest_target = world;
-       head = findradius(mon.origin, mon.target_range);
+       entity closest_target = NULL;
 
-       while(head) // find the closest acceptable target to pass to
+       // find the closest acceptable target to pass to
+       FOREACH_ENTITY_RADIUS(mon.origin, mon.target_range, it.monster_attack,
        {
-               if(head.monster_attack)
-               if(Monster_ValidTarget(mon, head))
+               if(Monster_ValidTarget(mon, it))
                {
                        // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
-                       vector head_center = CENTER_OR_VIEWOFS(head);
+                       vector head_center = CENTER_OR_VIEWOFS(it);
                        vector ent_center = CENTER_OR_VIEWOFS(mon);
 
                        if(closest_target)
                        {
                                vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
                                if(vlen2(ent_center - head_center) < vlen2(ent_center - closest_target_center))
-                                       { closest_target = head; }
+                                       { closest_target = it; }
                        }
-                       else { closest_target = head; }
+                       else { closest_target = it; }
                }
-
-               head = head.chain;
-       }
+       });
 
        return closest_target;
 }
@@ -191,7 +187,6 @@ void monster_changeteam(entity ent, float newteam)
 }
 
 .void(entity) monster_delayedfunc;
-void Monster_Delay_Action_self();
 void Monster_Delay_Action(entity this)
 {
        if(Monster_ValidTarget(this.owner, this.owner.enemy)) { this.monster_delayedfunc(this.owner); }
@@ -199,28 +194,22 @@ void Monster_Delay_Action(entity this)
        if(this.cnt > 1)
        {
                this.cnt -= 1;
-               this.think = Monster_Delay_Action_self;
+               setthink(this, Monster_Delay_Action);
                this.nextthink = time + this.count;
        }
        else
        {
-               this.think = SUB_Remove_self;
+               setthink(this, SUB_Remove);
                this.nextthink = time;
        }
 }
 
-void Monster_Delay_Action_self()
-{
-    SELFPARAM();
-       Monster_Delay_Action(self);
-}
-
 void Monster_Delay(entity this, int repeat_count, float defer_amnt, void(entity) func)
 {
        // deferred attacking, checks if monster is still alive and target is still valid before attacking
        entity e = spawn();
 
-       e.think = Monster_Delay_Action_self;
+       setthink(e, Monster_Delay_Action);
        e.nextthink = time + defer_amnt;
        e.count = defer_amnt;
        e.owner = this;
@@ -257,7 +246,7 @@ void Monster_Sound_Precache(string f)
        {
                if(tokenize_console(s) != 3)
                {
-                       LOG_TRACE("Invalid sound info line: ", s, "\n");
+                       LOG_TRACE("Invalid sound info line: ", s);
                        continue;
                }
                PrecacheGlobalSound(strcat(argv(1), " ", argv(2)));
@@ -312,7 +301,7 @@ bool Monster_Sounds_Load(entity this, string f, int first)
        fh = fopen(f, FILE_READ);
        if(fh < 0)
        {
-               LOG_TRACE("Monster sound file not found: ", f, "\n");
+               LOG_TRACE("Monster sound file not found: ", f);
                return false;
        }
        while((s = fgets(fh)))
@@ -348,7 +337,7 @@ void Monster_Sound(entity this, .string samplefield, float sound_delay, float de
        if(delaytoo)
        if(time < this.msound_delay)
                return; // too early
-       GlobalSound_string(this, this.(samplefield), chan, VOICETYPE_PLAYERSOUND);
+       GlobalSound_string(this, this.(samplefield), chan, VOL_BASE, VOICETYPE_PLAYERSOUND);
 
        this.msound_delay = time + sound_delay;
 }
@@ -401,7 +390,7 @@ bool Monster_Attack_Leap_Check(entity this, vector vel)
        return true;
 }
 
-bool Monster_Attack_Leap(entity this, vector anm, void() touchfunc, vector vel, float animtime)
+bool Monster_Attack_Leap(entity this, vector anm, void(entity this, entity toucher) touchfunc, vector vel, float animtime)
 {
        if(!Monster_Attack_Leap_Check(this, vel))
                return false;
@@ -415,7 +404,7 @@ bool Monster_Attack_Leap(entity this, vector anm, void() touchfunc, vector vel,
 
        if(this.flags & FL_MONSTER)
                this.state = MONSTER_ATTACK_RANGED;
-       this.touch = touchfunc;
+       settouch(this, touchfunc);
        this.origin_z += 1;
        this.velocity = vel;
        UNSET_ONGROUND(this);
@@ -425,7 +414,7 @@ bool Monster_Attack_Leap(entity this, vector anm, void() touchfunc, vector vel,
 
 void Monster_Attack_Check(entity this, entity targ)
 {
-       if((this == world || targ == world)
+       if((this == NULL || targ == NULL)
        || (!this.monster_attackfunc)
        || (time < this.attack_finished_single[0])
        ) { return; }
@@ -473,15 +462,15 @@ void Monster_UpdateModel(entity this)
        mon.mr_anim(mon, this);
 }
 
-void Monster_Touch()
-{SELFPARAM();
-       if(other == world) { return; }
+void Monster_Touch(entity this, entity toucher)
+{
+       if(toucher == NULL) { return; }
 
-       if(other.monster_attack)
-       if(this.enemy != other)
-       if(!IS_MONSTER(other))
-       if(Monster_ValidTarget(this, other))
-               this.enemy = other;
+       if(toucher.monster_attack)
+       if(this.enemy != toucher)
+       if(!IS_MONSTER(toucher))
+       if(Monster_ValidTarget(this, toucher))
+               this.enemy = toucher;
 }
 
 void Monster_Miniboss_Check(entity this)
@@ -516,14 +505,16 @@ bool Monster_Respawn_Check(entity this)
        return true;
 }
 
-void Monster_Respawn() { SELFPARAM(); Monster_Spawn(this, this.monsterid); }
+void Monster_Respawn(entity this) { Monster_Spawn(this, this.monsterid); }
+
+.vector        pos1, pos2;
 
 void Monster_Dead_Fade(entity this)
 {
        if(Monster_Respawn_Check(this))
        {
                this.spawnflags |= MONSTERFLAG_RESPAWNED;
-               this.think = Monster_Respawn;
+               setthink(this, Monster_Respawn);
                this.nextthink = time + this.respawntime;
                this.monster_lifetime = 0;
                this.deadflag = DEAD_RESPAWNING;
@@ -548,11 +539,12 @@ void Monster_Dead_Fade(entity this)
        }
 }
 
-void Monster_Use()
-{SELFPARAM();
-       if(Monster_ValidTarget(this, activator)) { this.enemy = activator; }
+void Monster_Use(entity this, entity actor, entity trigger)
+{
+       if(Monster_ValidTarget(this, actor)) { this.enemy = actor; }
 }
 
+.float pass_distance;
 vector Monster_Move_Target(entity this, entity targ)
 {
        // enemy is always preferred target
@@ -562,7 +554,7 @@ vector Monster_Move_Target(entity this, entity targ)
                targ_origin = WarpZone_RefSys_TransformOrigin(this.enemy, this, targ_origin); // origin of target as seen by the monster (us)
                WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this);
 
-               if((this.enemy == world)
+               if((this.enemy == NULL)
                        || (IS_DEAD(this.enemy) || this.enemy.health < 1)
                        || (STAT(FROZEN, this.enemy))
                        || (this.enemy.flags & FL_NOTARGET)
@@ -571,13 +563,13 @@ vector Monster_Move_Target(entity this, entity targ)
                        || (vdist(this.origin - targ_origin, >, this.target_range))
                        || ((trace_fraction < 1) && (trace_ent != this.enemy)))
                {
-                       this.enemy = world;
+                       this.enemy = NULL;
                        this.pass_distance = 0;
                }
 
                if(this.enemy)
                {
-                       /*WarpZone_TrailParticles(world, particleeffectnum(EFFECT_RED_PASS), this.origin, targ_origin);
+                       /*WarpZone_TrailParticles(NULL, particleeffectnum(EFFECT_RED_PASS), this.origin, targ_origin);
                        print("Trace origin: ", vtos(targ_origin), "\n");
                        print("Target origin: ", vtos(this.enemy.origin), "\n");
                        print("My origin: ", vtos(this.origin), "\n"); */
@@ -695,9 +687,12 @@ void Monster_CalculateVelocity(entity this, vector to, vector from, float turnra
        //this.angles = vectoangles(this.velocity);
 }
 
+.entity draggedby;
+.entity target2;
+
 void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
 {
-       if(this.target2) { this.goalentity = find(world, targetname, this.target2); }
+       if(this.target2) { this.goalentity = find(NULL, targetname, this.target2); }
 
        entity targ;
 
@@ -713,7 +708,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
                movelib_brake_simple(this, stpspeed);
                setanim(this, this.anim_idle, true, false, false);
 
-               this.enemy = world;
+               this.enemy = NULL;
                this.nextthink = time + this.ticrate;
 
                if(this.revive_progress >= 1)
@@ -732,7 +727,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
                movelib_brake_simple(this, stpspeed);
                setanim(this, this.anim_idle, true, false, false);
 
-               this.enemy = world;
+               this.enemy = NULL;
                this.nextthink = time + this.ticrate;
 
                if(this.health < 1)
@@ -757,7 +752,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
                        {
                                this.last_trace = time + 0.4;
 
-                               Damage (this, world, world, 2, DEATH_DROWN.m_id, this.origin, '0 0 0');
+                               Damage (this, NULL, NULL, 2, DEATH_DROWN.m_id, this.origin, '0 0 0');
                                this.angles = '90 90 0';
                                if(random() < 0.5)
                                {
@@ -773,15 +768,15 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
                        }
 
 
-                       this.movetype = MOVETYPE_BOUNCE;
+                       set_movetype(this, MOVETYPE_BOUNCE);
                        //this.velocity_z = -200;
 
                        return;
                }
-               else if(this.movetype == MOVETYPE_BOUNCE)
+               else if(this.move_movetype == MOVETYPE_BOUNCE)
                {
                        this.angles_x = 0;
-                       this.movetype = MOVETYPE_WALK;
+                       set_movetype(this, MOVETYPE_WALK);
                }
        }
 
@@ -789,7 +784,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
 
        if (MUTATOR_CALLHOOK(MonsterMove, this, runspeed, walkspeed, targ)
                || gameover
-               || this.draggedby != world
+               || this.draggedby != NULL
                || (round_handler_IsActive() && !round_handler_IsRoundStarted())
                || time < game_starttime
                || (autocvar_g_campaign && !campaign_bots_may_start)
@@ -802,14 +797,14 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
                return;
        }
 
-       targ = monster_target;
-       runspeed = bound(0, monster_speed_run * MONSTER_SKILLMOD(this), runspeed * 2.5); // limit maxspeed to prevent craziness
-       walkspeed = bound(0, monster_speed_walk * MONSTER_SKILLMOD(this), walkspeed * 2.5); // limit maxspeed to prevent craziness
+       targ = M_ARGV(3, entity);
+       runspeed = bound(0, M_ARGV(1, float) * MONSTER_SKILLMOD(this), runspeed * 2.5); // limit maxspeed to prevent craziness
+       walkspeed = bound(0, M_ARGV(2, float) * MONSTER_SKILLMOD(this), walkspeed * 2.5); // limit maxspeed to prevent craziness
 
        if(teamplay)
        if(autocvar_g_monsters_teams)
        if(DIFF_TEAM(this.monster_follow, this))
-               this.monster_follow = world;
+               this.monster_follow = NULL;
 
        if(time >= this.last_enemycheck)
        {
@@ -835,7 +830,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
        if(this.state == MONSTER_ATTACK_RANGED && IS_ONGROUND(this))
        {
                this.state = 0;
-               this.touch = Monster_Touch;
+               settouch(this, Monster_Touch);
        }
 
        if(this.state && time >= this.attack_finished_single[0])
@@ -873,7 +868,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
        }
        else
        {
-               entity e = find(world, targetname, this.target2);
+               entity e = find(NULL, targetname, this.target2);
                if(e.target2)
                        this.target2 = e.target2;
                else if(e.target)
@@ -907,20 +902,24 @@ void Monster_Remove(entity this)
        if(IS_CLIENT(this))
                return; // don't remove it?
 
-       .entity weaponentity = weaponentities[0];
        if(!this) { return; }
 
        if(!MUTATOR_CALLHOOK(MonsterRemove, this))
                Send_Effect(EFFECT_ITEM_PICKUP, this.origin, '0 0 0', 1);
 
-       if(this.(weaponentity)) { remove(this.(weaponentity)); }
-       if(this.iceblock) { remove(this.iceblock); }
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               .entity weaponentity = weaponentities[slot];
+               if(this.(weaponentity))
+                       delete(this.(weaponentity));
+       }
+       if(this.iceblock) { delete(this.iceblock); }
        WaypointSprite_Kill(this.sprite);
-       remove(this);
+       delete(this);
 }
 
-void Monster_Dead_Think()
-{SELFPARAM();
+void Monster_Dead_Think(entity this)
+{
        this.nextthink = time + this.ticrate;
 
        if(this.monster_lifetime != 0)
@@ -931,9 +930,9 @@ void Monster_Dead_Think()
        }
 }
 
-void Monster_Appear()
-{SELFPARAM();
-       this.enemy = activator;
+void Monster_Appear(entity this, entity actor, entity trigger)
+{
+       this.enemy = actor;
        this.spawnflags &= ~MONSTERFLAG_APPEAR; // otherwise, we get an endless loop
        Monster_Spawn(this, this.monsterid);
 }
@@ -943,7 +942,7 @@ bool Monster_Appear_Check(entity this, int monster_id)
        if(!(this.spawnflags & MONSTERFLAG_APPEAR))
                return false;
 
-       this.think = func_null;
+       setthink(this, func_null);
        this.monsterid = monster_id; // set so this monster is properly registered (otherwise, normal initialization is used)
        this.nextthink = 0;
        this.use = Monster_Appear;
@@ -961,8 +960,8 @@ void Monster_Reset(entity this)
 
        this.health = this.max_health;
        this.velocity = '0 0 0';
-       this.enemy = world;
-       this.goalentity = world;
+       this.enemy = NULL;
+       this.goalentity = NULL;
        this.attack_finished_single[0] = 0;
        this.moveto = this.origin;
 }
@@ -980,7 +979,7 @@ void Monster_Dead_Damage(entity this, entity inflictor, entity attacker, float d
                // number of monsters spawned with mobspawn command
                totalspawned -= 1;
 
-               this.think = SUB_Remove_self;
+               setthink(this, SUB_Remove);
                this.nextthink = time + 0.1;
                this.event_damage = func_null;
        }
@@ -988,7 +987,7 @@ void Monster_Dead_Damage(entity this, entity inflictor, entity attacker, float d
 
 void Monster_Dead(entity this, entity attacker, float gibbed)
 {
-       this.think = Monster_Dead_Think;
+       setthink(this, Monster_Dead_Think);
        this.nextthink = time;
        this.monster_lifetime = time + 5;
 
@@ -998,7 +997,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed)
                this.health = 0; // reset by Unfreeze
        }
 
-       monster_dropitem(this);
+       monster_dropitem(this, attacker);
 
        Monster_Sound(this, monstersound_death, 0, false, CH_VOICE);
 
@@ -1019,10 +1018,10 @@ void Monster_Dead(entity this, entity attacker, float gibbed)
        this.solid                      = SOLID_CORPSE;
        this.takedamage         = DAMAGE_AIM;
        this.deadflag           = DEAD_DEAD;
-       this.enemy                      = world;
-       this.movetype           = MOVETYPE_TOSS;
+       this.enemy                      = NULL;
+       set_movetype(this, MOVETYPE_TOSS);
        this.moveto                     = this.origin;
-       this.touch                      = Monster_Touch; // reset incase monster was pouncing
+       settouch(this, Monster_Touch); // reset incase monster was pouncing
        this.reset                      = func_null;
        this.state                      = 0;
        this.attack_finished_single[0] = 0;
@@ -1054,7 +1053,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
        if(time < this.spawnshieldtime && deathtype != DEATH_KILL.m_id)
                return;
 
-       if(deathtype == DEATH_FALL.m_id && this.draggedby != world)
+       if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL)
                return;
 
        vector v;
@@ -1064,12 +1063,8 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
        take = v_x;
        save = v_y;
 
-       damage_take = take;
-       frag_attacker = attacker;
-       frag_deathtype = deathtype;
        Monster mon = get_monsterinfo(this.monsterid);
-       mon.mr_pain(mon, this);
-       take = damage_take;
+       take = mon.mr_pain(mon, this, take, attacker, deathtype);
 
        if(take)
        {
@@ -1102,22 +1097,20 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
                        this.candrop = false; // killed by mobkill command
 
                // TODO: fix this?
-               activator = attacker;
-               other = this.enemy;
-               WITH(entity, self, this, SUB_UseTargets());
+               SUB_UseTargets(this, attacker, this.enemy);
                this.target2 = this.oldtarget2; // reset to original target on death, incase we respawn
 
                Monster_Dead(this, attacker, (this.health <= -100 || deathtype == DEATH_KILL.m_id));
 
                WaypointSprite_Kill(this.sprite);
 
-               MUTATOR_CALLHOOK(MonsterDies, this, attacker);
+               MUTATOR_CALLHOOK(MonsterDies, this, attacker, deathtype);
 
                if(this.health <= -100 || deathtype == DEATH_KILL.m_id) // check if we're already gibbed
                {
                        Violence_GibSplash(this, 1, 0.5, attacker);
 
-                       this.think = SUB_Remove_self;
+                       setthink(this, SUB_Remove);
                        this.nextthink = time + 0.1;
                }
        }
@@ -1126,7 +1119,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
 // don't check for enemies, just keep walking in a straight line
 void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff)
 {
-       if(gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || this.draggedby != world || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < this.spawn_time)
+       if(gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || this.draggedby != NULL || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < this.spawn_time)
        {
                mspeed = 0;
                if(time >= this.spawn_time)
@@ -1216,9 +1209,9 @@ void Monster_Anim(entity this)
        */
 }
 
-void Monster_Think()
-{SELFPARAM();
-       this.think = Monster_Think;
+void Monster_Think(entity this)
+{
+       setthink(this, Monster_Think);
        this.nextthink = this.ticrate;
 
        if(this.monster_lifetime)
@@ -1279,7 +1272,7 @@ bool Monster_Spawn_Setup(entity this)
 
        if(autocvar_g_monsters_healthbars)
        {
-               entity wp = WaypointSprite_Spawn(WP_Monster, 0, 1024, this, '0 0 1' * (this.maxs.z + 15), world, this.team, this, sprite, true, RADARICON_DANGER);
+               entity wp = WaypointSprite_Spawn(WP_Monster, 0, 1024, this, '0 0 1' * (this.maxs.z + 15), NULL, this.team, this, sprite, true, RADARICON_DANGER);
                wp.wp_extra = this.monsterid;
                wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 0 0');
                if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE))
@@ -1289,7 +1282,7 @@ bool Monster_Spawn_Setup(entity this)
                }
        }
 
-       this.think = Monster_Think;
+       setthink(this, Monster_Think);
        this.nextthink = time + this.ticrate;
 
        if(MUTATOR_CALLHOOK(MonsterSpawn, this))
@@ -1306,6 +1299,9 @@ bool Monster_Spawn(entity this, int mon_id)
 
        if(!autocvar_g_monsters) { Monster_Remove(this); return false; }
 
+       if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
+               IL_PUSH(g_monsters, this);
+
        if(Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed
 
        if(!this.monster_skill)
@@ -1333,12 +1329,12 @@ bool Monster_Spawn(entity this, int mon_id)
        this.damagedbycontents  = true;
        this.monsterid                  = mon_id;
        this.event_damage               = Monster_Damage;
-       this.touch                              = Monster_Touch;
+       settouch(this, Monster_Touch);
        this.use                                = Monster_Use;
        this.solid                              = SOLID_BBOX;
-       this.movetype                   = MOVETYPE_WALK;
+       set_movetype(this, MOVETYPE_WALK);
        this.spawnshieldtime    = time + autocvar_g_monsters_spawnshieldtime;
-       this.enemy                              = world;
+       this.enemy                              = NULL;
        this.velocity                   = '0 0 0';
        this.moveto                             = this.origin;
        this.pos1                               = this.origin;
@@ -1371,7 +1367,7 @@ bool Monster_Spawn(entity this, int mon_id)
        if(mon.spawnflags & MONSTER_TYPE_FLY)
        {
                this.flags |= FL_FLY;
-               this.movetype = MOVETYPE_FLY;
+               set_movetype(this, MOVETYPE_FLY);
        }
 
        if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))