]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/monsters/sv_monsters.qc
Clean up monster armor code
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / monsters / sv_monsters.qc
index ab715efd5c72506b2c046043955d54d4865e8633..1505327c61bc5ed407e36f7a1f0e1ccdb35a20a5 100644 (file)
@@ -60,18 +60,29 @@ float monster_isvalidtarget (entity targ, entity ent)
        if(!targ || !ent)
                return FALSE; // someone doesn't exist
                
+       if(targ == ent)
+               return FALSE; // don't attack ourselves
+               
+       traceline(ent.origin, targ.origin, MOVE_NORMAL, ent);
+               
+       if(trace_ent != targ)
+       {
+               if(trace_ent != world)
+                       targ = trace_ent;
+               else
+                       return FALSE;
+       }
+               
+       if(targ.vehicle_flags & VHF_ISVEHICLE)
+       if not((get_monsterinfo(ent.monsterid)).spawnflags & MON_FLAG_RANGED)
+               return FALSE; // melee attacks are useless against vehicles
+               
        if(time < game_starttime)
                return FALSE; // monsters do nothing before the match has started
-               
-       WarpZone_TraceLine(ent.origin, targ.origin, MOVE_NORMAL, ent);
        
        if(vlen(targ.origin - ent.origin) >= ent.target_range)
                return FALSE; // enemy is too far away
                
-       if not(targ.vehicle_flags & VHF_ISVEHICLE)
-       if(trace_ent != targ)
-               return FALSE; // we can't see the enemy
-               
        if(targ.takedamage == DAMAGE_NO)
                return FALSE; // enemy can't be damaged
                
@@ -84,7 +95,7 @@ float monster_isvalidtarget (entity targ, entity ent)
        if(IS_SPEC(targ) || IS_OBSERVER(targ))
                return FALSE; // enemy is a spectator
        
-       if not(targ.vehicle_flags & VHF_ISVEHICLE) // vehicles dont count as alive?
+       if not(targ.vehicle_flags & VHF_ISVEHICLE)
        if(targ.deadflag != DEAD_NO || ent.deadflag != DEAD_NO || targ.health <= 0 || ent.health <= 0)
                return FALSE; // enemy/self is dead
                
@@ -105,7 +116,7 @@ float monster_isvalidtarget (entity targ, entity ent)
        if not(IsDifferentTeam(targ, ent))
                return FALSE; // enemy is on our team
                
-       if(autocvar_g_monsters_target_infront)
+       if(autocvar_g_monsters_target_infront || ent.spawnflags & MONSTERFLAG_INFRONT)
        if(ent.enemy != targ)
        {
                float dot;
@@ -123,13 +134,32 @@ float monster_isvalidtarget (entity targ, entity ent)
 entity FindTarget (entity ent) 
 {
        if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return ent.enemy; } // Handled by a mutator
-       entity e;
        
-       for(e = world; (e = findflags(e, monster_attack, TRUE)); ) 
-       if(monster_isvalidtarget(e, ent))
-               return e;
-
-       return world;
+       entity head, closest_target = world;
+       head = findradius(ent.origin, ent.target_range);
+                       
+       while(head) // find the closest acceptable target to pass to
+       {
+               if(monster_isvalidtarget(head, ent))
+               {
+                       // 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 ent_center = CENTER_OR_VIEWOFS(ent);
+                                       
+                       //if(ctf_CheckPassDirection(head_center, ent_center, ent.v_angle, head.WarpZone_findradius_nearest))
+                       if(closest_target)
+                       {
+                               vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
+                               if(vlen(ent_center - head_center) < vlen(ent_center - closest_target_center))
+                                       { closest_target = head; }
+                       }
+                       else { closest_target = head; }
+               }
+               
+               head = head.chain;
+       }
+       
+       return closest_target;
 }
 
 void MonsterTouch ()
@@ -151,7 +181,7 @@ void monster_sound(string msound, float sound_delay, float delaytoo)
        if(msound == "")
                return; // sound doesn't exist
 
-       sound(self, CHAN_AUTO, msound, VOL_BASE, ATTN_NORM);
+       sound(self, CHAN_AUTO, msound, VOL_BASE, ATTEN_NORM);
 
        self.msound_delay = time + sound_delay;
 }
@@ -176,14 +206,23 @@ void monster_setupsounds(string mon)
        if(self.msound_sight == "") self.msound_sight = strzone(strcat("monsters/", mon, "_sight.wav"));
 }
 
-float monster_melee (entity targ, float damg, float er, float deathtype, float dostop)
+void monster_makevectors(entity e)
+{
+       vector v;
+               
+       v = CENTER_OR_VIEWOFS(e);
+       self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
+       self.v_angle_x = -self.v_angle_x;
+       
+       makevectors(self.v_angle);
+}
+
+float monster_melee(entity targ, float damg, float anim, float er, float anim_finished, float deathtype, float dostop)
 {
-       float dot, rdmg = damg * random();
+       float rdmg = damg * random();
 
        if (self.health <= 0)
-               return FALSE;
-       if (targ == world)
-               return FALSE;
+               return FALSE; // attacking while dead?!
                
        if(dostop)
        {
@@ -192,12 +231,18 @@ float monster_melee (entity targ, float damg, float er, float deathtype, float d
                self.state = MONSTER_STATE_ATTACK_MELEE;
                self.SendFlags |= MSF_MOVE;
        }
+       
+       monsters_setframe(anim);
+       
+       if(anim_finished != 0)
+               self.attack_finished_single = time + anim_finished;
 
-       makevectors (self.angles);
-       dot = normalize (targ.origin - self.origin) * v_forward;
+       monster_makevectors(targ);
+       
+       traceline(self.origin + self.view_ofs, self.origin + v_forward * er, 0, self);
        
-       if(dot > er)
-               Damage(targ, self, self, rdmg * monster_skill, deathtype, targ.origin, normalize(targ.origin - self.origin));
+       if(trace_ent.takedamage)
+               Damage(trace_ent, self, self, rdmg * monster_skill, deathtype, trace_ent.origin, normalize(trace_ent.origin - self.origin));
                
        return TRUE;
 }
@@ -250,14 +295,13 @@ void Monster_Fade ()
                self.takedamage = DAMAGE_NO;
                setorigin(self, self.pos1);
                self.angles = self.pos2;
-               self.health = self.max_health; // TODO: check if resetting to max_health is wise here
+               self.health = self.max_health;
                
                self.SendFlags |= MSF_MOVE;
                self.SendFlags |= MSF_STATUS;
-               
-               return;
        }
-       SUB_SetFade(self, time + 3, 1);
+       else
+               SUB_SetFade(self, time + 3, 1);
 }
 
 float Monster_CanJump (vector vel)
@@ -292,7 +336,7 @@ float monster_leap (float anm, void() touchfunc, vector vel, float anim_finished
        self.touch = touchfunc;
        self.origin_z += 1;
        self.velocity = vel;
-       self.flags &~= FL_ONGROUND;
+       self.flags &= ~FL_ONGROUND;
                
        self.attack_finished_single = time + anim_finished;
        
@@ -319,6 +363,7 @@ void monster_checkattack(entity e, entity targ)
                return;
        }
        
+       if(vlen(targ.origin - e.origin) > e.attack_range)
        if(e.monster_attackfunc(MONSTER_ATTACK_RANGED))
        {
                monster_sound(e.msound_attack_ranged, 0, FALSE);
@@ -326,42 +371,12 @@ void monster_checkattack(entity e, entity targ)
        }
 }
 
-void monster_makevectors(entity e)
-{
-       vector v;
-               
-       v = CENTER_OR_VIEWOFS(e);
-       self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
-       self.v_angle_x = -self.v_angle_x;
-       
-       makevectors(self.v_angle);
-}
-
 void monster_use ()
 {
-       if (self.enemy)
-               return;
-       if (self.health <= 0)
-               return;
-
-       if(!monster_isvalidtarget(activator, self))
-               return;
-
-       self.enemy = activator;
-}
-
-float trace_path(vector from, vector to)
-{
-       vector dir = normalize(to - from) * 15, offset = '0 0 0';
-       float trace1 = trace_fraction;
-       
-       offset_x = dir_y;
-       offset_y = -dir_x;
-       traceline (from+offset, to+offset, TRUE, self);
-       
-       traceline(from-offset, to-offset, TRUE, self);
-               
-       return ((trace1 < trace_fraction) ? trace1 : trace_fraction);
+       if not(self.enemy)
+       if(self.health > 0)
+       if(monster_isvalidtarget(activator, self))
+               self.enemy = activator;
 }
 
 .float last_trace;
@@ -371,6 +386,7 @@ vector monster_pickmovetarget(entity targ)
        // enemy is always preferred target
        if(self.enemy)
        {
+               makevectors(self.angles);
                self.monster_movestate = MONSTER_MOVE_ENEMY;
                self.last_trace = time + 1.2;
                return self.enemy.origin;
@@ -382,7 +398,7 @@ vector monster_pickmovetarget(entity targ)
                {
                        self.monster_movestate = MONSTER_MOVE_OWNER;
                        self.last_trace = time + 0.3;
-                       if(self.monster_owner && self.monster_owner.classname != "td_spawnpoint")
+                       if(self.monster_owner)
                                return self.monster_owner.origin;
                }
                case MONSTER_MOVE_SPAWNLOC:
@@ -404,11 +420,12 @@ vector monster_pickmovetarget(entity targ)
                        self.monster_movestate = MONSTER_MOVE_WANDER;
                        self.last_trace = time + 2;
                                
-                       self.angles_y = random() * 500;
+                       self.angles_y = rint(random() * 500);
                        makevectors(self.angles);
                        pos = self.origin + v_forward * 600;
                        
                        if(self.flags & FL_FLY || self.flags & FL_SWIM)
+                       if(self.spawnflags & MONSTERFLAG_FLY_VERTICAL)
                        {
                                pos_z = random() * 200;
                                if(random() >= 0.5)
@@ -440,7 +457,7 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_
                self.revive_progress = bound(0, self.revive_progress + frametime * self.revive_speed, 1);
                self.health = max(1, self.max_health * self.revive_progress);
                
-               if(self.sprite) WaypointSprite_UpdateHealth(self.sprite, self.health);
+               self.SendFlags |= MSF_STATUS;
                        
                movelib_beak_simple(stopspeed);
                        
@@ -511,7 +528,11 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_
                if(time >= self.spawn_time)
                        monsters_setframe(manim_idle);
                movelib_beak_simple(stopspeed);
-               self.SendFlags |= MSF_MOVE;
+               if(self.oldorigin != self.origin)
+               {
+                       self.oldorigin = self.origin;
+                       self.SendFlags |= MSF_MOVE;
+               }
                return;
        }
        
@@ -523,23 +544,24 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_
        if(autocvar_g_monsters_teams)
        if(IsDifferentTeam(self.monster_owner, self))
                self.monster_owner = world;
+       
+       if(self.enemy && self.enemy.health < 1)
+               self.enemy = world; // enough!
                
        if(time >= self.last_enemycheck)
        {
                if not(monster_isvalidtarget(self.enemy, self))
                        self.enemy = world;
+                       
+               if not(self.enemy)
+               {
+                       self.enemy = FindTarget(self);
+                       if(self.enemy)
+                               monster_sound(self.msound_sight, 0, FALSE);
+               }
+                       
                self.last_enemycheck = time + 2;
        }
-               
-       if(self.enemy && self.enemy.health < 1)
-               self.enemy = world; // enough!
-               
-       if not(self.enemy)
-       {
-               self.enemy = FindTarget(self);
-               if(self.enemy)
-                       monster_sound(self.msound_sight, 0, FALSE);
-       }
        
        if(self.state == MONSTER_STATE_ATTACK_MELEE && time >= self.attack_finished_single)
                self.state = 0;
@@ -579,22 +601,14 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_
        
        if(self.state == MONSTER_STATE_ATTACK_MELEE)
                self.moveto = self.origin;
-       else if(self.enemy)
-               self.moveto = self.moveto * 0.9 + ((self.origin + v_forward * 500) + randomvec() * 400) * 0.1;
-       
-       if not(self.flags & FL_FLY || self.flags & FL_SWIM)
-               self.moveto_z = self.origin_z; 
-       
-       float l = vlen(self.moveto - self.origin);
-       float t1 = trace_path(self.origin+'0 0 10', self.moveto+'0 0 10');
-       float t2 = trace_path(self.origin-'0 0 15', self.moveto-'0 0 15'); 
+               
+       if(self.enemy && self.enemy.vehicle)
+               runspeed = 0;
        
-       if(self.flags & FL_FLY || self.flags & FL_SWIM)
+       if(((self.flags & FL_FLY) || (self.flags & FL_SWIM)) && self.spawnflags & MONSTERFLAG_FLY_VERTICAL)
                v_forward = normalize(self.moveto - self.origin);
-       
-       if(t1*l-t2*l>50 && (t1*l > 100 || t1 > 0.8))
-       if(self.flags & FL_ONGROUND)
-               movelib_jump_simple(100);
+       else
+               self.moveto_z = self.origin_z; 
 
        if(vlen(self.origin - self.moveto) > 64)
        {
@@ -602,9 +616,10 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_
                        movelib_move_simple(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
                else
                        movelib_move_simple_gravity(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
+                       
                if(time > self.pain_finished)
                if(time > self.attack_finished_single)
-               if(vlen(self.velocity) > 0)
+               if(vlen(self.velocity) > 10)
                        monsters_setframe((self.enemy) ? manim_run : manim_walk);
                else
                        monsters_setframe(manim_idle);
@@ -645,14 +660,19 @@ void monster_dead_think()
        self.nextthink = time + 0.3; // don't need to update so often now
        
        self.deadflag = DEAD_DEAD;
-
+       
+       if(self.ltime != 0)
        if(time >= self.ltime)
        {
                Monster_Fade();
                return;
        }
        
-       self.SendFlags |= MSF_MOVE; // keep up to date on the monster's location
+       if(self.oldorigin != self.origin)
+       {
+               self.oldorigin = self.origin;
+               self.SendFlags |= MSF_MOVE;
+       }
 }
 
 void monsters_setstatus()
@@ -664,7 +684,7 @@ void monsters_setstatus()
 void Monster_Appear()
 {
        self.enemy = activator;
-       self.spawnflags &~= MONSTERFLAG_APPEAR;
+       self.spawnflags &= ~MONSTERFLAG_APPEAR;
        self.monster_spawnfunc();
 }
 
@@ -693,7 +713,7 @@ void monsters_reset()
        self.attack_finished_single = 0;
        self.moveto = self.origin;
        
-       WaypointSprite_UpdateHealth(self.sprite, self.health);
+       self.SendFlags |= MSF_STATUS;
 }
 
 float monster_send(entity to, float sf)
@@ -786,14 +806,6 @@ void monster_die()
        self.ltime = time + 5;
        
        monster_dropitem();
-
-       WaypointSprite_Kill(self.sprite);
-               
-       if(self.weaponentity)
-       {
-               remove(self.weaponentity);
-               self.weaponentity = world;
-       }
                
        monster_sound(self.msound_death, 0, FALSE);
                
@@ -836,41 +848,33 @@ void monsters_damage (entity inflictor, entity attacker, float damage, float dea
        if(time < self.spawnshieldtime)
                return;
                
-       if(deathtype != DEATH_KILL)
-               damage *= self.armorvalue;
+       vector v;
+       float take, save;
                
-       if(self.weaponentity && self.weaponentity.classname == "shield")
-               self.weaponentity.health -= damage;
+       v = healtharmor_applydamage(self.armorvalue, self.m_armor_blockpercent, damage);
+       take = v_x;
+       save = v_y;
                
-       self.health -= damage;
-       
-       if(self.sprite)
-               WaypointSprite_UpdateHealth(self.sprite, self.health);
+       self.health -= take;
                
        self.dmg_time = time;
 
        if(sound_allowed(MSG_BROADCAST, attacker) && deathtype != DEATH_DROWN)
-               spamsound (self, CH_PAIN, "misc/bodyimpact1.wav", VOL_BASE, ATTN_NORM);  // FIXME: PLACEHOLDER
+               spamsound (self, CH_PAIN, "misc/bodyimpact1.wav", VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
        
        self.velocity += force * self.damageforcescale;
                
        if(deathtype != DEATH_DROWN)
        {
-               Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
-               if (damage > 50)
+               Violence_GibSplash_At(hitloc, force, 2, bound(0, take, 200) / 16, self, attacker);
+               if (take > 50)
                        Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, self, attacker);
-               if (damage > 100)
+               if (take > 100)
                        Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, self, attacker);
        }
                
        if(self.health <= 0)
-       {        
-               if(self.sprite)
-               {
-                       // Update one more time to avoid waypoint fading without emptying healthbar
-                       WaypointSprite_UpdateHealth(self.sprite, 0);
-               }
-               
+       {
                if(deathtype == DEATH_KILL)
                        self.candrop = FALSE; // killed by mobkill command
                        
@@ -927,6 +931,9 @@ void monster_spawn()
        if not(self.monster_respawned)
        if not(self.skin)
                self.skin = rint(random() * 4);
+               
+       if not(self.attack_range)
+               self.attack_range = autocvar_g_monsters_attack_range;
        
        self.pos1 = self.origin;
        
@@ -936,57 +943,43 @@ void monster_spawn()
        
        if(teamplay)
                self.monster_attack = TRUE; // we can have monster enemies in team games
-               
-       if(autocvar_g_monsters_healthbars)
-       {
-               WaypointSprite_Spawn(strzone(strdecolorize(self.monster_name)), 0, 600, self, '0 0 1' * (self.maxs_z + 15), world, 0, self, sprite, FALSE, RADARICON_DANGER, ((self.team) ? Team_ColorRGB(self.team) : '1 0 0'));       
-               WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
-               WaypointSprite_UpdateHealth(self.sprite, self.health);
-       }
        
        monster_sound(self.msound_spawn, 0, FALSE);
-
-       MUTATOR_CALLHOOK(MonsterSpawn);
        
        self.think = monster_think;
        self.nextthink = time + self.ticrate;
        
-       self.SendFlags = MSF_SETUP;
+       self.SendFlags |= MSF_SETUP;
+       
+       MUTATOR_CALLHOOK(MonsterSpawn);
 }
 
 float monster_initialize(float mon_id, float nodrop)
 {
        if not(autocvar_g_monsters)
                return FALSE;
-               
-       vector min_s, max_s;
+       
        entity mon = get_monsterinfo(mon_id);
        
        // support for quake style removing monsters based on skill
-       if(monster_skill <= autocvar_g_monsters_skill_easy && (self.spawnflags & MONSTERSKILL_NOTEASY)) { return FALSE; }
-       if(monster_skill == autocvar_g_monsters_skill_normal && (self.spawnflags & MONSTERSKILL_NOTMEDIUM)) { return FALSE; }
-       if(monster_skill == autocvar_g_monsters_skill_hard && (self.spawnflags & MONSTERSKILL_NOTHARD)) { return FALSE; }
-       if(monster_skill == autocvar_g_monsters_skill_insane && (self.spawnflags & MONSTERSKILL_NOTINSANE)) { return FALSE; }
-       if(monster_skill >= autocvar_g_monsters_skill_nightmare && (self.spawnflags & MONSTERSKILL_NOTNIGHTMARE)) { return FALSE; }
-
-       if(self.monster_name == "")
-               self.monster_name = M_NAME(mon_id);
+       switch(monster_skill)
+       {
+               case 0:
+               case 1: if(self.spawnflags & MONSTERSKILL_NOTEASY)              return FALSE; break;
+               case 2: if(self.spawnflags & MONSTERSKILL_NOTMEDIUM)    return FALSE; break;
+               default:
+               case 3: if(self.spawnflags & MONSTERSKILL_NOTHARD)              return FALSE; break;
+       }
        
        if(self.team && !teamplay)
                self.team = 0;
-
-       self.flags = FL_MONSTER;
-               
+       
        if not(self.spawnflags & MONSTERFLAG_SPAWNED) // naturally spawned monster
        if not(self.monster_respawned)
                monsters_total += 1;
-               
-       min_s = mon.mins;
-       max_s = mon.maxs;
        
-       self.netname = mon.netname;
-
-       setsize(self, min_s, max_s);
+       setsize(self, mon.mins, mon.maxs);
+       self.flags                              = FL_MONSTER;
        self.takedamage                 = DAMAGE_AIM;
        self.bot_attack                 = TRUE;
        self.iscreature                 = TRUE;
@@ -1000,16 +993,18 @@ float monster_initialize(float mon_id, float nodrop)
        self.solid                              = SOLID_BBOX;
        self.movetype                   = MOVETYPE_WALK;
        self.spawnshieldtime    = time + autocvar_g_monsters_spawnshieldtime;
-       monsters_spawned           += 1;
        self.enemy                              = world;
        self.velocity                   = '0 0 0';
        self.moveto                             = self.origin;
        self.pos2                               = self.angles;
        self.reset                              = monsters_reset;
+       self.netname                    = mon.netname;
+       self.monster_name               = M_NAME(mon_id);
        self.candrop                    = TRUE;
        self.view_ofs                   = '0 0 1' * (self.maxs_z * 0.5);
        self.oldtarget2                 = self.target2;
        self.deadflag                   = DEAD_NO;
+       self.scale                              = 1;
        self.noalign                    = nodrop;
        self.spawn_time                 = time;
        self.gravity                    = 1;
@@ -1023,23 +1018,17 @@ float monster_initialize(float mon_id, float nodrop)
                self.flags |= FL_FLY;
                self.movetype = MOVETYPE_FLY;
        }
-       
-       if not(self.scale)
-               self.scale = 1;
                
        if(mon.spawnflags & MONSTER_SIZE_BROKEN)
                self.scale = 1.3;
        
-       if not(self.attack_range)
-               self.attack_range = 120;
-       
        if not(self.ticrate)
                self.ticrate = autocvar_g_monsters_think_delay;
                
        self.ticrate = bound(sys_frametime, self.ticrate, 60);
        
-       if not(self.armorvalue)
-               self.armorvalue = 1; // multiplier
+       if not(self.m_armor_blockpercent)
+               self.m_armor_blockpercent = 0.5;
        
        if not(self.target_range)
                self.target_range = autocvar_g_monsters_target_range;