]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/monsters/sv_monsters.qc
Merge branch 'Mario/minor_fixes' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / monsters / sv_monsters.qc
index 319b699a706372cc3aca360df4259d50dfac907c..1635b5cb0d235c6eeb84dac673c18d98e9421e96 100644 (file)
@@ -1,25 +1,33 @@
 #include "sv_monsters.qh"
 
+#include <common/constants.qh>
+#include <common/deathtypes/all.qh>
+#include <common/items/_mod.qh>
+#include <common/mapobjects/teleporters.qh>
+#include <common/mapobjects/triggers.qh>
+#include <common/monsters/all.qh>
+#include <common/mutators/mutator/nades/nades.qh>
+#include <common/mutators/mutator/status_effects/_mod.qh>
+#include <common/physics/movelib.qh>
+#include <common/stats.qh>
+#include <common/teams.qh>
+#include <common/turrets/sv_turrets.qh>
+#include <common/turrets/util.qh>
+#include <common/util.qh>
+#include <common/vehicles/all.qh>
+#include <common/weapons/_all.qh>
+#include <common/weapons/_mod.qh>
+#include <lib/csqcmodel/sv_model.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/cheats.qh>
+#include <server/client.qh>
 #include <server/command/_mod.qh>
-#include "../mapobjects/triggers.qh"
-#include <lib/csqcmodel/sv_model.qh>
+#include <server/damage.qh>
+#include <server/items/items.qh>
+#include <server/mutators/_mod.qh>
 #include <server/round_handler.qh>
+#include <server/steerlib.qh>
 #include <server/weapons/_mod.qh>
 
 void monsters_setstatus(entity this)
@@ -47,6 +55,8 @@ void monster_dropitem(entity this, entity attacker)
        {
                e.noalign = true;
                StartItem(e, e.monster_loot);
+               if(startitem_failed || wasfreed(e))
+                       return;
                e.gravity = 1;
                setorigin(e, org);
                e.velocity = randomvec() * 175 + '0 0 325';
@@ -92,16 +102,15 @@ bool Monster_ValidTarget(entity this, entity targ, bool skipfacing)
        if(!this || !targ) { return false; }
 
        if((targ == this)
+       || (IS_VEHICLE(targ) && !(this.monsterdef.spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
        || (time < game_starttime) // monsters do nothing before match has started
        || (targ.takedamage == DAMAGE_NO)
        || (game_stopped)
-       || (targ.items & IT_INVISIBILITY)
        || (IS_SPEC(targ) || IS_OBSERVER(targ)) // don't attack spectators
        || (!IS_VEHICLE(targ) && (IS_DEAD(targ) || IS_DEAD(this) || GetResource(targ, RES_HEALTH) <= 0 || GetResource(this, RES_HEALTH) <= 0))
        || (this.monster_follow == targ || targ.monster_follow == this)
        || (!IS_VEHICLE(targ) && (targ.flags & FL_NOTARGET))
        || (!autocvar_g_monsters_typefrag && PHYS_INPUT_BUTTON_CHAT(targ))
-       || (IS_VEHICLE(targ) && !((Monsters_from(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
        || (SAME_TEAM(targ, this))
        || (STAT(FROZEN, targ))
        || (targ.alpha != 0 && targ.alpha < 0.5)
@@ -236,7 +245,7 @@ void Monster_Delay_Action(entity this)
 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();
+       entity e = new_pure(Monster_Delay);
 
        setthink(e, Monster_Delay_Action);
        e.nextthink = time + defer_amnt;
@@ -285,7 +294,7 @@ void Monster_Sound_Precache(string f)
 
 void Monster_Sounds_Precache(entity this)
 {
-       string m = (Monsters_from(this.monsterid)).m_model.model_str();
+       string m = this.monsterdef.m_model.model_str();
        float globhandle, n, i;
        string f;
 
@@ -445,6 +454,7 @@ void Monster_Attack_Check(entity this, entity targ, .entity weaponentity)
 
        if((!this || !targ)
        || (!this.monster_attackfunc)
+       || (game_stopped)
        || (time < this.attack_finished_single[slot])
        || ((autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT)) && !monster_facing(this, targ))
        ) { return; }
@@ -490,7 +500,7 @@ void Monster_UpdateModel(entity this)
        this.anim_die2   = animfixfps(this, '9 1 0.01', '0 0 0');*/
 
        // then get the real values
-       Monster mon = Monsters_from(this.monsterid);
+       Monster mon = this.monsterdef;
        mon.mr_anim(mon, this);
 }
 
@@ -535,7 +545,7 @@ bool Monster_Respawn_Check(entity this)
        return true;
 }
 
-void Monster_Respawn(entity this) { Monster_Spawn(this, true, this.monsterid); }
+void Monster_Respawn(entity this) { Monster_Spawn(this, true, this.monsterdef); }
 
 .vector        pos1, pos2;
 
@@ -582,20 +592,6 @@ vector Monster_Move_Target(entity this, entity targ)
        {
                vector targ_origin = ((this.enemy.absmin + this.enemy.absmax) * 0.5);
                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);
-
-               // cases where the enemy may have changed their state (don't need to check everything here)
-               if(    (IS_DEAD(this.enemy) || GetResource(this.enemy, RES_HEALTH) < 1)
-                       || (STAT(FROZEN, this.enemy))
-                       || (this.enemy.flags & FL_NOTARGET)
-                       || (this.enemy.alpha < 0.5 && this.enemy.alpha != 0)
-                       || (this.enemy.takedamage == DAMAGE_NO)
-                       || (vdist(this.origin - targ_origin, >, this.target_range))
-                       || ((trace_fraction < 1) && (trace_ent != this.enemy))
-                       )
-               {
-                       this.enemy = NULL;
-               }
 
                if(this.enemy)
                {
@@ -857,7 +853,7 @@ void Monster_Dead_Think(entity this)
 {
        this.nextthink = time + this.ticrate;
 
-       Monster mon = Monsters_from(this.monsterid);
+       Monster mon = REGISTRY_GET(Monsters, this.monsterid);
        mon.mr_deadthink(mon, this);
 
        if(this.monster_lifetime != 0)
@@ -871,16 +867,16 @@ void Monster_Dead_Think(entity this)
 void Monster_Appear(entity this, entity actor, entity trigger)
 {
        this.enemy = actor;
-       Monster_Spawn(this, false, this.monsterid);
+       Monster_Spawn(this, false, this.monsterdef);
 }
 
-bool Monster_Appear_Check(entity this, int monster_id)
+bool Monster_Appear_Check(entity this, Monster monster_id)
 {
        if(!(this.spawnflags & MONSTERFLAG_APPEAR))
                return false;
 
        setthink(this, func_null);
-       this.monsterid = monster_id; // set so this monster is properly registered (otherwise, normal initialization is used)
+       this.monsterdef = monster_id; // set so this monster is properly registered (otherwise, normal initialization is used)
        this.nextthink = 0;
        this.use = Monster_Appear;
        this.flags = FL_MONSTER; // set so this monster can get butchered
@@ -890,6 +886,12 @@ bool Monster_Appear_Check(entity this, int monster_id)
 
 void Monster_Reset(entity this)
 {
+       if(this.spawnflags & MONSTERFLAG_SPAWNED)
+       {
+               Monster_Remove(this);
+               return;
+       }
+
        setorigin(this, this.pos1);
        this.angles = this.pos2;
 
@@ -971,7 +973,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed)
 
        CSQCModel_UnlinkEntity(this);
 
-       Monster mon = Monsters_from(this.monsterid);
+       Monster mon = this.monsterdef;
        mon.mr_death(mon, this);
 
        if(this.candrop && this.weapon)
@@ -992,7 +994,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
        //if(time < this.pain_finished && deathtype != DEATH_KILL.m_id)
                //return;
 
-       if(time < this.spawnshieldtime && deathtype != DEATH_KILL.m_id)
+       if(StatusEffects_active(STATUSEFFECT_SpawnShield, this) && deathtype != DEATH_KILL.m_id)
                return;
 
        if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL)
@@ -1002,7 +1004,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
        float take = v.x;
        //float save = v.y;
 
-       Monster mon = Monsters_from(this.monsterid);
+       Monster mon = this.monsterdef;
        take = mon.mr_pain(mon, this, take, attacker, deathtype);
 
        if(take)
@@ -1088,7 +1090,7 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff)
        bool reverse = false;
        if(trace_fraction != 1.0)
                reverse = true;
-       if(trace_ent && IS_PLAYER(trace_ent) && !(trace_ent.items & ITEM_Strength.m_itemid))
+       if(trace_ent && IS_PLAYER(trace_ent))
                reverse = false;
        if(trace_ent && IS_MONSTER(trace_ent))
                reverse = true;
@@ -1139,7 +1141,7 @@ void Monster_Anim(entity this)
        int animbits = deadbits;
        if(STAT(FROZEN, this))
                animbits |= ANIMSTATE_FROZEN;
-       if(this.crouch)
+       if(IS_DUCKED(this))
                animbits |= ANIMSTATE_DUCK; // not that monsters can crouch currently...
        animdecide_setstate(this, animbits, false);
        animdecide_setimplicitstate(this, (IS_ONGROUND(this)));
@@ -1160,7 +1162,8 @@ void Monster_Frozen_Think(entity this)
        {
                STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + this.ticrate * this.revive_speed, 1);
                SetResourceExplicit(this, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * this.max_health));
-               this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1);
+               if (this.iceblock)
+                       this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1);
 
                if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite)
                        WaypointSprite_UpdateHealth(this.sprite, GetResource(this, RES_HEALTH));
@@ -1193,7 +1196,28 @@ void Monster_Frozen_Think(entity this)
 void Monster_Enemy_Check(entity this)
 {
        if(this.enemy)
-               return;
+       {
+               vector targ_origin = ((this.enemy.absmin + this.enemy.absmax) * 0.5);
+               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);
+
+               // cases where the enemy may have changed their state (don't need to check everything here)
+               if(    (IS_DEAD(this.enemy) || GetResource(this.enemy, RES_HEALTH) < 1)
+                       || (STAT(FROZEN, this.enemy))
+                       || (this.enemy.flags & FL_NOTARGET)
+                       || (this.enemy.alpha < 0.5 && this.enemy.alpha != 0)
+                       || (this.enemy.takedamage == DAMAGE_NO)
+                       || (vdist(this.origin - targ_origin, >, this.target_range))
+                       || ((trace_fraction < 1) && (trace_ent != this.enemy))
+                       )
+               {
+                       this.enemy = NULL;
+               }
+               else
+               {
+                       return;
+               }
+       }
 
        this.enemy = Monster_FindTarget(this);
        if(this.enemy)
@@ -1228,7 +1252,7 @@ void Monster_Think(entity this)
                this.last_enemycheck = time + 1; // check for enemies every second
        }
 
-       Monster mon = Monsters_from(this.monsterid);
+       Monster mon = this.monsterdef;
        if(mon.mr_think(mon, this))
        {
                Monster_Move(this, this.speed2, this.speed, this.stopspeed);
@@ -1244,7 +1268,7 @@ void Monster_Think(entity this)
 
 bool Monster_Spawn_Setup(entity this)
 {
-       Monster mon = Monsters_from(this.monsterid);
+       Monster mon = this.monsterdef;
        mon.mr_setup(mon, this);
 
        // ensure some basic needs are met
@@ -1290,7 +1314,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), NULL, this.team, this, sprite, true, RADARICON_DANGER);
-               wp.wp_extra = this.monsterid;
+               wp.wp_extra = this.monsterdef.monsterid;
                wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 0 0');
                if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE))
                {
@@ -1308,12 +1332,11 @@ bool Monster_Spawn_Setup(entity this)
        return true;
 }
 
-bool Monster_Spawn(entity this, bool check_appear, int mon_id)
+bool Monster_Spawn(entity this, bool check_appear, Monster mon)
 {
        // setup the basic required properties for a monster
-       entity mon = Monsters_from(mon_id);
-       if(!mon.monsterid) { return false; } // invalid monster
 
+       if(!mon || mon == MON_Null) { return false; } // invalid monster
        if(!autocvar_g_monsters) { Monster_Remove(this); return false; }
 
        if(!(this.spawnflags & MONSTERFLAG_RESPAWNED) && !(this.flags & FL_MONSTER))
@@ -1325,7 +1348,7 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id)
                        precache_model(this.mdl_dead);
        }
 
-       if(check_appear && Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed
+       if(check_appear && Monster_Appear_Check(this, mon)) { return true; } // return true so the monster isn't removed
 
        if(!this.monster_skill)
                this.monster_skill = cvar("g_monsters_skill");
@@ -1350,6 +1373,14 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id)
        if(!this.monster_name || this.monster_name == "")
                this.monster_name = mon.monster_name;
 
+       if(this.statuseffects && this.statuseffects.owner == this)
+       {
+               StatusEffects_clearall(this.statuseffects);
+               StatusEffects_update(this);
+       }
+       else
+               this.statuseffects = NULL;
+
        this.flags                              = FL_MONSTER;
        this.classname                  = "monster";
        this.takedamage                 = DAMAGE_AIM;
@@ -1361,14 +1392,14 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id)
        if(!this.damagedbycontents)
                IL_PUSH(g_damagedbycontents, this);
        this.damagedbycontents  = true;
-       this.monsterid                  = mon_id;
+       this.monsterdef                 = mon;
        this.event_damage               = Monster_Damage;
        this.event_heal                 = Monster_Heal;
        settouch(this, Monster_Touch);
        this.use                                = Monster_Use;
        this.solid                              = SOLID_BBOX;
        set_movetype(this, MOVETYPE_WALK);
-       this.spawnshieldtime    = time + autocvar_g_monsters_spawnshieldtime;
+       StatusEffects_apply(STATUSEFFECT_SpawnShield, this, time + autocvar_g_monsters_spawnshieldtime, 0);
        this.enemy                              = NULL;
        this.velocity                   = '0 0 0';
        this.moveto                             = this.origin;
@@ -1405,7 +1436,7 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id)
        if((mon.spawnflags & MONSTER_SIZE_QUAKE) && autocvar_g_monsters_quake_resize && !(this.spawnflags & MONSTERFLAG_RESPAWNED))
                this.scale *= 1.3;
 
-       setsize(this, mon.m_mins * this.scale, mon.m_maxs * this.scale);
+       setsize(this, RoundPerfectVector(mon.m_mins * this.scale), RoundPerfectVector(mon.m_maxs * this.scale));
        this.view_ofs                   = '0 0 0.7' * (this.maxs_z * 0.5);
 
        this.ticrate = bound(sys_frametime, ((!this.ticrate) ? autocvar_g_monsters_think_delay : this.ticrate), 60);