]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/monsters/sv_monsters.qc
Merge branch 'master' into Mario/wepent_experimental
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / monsters / sv_monsters.qc
index c0a15e97b141f0c168960d30d9f9f8c25a7cb37e..a89e55b1ce0b6644ddde27b59bacb7b30dbefa03 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)
 {
@@ -50,7 +48,7 @@ void monster_dropitem(entity this, entity attacker)
                e.noalign = true;
                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,16 +92,17 @@ 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
                return false;
        }
 
-       traceline(this.origin + this.view_ofs, targ.origin, 0, this);
+       traceline(this.origin + this.view_ofs, targ.origin, MOVE_NOMONSTERS, this);
 
-       if((trace_fraction < 1) && (trace_ent != targ))
-               return false;
+       if(trace_fraction < 1)
+               return false; // solid
 
        if(autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT))
        if(this.enemy != targ)
@@ -123,29 +122,26 @@ entity Monster_FindTarget(entity mon)
 {
        if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return mon.enemy; } // Handled by a mutator
 
-       entity head, closest_target = NULL;
-       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;
 }
@@ -250,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)));
@@ -305,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)))
@@ -341,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;
 }
@@ -394,7 +390,7 @@ bool Monster_Attack_Leap_Check(entity this, vector vel)
        return true;
 }
 
-bool Monster_Attack_Leap(entity this, vector anm, void(entity this) 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;
@@ -416,16 +412,18 @@ bool Monster_Attack_Leap(entity this, vector anm, void(entity this) touchfunc, v
        return true;
 }
 
-void Monster_Attack_Check(entity this, entity targ)
+void Monster_Attack_Check(entity this, entity targ, .entity weaponentity)
 {
+       int slot = weaponslot(weaponentity);
+
        if((this == NULL || targ == NULL)
        || (!this.monster_attackfunc)
-       || (time < this.attack_finished_single[0])
+       || (time < this.attack_finished_single[slot])
        ) { return; }
 
        if(vdist(targ.origin - this.origin, <=, this.attack_range))
        {
-               bool attack_success = this.monster_attackfunc(MONSTER_ATTACK_MELEE, this, targ);
+               bool attack_success = this.monster_attackfunc(MONSTER_ATTACK_MELEE, this, targ, weaponentity);
                if(attack_success == 1)
                        Monster_Sound(this, monstersound_melee, 0, false, CH_VOICE);
                else if(attack_success > 0)
@@ -434,7 +432,7 @@ void Monster_Attack_Check(entity this, entity targ)
 
        if(vdist(targ.origin - this.origin, >, this.attack_range))
        {
-               float attack_success = this.monster_attackfunc(MONSTER_ATTACK_RANGED, this, targ);
+               float attack_success = this.monster_attackfunc(MONSTER_ATTACK_RANGED, this, targ, weaponentity);
                if(attack_success == 1)
                        Monster_Sound(this, monstersound_melee, 0, false, CH_VOICE);
                else if(attack_success > 0)
@@ -466,15 +464,15 @@ void Monster_UpdateModel(entity this)
        mon.mr_anim(mon, this);
 }
 
-void Monster_Touch(entity this)
+void Monster_Touch(entity this, entity toucher)
 {
-       if(other == NULL) { return; }
+       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)
@@ -509,7 +507,9 @@ bool Monster_Respawn_Check(entity this)
        return true;
 }
 
-void Monster_Respawn(entity this) { Monster_Spawn(this, this.monsterid); }
+void Monster_Respawn(entity this) { Monster_Spawn(this, true, this.monsterid); }
+
+.vector        pos1, pos2;
 
 void Monster_Dead_Fade(entity this)
 {
@@ -546,6 +546,7 @@ 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
@@ -688,11 +689,15 @@ 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(NULL, targetname, this.target2); }
+       // update goal entity if lost
+       if(this.target2 && this.goalentity.targetname != this.target2) { this.goalentity = find(NULL, targetname, this.target2); }
 
-       entity targ;
+       entity targ = this.goalentity;
 
        if(STAT(FROZEN, this) == 2)
        {
@@ -766,20 +771,18 @@ 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);
                }
        }
 
-       targ = this.goalentity;
-
        if (MUTATOR_CALLHOOK(MonsterMove, this, runspeed, walkspeed, targ)
                || gameover
                || this.draggedby != NULL
@@ -866,10 +869,10 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
        }
        else
        {
-               entity e = find(NULL, targetname, this.target2);
+               entity e = this.goalentity; //find(NULL, targetname, this.target2);
                if(e.target2)
                        this.target2 = e.target2;
-               else if(e.target)
+               else if(e.target) // compatibility
                        this.target2 = e.target;
 
                movelib_brake_simple(this, stpspeed);
@@ -892,7 +895,8 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
                this.angles_y += turny;
        }
 
-       Monster_Attack_Check(this, this.enemy);
+       .entity weaponentity = weaponentities[0]; // TODO?
+       Monster_Attack_Check(this, this.enemy, weaponentity);
 }
 
 void Monster_Remove(entity this)
@@ -900,16 +904,20 @@ 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(entity this)
@@ -927,8 +935,7 @@ void Monster_Dead_Think(entity this)
 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);
+       Monster_Spawn(this, false, this.monsterid);
 }
 
 bool Monster_Appear_Check(entity this, int monster_id)
@@ -1013,7 +1020,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed)
        this.takedamage         = DAMAGE_AIM;
        this.deadflag           = DEAD_DEAD;
        this.enemy                      = NULL;
-       this.movetype           = MOVETYPE_TOSS;
+       set_movetype(this, MOVETYPE_TOSS);
        this.moveto                     = this.origin;
        settouch(this, Monster_Touch); // reset incase monster was pouncing
        this.reset                      = func_null;
@@ -1030,7 +1037,10 @@ void Monster_Dead(entity this, entity attacker, float gibbed)
        mon.mr_death(mon, this);
 
        if(this.candrop && this.weapon)
-               W_ThrowNewWeapon(this, this.weapon, 0, this.origin, randomvec() * 150 + '0 0 325');
+       {
+               .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+               W_ThrowNewWeapon(this, this.weapon, 0, this.origin, randomvec() * 150 + '0 0 325', weaponentity);
+       }
 }
 
 void Monster_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
@@ -1122,23 +1132,19 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff)
                return;
        }
 
-       float reverse = false;
-       vector a, b;
-
        makevectors(this.angles);
-       a = this.origin + '0 0 16';
-       b = this.origin + '0 0 16' + v_forward * 32;
+       vector a = CENTER_OR_VIEWOFS(this);
+       vector b = CENTER_OR_VIEWOFS(this) + v_forward * 32;
 
        traceline(a, b, MOVE_NORMAL, this);
 
+       bool reverse = false;
        if(trace_fraction != 1.0)
-       {
                reverse = true;
-
-               if(trace_ent)
-               if(IS_PLAYER(trace_ent) && !(trace_ent.items & IT_STRENGTH))
-                       reverse = false;
-       }
+       if(trace_ent && IS_PLAYER(trace_ent) && !(trace_ent.items & ITEM_Strength.m_itemid))
+               reverse = false;
+       if(trace_ent && IS_MONSTER(trace_ent))
+               reverse = true;
 
        // TODO: fix this... tracing is broken if the floor is thin
        /*
@@ -1206,7 +1212,7 @@ void Monster_Anim(entity this)
 void Monster_Think(entity this)
 {
        setthink(this, Monster_Think);
-       this.nextthink = this.ticrate;
+       this.nextthink = time + this.ticrate;
 
        if(this.monster_lifetime)
        if(time >= this.monster_lifetime)
@@ -1285,7 +1291,7 @@ bool Monster_Spawn_Setup(entity this)
        return true;
 }
 
-bool Monster_Spawn(entity this, int mon_id)
+bool Monster_Spawn(entity this, bool check_appear, int mon_id)
 {
        // setup the basic required properties for a monster
        entity mon = Monsters_from(mon_id);
@@ -1293,7 +1299,10 @@ bool Monster_Spawn(entity this, int mon_id)
 
        if(!autocvar_g_monsters) { Monster_Remove(this); return false; }
 
-       if(Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed
+       if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
+               IL_PUSH(g_monsters, this);
+
+       if(check_appear && Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed
 
        if(!this.monster_skill)
                this.monster_skill = cvar("g_monsters_skill");
@@ -1314,16 +1323,20 @@ bool Monster_Spawn(entity this, int mon_id)
        this.flags                              = FL_MONSTER;
        this.classname                  = "monster";
        this.takedamage                 = DAMAGE_AIM;
+       if(!this.bot_attack)
+               IL_PUSH(g_bot_targets, this);
        this.bot_attack                 = true;
        this.iscreature                 = true;
        this.teleportable               = true;
+       if(!this.damagedbycontents)
+               IL_PUSH(g_damagedbycontents, this);
        this.damagedbycontents  = true;
        this.monsterid                  = mon_id;
        this.event_damage               = Monster_Damage;
        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                              = NULL;
        this.velocity                   = '0 0 0';
@@ -1339,13 +1352,13 @@ bool Monster_Spawn(entity this, int mon_id)
        this.oldtarget2                 = this.target2;
        this.pass_distance              = 0;
        this.deadflag                   = DEAD_NO;
-       this.noalign                    = ((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM));
        this.spawn_time                 = time;
        this.gravity                    = 1;
        this.monster_moveto             = '0 0 0';
        this.monster_face               = '0 0 0';
        this.dphitcontentsmask  = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
 
+       if(!this.noalign) { this.noalign = ((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM)); }
        if(!this.scale) { this.scale = 1; }
        if(autocvar_g_monsters_edit) { this.grab = 1; }
        if(autocvar_g_fullbrightplayers) { this.effects |= EF_FULLBRIGHT; }
@@ -1358,7 +1371,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))