From: Mario Date: Sun, 28 Apr 2013 23:37:08 +0000 (+1000) Subject: Rename shalrath to mage X-Git-Tag: xonotic-v0.8.0~241^2^2~266 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=2c849a905f5ea64b47cf4369e9a3931734a85e0a;p=xonotic%2Fxonotic-data.pk3dir.git Rename shalrath to mage --- diff --git a/monsters.cfg b/monsters.cfg index cd9d07c42..de00c3544 100644 --- a/monsters.cfg +++ b/monsters.cfg @@ -176,35 +176,35 @@ set g_monster_fish_drop_size small "Size of the item Rotfish drop. Possible valu set g_monster_fish_speed_walk 40 "Rotfish walk speed" set g_monster_fish_speed_run 70 "Rotfish run speed" -// Vore -set g_monster_shalrath 1 "Enable Vores" -set g_monster_shalrath_health 400 "Vore health" -set g_monster_shalrath_drop health "Vore drops this item on death" -set g_monster_shalrath_drop_size medium "Size of the item Vores drop. Possible values are: small, medium, large" -set g_monster_shalrath_speed 50 "Vore move speed" -set g_monster_shalrath_attack_spike_damage 30 "Vore homing spike explosion damage" -set g_monster_shalrath_attack_spike_radius 60 "Vore homing spike explosion radius" -set g_monster_shalrath_attack_spike_delay 2 "Delay between Vore homing spike attacks" -set g_monster_shalrath_attack_melee_damage 30 "Vore magic attack damage" -set g_monster_shalrath_attack_melee_delay 0.7 "Delay between Vore melee attacks" -set g_monster_shalrath_heal_self 50 "Amount of health Vore will regenerate every attack when its low on health" -set g_monster_shalrath_heal_friends 15 "Amount of health Vore will regenerate nearby friends" -set g_monster_shalrath_heal_minhealth 250 "Health limit below which Vore will try to heal itself" -set g_monster_shalrath_heal_range 200 "Maximum healing distance" -set g_monster_shalrath_heal_delay 1.5 "Delay between healing bursts" -set g_monster_shalrath_shield_blockpercent 40 "% of damage inflicted on Vore if using a force field" -set g_monster_shalrath_shield_delay 7 "Refire for Vore force shield" -set g_monster_shalrath_shield_time 3 "Amount of time Vore force field lasts" -set g_monster_shalrath_attack_grenade_damage 25 "Vore fake item grenade attack damage" -set g_monster_shalrath_attack_grenade_edgedamage 20 "Vore fake item grenade attack edge damage" -set g_monster_shalrath_attack_grenade_radius 100 "Vore fake item grenade attack explosion radius" -set g_monster_shalrath_attack_grenade_lifetime 5 "Vore fake item grenade life time" -set g_monster_shalrath_attack_grenade_speed 150 "Vore fake item grenade forward speed" -set g_monster_shalrath_attack_grenade_speed_up 95 "Vore fake item grenade upwards speed" -set g_monster_shalrath_attack_grenade_speed_z 0 "Vore fake item grenade speed angle" -set g_monster_shalrath_attack_grenade_spread 0 "Vore fake item grenade spread" -set g_monster_shalrath_attack_grenade_force 170 "Vore fake item grenade damage knockback" -set g_monster_shalrath_attack_grenade_chance 30 "% chance of Vore attack being fake item grenade" +// Mage +set g_monster_mage 1 "Enable Mages" +set g_monster_mage_health 400 "Mage health" +set g_monster_mage_drop health "Mage drops this item on death" +set g_monster_mage_drop_size medium "Size of the item Mages drop. Possible values are: small, medium, large" +set g_monster_mage_speed 50 "Mage move speed" +set g_monster_mage_attack_spike_damage 30 "Mage homing spike explosion damage" +set g_monster_mage_attack_spike_radius 60 "Mage homing spike explosion radius" +set g_monster_mage_attack_spike_delay 2 "Delay between Mage homing spike attacks" +set g_monster_mage_attack_melee_damage 30 "Mage magic attack damage" +set g_monster_mage_attack_melee_delay 0.7 "Delay between Mage melee attacks" +set g_monster_mage_heal_self 50 "Amount of health Mage will regenerate every attack when its low on health" +set g_monster_mage_heal_friends 15 "Amount of health Mage will regenerate nearby friends" +set g_monster_mage_heal_minhealth 250 "Health limit below which Mage will try to heal itself" +set g_monster_mage_heal_range 200 "Maximum healing distance" +set g_monster_mage_heal_delay 1.5 "Delay between healing bursts" +set g_monster_mage_shield_blockpercent 40 "% of damage inflicted on Mage if using a force field" +set g_monster_mage_shield_delay 7 "Refire for Mage force shield" +set g_monster_mage_shield_time 3 "Amount of time Mage force field lasts" +set g_monster_mage_attack_grenade_damage 25 "Mage fake item grenade attack damage" +set g_monster_mage_attack_grenade_edgedamage 20 "Mage fake item grenade attack edge damage" +set g_monster_mage_attack_grenade_radius 100 "Mage fake item grenade attack explosion radius" +set g_monster_mage_attack_grenade_lifetime 5 "Mage fake item grenade life time" +set g_monster_mage_attack_grenade_speed 150 "Mage fake item grenade forward speed" +set g_monster_mage_attack_grenade_speed_up 95 "Mage fake item grenade upwards speed" +set g_monster_mage_attack_grenade_speed_z 0 "Mage fake item grenade speed angle" +set g_monster_mage_attack_grenade_spread 0 "Mage fake item grenade spread" +set g_monster_mage_attack_grenade_force 170 "Mage fake item grenade damage knockback" +set g_monster_mage_attack_grenade_chance 30 "% chance of Mage attack being fake item grenade" // Zombie set g_monster_zombie 1 "Enable Zombies" diff --git a/qcsrc/client/monsters.qc b/qcsrc/client/monsters.qc index 43cd6ae69..1a260a001 100644 --- a/qcsrc/client/monsters.qc +++ b/qcsrc/client/monsters.qc @@ -77,7 +77,7 @@ void monster_precache(float _mid) } case MONSTER_MAGE: { - precache_model(SHALRATH_MODEL); + precache_model(MAGE_MODEL); break; } case MONSTER_SPIDER: @@ -197,10 +197,10 @@ void monster_mid2info(float _mid) } case MONSTER_MAGE: { - mid2info_model = SHALRATH_MODEL; + mid2info_model = MAGE_MODEL; mid2info_name = "Mage"; - mid2info_min = SHALRATH_MIN; - mid2info_max = SHALRATH_MAX; + mid2info_min = MAGE_MIN; + mid2info_max = MAGE_MAX; break; } case MONSTER_SPIDER: diff --git a/qcsrc/client/projectile.qc b/qcsrc/client/projectile.qc index af8c5ce38..591d12047 100644 --- a/qcsrc/client/projectile.qc +++ b/qcsrc/client/projectile.qc @@ -290,7 +290,7 @@ void Ent_Projectile() case PROJECTILE_FLAC: setmodel(self, "models/hagarmissile.mdl"); self.scale = 0.4; self.traileffect = particleeffectnum("TR_SEEKER"); break; case PROJECTILE_SEEKER: setmodel(self, "models/tagrocket.md3"); self.traileffect = particleeffectnum("TR_SEEKER"); break; - case PROJECTILE_VORE_SPIKE: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_VORESPIKE"); break; + case PROJECTILE_MAGE_SPIKE: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_VORESPIKE"); break; case PROJECTILE_RAPTORBOMB: setmodel(self, "models/vehicles/clusterbomb.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break; case PROJECTILE_RAPTORBOMBLET: setmodel(self, "models/vehicles/bomblet.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break; diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index d87904488..6099c762f 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -361,7 +361,7 @@ float PROJECTILE_WAKICANNON = 29; float PROJECTILE_BUMBLE_GUN = 30; float PROJECTILE_BUMBLE_BEAM = 31; -float PROJECTILE_VORE_SPIKE = 32; +float PROJECTILE_MAGE_SPIKE = 32; float SPECIES_HUMAN = 0; float SPECIES_ROBOT_SOLID = 1; diff --git a/qcsrc/server/monsters/monster/mage.qc b/qcsrc/server/monsters/monster/mage.qc new file mode 100644 index 000000000..3aefe89c5 --- /dev/null +++ b/qcsrc/server/monsters/monster/mage.qc @@ -0,0 +1,384 @@ +// size +const vector MAGE_MIN = '-36 -36 -24'; +const vector MAGE_MAX = '36 36 50'; + +// model +string MAGE_MODEL = "models/monsters/mage.dpm"; + +#ifdef SVQC +// cvars +float autocvar_g_monster_mage; +float autocvar_g_monster_mage_health; +float autocvar_g_monster_mage_speed; +float autocvar_g_monster_mage_attack_spike_damage; +float autocvar_g_monster_mage_attack_spike_radius; +float autocvar_g_monster_mage_attack_spike_delay; +float autocvar_g_monster_mage_attack_melee_damage; +float autocvar_g_monster_mage_attack_melee_delay; +float autocvar_g_monster_mage_heal_self; +float autocvar_g_monster_mage_heal_friends; +float autocvar_g_monster_mage_heal_minhealth; +float autocvar_g_monster_mage_heal_range; +float autocvar_g_monster_mage_heal_delay; +float autocvar_g_monster_mage_shield_time; +float autocvar_g_monster_mage_shield_delay; +float autocvar_g_monster_mage_shield_blockpercent; +float autocvar_g_monster_mage_attack_grenade_damage; +float autocvar_g_monster_mage_attack_grenade_edgedamage; +float autocvar_g_monster_mage_attack_grenade_radius; +float autocvar_g_monster_mage_attack_grenade_lifetime; +float autocvar_g_monster_mage_attack_grenade_force; +float autocvar_g_monster_mage_attack_grenade_chance; + +// animations +const float mage_anim_idle = 0; +const float mage_anim_walk = 1; +const float mage_anim_attack = 2; +const float mage_anim_pain = 3; +const float mage_anim_death = 4; +const float mage_anim_run = 5; + +void() mage_heal; +void() mage_shield; +void() mage_shield_die; + +void mage_think() +{ + entity head; + float friend_needshelp = FALSE, need_hpammo = FALSE; + + FOR_EACH_PLAYER(head) + { + need_hpammo = ((g_minstagib) ? head.ammo_cells < start_ammo_cells : head.health < autocvar_g_balance_health_regenstable); + if not(IsDifferentTeam(head, self)) + if(head.health > 0) + if(vlen(head.origin - self.origin) < autocvar_g_monster_mage_heal_range) + if(need_hpammo) + { + friend_needshelp = TRUE; + break; // found 1 player near us who is low on health + } + } + FOR_EACH_MONSTER(head) if(head != self) + { + if not(IsDifferentTeam(head, self)) + if(head.health > 0) + if(vlen(head.origin - self.origin) < autocvar_g_monster_mage_heal_range) + if(head.health < head.max_health) + { + friend_needshelp = TRUE; + break; // found 1 player near us who is low on health + } + } + + self.think = mage_think; + self.nextthink = time + self.ticrate; + + if(self.weaponentity) + if(time >= self.weaponentity.ltime) + mage_shield_die(); + + if(self.health < autocvar_g_monster_mage_heal_minhealth || friend_needshelp) + if(time >= self.attack_finished_single) + if(random() < 0.5) + mage_heal(); + + if(self.enemy) + if(self.health < self.max_health) + if(time >= self.lastshielded) + if(random() < 0.5) + mage_shield(); + + monster_move(autocvar_g_monster_mage_speed, autocvar_g_monster_mage_speed, 50, mage_anim_walk, mage_anim_run, mage_anim_idle); +} + +void mageattack_melee() +{ + monster_melee(self.enemy, autocvar_g_monster_mage_attack_melee_damage, 0.3, DEATH_MONSTER_MAGE, TRUE); + + self.delay = -1; + self.monster_delayedattack = func_null; +} + +void mage_grenade_explode() +{ + pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1); + + sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM); + RadiusDamage (self, self.realowner, autocvar_g_monster_mage_attack_grenade_damage, autocvar_g_monster_mage_attack_grenade_edgedamage, autocvar_g_monster_mage_attack_grenade_radius, world, autocvar_g_monster_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); + + W_SetupShot_ProjectileSize (self, '-64 -64 -64', '64 64 64', FALSE, 4, "", CH_WEAPON_A, autocvar_g_monster_mage_attack_grenade_damage); + w_shotdir = v_forward; // no TrueAim for grenades please + + 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, w_shotorg); + setsize(gren, '-64 -64 -64', '64 64 64'); + + gren.nextthink = time + autocvar_g_monster_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_SETUPPROJECTILEVELOCITY_UP(gren, g_monster_mage_attack_grenade); + + 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; + + pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1); + RadiusDamage (self, self.realowner, autocvar_g_monster_mage_attack_spike_damage, autocvar_g_monster_mage_attack_spike_damage * 0.5, autocvar_g_monster_mage_attack_spike_radius, world, 0, DEATH_MONSTER_MAGE, other); + + remove (self); +} + +void mage_spike_touch() +{ + PROJECTILE_TOUCH; + + mage_spike_explode(); +} + +void mage_spike_think() +{ + if(self.enemy.health <= 0 || self.owner.health <= 0 || time >= self.ltime) + { + mage_spike_explode(); + return; + } + + vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin); + + UpdateCSQCProjectile(self); + + if (monster_skill == 3) + self.velocity = dir * 350; + else + self.velocity = dir * 250; + + self.nextthink = time + 0.2; + self.think = mage_spike_think; +} + +void mage_spike() +{ + entity missile; + vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin); + + makevectors(self.angles); + + missile = spawn (); + missile.owner = missile.realowner = self; + missile.think = mage_spike_think; + missile.ltime = time + 7; + missile.nextthink = time; + missile.solid = SOLID_BBOX; + missile.movetype = MOVETYPE_FLYMISSILE; + missile.flags = FL_PROJECTILE; + setorigin(missile, self.origin + v_forward * 14 + '0 0 30' + v_right * -14); + setsize (missile, '0 0 0', '0 0 0'); + missile.velocity = dir * 400; + missile.avelocity = '300 300 300'; + missile.enemy = self.enemy; + missile.touch = mage_spike_touch; + + CSQCProjectile(missile, TRUE, PROJECTILE_MAGE_SPIKE, TRUE); +} + +void mage_heal() +{ + entity head; + if(self.health < self.max_health) // only show our effect if we are healing ourself too + pointparticles(particleeffectnum("healing_fx"), self.origin, '0 0 0', 1); + self.health = bound(0, self.health + autocvar_g_monster_mage_heal_self, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + monsters_setframe(mage_anim_attack); + self.attack_finished_single = time + autocvar_g_monster_mage_heal_delay; + + for(head = world; (head = findfloat(head, monster_attack, TRUE)); ) + { + if(head.health > 0) + if not(head.frozen) + if(vlen(head.origin - self.origin) < autocvar_g_monster_mage_heal_range) + if not(IsDifferentTeam(head, self)) + { + if(IS_PLAYER(head)) + { + if((g_minstagib && head.ammo_cells < start_ammo_cells) || head.health < start_health) + pointparticles(particleeffectnum(((g_minstagib) ? "ammoregen_fx" : "healing_fx")), head.origin, '0 0 0', 1); + if(g_minstagib) + head.ammo_cells = bound(0, head.ammo_cells + 1, start_ammo_cells); + else + head.health = bound(0, head.health + autocvar_g_monster_mage_heal_friends, g_pickup_healthmedium_max); + } + else + { + if(head.health < head.max_health) + pointparticles(particleeffectnum("healing_fx"), head.origin, '0 0 0', 1); + head.health = bound(0, head.health + autocvar_g_monster_mage_heal_friends, head.max_health); + WaypointSprite_UpdateHealth(head.sprite, head.health); + } + } + } +} + +void mage_shield_die() +{ + if not(self.weaponentity) + return; // why would this be called without a shield? + + self.armorvalue = 1; + + remove(self.weaponentity); + + self.weaponentity = world; +} + +void mage_shield() +{ + if(self.weaponentity) + return; // already have a shield + + entity shield = spawn(); + + shield.owner = self; + shield.team = self.team; + shield.ltime = time + autocvar_g_monster_mage_shield_time; + shield.health = 70; + shield.classname = "shield"; + shield.effects = EF_ADDITIVE; + shield.movetype = MOVETYPE_NOCLIP; + shield.solid = SOLID_TRIGGER; + shield.avelocity = '7 0 11'; + shield.scale = self.scale * 0.6; + + setattachment(shield, self, ""); + setmodel(shield, "models/ctf/shield.md3"); + setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); + + self.weaponentity = shield; + + self.lastshielded = time + autocvar_g_monster_mage_shield_delay; + + monsters_setframe(mage_anim_attack); + self.attack_finished_single = time + 1; + + self.armorvalue = autocvar_g_monster_mage_shield_blockpercent / 100; +} + +float mage_attack(float attack_type) +{ + switch(attack_type) + { + case MONSTER_ATTACK_MELEE: + { + self.monster_delayedattack = mageattack_melee; + self.delay = time + 0.2; + monsters_setframe(mage_anim_attack); + self.attack_finished_single = time + autocvar_g_monster_mage_attack_melee_delay; + + return TRUE; + } + case MONSTER_ATTACK_RANGED: + { + if(random() < autocvar_g_monster_mage_attack_grenade_chance / 100) + { + mage_throw_itemgrenade(); + return TRUE; + } + + monsters_setframe(mage_anim_attack); + self.delay = time + 0.2; + self.attack_finished_single = time + autocvar_g_monster_mage_attack_spike_delay; + self.monster_delayedattack = mage_spike; + + return TRUE; + } + } + + return FALSE; +} + +void mage_die() +{ + Monster_CheckDropCvars ("mage"); + + self.think = monster_dead_think; + self.nextthink = time + self.ticrate; + self.ltime = time + 5; + monsters_setframe(mage_anim_death); + + monster_hook_death(); // for post-death mods +} + +void mage_spawn() +{ + if not(self.health) + self.health = autocvar_g_monster_mage_health; + + self.damageforcescale = 0.003; + self.classname = "monster_mage"; + self.monster_attackfunc = mage_attack; + self.nextthink = time + random() * 0.5 + 0.1; + self.think = mage_think; + + monsters_setframe(mage_anim_walk); + + monster_setupsounds("mage"); + + monster_hook_spawn(); // for post-spawn mods +} + +void spawnfunc_monster_mage() +{ + if not(autocvar_g_monster_mage) { remove(self); return; } + + self.monster_spawnfunc = spawnfunc_monster_mage; + + if(Monster_CheckAppearFlags(self)) + return; + + if not (monster_initialize( + "Mage", MONSTER_MAGE, + MAGE_MIN, MAGE_MAX, + FALSE, + mage_die, mage_spawn)) + { + remove(self); + return; + } +} + +// compatibility with old spawns +void spawnfunc_monster_shalrath() { spawnfunc_monster_mage(); } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/shalrath.qc b/qcsrc/server/monsters/monster/shalrath.qc deleted file mode 100644 index 1ec6cec90..000000000 --- a/qcsrc/server/monsters/monster/shalrath.qc +++ /dev/null @@ -1,390 +0,0 @@ -// size -const vector SHALRATH_MIN = '-36 -36 -24'; -const vector SHALRATH_MAX = '36 36 50'; - -// model -string SHALRATH_MODEL = "models/monsters/mage.dpm"; - -#ifdef SVQC -// cvars -float autocvar_g_monster_shalrath; -float autocvar_g_monster_shalrath_health; -float autocvar_g_monster_shalrath_speed; -float autocvar_g_monster_shalrath_attack_spike_damage; -float autocvar_g_monster_shalrath_attack_spike_radius; -float autocvar_g_monster_shalrath_attack_spike_delay; -float autocvar_g_monster_shalrath_attack_melee_damage; -float autocvar_g_monster_shalrath_attack_melee_delay; -float autocvar_g_monster_shalrath_heal_self; -float autocvar_g_monster_shalrath_heal_friends; -float autocvar_g_monster_shalrath_heal_minhealth; -float autocvar_g_monster_shalrath_heal_range; -float autocvar_g_monster_shalrath_heal_delay; -float autocvar_g_monster_shalrath_shield_time; -float autocvar_g_monster_shalrath_shield_delay; -float autocvar_g_monster_shalrath_shield_blockpercent; -float autocvar_g_monster_shalrath_attack_grenade_damage; -float autocvar_g_monster_shalrath_attack_grenade_edgedamage; -float autocvar_g_monster_shalrath_attack_grenade_radius; -float autocvar_g_monster_shalrath_attack_grenade_lifetime; -float autocvar_g_monster_shalrath_attack_grenade_force; -float autocvar_g_monster_shalrath_attack_grenade_chance; - -// animations -const float shalrath_anim_idle = 0; -const float shalrath_anim_walk = 1; -const float shalrath_anim_attack = 2; -const float shalrath_anim_pain = 3; -const float shalrath_anim_death = 4; -const float shalrath_anim_run = 5; - -void() ShalMissile; -void() shalrath_heal; -void() shalrath_shield; -void() shalrath_shield_die; - -void shalrath_think() -{ - entity head; - float friend_needshelp = FALSE, need_hpammo = FALSE; - - FOR_EACH_PLAYER(head) - { - need_hpammo = ((g_minstagib) ? head.ammo_cells < start_ammo_cells : head.health < autocvar_g_balance_health_regenstable); - if not(IsDifferentTeam(head, self)) - if(head.health > 0) - if(vlen(head.origin - self.origin) < autocvar_g_monster_shalrath_heal_range) - if(need_hpammo) - { - friend_needshelp = TRUE; - break; // found 1 player near us who is low on health - } - } - FOR_EACH_MONSTER(head) if(head != self) - { - if not(IsDifferentTeam(head, self)) - if(head.health > 0) - if(vlen(head.origin - self.origin) < autocvar_g_monster_shalrath_heal_range) - if(head.health < head.max_health) - { - friend_needshelp = TRUE; - break; // found 1 player near us who is low on health - } - } - - self.think = shalrath_think; - self.nextthink = time + self.ticrate; - - if(self.weaponentity) - if(time >= self.weaponentity.ltime) - shalrath_shield_die(); - - if(self.health < autocvar_g_monster_shalrath_heal_minhealth || friend_needshelp) - if(time >= self.attack_finished_single) - if(random() < 0.5) - shalrath_heal(); - - if(self.enemy) - if(self.health < self.max_health) - if(time >= self.lastshielded) - if(random() < 0.5) - shalrath_shield(); - - monster_move(autocvar_g_monster_shalrath_speed, autocvar_g_monster_shalrath_speed, 50, shalrath_anim_walk, shalrath_anim_run, shalrath_anim_idle); -} - -void shalrathattack_melee() -{ - monster_melee(self.enemy, autocvar_g_monster_shalrath_attack_melee_damage, 0.3, DEATH_MONSTER_MAGE, TRUE); - - self.delay = -1; - self.monster_delayedattack = func_null; -} - -void shalrath_grenade_explode() -{ - pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1); - - sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM); - RadiusDamage (self, self.realowner, autocvar_g_monster_shalrath_attack_grenade_damage, autocvar_g_monster_shalrath_attack_grenade_edgedamage, autocvar_g_monster_shalrath_attack_grenade_radius, world, autocvar_g_monster_shalrath_attack_grenade_force, DEATH_MONSTER_MAGE, other); - remove(self); -} - -void shalrath_grenade_touch() -{ - if(IS_PLAYER(other)) - { - PROJECTILE_TOUCH; - shalrath_grenade_explode(); - return; - } -} - -void shalrath_throw_itemgrenade() -{ - makevectors(self.angles); - - W_SetupShot_ProjectileSize (self, '-64 -64 -64', '64 64 64', FALSE, 4, "", CH_WEAPON_A, autocvar_g_monster_shalrath_attack_grenade_damage); - w_shotdir = v_forward; // no TrueAim for grenades please - - 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, w_shotorg); - setsize(gren, '-64 -64 -64', '64 64 64'); - - gren.nextthink = time + autocvar_g_monster_shalrath_attack_grenade_lifetime; - gren.think = shalrath_grenade_explode; - gren.use = shalrath_grenade_explode; - gren.touch = shalrath_grenade_touch; - - gren.missile_flags = MIF_SPLASH | MIF_ARC; - W_SETUPPROJECTILEVELOCITY_UP(gren, g_monster_shalrath_attack_grenade); - - gren.flags = FL_PROJECTILE; - - setmodel(gren, "models/items/g_h50.md3"); - - self.attack_finished_single = time + 1.5; -} - -void ShalHome() -{ - local vector dir = '0 0 0', vtemp = self.enemy.origin + '0 0 10'; - - if (self.enemy.health <= 0 || self.owner.health <= 0 || time >= self.ltime) - { - remove(self); - return; - } - dir = normalize(vtemp - self.origin); - UpdateCSQCProjectile(self); - if (monster_skill == 3) - self.velocity = dir * 350; - else - self.velocity = dir * 250; - self.nextthink = time + 0.2; - self.think = ShalHome; -} - -void shal_spike_explode() -{ - self.event_damage = func_null; - - pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1); - RadiusDamage (self, self.realowner, autocvar_g_monster_shalrath_attack_spike_damage, autocvar_g_monster_shalrath_attack_spike_damage * 0.5, autocvar_g_monster_shalrath_attack_spike_radius, world, 0, DEATH_MONSTER_MAGE, other); - - remove (self); -} - -void shal_spike_touchexplode() -{ - PROJECTILE_TOUCH; - - shal_spike_explode(); -} - -void ShalMissile() -{ - local entity missile = world; - local vector dir = '0 0 0'; - local float dist = 0; - - self.effects |= EF_MUZZLEFLASH; - - missile = spawn (); - missile.owner = missile.realowner = self; - - monster_makevectors(self.enemy); - - dir = normalize((self.enemy.origin + '0 0 10') - self.origin); - dist = vlen (self.enemy.origin - self.origin); - - missile.think = ShalHome; - missile.ltime = time + 7; - missile.nextthink = time; - missile.solid = SOLID_BBOX; - missile.movetype = MOVETYPE_FLYMISSILE; - missile.flags = FL_PROJECTILE; - setorigin (missile, self.origin + v_forward * 14 + '0 0 30' + v_right * -14); - setsize (missile, '0 0 0', '0 0 0'); - missile.velocity = dir * 400; - missile.avelocity = '300 300 300'; - missile.enemy = self.enemy; - missile.touch = shal_spike_touchexplode; - - CSQCProjectile(missile, TRUE, PROJECTILE_VORE_SPIKE, TRUE); -} - -void shalrath_heal() -{ - entity head; - if(self.health < self.max_health) // only show our effect if we are healing ourself too - pointparticles(particleeffectnum("healing_fx"), self.origin, '0 0 0', 1); - self.health = bound(0, self.health + autocvar_g_monster_shalrath_heal_self, self.max_health); - WaypointSprite_UpdateHealth(self.sprite, self.health); - monsters_setframe(shalrath_anim_attack); - self.attack_finished_single = time + autocvar_g_monster_shalrath_heal_delay; - - for(head = world; (head = findfloat(head, monster_attack, TRUE)); ) - { - if(head.health > 0) - if not(head.frozen) - if(vlen(head.origin - self.origin) < autocvar_g_monster_shalrath_heal_range) - if not(IsDifferentTeam(head, self)) - { - if(IS_PLAYER(head)) - { - if((g_minstagib && head.ammo_cells < start_ammo_cells) || head.health < start_health) - pointparticles(particleeffectnum(((g_minstagib) ? "ammoregen_fx" : "healing_fx")), head.origin, '0 0 0', 1); - if(g_minstagib) - head.ammo_cells = bound(0, head.ammo_cells + 1, start_ammo_cells); - else - head.health = bound(0, head.health + autocvar_g_monster_shalrath_heal_friends, g_pickup_healthmedium_max); - } - else - { - if(head.health < head.max_health) - pointparticles(particleeffectnum("healing_fx"), head.origin, '0 0 0', 1); - head.health = bound(0, head.health + autocvar_g_monster_shalrath_heal_friends, head.max_health); - WaypointSprite_UpdateHealth(head.sprite, head.health); - } - } - } -} - -void shalrath_shield_die() -{ - if not(self.weaponentity) - return; // why would this be called without a shield? - - self.armorvalue = 1; - - remove(self.weaponentity); - - self.weaponentity = world; -} - -void shalrath_shield() -{ - if(self.weaponentity) - return; // already have a shield - - entity shield = spawn(); - - shield.owner = self; - shield.team = self.team; - shield.ltime = time + autocvar_g_monster_shalrath_shield_time; - shield.health = 70; - shield.classname = "shield"; - shield.effects = EF_ADDITIVE; - shield.movetype = MOVETYPE_NOCLIP; - shield.solid = SOLID_TRIGGER; - shield.avelocity = '7 0 11'; - shield.scale = self.scale * 0.6; - - setattachment(shield, self, ""); - setmodel(shield, "models/ctf/shield.md3"); - setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); - - self.weaponentity = shield; - - self.lastshielded = time + autocvar_g_monster_shalrath_shield_delay; - - monsters_setframe(shalrath_anim_attack); - self.attack_finished_single = time + 1; - - self.armorvalue = autocvar_g_monster_shalrath_shield_blockpercent / 100; -} - -float mage_attack(float attack_type) -{ - switch(attack_type) - { - case MONSTER_ATTACK_MELEE: - { - self.monster_delayedattack = shalrathattack_melee; - self.delay = time + 0.2; - monsters_setframe(shalrath_anim_attack); - self.attack_finished_single = time + autocvar_g_monster_shalrath_attack_melee_delay; - - return TRUE; - } - case MONSTER_ATTACK_RANGED: - { - if(random() < autocvar_g_monster_shalrath_attack_grenade_chance / 100) - { - shalrath_throw_itemgrenade(); - return TRUE; - } - - monsters_setframe(shalrath_anim_attack); - self.delay = time + 0.2; - self.attack_finished_single = time + autocvar_g_monster_shalrath_attack_spike_delay; - self.monster_delayedattack = ShalMissile; - - return TRUE; - } - } - - return FALSE; -} - -void shalrath_die() -{ - Monster_CheckDropCvars ("shalrath"); - - self.think = monster_dead_think; - self.nextthink = time + self.ticrate; - self.ltime = time + 5; - monsters_setframe(shalrath_anim_death); - - monster_hook_death(); // for post-death mods -} - -void shalrath_spawn() -{ - if not(self.health) - self.health = autocvar_g_monster_shalrath_health; - - self.damageforcescale = 0.003; - self.classname = "monster_shalrath"; - self.monster_attackfunc = mage_attack; - self.nextthink = time + random() * 0.5 + 0.1; - self.think = shalrath_think; - - monsters_setframe(shalrath_anim_walk); - - monster_setupsounds("shalrath"); - - monster_hook_spawn(); // for post-spawn mods -} - -void spawnfunc_monster_mage() -{ - if not(autocvar_g_monster_shalrath) { remove(self); return; } - - self.monster_spawnfunc = spawnfunc_monster_mage; - - if(Monster_CheckAppearFlags(self)) - return; - - if not (monster_initialize( - "Mage", MONSTER_MAGE, - SHALRATH_MIN, SHALRATH_MAX, - FALSE, - shalrath_die, shalrath_spawn)) - { - remove(self); - return; - } -} - -// compatibility with old spawns -void spawnfunc_monster_shalrath() { spawnfunc_monster_mage(); } - -#endif // SVQC diff --git a/qcsrc/server/monsters/monsters.qh b/qcsrc/server/monsters/monsters.qh index 737c6c6b6..d6eb119a6 100644 --- a/qcsrc/server/monsters/monsters.qh +++ b/qcsrc/server/monsters/monsters.qh @@ -16,6 +16,6 @@ #include "monster/slime.qc" #include "monster/knight.qc" #include "monster/fish.qc" -#include "monster/shalrath.qc" +#include "monster/mage.qc" #include "monster/zombie.qc" #include "monster/spider.qc" diff --git a/qcsrc/server/mutators/gamemode_towerdefense.qc b/qcsrc/server/mutators/gamemode_towerdefense.qc index a42a7aa0e..e5adb82ac 100644 --- a/qcsrc/server/mutators/gamemode_towerdefense.qc +++ b/qcsrc/server/mutators/gamemode_towerdefense.qc @@ -448,7 +448,7 @@ float RandomMonster() RandomSelection_Init(); if(n_animuses) RandomSelection_Add(world, MONSTER_ANIMUS, "", 1, 1); - if(n_shalraths) RandomSelection_Add(world, MONSTER_MAGE, "", 1, 1); + if(n_mages) RandomSelection_Add(world, MONSTER_MAGE, "", 1, 1); if(n_soldiers) RandomSelection_Add(world, MONSTER_MARINE, "", 1, 1); if(n_knights) RandomSelection_Add(world, MONSTER_KNIGHT, "", 1, 1); if(n_zombies) RandomSelection_Add(world, MONSTER_ZOMBIE, "", 1, 1); @@ -506,12 +506,12 @@ void queue_monsters(float maxmonsters) n_ogres = DistributeEvenly_Get(1); n_dogs = DistributeEvenly_Get(1); n_bruisers = DistributeEvenly_Get(1); - n_shalraths = DistributeEvenly_Get(1); + n_mages = DistributeEvenly_Get(1); n_soldiers = DistributeEvenly_Get(1); - n_knights = DistributeEvenly_Get(1); + n_knights = DistributeEvenly_Get(1); n_zombies = DistributeEvenly_Get(1); n_spiders = DistributeEvenly_Get(1); - n_slimes = DistributeEvenly_Get(0.7); + n_slimes = DistributeEvenly_Get(0.7); n_shamblers = DistributeEvenly_Get(0.3); if(flyspawns_count > 0) n_wyverns = DistributeEvenly_Get(1); @@ -849,7 +849,7 @@ MUTATOR_HOOKFUNCTION(td_MonsterSpawn) case MONSTER_SLIME: n_slimes -= 1; break; case MONSTER_KNIGHT: n_knights -= 1; break; case MONSTER_FISH: n_fish -= 1; break; - case MONSTER_MAGE: n_shalraths -= 1; break; + case MONSTER_MAGE: n_mages -= 1; break; case MONSTER_SPIDER: n_spiders -= 1; break; } diff --git a/qcsrc/server/mutators/gamemode_towerdefense.qh b/qcsrc/server/mutators/gamemode_towerdefense.qh index 03e0f2d7a..63444be2c 100644 --- a/qcsrc/server/mutators/gamemode_towerdefense.qh +++ b/qcsrc/server/mutators/gamemode_towerdefense.qh @@ -1,6 +1,6 @@ // Counters float monster_count, totalmonsters; -float n_bruisers, n_dogs, n_ogres, n_shamblers, n_wyverns, n_shalraths, n_soldiers, n_knights, n_animuses, n_zombies, n_slimes, n_fish, n_spiders; +float n_bruisers, n_dogs, n_ogres, n_shamblers, n_wyverns, n_mages, n_soldiers, n_knights, n_animuses, n_zombies, n_slimes, n_fish, n_spiders; float current_monsters; float waterspawns_count, flyspawns_count; float wave_count, max_waves;