]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/monsters/sv_monsters.qc
Merge branch 'TimePath/spawnfunc' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / monsters / sv_monsters.qc
index b6e6fd1c8c27b4ea11a86e13348fa4127106d8c3..18bf6af2a9a64f238b39d94b3316337e56c862b6 100644 (file)
     #include "../../server/defs.qh"
     #include "../deathtypes.qh"
     #include "../../server/mutators/mutators_include.qh"
-    #include "../../server/tturrets/include/turrets_early.qh"
-    #include "../../server/steerlib.qh"
-    #include "../../server/vehicles/vehicle.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 "../../csqcmodellib/sv_model.qh"
     #include "../../server/round_handler.qh"
-    #include "../../server/tturrets/include/turrets.qh"
 #endif
 
 void monsters_setstatus()
-{
+{SELFPARAM();
        self.stat_monsters_total = monsters_total;
        self.stat_monsters_killed = monsters_killed;
 }
 
 void monster_dropitem()
-{
+{SELFPARAM();
        if(!self.candrop || !self.monster_loot)
                return;
 
        vector org = self.origin + ((self.mins + self.maxs) * 0.5);
-       entity e = spawn(), oldself = self;
+       entity e = spawn();
 
        e.monster_loot = self.monster_loot;
 
-       other = e;
-       MUTATOR_CALLHOOK(MonsterDropItem);
+       MUTATOR_CALLHOOK(MonsterDropItem, e);
        e = other;
 
        if(e && e.monster_loot)
        {
-               self = e;
+               setself(e);
                e.noalign = true;
-               e.monster_loot();
+               e.monster_loot(e);
                e.gravity = 1;
                e.movetype = MOVETYPE_TOSS;
                e.reset = SUB_Remove;
@@ -59,13 +58,13 @@ void monster_dropitem()
                e.item_spawnshieldtime = time + 0.7;
                e.classname = "droppedweapon"; // use weapon handling to remove it on touch
                SUB_SetFade(e, time + autocvar_g_monsters_drop_time, 1);
-               self = oldself;
+               setself(this);
        }
 }
 
 void monster_makevectors(entity e)
-{
-       if(self.flags & FL_MONSTER)
+{SELFPARAM();
+       if(IS_MONSTER(self))
        {
                vector v;
 
@@ -82,20 +81,20 @@ void monster_makevectors(entity e)
 // ===============
 
 bool Monster_ValidTarget(entity mon, entity player)
-{
+{SELFPARAM();
        // ensure we're not checking nonexistent monster/target
        if(!mon || !player) { return false; }
 
        if((player == mon)
        || (autocvar_g_monsters_lineofsight && !checkpvs(mon.origin + mon.view_ofs, player)) // enemy cannot be seen
-       || ((player.vehicle_flags & VHF_ISVEHICLE) && !((get_monsterinfo(mon.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
+       || (IS_VEHICLE(player) && !((get_monsterinfo(mon.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
        || (time < game_starttime) // monsters do nothing before match has started
        || (player.takedamage == DAMAGE_NO)
        || (player.items & IT_INVISIBILITY)
        || (IS_SPEC(player) || IS_OBSERVER(player)) // don't attack spectators
-       || (!(player.vehicle_flags & VHF_ISVEHICLE) && (player.deadflag != DEAD_NO || mon.deadflag != DEAD_NO || player.health <= 0 || mon.health <= 0))
+       || (!IS_VEHICLE(player) && (player.deadflag != DEAD_NO || mon.deadflag != DEAD_NO || player.health <= 0 || mon.health <= 0))
        || (mon.monster_follow == player || player.monster_follow == mon)
-       || (!(player.vehicle_flags & VHF_ISVEHICLE) && (player.flags & FL_NOTARGET))
+       || (!IS_VEHICLE(player) && (player.flags & FL_NOTARGET))
        || (!autocvar_g_monsters_typefrag && player.BUTTON_CHAT)
        || (SAME_TEAM(player, mon))
        || (player.frozen)
@@ -119,7 +118,7 @@ bool Monster_ValidTarget(entity mon, entity player)
                makevectors (mon.angles);
                dot = normalize (player.origin - mon.origin) * v_forward;
 
-               if(dot <= 0.3) { return false; }
+               if(dot <= autocvar_g_monsters_target_infront_range) { return false; }
        }
 
        return true; // this target is valid!
@@ -182,11 +181,11 @@ void monster_setupcolors(entity mon)
 void monster_changeteam(entity ent, float newteam)
 {
        if(!teamplay) { return; }
-       
+
        ent.team = newteam;
        ent.monster_attack = true; // new team, activate attacking
        monster_setupcolors(ent);
-       
+
        if(ent.sprite)
        {
                WaypointSprite_UpdateTeamRadar(ent.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0'));
@@ -197,9 +196,9 @@ void monster_changeteam(entity ent, float newteam)
 }
 
 void Monster_Delay_Action()
-{
+{SELFPARAM();
        entity oldself = self;
-       self = self.owner;
+       setself(self.owner);
        if(Monster_ValidTarget(self, self.enemy)) { oldself.use(); }
 
        if(oldself.cnt > 0)
@@ -216,7 +215,7 @@ void Monster_Delay_Action()
 }
 
 void Monster_Delay(float repeat_count, float repeat_defer, float defer_amnt, void() func)
-{
+{SELFPARAM();
        // deferred attacking, checks if monster is still alive and target is still valid before attacking
        entity e = spawn();
 
@@ -258,7 +257,7 @@ void Monster_Sound_Precache(string f)
        {
                if(tokenize_console(s) != 3)
                {
-                       dprint("Invalid sound info line: ", s, "\n");
+                       LOG_TRACE("Invalid sound info line: ", s, "\n");
                        continue;
                }
                PrecacheGlobalSound(strcat(argv(1), " ", argv(2)));
@@ -267,7 +266,7 @@ void Monster_Sound_Precache(string f)
 }
 
 void Monster_Sounds_Precache()
-{
+{SELFPARAM();
        string m = (get_monsterinfo(self.monsterid)).model;
        float globhandle, n, i;
        string f;
@@ -286,7 +285,7 @@ void Monster_Sounds_Precache()
 }
 
 void Monster_Sounds_Clear()
-{
+{SELFPARAM();
 #define _MSOUND(m) if(self.monstersound_##m) { strunzone(self.monstersound_##m); self.monstersound_##m = string_null; }
        ALLMONSTERSOUNDS
 #undef _MSOUND
@@ -305,15 +304,15 @@ void Monster_Sounds_Clear()
        return string_null;
 }
 
-float Monster_Sounds_Load(string f, float first)
-{
+bool Monster_Sounds_Load(string f, int first)
+{SELFPARAM();
        float fh;
        string s;
        var .string field;
        fh = fopen(f, FILE_READ);
        if(fh < 0)
        {
-               dprint("Monster sound file not found: ", f, "\n");
+               LOG_TRACE("Monster sound file not found: ", f, "\n");
                return false;
        }
        while((s = fgets(fh)))
@@ -333,7 +332,7 @@ float Monster_Sounds_Load(string f, float first)
 
 .int skin_for_monstersound;
 void Monster_Sounds_Update()
-{
+{SELFPARAM();
        if(self.skin == self.skin_for_monstersound) { return; }
 
        self.skin_for_monstersound = self.skin;
@@ -343,7 +342,7 @@ void Monster_Sounds_Update()
 }
 
 void Monster_Sound(.string samplefield, float sound_delay, float delaytoo, float chan)
-{
+{SELFPARAM();
        if(!autocvar_g_monsters_sounds) { return; }
 
        if(delaytoo)
@@ -360,7 +359,7 @@ void Monster_Sound(.string samplefield, float sound_delay, float delaytoo, float
 // =======================
 
 float Monster_Attack_Melee(entity targ, float damg, vector anim, float er, float animtime, int deathtype, float dostop)
-{
+{SELFPARAM();
        if(dostop && (self.flags & FL_MONSTER)) { self.state = MONSTER_ATTACK_MELEE; }
 
        setanim(self, anim, false, true, false);
@@ -381,7 +380,7 @@ float Monster_Attack_Melee(entity targ, float damg, vector anim, float er, float
 }
 
 float Monster_Attack_Leap_Check(vector vel)
-{
+{SELFPARAM();
        if(self.state && (self.flags & FL_MONSTER))
                return false; // already attacking
        if(!(self.flags & FL_ONGROUND))
@@ -403,7 +402,7 @@ float Monster_Attack_Leap_Check(vector vel)
 }
 
 bool Monster_Attack_Leap(vector anm, void() touchfunc, vector vel, float animtime)
-{
+{SELFPARAM();
        if(!Monster_Attack_Leap_Check(vel))
                return false;
 
@@ -457,19 +456,37 @@ void Monster_Attack_Check(entity e, entity targ)
 // Main monster functions
 // ======================
 
+void Monster_UpdateModel()
+{SELFPARAM();
+       // assume some defaults
+       /*self.anim_idle   = animfixfps(self, '0 1 0.01', '0 0 0');
+       self.anim_walk   = animfixfps(self, '1 1 0.01', '0 0 0');
+       self.anim_run    = animfixfps(self, '2 1 0.01', '0 0 0');
+       self.anim_fire1  = animfixfps(self, '3 1 0.01', '0 0 0');
+       self.anim_fire2  = animfixfps(self, '4 1 0.01', '0 0 0');
+       self.anim_melee  = animfixfps(self, '5 1 0.01', '0 0 0');
+       self.anim_pain1  = animfixfps(self, '6 1 0.01', '0 0 0');
+       self.anim_pain2  = animfixfps(self, '7 1 0.01', '0 0 0');
+       self.anim_die1   = animfixfps(self, '8 1 0.01', '0 0 0');
+       self.anim_die2   = animfixfps(self, '9 1 0.01', '0 0 0');*/
+
+       // then get the real values
+       MON_ACTION(self.monsterid, MR_ANIM);
+}
+
 void Monster_Touch()
-{
+{SELFPARAM();
        if(other == world) { return; }
 
        if(other.monster_attack)
        if(self.enemy != other)
-       if(!(self.flags & FL_MONSTER))
+       if(!IS_MONSTER(other))
        if(Monster_ValidTarget(self, other))
                self.enemy = other;
 }
 
 void Monster_Miniboss_Check()
-{
+{SELFPARAM();
        if(MUTATOR_CALLHOOK(MonsterCheckBossFlag))
                return;
 
@@ -481,22 +498,29 @@ void Monster_Miniboss_Check()
                self.health += autocvar_g_monsters_miniboss_healthboost;
                self.effects |= EF_RED;
                if(!self.weapon)
-                       self.weapon = WEP_VORTEX;
+                       self.weapon = WEP_VORTEX.m_id;
        }
 }
 
-float Monster_Respawn_Check()
-{
-       if(self.spawnflags & MONSTERFLAG_NORESPAWN) { return false; }
-       if(!autocvar_g_monsters_respawn) { return false; }
+bool Monster_Respawn_Check()
+{SELFPARAM();
+       if(self.deadflag == DEAD_DEAD) // don't call when monster isn't dead
+       if(MUTATOR_CALLHOOK(MonsterRespawn, self))
+               return true; // enabled by a mutator
+
+       if(self.spawnflags & MONSTERFLAG_NORESPAWN)
+               return false;
+
+       if(!autocvar_g_monsters_respawn)
+               return false;
 
        return true;
 }
 
-void Monster_Respawn() { Monster_Spawn(self.monsterid); }
+void Monster_Respawn() { SELFPARAM(); Monster_Spawn(self.monsterid); }
 
 void Monster_Dead_Fade()
-{
+{SELFPARAM();
        if(Monster_Respawn_Check())
        {
                self.spawnflags |= MONSTERFLAG_RESPAWNED;
@@ -514,7 +538,7 @@ void Monster_Dead_Fade()
                setorigin(self, self.pos1);
                self.angles = self.pos2;
                self.health = self.max_health;
-               setmodel(self, "null");
+               setmodel(self, MDL_Null);
        }
        else
        {
@@ -526,12 +550,12 @@ void Monster_Dead_Fade()
 }
 
 void Monster_Use()
-{
+{SELFPARAM();
        if(Monster_ValidTarget(self, activator)) { self.enemy = activator; }
 }
 
 vector Monster_Move_Target(entity targ)
-{
+{SELFPARAM();
        // enemy is always preferred target
        if(self.enemy)
        {
@@ -555,7 +579,7 @@ vector Monster_Move_Target(entity targ)
 
                if(self.enemy)
                {
-                       /*WarpZone_TrailParticles(world, particleeffectnum("red_pass"), self.origin, targ_origin);
+                       /*WarpZone_TrailParticles(world, particleeffectnum(EFFECT_RED_PASS), self.origin, targ_origin);
                        print("Trace origin: ", vtos(targ_origin), "\n");
                        print("Target origin: ", vtos(self.enemy.origin), "\n");
                        print("My origin: ", vtos(self.origin), "\n"); */
@@ -640,7 +664,7 @@ vector Monster_Move_Target(entity targ)
 }
 
 void Monster_CalculateVelocity(entity mon, vector to, vector from, float turnrate, float movespeed)
-{
+{SELFPARAM();
        float current_distance = vlen((('1 0 0' * to.x) + ('0 1 0' * to.y)) - (('1 0 0' * from.x) + ('0 1 0' * from.y))); // for the sake of this check, exclude Z axis
        float initial_height = 0; //min(50, (targ_distance * tanh(20)));
        float current_height = (initial_height * min(1, (self.pass_distance) ? (current_distance / self.pass_distance) : current_distance));
@@ -674,7 +698,7 @@ void Monster_CalculateVelocity(entity mon, vector to, vector from, float turnrat
 }
 
 void Monster_Move(float runspeed, float walkspeed, float stpspeed)
-{
+{SELFPARAM();
        if(self.target2) { self.goalentity = find(world, targetname, self.target2); }
 
        entity targ;
@@ -765,18 +789,13 @@ void Monster_Move(float runspeed, float walkspeed, float stpspeed)
 
        targ = self.goalentity;
 
-       monster_target = targ;
-       monster_speed_run = runspeed;
-       monster_speed_walk = walkspeed;
-
-       if((MUTATOR_CALLHOOK(MonsterMove))
-       || (gameover)
-       || (self.draggedby != world)
-       || (round_handler_IsActive() && !round_handler_IsRoundStarted())
-       || (time < game_starttime)
-       || (autocvar_g_campaign && !campaign_bots_may_start)
-       || (time < self.spawn_time)
-       )
+       if (MUTATOR_CALLHOOK(MonsterMove, runspeed, walkspeed, targ)
+               || gameover
+               || self.draggedby != world
+               || (round_handler_IsActive() && !round_handler_IsRoundStarted())
+               || time < game_starttime
+               || (autocvar_g_campaign && !campaign_bots_may_start)
+               || time < self.spawn_time)
        {
                runspeed = walkspeed = 0;
                if(time >= self.spawn_time)
@@ -812,7 +831,7 @@ void Monster_Move(float runspeed, float walkspeed, float stpspeed)
                                self.moveto = WarpZone_RefSys_TransformOrigin(self.enemy, self, (0.5 * (self.enemy.absmin + self.enemy.absmax)));
                                self.monster_moveto = '0 0 0';
                                self.monster_face = '0 0 0';
-                               
+
                                self.pass_distance = vlen((('1 0 0' * self.enemy.origin_x) + ('0 1 0' * self.enemy.origin_y)) - (('1 0 0' *  self.origin_x) + ('0 1 0' *  self.origin_y)));
                                Monster_Sound(monstersound_sight, 0, false, CH_VOICE);
                        }
@@ -848,7 +867,7 @@ void Monster_Move(float runspeed, float walkspeed, float stpspeed)
 
        if(vlen(self.origin - self.moveto) > 100)
        {
-               float do_run = !!(self.enemy);
+               float do_run = (self.enemy || self.monster_moveto);
                if((self.flags & FL_ONGROUND) || ((self.flags & FL_FLY) || (self.flags & FL_SWIM)))
                        Monster_CalculateVelocity(self, self.moveto, self.origin, true, ((do_run) ? runspeed : walkspeed));
 
@@ -895,8 +914,8 @@ void Monster_Remove(entity mon)
 {
        if(!mon) { return; }
 
-       if(!MUTATOR_CALLHOOK(MonsterRemove))
-               pointparticles(particleeffectnum("item_pickup"), mon.origin, '0 0 0', 1);
+       if(!MUTATOR_CALLHOOK(MonsterRemove, mon))
+               Send_Effect(EFFECT_ITEM_PICKUP, mon.origin, '0 0 0', 1);
 
        if(mon.weaponentity) { remove(mon.weaponentity); }
        if(mon.iceblock) { remove(mon.iceblock); }
@@ -905,7 +924,7 @@ void Monster_Remove(entity mon)
 }
 
 void Monster_Dead_Think()
-{
+{SELFPARAM();
        self.nextthink = time + self.ticrate;
 
        if(self.monster_lifetime != 0)
@@ -917,7 +936,7 @@ void Monster_Dead_Think()
 }
 
 void Monster_Appear()
-{
+{SELFPARAM();
        self.enemy = activator;
        self.spawnflags &= ~MONSTERFLAG_APPEAR; // otherwise, we get an endless loop
        Monster_Spawn(self.monsterid);
@@ -938,7 +957,7 @@ float Monster_Appear_Check(entity ent, float monster_id)
 }
 
 void Monster_Reset()
-{
+{SELFPARAM();
        setorigin(self, self.pos1);
        self.angles = self.pos2;
 
@@ -953,7 +972,7 @@ void Monster_Reset()
 }
 
 void Monster_Dead_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{
+{SELFPARAM();
        self.health -= damage;
 
        Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
@@ -972,7 +991,7 @@ void Monster_Dead_Damage(entity inflictor, entity attacker, float damage, int de
 }
 
 void Monster_Dead(entity attacker, float gibbed)
-{
+{SELFPARAM();
        self.think = Monster_Dead_Think;
        self.nextthink = time;
        self.monster_lifetime = time + 5;
@@ -1025,8 +1044,8 @@ void Monster_Dead(entity attacker, float gibbed)
 }
 
 void Monster_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{
-       if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL)
+{SELFPARAM();
+       if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL && !ITEM_DAMAGE_NEEDKILL(deathtype))
                return;
 
        if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
@@ -1066,7 +1085,7 @@ void Monster_Damage(entity inflictor, entity attacker, float damage, int deathty
        self.dmg_time = time;
 
        if(sound_allowed(MSG_BROADCAST, attacker) && deathtype != DEATH_DROWN)
-               spamsound (self, CH_PAIN, "misc/bodyimpact1.wav", VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
+               spamsound (self, CH_PAIN, SND(BODYIMPACT1), VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
 
        self.velocity += force * self.damageforcescale;
 
@@ -1094,9 +1113,8 @@ void Monster_Damage(entity inflictor, entity attacker, float damage, int deathty
 
                WaypointSprite_Kill(self.sprite);
 
-               frag_attacker = attacker;
                frag_target = self;
-               MUTATOR_CALLHOOK(MonsterDies);
+               MUTATOR_CALLHOOK(MonsterDies, attacker);
 
                if(self.health <= -100 || deathtype == DEATH_KILL) // check if we're already gibbed
                {
@@ -1110,7 +1128,7 @@ void Monster_Damage(entity inflictor, entity attacker, float damage, int deathty
 
 // don't check for enemies, just keep walking in a straight line
 void Monster_Move_2D(float mspeed, float allow_jumpoff)
-{
+{SELFPARAM();
        if(gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || self.draggedby != world || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < self.spawn_time)
        {
                mspeed = 0;
@@ -1122,22 +1140,22 @@ void Monster_Move_2D(float mspeed, float allow_jumpoff)
 
        float reverse = FALSE;
        vector a, b;
-       
+
        makevectors(self.angles);
        a = self.origin + '0 0 16';
        b = self.origin + '0 0 16' + v_forward * 32;
-       
+
        traceline(a, b, MOVE_NORMAL, self);
-       
+
        if(trace_fraction != 1.0)
        {
                reverse = TRUE;
-               
+
                if(trace_ent)
                if(IS_PLAYER(trace_ent) && !(trace_ent.items & IT_STRENGTH))
                        reverse = FALSE;
        }
-       
+
        // TODO: fix this... tracing is broken if the floor is thin
        /*
        if(!allow_jumpoff)
@@ -1147,15 +1165,15 @@ void Monster_Move_2D(float mspeed, float allow_jumpoff)
                if(trace_fraction == 1.0)
                        reverse = TRUE;
        } */
-       
+
        if(reverse)
        {
                self.angles_y = anglemods(self.angles_y - 180);
                makevectors(self.angles);
        }
-       
+
        movelib_move_simple_gravity(v_forward, mspeed, 1);
-       
+
        if(time > self.pain_finished)
        if(time > self.attack_finished_single)
        if(vlen(self.velocity) > 10)
@@ -1164,8 +1182,45 @@ void Monster_Move_2D(float mspeed, float allow_jumpoff)
                setanim(self, self.anim_idle, true, false, false);
 }
 
+void Monster_Anim()
+{SELFPARAM();
+       int deadbits = (self.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2));
+       if(self.deadflag)
+       {
+               if (!deadbits)
+               {
+                       // Decide on which death animation to use.
+                       if(random() < 0.5)
+                               deadbits = ANIMSTATE_DEAD1;
+                       else
+                               deadbits = ANIMSTATE_DEAD2;
+               }
+       }
+       else
+       {
+               // Clear a previous death animation.
+               deadbits = 0;
+       }
+       int animbits = deadbits;
+       if(self.frozen)
+               animbits |= ANIMSTATE_FROZEN;
+       if(self.crouch)
+               animbits |= ANIMSTATE_DUCK; // not that monsters can crouch currently...
+       animdecide_setstate(self, animbits, false);
+       animdecide_setimplicitstate(self, (self.flags & FL_ONGROUND));
+
+       /* // weapon entities for monsters?
+       if (self.weaponentity)
+       {
+               updateanim(self.weaponentity);
+               if (!self.weaponentity.animstate_override)
+                       setanim(self.weaponentity, self.weaponentity.anim_idle, true, false, false);
+       }
+       */
+}
+
 void Monster_Think()
-{
+{SELFPARAM();
        self.think = Monster_Think;
        self.nextthink = self.ticrate;
 
@@ -1179,11 +1234,13 @@ void Monster_Think()
        if(MON_ACTION(self.monsterid, MR_THINK))
                Monster_Move(self.speed2, self.speed, self.stopspeed);
 
-       CSQCMODEL_AUTOUPDATE();
+       Monster_Anim();
+
+       CSQCMODEL_AUTOUPDATE(self);
 }
 
 float Monster_Spawn_Setup()
-{
+{SELFPARAM();
        MON_ACTION(self.monsterid, MR_SETUP);
 
        // ensure some basic needs are met
@@ -1223,9 +1280,9 @@ float Monster_Spawn_Setup()
 
        if(autocvar_g_monsters_healthbars)
        {
-               WaypointSprite_Spawn(self.monster_name, 0, 1024, self, '0 0 1' * (self.maxs_z + 15), world, self.team, 
-                                                        self, sprite, true, RADARICON_DANGER, ((self.team) ? Team_ColorRGB(self.team) : '1 0 0'));
-
+               entity wp = WaypointSprite_Spawn(WP_Monster, 0, 1024, self, '0 0 1' * (self.maxs.z + 15), world, self.team, self, sprite, true, RADARICON_DANGER);
+               wp.wp_extra = self.monsterid;
+               wp.colormod = ((self.team) ? Team_ColorRGB(self.team) : '1 0 0');
                if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE))
                {
                        WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
@@ -1243,7 +1300,7 @@ float Monster_Spawn_Setup()
 }
 
 bool Monster_Spawn(int mon_id)
-{
+{SELFPARAM();
        // setup the basic required properties for a monster
        entity mon = get_monsterinfo(mon_id);
        if(!mon.monsterid) { return false; } // invalid monster
@@ -1268,7 +1325,7 @@ bool Monster_Spawn(int mon_id)
        if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) // don't count re-spawning monsters either
                monsters_total += 1;
 
-       setmodel(self, self.mdl);
+       _setmodel(self, self.mdl);
        self.flags                              = FL_MONSTER;
        self.classname                  = "monster";
        self.takedamage                 = DAMAGE_AIM;
@@ -1304,7 +1361,7 @@ bool Monster_Spawn(int mon_id)
        self.monster_moveto             = '0 0 0';
        self.monster_face               = '0 0 0';
        self.dphitcontentsmask  = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
-       
+
        if(!self.scale) { self.scale = 1; }
        if(autocvar_g_monsters_edit) { self.grab = 1; }
        if(autocvar_g_fullbrightplayers) { self.effects |= EF_FULLBRIGHT; }
@@ -1325,6 +1382,8 @@ bool Monster_Spawn(int mon_id)
 
        self.ticrate = bound(sys_frametime, ((!self.ticrate) ? autocvar_g_monsters_think_delay : self.ticrate), 60);
 
+       Monster_UpdateModel();
+
        if(!Monster_Spawn_Setup())
        {
                Monster_Remove(self);
@@ -1341,7 +1400,7 @@ bool Monster_Spawn(int mon_id)
        if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
                monster_setupcolors(self);
 
-       CSQCMODEL_AUTOINIT();
+       CSQCMODEL_AUTOINIT(self);
 
        return true;
 }