]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/monsters/monster/mage.qc
Use shield if enemy is too close
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / monsters / monster / mage.qc
index e308b618e6cff76b7998e71a460eba9a3d7fb702..9e78ce1ac9c14d483f173743d1c9e52ebb2e85b4 100644 (file)
@@ -22,16 +22,6 @@ REGISTER_MONSTER(
        MON_ADD_CVAR(monster, attack_spike_smart_trace_min) \
        MON_ADD_CVAR(monster, attack_spike_smart_trace_max) \
        MON_ADD_CVAR(monster, attack_spike_smart_mindist) \
-       MON_ADD_CVAR(monster, attack_melee_damage) \
-       MON_ADD_CVAR(monster, attack_melee_delay) \
-       MON_ADD_CVAR(monster, attack_grenade_damage) \
-       MON_ADD_CVAR(monster, attack_grenade_edgedamage) \
-       MON_ADD_CVAR(monster, attack_grenade_force) \
-       MON_ADD_CVAR(monster, attack_grenade_radius) \
-       MON_ADD_CVAR(monster, attack_grenade_lifetime) \
-       MON_ADD_CVAR(monster, attack_grenade_chance) \
-       MON_ADD_CVAR(monster, attack_grenade_speed) \
-       MON_ADD_CVAR(monster, attack_grenade_speed_up) \
        MON_ADD_CVAR(monster, heal_self) \
        MON_ADD_CVAR(monster, heal_allies) \
        MON_ADD_CVAR(monster, heal_minhealth) \
@@ -59,20 +49,20 @@ const float mage_anim_run           = 5;
 void() mage_heal;
 void() mage_shield;
 
+.entity mage_spike;
+
 float friend_needshelp(entity e)
 {
        if(e == world)
                return FALSE;
        if(e.health <= 0)
                return FALSE;
-       if(vlen(e.origin - self.origin) > MON_CVAR(mage, heal_range))
-               return FALSE;
-       if(IsDifferentTeam(e, self))
+       if(DIFF_TEAM(e, self) && e != self.monster_owner)
                return FALSE;
        if(e.frozen)
                return FALSE;
        if(!IS_PLAYER(e))
-               return (e.health < e.max_health);
+               return (e.flags & FL_MONSTER && e.health < e.max_health);
        if(e.items & IT_INVINCIBLE)
                return FALSE;
 
@@ -87,65 +77,14 @@ float friend_needshelp(entity e)
        return FALSE;
 }
 
-void mageattack_melee()
-{
-       monster_melee(self.enemy, MON_CVAR(mage, attack_melee_damage), mage_anim_attack, self.attack_range, MON_CVAR(mage, attack_melee_delay) - 0.2, DEATH_MONSTER_MAGE, TRUE);
-}
-
-void mage_grenade_explode()
-{
-       pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1);
-       
-       sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
-       RadiusDamage (self, self.realowner, MON_CVAR(mage, attack_grenade_damage), MON_CVAR(mage, attack_grenade_edgedamage), MON_CVAR(mage, attack_grenade_radius), world, MON_CVAR(mage, attack_grenade_force), DEATH_MONSTER_MAGE, other);
-       remove(self);
-}
-
-void mage_grenade_touch()
-{
-       if(IS_PLAYER(other))
-       {
-               PROJECTILE_TOUCH;
-               mage_grenade_explode();
-               return;
-       }
-}
-
-void mage_throw_itemgrenade()
-{
-       makevectors(self.angles);
-       
-       entity gren = spawn ();
-       gren.owner = gren.realowner = self;
-       gren.classname = "grenade";
-       gren.bot_dodge = FALSE;
-       gren.movetype = MOVETYPE_BOUNCE;
-       gren.solid = SOLID_TRIGGER;
-       gren.projectiledeathtype = DEATH_MONSTER_MAGE;
-       setorigin(gren, CENTER_OR_VIEWOFS(self));
-       setsize(gren, '-64 -64 -64', '64 64 64');
-
-       gren.nextthink = time + MON_CVAR(mage, attack_grenade_lifetime);
-       gren.think = mage_grenade_explode;
-       gren.use = mage_grenade_explode;
-       gren.touch = mage_grenade_touch;
-
-       gren.missile_flags = MIF_SPLASH | MIF_ARC;
-       W_SetupProjectileVelocityEx(gren, v_forward, v_up, MON_CVAR(mage, attack_grenade_speed), MON_CVAR(mage, attack_grenade_speed_up), 0, 0, FALSE);
-       
-       gren.flags = FL_PROJECTILE;
-       
-       setmodel(gren, "models/items/g_h50.md3");
-       
-       self.attack_finished_single = time + 1.5;
-}
-
 void mage_spike_explode()
 {
        self.event_damage = func_null;
        
        sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
        
+       self.realowner.mage_spike = world;
+       
        pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1);
        RadiusDamage (self, self.realowner, MON_CVAR(mage, attack_spike_damage), MON_CVAR(mage, attack_spike_damage) * 0.5, MON_CVAR(mage, attack_spike_radius), world, 0, DEATH_MONSTER_MAGE, other);
 
@@ -223,7 +162,7 @@ void mage_spike_think()
        UpdateCSQCProjectile(self);
 }
 
-void mage_spike()
+void mage_attack_spike()
 {
        entity missile;
        vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
@@ -245,6 +184,8 @@ void mage_spike()
        missile.enemy = self.enemy;
        missile.touch = mage_spike_touch;
        
+       self.mage_spike = missile;
+       
        CSQCProjectile(missile, TRUE, PROJECTILE_MAGE_SPIKE, TRUE);
 }
 
@@ -253,7 +194,7 @@ void mage_heal()
        entity head;
        float washealed = FALSE;
        
-       for(head = world; (head = findfloat(head, monster_attack, TRUE)); ) if(friend_needshelp(head))
+       for(head = findradius(self.origin, MON_CVAR(mage, heal_range)); head; head = head.chain) if(friend_needshelp(head))
        {
                washealed = TRUE;
                string fx = "";
@@ -291,60 +232,91 @@ void mage_heal()
                {
                        pointparticles(particleeffectnum("healing_fx"), head.origin, '0 0 0', 1);
                        head.health = bound(0, head.health + MON_CVAR(mage, heal_allies), head.max_health);
-                       head.SendFlags |= MSF_STATUS;
+                       WaypointSprite_UpdateHealth(head.sprite, head.health);
                }
        }
        
        if(washealed)
        {
-               monsters_setframe(mage_anim_attack);
+               self.frame = mage_anim_attack;
                self.attack_finished_single = time + MON_CVAR(mage, heal_delay);
        }
 }
 
+void mage_teleport()
+{
+       if(vlen(self.enemy.origin - self.origin) >= 500)
+               return;
+
+       makevectors(self.enemy.angles);
+       tracebox(self.enemy.origin + ((v_forward * -1) * 200), self.mins, self.maxs, self.origin - '0 0 5', MOVE_NORMAL, self);
+       
+       if(trace_fraction <= 1)
+       {
+               pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1);
+               setorigin(self, self.enemy.origin + ((v_forward * -1) * 200));
+       }
+       
+       self.attack_finished_single = time + 0.2;
+}
+
 void mage_shield_think()
 {
        self.nextthink = time;
+       setorigin(self, self.owner.origin);
 
        if(time >= self.ltime || self.owner.health <= 0)
        {
                self.owner.armorvalue = 0;
                self.owner.m_armor_blockpercent = autocvar_g_monsters_armor_blockpercent;
+               self.owner.weaponentity = world;
                remove(self);
                return;
        }
 }
 
+void mage_shield_touch()
+{
+       if(other == self.realowner)
+               return;
+               
+       vector mymid = (self.absmin + self.absmax) * 0.5;
+       vector othermid = (other.absmin + other.absmax) * 0.5;
+
+       Damage(other, self, self.realowner, 1, DEATH_MONSTER_MAGE, mymid, normalize(othermid - mymid) * 100);
+}
+
 void mage_shield()
 {
        if(self.weaponentity)
                return; // already have a shield
-               
-       entity shield = spawn();
 
-       shield.owner = self;
+       entity shield = spawn();
+       
+       shield.owner = shield.realowner = self;
+       shield.enemy = self;
        shield.team = self.team;
-       shield.ltime = time + MON_CVAR(mage, shield_time);
+       shield.touch = mage_shield_touch;
        shield.classname = "shield";
+       shield.ltime = time + MON_CVAR(mage, shield_time);
        shield.effects = EF_ADDITIVE;
        shield.movetype = MOVETYPE_NOCLIP;
        shield.solid = SOLID_TRIGGER;
        shield.avelocity = '7 0 11';
-       shield.scale = self.scale * 0.6;
+       shield.scale = self.scale * 0.5;
        shield.think = mage_shield_think;
        shield.nextthink = time;
        
-       setattachment(shield, self, "");
-       setmodel(shield, "models/ctf/shield.md3");
-       setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs);
-       
        self.lastshielded = time + MON_CVAR(mage, shield_delay);
-       
-       monsters_setframe(mage_anim_attack);
+       self.weaponentity = shield;
+       self.frame = mage_anim_attack;
        self.attack_finished_single = time + 1;
-       
        self.m_armor_blockpercent = MON_CVAR(mage, shield_blockpercent);
        self.armorvalue = self.health;
+       
+       setorigin(shield, self.origin);
+       setmodel(shield, "models/ctf/shield.md3");
+       setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs);
 }
 
 float mage_attack(float attack_type)
@@ -353,25 +325,32 @@ float mage_attack(float attack_type)
        {
                case MONSTER_ATTACK_MELEE:
                {
-                       monsters_setframe(mage_anim_attack);
-                       self.attack_finished_single = time + MON_CVAR(mage, attack_melee_delay);
-                       defer(0.2, mageattack_melee);
-                       
+                       mage_shield();
+                               
                        return TRUE;
                }
                case MONSTER_ATTACK_RANGED:
                {
-                       if(random() < MON_CVAR(mage, attack_grenade_chance) / 100)
+                       if not(self.mage_spike)
                        {
-                               mage_throw_itemgrenade();
-                               return TRUE;
+                               if(random() <= 0.4)
+                               {
+                                       mage_teleport();
+                                       return TRUE;
+                               }
+                               else
+                               {
+                                       self.frame = mage_anim_attack;
+                                       self.attack_finished_single = time + MON_CVAR(mage, attack_spike_delay);
+                                       defer(0.2, mage_attack_spike);
+                                       return TRUE;
+                               }
                        }
-       
-                       monsters_setframe(mage_anim_attack);
-                       self.attack_finished_single = time + MON_CVAR(mage, attack_spike_delay);
-                       defer(0.2, mage_spike);
                        
-                       return TRUE;
+                       if(self.mage_spike)
+                               return TRUE;
+                       else
+                               return FALSE;
                }
        }
        
@@ -402,19 +381,12 @@ float m_mage(float req)
                        entity head;
                        float need_help = FALSE;
                        
-                       FOR_EACH_PLAYER(head)
-                       if(friend_needshelp(head))
-                       {
-                               need_help = TRUE;
-                               break; // found 1 player near us who is low on health
-                       }
-                       if(!need_help)
-                       FOR_EACH_MONSTER(head)
+                       for(head = findradius(self.origin, MON_CVAR(mage, heal_range)); head; head = head.chain)
                        if(head != self)
                        if(friend_needshelp(head))
                        {
                                need_help = TRUE;
-                               break; // found 1 player near us who is low on health
+                               break;
                        }
                                
                        if(self.health < MON_CVAR(mage, heal_minhealth) || need_help)
@@ -433,7 +405,7 @@ float m_mage(float req)
                }
                case MR_DEATH:
                {
-                       monsters_setframe(mage_anim_death);
+                       self.frame = mage_anim_death;
                        return TRUE;
                }
                case MR_SETUP:
@@ -442,13 +414,15 @@ float m_mage(float req)
                        
                        self.monster_loot = spawnfunc_item_health_large;
                        self.monster_attackfunc = mage_attack;
-                       monsters_setframe(mage_anim_walk);
+                       self.frame = mage_anim_walk;
                        
                        return TRUE;
                }
-               case MR_INIT:
+               case MR_PRECACHE:
                {
-                       // nothing
+                       precache_model ("models/monsters/mage.dpm");
+                       precache_model ("models/ctf/shield.md3");
+                       precache_sound ("weapons/grenade_impact.wav");
                        return TRUE;
                }
                case MR_CONFIG:
@@ -467,17 +441,9 @@ float m_mage(float req)
 {
        switch(req)
        {
-               case MR_DEATH:
-               {
-                       // nothing
-                       return TRUE;
-               }
-               case MR_INIT:
+               case MR_PRECACHE:
                {
                        precache_model ("models/monsters/mage.dpm");
-                       precache_model ("models/items/g_h50.md3");
-                       precache_model ("models/ctf/shield.md3");
-                       precache_sound ("weapons/grenade_impact.wav");
                        return TRUE;
                }
        }