]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/monsters_broken
authorTimePath <andrew.hardaker1995@gmail.com>
Sat, 29 Aug 2015 12:26:05 +0000 (22:26 +1000)
committerTimePath <andrew.hardaker1995@gmail.com>
Sat, 29 Aug 2015 12:26:05 +0000 (22:26 +1000)
# Conflicts:
# qcsrc/common/monsters/sv_monsters.qc

1  2 
qcsrc/client/csqcmodel_hooks.qc
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/monster/shambler.qc
qcsrc/common/monsters/monster/spider.qc
qcsrc/common/monsters/monster/wyvern.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/server/command/sv_cmd.qc

index 20bf6c0e8dbd167e0ee3671f983ee47b140f5c2d,c3e81478c4f2819c9fe9999a4ad715906e3fcd07..91ab02098ceb6ab824b7044f24307b4582973c7d
@@@ -8,6 -8,7 +8,7 @@@
  
  #include "../common/animdecide.qh"
  #include "../common/csqcmodel_settings.qh"
+ #include "../common/effects.qh"
  #include "../common/teams.qh"
  #include "../common/triggers/trigger/viewloc.qh"
  
@@@ -527,7 -528,7 +528,7 @@@ void CSQCModel_Effects_Apply(void
        self.traileffect = 0;
  
        if(eff & EF_BRIGHTFIELD)
-               self.traileffect = particleeffectnum("TR_NEXUIZPLASMA");
+               self.traileffect = particleeffectnum(EFFECT_TR_NEXUIZPLASMA);
        // ignoring EF_MUZZLEFLASH
        if(eff & EF_BRIGHTLIGHT)
                adddynamiclight(self.origin, 400, '3 3 3');
        if(eff & EF_FULLBRIGHT)
                self.renderflags |= RF_FULLBRIGHT;
        if(eff & EF_FLAME)
-               pointparticles(particleeffectnum("EF_FLAME"), self.origin, '0 0 0', bound(0, frametime, 0.1));
+               pointparticles(particleeffectnum(EFFECT_EF_FLAME), self.origin, '0 0 0', bound(0, frametime, 0.1));
        if(eff & EF_STARDUST)
-               pointparticles(particleeffectnum("EF_STARDUST"), self.origin, '0 0 0', bound(0, frametime, 0.1));
+               pointparticles(particleeffectnum(EFFECT_EF_STARDUST), self.origin, '0 0 0', bound(0, frametime, 0.1));
        if(eff & EF_NOSHADOW)
                self.renderflags |= RF_NOSHADOW;
        if(eff & EF_NODEPTHTEST)
                self.renderflags |= RF_DYNAMICMODELLIGHT;
        // ignoring EF_UNUSED18, EF_UNUSED19, EF_RESTARTANIM_BIT, EF_TELEPORT_BIT, EF_LOWPRECISION
        if(self.csqcmodel_modelflags & MF_ROCKET)
-               self.traileffect = particleeffectnum("TR_ROCKET");
+               self.traileffect = particleeffectnum(EFFECT_TR_ROCKET);
        if(self.csqcmodel_modelflags & MF_GRENADE)
-               self.traileffect = particleeffectnum("TR_GRENADE");
+               self.traileffect = particleeffectnum(EFFECT_TR_GRENADE);
        if(self.csqcmodel_modelflags & MF_GIB)
-               self.traileffect = particleeffectnum("TR_BLOOD");
+               self.traileffect = particleeffectnum(EFFECT_TR_BLOOD);
        if(self.csqcmodel_modelflags & MF_ROTATE)
        {
                self.renderflags |= RF_USEAXIS;
                makevectors(self.angles + '0 100 0' * fmod(time, 3.6));
        }
        if(self.csqcmodel_modelflags & MF_TRACER)
-               self.traileffect = particleeffectnum("TR_WIZSPIKE");
+               self.traileffect = particleeffectnum(EFFECT_TR_WIZSPIKE);
        if(self.csqcmodel_modelflags & MF_ZOMGIB)
-               self.traileffect = particleeffectnum("TR_SLIGHTBLOOD");
+               self.traileffect = particleeffectnum(EFFECT_TR_SLIGHTBLOOD);
        if(self.csqcmodel_modelflags & MF_TRACER2)
-               self.traileffect = particleeffectnum("TR_KNIGHTSPIKE");
+               self.traileffect = particleeffectnum(EFFECT_TR_KNIGHTSPIKE);
        if(self.csqcmodel_modelflags & MF_TRACER3)
-               self.traileffect = particleeffectnum("TR_VORESPIKE");
+               self.traileffect = particleeffectnum(EFFECT_TR_VORESPIKE);
  
        if(self.drawmask)
                Projectile_DrawTrail(self.origin);
@@@ -641,7 -642,6 +642,7 @@@ void CSQCModel_Hook_PreDraw(bool isplay
        {
                CSQCPlayer_ModelAppearance_Apply(self.entnum == player_localnum + 1);
                CSQCPlayer_LOD_Apply();
 +
                if(!isplayer)
                {
                        skeleton_loadinfo(self);
index d586321f6c45735cd8c9ad13741fc346c41fd2d6,46ce94dd7e261d6f7781fd85b89afeead17a849f..8f92d693a6e614fbf2422f36722a8484b84d9b06
@@@ -1,5 -1,5 +1,5 @@@
  #ifndef MENUQC
 -bool m_mage(int);
 +bool M_Mage(int);
  #endif
  REGISTER_MONSTER_SIMPLE(
  /* MON_##id   */ MAGE,
  /* fullname   */ _("Mage")
  ) {
  #ifndef MENUQC
 -    this.monster_func = m_mage;
 +    this.monster_func = M_Mage;
 +    this.monster_func(MR_PRECACHE);
  #endif
  }
  
  #ifdef SVQC
  float autocvar_g_monster_mage_health;
 +float autocvar_g_monster_mage_damageforcescale = 0.5;
  float autocvar_g_monster_mage_attack_spike_damage;
  float autocvar_g_monster_mage_attack_spike_radius;
  float autocvar_g_monster_mage_attack_spike_delay;
@@@ -45,29 -43,26 +45,29 @@@ float autocvar_g_monster_mage_speed_sto
  float autocvar_g_monster_mage_speed_run;
  float autocvar_g_monster_mage_speed_walk;
  
 +/*
  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() M_Mage_Defend_Heal;
 +void() M_Mage_Defend_Shield;
  
  .entity mage_spike;
 -.float shield_ltime;
 +.float mage_shield_delay;
 +.float mage_shield_time;
  
 -float friend_needshelp(entity e)
 +float M_Mage_Defend_Heal_Check(entity e)
  {
        if(e == world)
                return false;
        if(e.health <= 0)
                return false;
 -      if(DIFF_TEAM(e, self) && e != self.monster_owner)
 +      if(DIFF_TEAM(e, self) && e != self.monster_follow)
                return false;
        if(e.frozen)
                return false;
@@@ -87,7 -82,7 +87,7 @@@
        return false;
  }
  
 -void mage_spike_explode()
 +void M_Mage_Attack_Spike_Explode()
  {
        self.event_damage = func_null;
  
  
        self.realowner.mage_spike = world;
  
-       Send_Effect("explosion_small", self.origin, '0 0 0', 1);
+       Send_Effect(EFFECT_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, world, 0, DEATH_MONSTER_MAGE, other);
  
        remove (self);
  }
  
 -void mage_spike_touch()
 +void M_Mage_Attack_Spike_Touch()
  {
        PROJECTILE_TOUCH;
  
 -      mage_spike_explode();
 +      M_Mage_Attack_Spike_Explode();
  }
  
  // copied from W_Seeker_Think
 -void mage_spike_think()
 +void M_Mage_Attack_Spike_Think()
  {
        entity e;
        vector desireddir, olddir, newdir, eorg;
        if (time > self.ltime || self.enemy.health <= 0 || self.owner.health <= 0)
        {
                self.projectiledeathtype |= HITTYPE_SPLASH;
 -              mage_spike_explode();
 +              M_Mage_Attack_Spike_Explode();
        }
  
        spd = vlen(self.velocity);
        UpdateCSQCProjectile(self);
  }
  
 -void mage_attack_spike()
 +void M_Mage_Attack_Spike()
  {
        entity missile;
        vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
  
        missile = spawn ();
        missile.owner = missile.realowner = self;
 -      missile.think = mage_spike_think;
 +      missile.think = M_Mage_Attack_Spike_Think;
        missile.ltime = time + 7;
        missile.nextthink = time;
        missile.solid = SOLID_BBOX;
        missile.velocity = dir * 400;
        missile.avelocity = '300 300 300';
        missile.enemy = self.enemy;
 -      missile.touch = mage_spike_touch;
 +      missile.touch = M_Mage_Attack_Spike_Touch;
  
        self.mage_spike = missile;
  
        CSQCProjectile(missile, true, PROJECTILE_MAGE_SPIKE, true);
  }
  
 -void mage_heal()
 +void M_Mage_Defend_Heal()
  {
        entity head;
        float washealed = false;
  
 -      for(head = findradius(self.origin, (autocvar_g_monster_mage_heal_range)); head; head = head.chain) if(friend_needshelp(head))
 +      for(head = findradius(self.origin, (autocvar_g_monster_mage_heal_range)); head; head = head.chain) if(M_Mage_Defend_Heal_Check(head))
        {
                washealed = true;
                string fx = "";
                        {
                                case 0:
                                        if(head.health < autocvar_g_balance_health_regenstable) head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_health_regenstable);
-                                       fx = "healing_fx";
+                                       fx = EFFECT_HEALING.eent_eff_name;
                                        break;
                                case 1:
                                        if(head.ammo_cells) head.ammo_cells = bound(head.ammo_cells, head.ammo_cells + 1, g_pickup_cells_max);
                                        break;
                                case 3:
                                        head.health = bound(0, head.health - ((head == self)  ? (autocvar_g_monster_mage_heal_self) : (autocvar_g_monster_mage_heal_allies)), autocvar_g_balance_health_regenstable);
-                                       fx = "rage";
+                                       fx = EFFECT_RAGE.eent_eff_name;
                                        break;
                        }
  
-                       Send_Effect(fx, head.origin, '0 0 0', 1);
+                       Send_Effect_(fx, head.origin, '0 0 0', 1);
                }
                else
                {
-                       Send_Effect("healing_fx", head.origin, '0 0 0', 1);
+                       Send_Effect(EFFECT_HEALING, head.origin, '0 0 0', 1);
                        head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), head.max_health);
 -                      if(!(head.spawnflags & MONSTERFLAG_INVINCIBLE))
 +                      if(!(head.spawnflags & MONSTERFLAG_INVINCIBLE) && head.sprite)
                                WaypointSprite_UpdateHealth(head.sprite, head.health);
                }
        }
  
        if(washealed)
        {
 -              self.frame = mage_anim_attack;
 +              setanim(self, self.anim_shoot, true, true, true);
                self.attack_finished_single = time + (autocvar_g_monster_mage_heal_delay);
 +              self.anim_finished = time + 1.5;
        }
  }
  
 -void mage_push()
 +void M_Mage_Attack_Push()
  {
        sound(self, CH_SHOTS, W_Sound("tagexp1"), 1, ATTEN_NORM);
        RadiusDamage (self, self, (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_radius), world, world, (autocvar_g_monster_mage_attack_push_force), DEATH_MONSTER_MAGE, self.enemy);
-       Send_Effect("TE_EXPLOSION", self.origin, '0 0 0', 1);
+       Send_Effect(EFFECT_TE_EXPLOSION, self.origin, '0 0 0', 1);
  
 -      self.frame = mage_anim_attack;
 +      setanim(self, self.anim_shoot, true, true, true);
        self.attack_finished_single = time + (autocvar_g_monster_mage_attack_push_delay);
  }
  
 -void mage_teleport()
 +void M_Mage_Attack_Teleport()
  {
        if(vlen(self.enemy.origin - self.origin) >= 500)
                return;
        if(trace_fraction < 1)
                return;
  
-       Send_Effect("spawn_event_neutral", self.origin, '0 0 0', 1);
+       Send_Effect(EFFECT_SPAWN_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_remove()
 +void M_Mage_Defend_Shield_Remove()
  {
        self.effects &= ~(EF_ADDITIVE | EF_BLUE);
 -      self.armorvalue = 0;
 -      self.m_armor_blockpercent = autocvar_g_monsters_armor_blockpercent;
 +      self.armorvalue = autocvar_g_monsters_armor_blockpercent;
  }
  
 -void mage_shield()
 +void M_Mage_Defend_Shield()
  {
        self.effects |= (EF_ADDITIVE | EF_BLUE);
 -      self.lastshielded = time + (autocvar_g_monster_mage_shield_delay);
 -      self.m_armor_blockpercent = (autocvar_g_monster_mage_shield_blockpercent);
 -      self.armorvalue = self.health;
 -      self.shield_ltime = time + (autocvar_g_monster_mage_shield_time);
 -      self.frame = mage_anim_attack;
 +      self.mage_shield_delay = time + (autocvar_g_monster_mage_shield_delay);
 +      self.armorvalue = (autocvar_g_monster_mage_shield_blockpercent);
 +      self.mage_shield_time = time + (autocvar_g_monster_mage_shield_time);
 +      setanim(self, self.anim_shoot, true, true, true);
        self.attack_finished_single = time + 1;
 +      self.anim_finished = time + 1;
  }
  
 -float mage_attack(float attack_type)
 +float M_Mage_Attack(float attack_type)
  {
        switch(attack_type)
        {
                {
                        if(random() <= 0.7)
                        {
 -                              mage_push();
 +                              M_Mage_Attack_Push();
                                return true;
                        }
  
                        {
                                if(random() <= 0.4)
                                {
 -                                      mage_teleport();
 +                                      M_Mage_Attack_Teleport();
                                        return true;
                                }
                                else
                                {
 -                                      self.frame = mage_anim_attack;
 +                                      setanim(self, self.anim_shoot, true, true, true);
                                        self.attack_finished_single = time + (autocvar_g_monster_mage_attack_spike_delay);
 -                                      defer(0.2, mage_attack_spike);
 +                                      self.anim_finished = time + 1;
 +                                      Monster_Delay(1, 0, 0.2, M_Mage_Attack_Spike);
                                        return true;
                                }
                        }
        return false;
  }
  
 -void spawnfunc_monster_mage()
 -{
 -      self.classname = "monster_mage";
 -
 -      if(!monster_initialize(MON_MAGE.monsterid)) { remove(self); return; }
 -}
 +void spawnfunc_monster_mage() { Monster_Spawn(MON_MAGE.monsterid); }
  
 -// compatibility with old spawns
 -void spawnfunc_monster_shalrath() { spawnfunc_monster_mage(); }
 +#endif // SVQC
  
 -float m_mage(float req)
 +bool M_Mage(int req)
  {
        switch(req)
        {
 +              #ifdef SVQC
                case MR_THINK:
                {
                        entity head;
 -                      float need_help = false;
 +                      bool need_help = false;
  
 -                      for(head = findradius(self.origin, (autocvar_g_monster_mage_heal_range)); head; head = head.chain)
 +                      for(head = world; (head = findfloat(head, iscreature, true)); )
                        if(head != self)
 -                      if(friend_needshelp(head))
 +                      if(vlen(head.origin - self.origin) <= (autocvar_g_monster_mage_heal_range))
 +                      if(M_Mage_Defend_Heal_Check(head))
                        {
                                need_help = true;
                                break;
                        if(self.health < (autocvar_g_monster_mage_heal_minhealth) || need_help)
                        if(time >= self.attack_finished_single)
                        if(random() < 0.5)
 -                              mage_heal();
 +                              M_Mage_Defend_Heal();
  
 -                      if(time >= self.shield_ltime && self.armorvalue)
 -                              mage_shield_remove();
 +                      if(time >= self.mage_shield_time && self.armorvalue)
 +                              M_Mage_Defend_Shield_Remove();
  
                        if(self.enemy)
                        if(self.health < self.max_health)
 -                      if(time >= self.lastshielded)
 +                      if(time >= self.mage_shield_delay)
                        if(random() < 0.5)
 -                              mage_shield();
 +                              M_Mage_Defend_Shield();
  
 -                      monster_move((autocvar_g_monster_mage_speed_run), (autocvar_g_monster_mage_speed_walk), (autocvar_g_monster_mage_speed_stop), mage_anim_walk, mage_anim_run, mage_anim_idle);
 +                      return true;
 +              }
 +              case MR_PAIN:
 +              {
                        return true;
                }
                case MR_DEATH:
                {
 -                      self.frame = mage_anim_death;
 +                      setanim(self, self.anim_die1, false, true, true);
                        return true;
                }
 +              #endif
 +              #ifndef MENUQC
 +              case MR_ANIM:
 +              {
 +                      vector none = '0 0 0';
 +                      self.anim_die1 = animfixfps(self, '4 1 0.5', none); // 2 seconds
 +                      self.anim_walk = animfixfps(self, '1 1 1', none);
 +                      self.anim_idle = animfixfps(self, '0 1 1', none);
 +                      self.anim_pain1 = animfixfps(self, '3 1 2', none); // 0.5 seconds
 +                      self.anim_shoot = animfixfps(self, '2 1 5', none); // analyze models and set framerate
 +                      self.anim_run = animfixfps(self, '5 1 1', none);
 +
 +                      return true;
 +              }
 +              #endif
 +              #ifdef SVQC
                case MR_SETUP:
                {
                        if(!self.health) self.health = (autocvar_g_monster_mage_health);
 +                      if(!self.speed) { self.speed = (autocvar_g_monster_mage_speed_walk); }
 +                      if(!self.speed2) { self.speed2 = (autocvar_g_monster_mage_speed_run); }
 +                      if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_mage_speed_stop); }
 +                      if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_mage_damageforcescale); }
  
                        self.monster_loot = spawnfunc_item_health_large;
 -                      self.monster_attackfunc = mage_attack;
 -                      self.frame = mage_anim_walk;
 +                      self.monster_attackfunc = M_Mage_Attack;
  
                        return true;
                }
                        precache_sound (W_Sound("tagexp1"));
                        return true;
                }
 +              #endif
        }
  
        return true;
  }
 -
 -#endif // SVQC
 -#ifdef CSQC
 -float m_mage(float req)
 -{
 -      switch(req)
 -      {
 -              case MR_PRECACHE:
 -              {
 -                      return true;
 -              }
 -      }
 -
 -      return true;
 -}
 -
 -#endif // CSQC
index 49bc3d8e690098a37e701b7dbc4f9595d5c120f0,80325a2fa511c1ea9c4e853bb22bce493d91275d..4640fe945bcf33f6e9ea8744bdf0d79303334d2f
@@@ -1,5 -1,5 +1,5 @@@
  #ifndef MENUQC
 -bool m_shambler(int);
 +bool M_Shambler(int);
  #endif
  REGISTER_MONSTER_SIMPLE(
  /* MON_##id   */ SHAMBLER,
  /* fullname   */ _("Shambler")
  ) {
  #ifndef MENUQC
 -      this.monster_func = m_shambler;
 +      this.monster_func = M_Shambler;
 +      this.monster_func(MR_PRECACHE);
  #endif
  }
  
  #ifdef SVQC
  float autocvar_g_monster_shambler_health;
 +float autocvar_g_monster_shambler_damageforcescale = 0.1;
  float autocvar_g_monster_shambler_attack_smash_damage;
 +float autocvar_g_monster_shambler_attack_smash_range;
  float autocvar_g_monster_shambler_attack_claw_damage;
  float autocvar_g_monster_shambler_attack_lightning_damage;
 +float autocvar_g_monster_shambler_attack_lightning_damage_zap = 15;
  float autocvar_g_monster_shambler_attack_lightning_force;
  float autocvar_g_monster_shambler_attack_lightning_radius;
  float autocvar_g_monster_shambler_attack_lightning_radius_zap;
@@@ -32,7 -28,6 +32,7 @@@ float autocvar_g_monster_shambler_speed
  float autocvar_g_monster_shambler_speed_run;
  float autocvar_g_monster_shambler_speed_walk;
  
 +/*
  const float shambler_anim_stand               = 0;
  const float shambler_anim_walk                = 1;
  const float shambler_anim_run         = 2;
@@@ -42,41 -37,38 +42,41 @@@ const float shambler_anim_swingl   = 5
  const float shambler_anim_magic               = 6;
  const float shambler_anim_pain                = 7;
  const float shambler_anim_death               = 8;
 +*/
  
  .float shambler_lastattack; // delay attacks separately
  
 -void shambler_smash()
 +void M_Shambler_Attack_Smash()
  {
        makevectors(self.angles);
-       Send_Effect("explosion_medium", (self.origin + (v_forward * 150)) - ('0 0 1' * self.maxs.z), '0 0 0', 1);
+       Send_Effect(EFFECT_EXPLOSION_MEDIUM, (self.origin + (v_forward * 150)) - ('0 0 1' * self.maxs.z), '0 0 0', 1);
        sound(self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM);
  
 -      tracebox(self.origin + v_forward * 50, self.mins * 0.5, self.maxs * 0.5, self.origin + v_forward * 500, MOVE_NORMAL, self);
 +      // RadiusDamage does NOT support custom starting location, which means we must use this hack...
 +
 +      tracebox(self.origin + v_forward * 50, self.mins * 0.5, self.maxs * 0.5, self.origin + v_forward * autocvar_g_monster_shambler_attack_smash_range, MOVE_NORMAL, self);
  
        if(trace_ent.takedamage)
 -              Damage(trace_ent, self, self, (autocvar_g_monster_shambler_attack_smash_damage) * Monster_SkillModifier(), DEATH_MONSTER_SHAMBLER_SMASH, trace_ent.origin, normalize(trace_ent.origin - self.origin));
 +              Damage(trace_ent, self, self, (autocvar_g_monster_shambler_attack_smash_damage) * MONSTER_SKILLMOD(self), DEATH_MONSTER_SHAMBLER_SMASH, trace_ent.origin, normalize(trace_ent.origin - self.origin));
  }
  
 -void shambler_swing()
 +void M_Shambler_Attack_Swing()
  {
        float r = (random() < 0.5);
 -      monster_melee(self.enemy, (autocvar_g_monster_shambler_attack_claw_damage), ((r) ? shambler_anim_swingr : shambler_anim_swingl), self.attack_range, 0.8, DEATH_MONSTER_SHAMBLER_CLAW, true);
 -      if(r)
 +      if(r && Monster_Attack_Melee(self.enemy, (autocvar_g_monster_shambler_attack_claw_damage), ((r) ? self.anim_melee2 : self.anim_melee3), self.attack_range, 0.8, DEATH_MONSTER_SHAMBLER_CLAW, true))
        {
 -              defer(0.5, shambler_swing);
 +              Monster_Delay(1, 0, 0.5, M_Shambler_Attack_Swing);
                self.attack_finished_single += 0.5;
 +              self.anim_finished = self.attack_finished_single;
        }
  }
  
 -void shambler_lightning_explode()
 +void M_Shambler_Attack_Lightning_Explode()
  {
        entity head;
  
        sound(self, CH_SHOTS, W_Sound("electro_impact"), VOL_BASE, ATTEN_NORM);
-       Send_Effect("electro_impact", '0 0 0', '0 0 0', 1);
+       Send_Effect(EFFECT_ELECTRO_IMPACT, '0 0 0', '0 0 0', 1);
  
        self.event_damage = func_null;
        self.takedamage = DAMAGE_NO;
        for(head = findradius(self.origin, (autocvar_g_monster_shambler_attack_lightning_radius_zap)); head; head = head.chain) if(head != self.realowner) if(head.takedamage)
        {
                te_csqc_lightningarc(self.origin, head.origin);
 -              Damage(head, self, self.realowner, (autocvar_g_monster_shambler_attack_lightning_damage) * Monster_SkillModifier(), DEATH_MONSTER_SHAMBLER_ZAP, head.origin, '0 0 0');
 +              Damage(head, self, self.realowner, (autocvar_g_monster_shambler_attack_lightning_damage_zap) * MONSTER_SKILLMOD(self), DEATH_MONSTER_SHAMBLER_ZAP, head.origin, '0 0 0');
        }
  
        self.think = SUB_Remove;
        self.nextthink = time + 0.2;
  }
  
 -void shambler_lightning_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
 +void M_Shambler_Attack_Lightning_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
  {
        if (self.health <= 0)
                return;
                W_PrepareExplosionByDamage(attacker, self.use);
  }
  
 -void shambler_lightning_touch()
 +void M_Shambler_Attack_Lightning_Touch()
  {
        PROJECTILE_TOUCH;
  
        self.use ();
  }
  
 -void shambler_lightning_think()
 +void M_Shambler_Attack_Lightning_Think()
  {
        self.nextthink = time;
        if (time > self.cnt)
        {
                other = world;
 -              shambler_lightning_explode();
 +              M_Shambler_Attack_Lightning_Explode();
                return;
        }
  }
  
 -void shambler_lightning()
 +void M_Shambler_Attack_Lightning()
  {
        entity gren;
  
  
        gren.cnt = time + 5;
        gren.nextthink = time;
 -      gren.think = shambler_lightning_think;
 -      gren.use = shambler_lightning_explode;
 -      gren.touch = shambler_lightning_touch;
 +      gren.think = M_Shambler_Attack_Lightning_Think;
 +      gren.use = M_Shambler_Attack_Lightning_Explode;
 +      gren.touch = M_Shambler_Attack_Lightning_Touch;
  
        gren.takedamage = DAMAGE_YES;
        gren.health = 50;
        gren.damageforcescale = 0;
 -      gren.event_damage = shambler_lightning_damage;
 +      gren.event_damage = M_Shambler_Attack_Lightning_Damage;
        gren.damagedbycontents = true;
        gren.missile_flags = MIF_SPLASH | MIF_ARC;
        W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, false);
        CSQCProjectile(gren, true, PROJECTILE_SHAMBLER_LIGHTNING, true);
  }
  
 -float shambler_attack(float attack_type)
 +float M_Shambler_Attack(float attack_type)
  {
        switch(attack_type)
        {
                case MONSTER_ATTACK_MELEE:
                {
 -                      shambler_swing();
 +                      M_Shambler_Attack_Swing();
                        return true;
                }
                case MONSTER_ATTACK_RANGED:
                {
 +                      float randomness = random(), enemy_len = vlen(self.enemy.origin - self.origin);
 +
                        if(time >= self.shambler_lastattack) // shambler doesn't attack much
                        if(self.flags & FL_ONGROUND)
 -                      if(random() <= 0.5 && vlen(self.enemy.origin - self.origin) <= 500)
 +                      if(randomness <= 0.5 && enemy_len <= autocvar_g_monster_shambler_attack_smash_range)
                        {
 -                              self.frame = shambler_anim_smash;
 -                              defer(0.7, shambler_smash);
 +                              setanim(self, self.anim_melee2, true, true, false);
 +                              Monster_Delay(1, 0, 0.7, M_Shambler_Attack_Smash);
                                self.attack_finished_single = time + 1.1;
 -                              self.shambler_lastattack = time + 3;
 +                              self.anim_finished = time + 1.1;
 +                              self.state = MONSTER_ATTACK_MELEE; // kinda a melee attack
 +                              self.shambler_lastattack = time + 3 + random() * 1.5;
                                return true;
                        }
 -                      else if(random() <= 0.1) // small chance, don't want this spammed
 +                      else if(randomness <= 0.1 && enemy_len >= autocvar_g_monster_shambler_attack_smash_range * 1.5) // small chance, don't want this spammed
                        {
 -                              self.frame = shambler_anim_magic;
 +                              setanim(self, self.anim_shoot, true, true, false);
 +                              self.state = MONSTER_ATTACK_MELEE; // maybe we should rename this to something more general
                                self.attack_finished_single = time + 1.1;
 -                              self.shambler_lastattack = time + 3;
 -                              defer(0.6, shambler_lightning);
 +                              self.anim_finished = 1.1;
 +                              self.shambler_lastattack = time + 3 + random() * 1.5;
 +                              Monster_Delay(1, 0, 0.6, M_Shambler_Attack_Lightning);
                                return true;
                        }
  
        return false;
  }
  
 -void spawnfunc_monster_shambler()
 -{
 -      self.classname = "monster_shambler";
 -
 -      if(!monster_initialize(MON_SHAMBLER.monsterid)) { remove(self); return; }
 -}
 +void spawnfunc_monster_shambler() { Monster_Spawn(MON_SHAMBLER.monsterid); }
 +#endif // SVQC
  
 -float m_shambler(float req)
 +bool M_Shambler(int req)
  {
        switch(req)
        {
 +              #ifdef SVQC
                case MR_THINK:
                {
 -                      monster_move((autocvar_g_monster_shambler_speed_run), (autocvar_g_monster_shambler_speed_walk), (autocvar_g_monster_shambler_speed_stop), shambler_anim_run, shambler_anim_walk, shambler_anim_stand);
 +                      return true;
 +              }
 +              case MR_PAIN:
 +              {
 +                      self.pain_finished = time + 0.5;
 +                      setanim(self, self.anim_pain1, true, true, false);
                        return true;
                }
                case MR_DEATH:
                {
 -                      self.frame = shambler_anim_death;
 +                      setanim(self, self.anim_die1, false, true, true);
                        return true;
                }
 +              #endif
 +              #ifndef MENUQC
 +              case MR_ANIM:
 +              {
 +                      vector none = '0 0 0';
 +                      self.anim_die1 = animfixfps(self, '8 1 0.5', none); // 2 seconds
 +                      self.anim_walk = animfixfps(self, '1 1 1', none);
 +                      self.anim_idle = animfixfps(self, '0 1 1', none);
 +                      self.anim_pain1 = animfixfps(self, '7 1 2', none); // 0.5 seconds
 +                      self.anim_melee1 = animfixfps(self, '3 1 5', none); // analyze models and set framerate
 +                      self.anim_melee2 = animfixfps(self, '4 1 5', none); // analyze models and set framerate
 +                      self.anim_melee3 = animfixfps(self, '5 1 5', none); // analyze models and set framerate
 +                      self.anim_shoot = animfixfps(self, '6 1 5', none); // analyze models and set framerate
 +                      self.anim_run = animfixfps(self, '2 1 1', none);
 +
 +                      return true;
 +              }
 +              #endif
 +              #ifdef SVQC
                case MR_SETUP:
                {
                        if(!self.health) self.health = (autocvar_g_monster_shambler_health);
                        if(!self.attack_range) self.attack_range = 150;
 +                      if(!self.speed) { self.speed = (autocvar_g_monster_shambler_speed_walk); }
 +                      if(!self.speed2) { self.speed2 = (autocvar_g_monster_shambler_speed_run); }
 +                      if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_shambler_speed_stop); }
 +                      if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_shambler_damageforcescale); }
  
                        self.monster_loot = spawnfunc_item_health_mega;
 -                      self.monster_attackfunc = shambler_attack;
 -                      self.frame = shambler_anim_stand;
 -                      self.weapon = WEP_VORTEX.m_id;
 +                      self.weapon = WEP_ELECTRO.m_id; // matches attacks better than WEP_VORTEX
 +
 +                      setanim(self, self.anim_shoot, false, true, true);
 +                      self.spawn_time = self.animstate_endtime;
 +                      self.spawnshieldtime = self.spawn_time;
 +                      self.monster_attackfunc = M_Shambler_Attack;
  
                        return true;
                }
                        precache_model("models/monsters/shambler.mdl");
                        return true;
                }
 +              #endif
        }
  
        return true;
  }
 -
 -#endif // SVQC
 -#ifdef CSQC
 -float m_shambler(float req)
 -{
 -      switch(req)
 -      {
 -              case MR_PRECACHE:
 -              {
 -                      return true;
 -              }
 -      }
 -
 -      return true;
 -}
 -
 -#endif // CSQC
index 3c92f3b142f9afbd0879fef10779f2986b8a3a5f,21e0dbeb5c0ebc8e5dc3bdc4e15c663ea75bdfbf..dae97c187ba3a29ecc2b4894a8e79f37f0ac1307
@@@ -1,5 -1,5 +1,5 @@@
  #ifndef MENUQC
 -bool m_spider(int);
 +bool M_Spider(int);
  #endif
  REGISTER_MONSTER_SIMPLE(
  /* MON_##id   */ SPIDER,
  /* fullname   */ _("Spider")
  ) {
  #ifndef MENUQC
 -      this.monster_func = m_spider;
 +      this.monster_func = M_Spider;
 +      this.monster_func(MR_PRECACHE);
  #endif
  }
  
  #ifdef SVQC
  float autocvar_g_monster_spider_health;
 +float autocvar_g_monster_spider_damageforcescale = 0.6;
  float autocvar_g_monster_spider_attack_bite_damage;
  float autocvar_g_monster_spider_attack_bite_delay;
  float autocvar_g_monster_spider_attack_web_damagetime;
@@@ -28,21 -26,19 +28,21 @@@ float autocvar_g_monster_spider_speed_s
  float autocvar_g_monster_spider_speed_run;
  float autocvar_g_monster_spider_speed_walk;
  
 +/*
  const float spider_anim_idle          = 0;
  const float spider_anim_walk          = 1;
  const float spider_anim_attack                = 2;
  const float spider_anim_attack2               = 3;
 +*/
  
  .float spider_web_delay;
  
 -void spider_web_explode()
 +void M_Spider_Attack_Web_Explode()
  {
        entity e;
        if(self)
        {
-               Send_Effect("electro_impact", self.origin, '0 0 0', 1);
+               Send_Effect(EFFECT_ELECTRO_IMPACT, self.origin, '0 0 0', 1);
                RadiusDamage(self, self.realowner, 0, 0, 25, world, world, 25, self.projectiledeathtype, world);
  
                for(e = findradius(self.origin, 25); e; e = e.chain) if(e != self) if(e.takedamage && e.deadflag == DEAD_NO) if(e.health > 0) if(e.monsterid != MON_SPIDER.monsterid)
        }
  }
  
 -void spider_web_touch()
 +void M_Spider_Attack_Web_Touch()
  {
        PROJECTILE_TOUCH;
  
 -      spider_web_explode();
 +      M_Spider_Attack_Web_Explode();
  }
  
 -void spider_shootweb()
 +void M_Spider_Attack_Web()
  {
        monster_makevectors(self.enemy);
  
@@@ -68,7 -64,7 +68,7 @@@
        entity proj = spawn ();
        proj.classname = "plasma";
        proj.owner = proj.realowner = self;
 -      proj.use = spider_web_touch;
 +      proj.use = M_Spider_Attack_Web_Explode;
        proj.think = adaptor_think2use_hittype_splash;
        proj.bot_dodge = true;
        proj.bot_dodgerating = 0;
@@@ -81,7 -77,7 +81,7 @@@
        //proj.glow_color = 45;
        proj.movetype = MOVETYPE_BOUNCE;
        W_SetupProjVelocity_Explicit(proj, v_forward, v_up, (autocvar_g_monster_spider_attack_web_speed), (autocvar_g_monster_spider_attack_web_speed_up), 0, 0, false);
 -      proj.touch = spider_web_touch;
 +      proj.touch = M_Spider_Attack_Web_Touch;
        setsize(proj, '-4 -4 -4', '4 4 4');
        proj.takedamage = DAMAGE_NO;
        proj.damageforcescale = 0;
        CSQCProjectile(proj, true, PROJECTILE_ELECTRO, true);
  }
  
 -float spider_attack(float attack_type)
 +bool M_Spider_Attack(int attack_type)
  {
        switch(attack_type)
        {
                case MONSTER_ATTACK_MELEE:
                {
 -                      return monster_melee(self.enemy, (autocvar_g_monster_spider_attack_bite_damage), ((random() > 0.5) ? spider_anim_attack : spider_anim_attack2), self.attack_range, (autocvar_g_monster_spider_attack_bite_delay), DEATH_MONSTER_SPIDER, true);
 +                      return Monster_Attack_Melee(self.enemy, (autocvar_g_monster_spider_attack_bite_damage), ((random() > 0.5) ? self.anim_melee : self.anim_shoot), self.attack_range, (autocvar_g_monster_spider_attack_bite_delay), DEATH_MONSTER_SPIDER, true);
                }
                case MONSTER_ATTACK_RANGED:
                {
                        if(time >= self.spider_web_delay)
                        {
 -                              self.frame = spider_anim_attack2;
 +                              setanim(self, self.anim_shoot, true, true, true);
                                self.attack_finished_single = time + (autocvar_g_monster_spider_attack_web_delay);
 -                              spider_shootweb();
 +                              self.anim_finished = time + 1;
 +                              M_Spider_Attack_Web();
                                self.spider_web_delay = time + 3;
                                return true;
                        }
        return false;
  }
  
 -void spawnfunc_monster_spider()
 -{
 -      self.classname = "monster_spider";
 -
 -      if(!monster_initialize(MON_SPIDER.monsterid)) { remove(self); return; }
 -}
 +void spawnfunc_monster_spider() { Monster_Spawn(MON_SPIDER.monsterid); }
 +#endif // SVQC
  
 -float m_spider(float req)
 +bool M_Spider(int req)
  {
        switch(req)
        {
 +              #ifdef SVQC
                case MR_THINK:
                {
 -                      monster_move((autocvar_g_monster_spider_speed_run), (autocvar_g_monster_spider_speed_walk), (autocvar_g_monster_spider_speed_stop), spider_anim_walk, spider_anim_walk, spider_anim_idle);
 +                      return true;
 +              }
 +              case MR_PAIN:
 +              {
                        return true;
                }
                case MR_DEATH:
                {
 -                      self.frame = spider_anim_attack;
 +                      setanim(self, self.anim_melee, false, true, true);
                        self.angles_x = 180;
                        return true;
                }
 +              #endif
 +              #ifndef MENUQC
 +              case MR_ANIM:
 +              {
 +                      vector none = '0 0 0';
 +                      self.anim_walk = animfixfps(self, '1 1 1', none);
 +                      self.anim_idle = animfixfps(self, '0 1 1', none);
 +                      self.anim_melee = animfixfps(self, '2 1 5', none); // analyze models and set framerate
 +                      self.anim_shoot = animfixfps(self, '3 1 5', none); // analyze models and set framerate
 +                      self.anim_run = animfixfps(self, '1 1 1', none);
 +
 +                      return true;
 +              }
 +              #endif
 +              #ifdef SVQC
                case MR_SETUP:
                {
                        if(!self.health) self.health = (autocvar_g_monster_spider_health);
 +                      if(!self.speed) { self.speed = (autocvar_g_monster_spider_speed_walk); }
 +                      if(!self.speed2) { self.speed2 = (autocvar_g_monster_spider_speed_run); }
 +                      if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_spider_speed_stop); }
 +                      if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_spider_damageforcescale); }
  
                        self.monster_loot = spawnfunc_item_health_medium;
 -                      self.monster_attackfunc = spider_attack;
 -                      self.frame = spider_anim_idle;
 +                      self.monster_attackfunc = M_Spider_Attack;
  
                        return true;
                }
                        precache_sound (W_Sound("electro_fire2"));
                        return true;
                }
 +              #endif
        }
  
        return true;
  }
 -
 -#endif // SVQC
 -#ifdef CSQC
 -float m_spider(float req)
 -{
 -      switch(req)
 -      {
 -              case MR_PRECACHE:
 -              {
 -                      return true;
 -              }
 -      }
 -
 -      return true;
 -}
 -
 -#endif // CSQC
index bbe6c5d08221bfade8cab07bdf876a305ba3377e,355d9fa23801ebca0e25f791b82310720de48c08..4dd3fd71f5cc68a78633b866a46149675d7bfd0a
@@@ -1,5 -1,5 +1,5 @@@
  #ifndef MENUQC
 -bool m_wyvern(int);
 +bool M_Wyvern(int);
  #endif
  REGISTER_MONSTER_SIMPLE(
  /* MON_##id   */ WYVERN,
  /* fullname   */ _("Wyvern")
  ) {
  #ifndef MENUQC
 -      this.monster_func = m_wyvern;
 +      this.monster_func = M_Wyvern;
 +      this.monster_func(MR_PRECACHE);
  #endif
  }
  
  #ifdef SVQC
  float autocvar_g_monster_wyvern_health;
 +float autocvar_g_monster_wyvern_damageforcescale = 0.6;
  float autocvar_g_monster_wyvern_attack_fireball_damage;
  float autocvar_g_monster_wyvern_attack_fireball_edgedamage;
  float autocvar_g_monster_wyvern_attack_fireball_damagetime;
@@@ -28,38 -26,36 +28,38 @@@ float autocvar_g_monster_wyvern_speed_s
  float autocvar_g_monster_wyvern_speed_run;
  float autocvar_g_monster_wyvern_speed_walk;
  
 +/*
  const float wyvern_anim_hover = 0;
  const float wyvern_anim_fly           = 1;
  const float wyvern_anim_magic = 2;
  const float wyvern_anim_pain  = 3;
  const float wyvern_anim_death = 4;
 +*/
  
 -void wyvern_fireball_explode()
 +void M_Wyvern_Attack_Fireball_Explode()
  {
        entity e;
        if(self)
        {
-               Send_Effect("fireball_explode", self.origin, '0 0 0', 1);
+               Send_Effect(EFFECT_FIREBALL_EXPLODE, self.origin, '0 0 0', 1);
  
                RadiusDamage(self, self.realowner, (autocvar_g_monster_wyvern_attack_fireball_damage), (autocvar_g_monster_wyvern_attack_fireball_edgedamage), (autocvar_g_monster_wyvern_attack_fireball_force), world, world, (autocvar_g_monster_wyvern_attack_fireball_radius), self.projectiledeathtype, world);
  
                for(e = world; (e = findfloat(e, takedamage, DAMAGE_AIM)); ) if(vlen(e.origin - self.origin) <= (autocvar_g_monster_wyvern_attack_fireball_radius))
 -                      Fire_AddDamage(e, self, 5 * Monster_SkillModifier(), (autocvar_g_monster_wyvern_attack_fireball_damagetime), self.projectiledeathtype);
 +                      Fire_AddDamage(e, self, 5 * MONSTER_SKILLMOD(self), (autocvar_g_monster_wyvern_attack_fireball_damagetime), self.projectiledeathtype);
  
                remove(self);
        }
  }
  
 -void wyvern_fireball_touch()
 +void M_Wyvern_Attack_Fireball_Touch()
  {
        PROJECTILE_TOUCH;
  
 -      wyvern_fireball_explode();
 +      M_Wyvern_Attack_Fireball_Explode();
  }
  
 -void wyvern_fireball()
 +void M_Wyvern_Attack_Fireball()
  {
        entity missile = spawn();
        vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
        missile.velocity = dir * (autocvar_g_monster_wyvern_attack_fireball_speed);
        missile.avelocity = '300 300 300';
        missile.nextthink = time + 5;
 -      missile.think = wyvern_fireball_explode;
 +      missile.think = M_Wyvern_Attack_Fireball_Explode;
        missile.enemy = self.enemy;
 -      missile.touch = wyvern_fireball_touch;
 +      missile.touch = M_Wyvern_Attack_Fireball_Touch;
        CSQCProjectile(missile, true, PROJECTILE_FIREMINE, true);
  }
  
 -float wyvern_attack(float attack_type)
 +float M_Wyvern_Attack(float attack_type)
  {
        switch(attack_type)
        {
@@@ -90,9 -86,8 +90,9 @@@
                case MONSTER_ATTACK_RANGED:
                {
                        self.attack_finished_single = time + 1.2;
 +                      self.anim_finished = time + 1.2;
  
 -                      wyvern_fireball();
 +                      M_Wyvern_Attack_Fireball();
  
                        return true;
                }
        return false;
  }
  
 -void spawnfunc_monster_wyvern()
 -{
 -      self.classname = "monster_wyvern";
 -
 -      if(!monster_initialize(MON_WYVERN.monsterid)) { remove(self); return; }
 -}
 -
 -// compatibility with old spawns
 -void spawnfunc_monster_wizard() { spawnfunc_monster_wyvern(); }
 +void spawnfunc_monster_wyvern() { Monster_Spawn(MON_WYVERN.monsterid); }
 +#endif // SVQC
  
 -float m_wyvern(float req)
 +bool M_Wyvern(int req)
  {
        switch(req)
        {
 +              #ifdef SVQC
                case MR_THINK:
                {
 -                      monster_move((autocvar_g_monster_wyvern_speed_run), (autocvar_g_monster_wyvern_speed_walk), (autocvar_g_monster_wyvern_speed_stop), wyvern_anim_fly, wyvern_anim_hover, wyvern_anim_hover);
 +                      return true;
 +              }
 +              case MR_PAIN:
 +              {
 +                      self.pain_finished = time + 0.5;
 +                      setanim(self, self.anim_pain1, true, true, false);
                        return true;
                }
                case MR_DEATH:
                {
 -                      self.frame = wyvern_anim_death;
 +                      setanim(self, self.anim_die1, false, true, true);
                        self.velocity_x = -200 + 400 * random();
                        self.velocity_y = -200 + 400 * random();
                        self.velocity_z = 100 + 100 * random();
                        return true;
                }
 +              #endif
 +              #ifndef MENUQC
 +              case MR_ANIM:
 +              {
 +                      vector none = '0 0 0';
 +                      self.anim_die1 = animfixfps(self, '4 1 0.5', none); // 2 seconds
 +                      self.anim_walk = animfixfps(self, '1 1 1', none);
 +                      self.anim_idle = animfixfps(self, '0 1 1', none);
 +                      self.anim_pain1 = animfixfps(self, '3 1 2', none); // 0.5 seconds
 +                      self.anim_shoot = animfixfps(self, '2 1 5', none); // analyze models and set framerate
 +                      self.anim_run = animfixfps(self, '1 1 1', none);
 +
 +                      return true;
 +              }
 +              #endif
 +              #ifdef SVQC
                case MR_SETUP:
                {
                        if(!self.health) self.health = (autocvar_g_monster_wyvern_health);
 +                      if(!self.speed) { self.speed = (autocvar_g_monster_wyvern_speed_walk); }
 +                      if(!self.speed2) { self.speed2 = (autocvar_g_monster_wyvern_speed_run); }
 +                      if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_wyvern_speed_stop); }
 +                      if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_wyvern_damageforcescale); }
  
                        self.monster_loot = spawnfunc_item_cells;
 -                      self.monster_attackfunc = wyvern_attack;
 -                      self.frame = wyvern_anim_hover;
 +                      self.monster_attackfunc = M_Wyvern_Attack;
  
                        return true;
                }
                        precache_model("models/monsters/wizard.mdl");
                        return true;
                }
 +              #endif
        }
  
        return true;
  }
 -
 -#endif // SVQC
 -#ifdef CSQC
 -float m_wyvern(float req)
 -{
 -      switch(req)
 -      {
 -              case MR_PRECACHE:
 -              {
 -                      return true;
 -              }
 -      }
 -
 -      return true;
 -}
 -
 -#endif // CSQC
index c977aa67e121e8afe284a7544fb593f0307cf5be,9f8028aa8fe0204db44ed11d3446fb8cd6bcb541..e7ea9f8c48520182793b32e30a25a6457e6e1774
      #include "../../server/round_handler.qh"
  #endif
  
 -// =========================
 -//    SVQC Monster Properties
 -// =========================
 -
 +void monsters_setstatus()
 +{
 +      self.stat_monsters_total = monsters_total;
 +      self.stat_monsters_killed = monsters_killed;
 +}
  
  void monster_dropitem()
  {
        }
  }
  
 -float Monster_SkillModifier()
 -{
 -      float t = 0.5+self.monster_skill*((1.2-0.3)/10);
 -
 -      return t;
 -}
 -
 -float monster_isvalidtarget (entity targ, entity ent)
 +void monster_makevectors(entity e)
  {
 -      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)
 -              //return false;
 -
 -      if(IS_VEHICLE(targ))
 -      if(!((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
 -
 -      if(targ.takedamage == DAMAGE_NO)
 -              return false; // enemy can't be damaged
 -
 -      if(targ.items & IT_INVISIBILITY)
 -              return false; // enemy is invisible
 -
 -      if(substring(targ.classname, 0, 10) == "onslaught_")
 -              return false; // don't attack onslaught targets
 -
 -      if(IS_SPEC(targ) || IS_OBSERVER(targ))
 -              return false; // enemy is a spectator
 -
 -      if(!IS_VEHICLE(targ))
 -      if(targ.deadflag != DEAD_NO || ent.deadflag != DEAD_NO || targ.health <= 0 || ent.health <= 0)
 -              return false; // enemy/self is dead
 +      if(IS_MONSTER(self))
 +      {
 +              vector v;
  
 -      if(ent.monster_owner == targ)
 -              return false; // don't attack our master
 +              v = e.origin + (e.mins + e.maxs) * 0.5;
 +              self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
 +              self.v_angle_x = -self.v_angle_x;
 +      }
  
 -      if(targ.monster_owner == ent)
 -              return false; // don't attack our pet
 +      makevectors(self.v_angle);
 +}
  
 -      if(!IS_VEHICLE(targ))
 -      if(targ.flags & FL_NOTARGET)
 -              return false; // enemy can't be targeted
 +// ===============
 +// Target handling
 +// ===============
  
 -      if(!autocvar_g_monsters_typefrag)
 -      if(targ.BUTTON_CHAT)
 -              return false; // no typefragging!
 +bool Monster_ValidTarget(entity mon, entity player)
 +{
 +      // ensure we're not checking nonexistent monster/target
 +      if(!mon || !player) { return false; }
 +
 +      if((player == mon)
 +      || (autocvar_g_monsters_lineofsight && !checkpvs(mon.origin + mon.view_ofs, player)) // enemy cannot be seen
 +      || (IS_VEHICLE(player) && !((get_monsterinfo(mon.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
 +      || (time < game_starttime) // monsters do nothing before match has started
 +      || (player.takedamage == DAMAGE_NO)
 +      || (player.items & IT_INVISIBILITY)
 +      || (IS_SPEC(player) || IS_OBSERVER(player)) // don't attack spectators
 +      || (!IS_VEHICLE(player) && (player.deadflag != DEAD_NO || mon.deadflag != DEAD_NO || player.health <= 0 || mon.health <= 0))
 +      || (mon.monster_follow == player || player.monster_follow == mon)
 +      || (!IS_VEHICLE(player) && (player.flags & FL_NOTARGET))
 +      || (!autocvar_g_monsters_typefrag && player.BUTTON_CHAT)
 +      || (SAME_TEAM(player, mon))
 +      || (player.frozen)
 +      || (player.alpha != 0 && player.alpha < 0.5)
 +      )
 +      {
 +              // if any of the above checks fail, target is not valid
 +              return false;
 +      }
  
 -      if(SAME_TEAM(targ, ent))
 -              return false; // enemy is on our team
 +      traceline(mon.origin + self.view_ofs, player.origin, 0, mon);
  
 -      if (targ.frozen)
 -              return false; // ignore frozen
 +      if((trace_fraction < 1) && (trace_ent != player))
 +              return false;
  
 -      if(autocvar_g_monsters_target_infront || (ent.spawnflags & MONSTERFLAG_INFRONT))
 -      if(ent.enemy != targ)
 +      if(autocvar_g_monsters_target_infront || (mon.spawnflags & MONSTERFLAG_INFRONT))
 +      if(mon.enemy != player)
        {
                float dot;
  
 -              makevectors (ent.angles);
 -              dot = normalize (targ.origin - ent.origin) * v_forward;
 +              makevectors (mon.angles);
 +              dot = normalize (player.origin - mon.origin) * v_forward;
  
 -              if(dot <= 0.3)
 -                      return false;
 +              if(dot <= 0.3) { return false; }
        }
  
 -      return true;
 +      return true; // this target is valid!
  }
  
 -entity FindTarget (entity ent)
 +entity Monster_FindTarget(entity mon)
  {
 -      if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return ent.enemy; } // Handled by a mutator
 +      if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return mon.enemy; } // Handled by a mutator
  
        entity head, closest_target = world;
 -      head = findradius(ent.origin, ent.target_range);
 -      //head = WarpZone_FindRadius(ent.origin, ent.target_range, true);
 +      head = findradius(mon.origin, mon.target_range);
  
        while(head) // find the closest acceptable target to pass to
        {
                if(head.monster_attack)
 -              if(monster_isvalidtarget(head, ent))
 +              if(Monster_ValidTarget(mon, head))
                {
                        // 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 = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head));
 -                      vector ent_center = CENTER_OR_VIEWOFS(ent);
 +                      vector ent_center = CENTER_OR_VIEWOFS(mon);
  
 -                      traceline(ent_center, head_center, MOVE_NORMAL, ent);
 -
 -                      if(trace_ent == head)
                        if(closest_target)
                        {
                                vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
 -                              //vector closest_target_center = WarpZone_UnTransformOrigin(closest_target, CENTER_OR_VIEWOFS(closest_target));
                                if(vlen(ent_center - head_center) < vlen(ent_center - closest_target_center))
                                        { closest_target = head; }
                        }
        return closest_target;
  }
  
 -void MonsterTouch ()
 +void monster_setupcolors(entity mon)
  {
 -      if(other == world)
 -              return;
 +      if(IS_PLAYER(mon.realowner))
 +              mon.colormap = mon.realowner.colormap;
 +      else if(teamplay && mon.team)
 +              mon.colormap = 1024 + (mon.team - 1) * 17;
 +      else
 +      {
 +              if(mon.monster_skill <= MONSTER_SKILL_EASY)
 +                      mon.colormap = 1029;
 +              else if(mon.monster_skill <= MONSTER_SKILL_MEDIUM)
 +                      mon.colormap = 1027;
 +              else if(mon.monster_skill <= MONSTER_SKILL_HARD)
 +                      mon.colormap = 1038;
 +              else if(mon.monster_skill <= MONSTER_SKILL_INSANE)
 +                      mon.colormap = 1028;
 +              else if(mon.monster_skill <= MONSTER_SKILL_NIGHTMARE)
 +                      mon.colormap = 1032;
 +              else
 +                      mon.colormap = 1024;
 +      }
 +}
  
 -      if(self.enemy != other)
 -      if(!IS_MONSTER(other))
 -      if(monster_isvalidtarget(other, self))
 -              self.enemy = other;
 +void monster_changeteam(entity ent, float newteam)
 +{
 +      if(!teamplay) { return; }
 +      
 +      ent.team = newteam;
 +      ent.monster_attack = true; // new team, activate attacking
 +      monster_setupcolors(ent);
 +      
 +      if(ent.sprite)
 +      {
 +              WaypointSprite_UpdateTeamRadar(ent.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0'));
 +
 +              ent.sprite.team = newteam;
 +              ent.sprite.SendFlags |= 1;
 +      }
 +}
 +
 +void Monster_Delay_Action()
 +{
 +      entity oldself = self;
 +      self = self.owner;
 +      if(Monster_ValidTarget(self, self.enemy)) { oldself.use(); }
 +
 +      if(oldself.cnt > 0)
 +      {
 +              oldself.cnt -= 1;
 +              oldself.think = Monster_Delay_Action;
 +              oldself.nextthink = time + oldself.respawn_time;
 +      }
 +      else
 +      {
 +              oldself.think = SUB_Remove;
 +              oldself.nextthink = time;
 +      }
  }
  
 +void Monster_Delay(float repeat_count, float repeat_defer, float defer_amnt, void() func)
 +{
 +      // deferred attacking, checks if monster is still alive and target is still valid before attacking
 +      entity e = spawn();
 +
 +      e.think = Monster_Delay_Action;
 +      e.nextthink = time + defer_amnt;
 +      e.count = defer_amnt;
 +      e.owner = self;
 +      e.use = func;
 +      e.cnt = repeat_count;
 +      e.respawn_time = repeat_defer;
 +}
 +
 +
 +// ==============
 +// Monster sounds
 +// ==============
 +
  string get_monster_model_datafilename(string m, float sk, string fil)
  {
        if(m)
        return strcat(m, ".", fil);
  }
  
 -void PrecacheMonsterSounds(string f)
 +void Monster_Sound_Precache(string f)
  {
        float fh;
        string s;
        fclose(fh);
  }
  
 -void precache_monstersounds()
 +void Monster_Sounds_Precache()
  {
        string m = (get_monsterinfo(self.monsterid)).model;
        float globhandle, n, i;
        {
                //print(search_getfilename(globhandle, i), "\n");
                f = search_getfilename(globhandle, i);
 -              PrecacheMonsterSounds(f);
 +              Monster_Sound_Precache(f);
        }
        search_end(globhandle);
  }
  
 -void ClearMonsterSounds()
 +void Monster_Sounds_Clear()
  {
  #define _MSOUND(m) if(self.monstersound_##m) { strunzone(self.monstersound_##m); self.monstersound_##m = string_null; }
        ALLMONSTERSOUNDS
  #undef _MSOUND
  }
  
 -.string GetMonsterSoundSampleField(string type)
 +.string Monster_Sound_SampleField(string type)
  {
        GetMonsterSoundSampleField_notFound = 0;
        switch(type)
        return string_null;
  }
  
 -float LoadMonsterSounds(string f, float first)
 +bool Monster_Sounds_Load(string f, int first)
  {
        float fh;
        string s;
        if(fh < 0)
        {
                LOG_TRACE("Monster sound file not found: ", f, "\n");
 -              return 0;
 +              return false;
        }
        while((s = fgets(fh)))
        {
                if(tokenize_console(s) != 3)
                        continue;
 -              field = GetMonsterSoundSampleField(argv(0));
 +              field = Monster_Sound_SampleField(argv(0));
                if(GetMonsterSoundSampleField_notFound)
                        continue;
                if (self.(field))
                self.(field) = strzone(strcat(argv(1), " ", argv(2)));
        }
        fclose(fh);
 -      return 1;
 +      return true;
  }
  
  .int skin_for_monstersound;
 -void UpdateMonsterSounds()
 +void Monster_Sounds_Update()
  {
 -      entity mon = get_monsterinfo(self.monsterid);
 +      if(self.skin == self.skin_for_monstersound) { return; }
  
 -      if(self.skin == self.skin_for_monstersound)
 -              return;
        self.skin_for_monstersound = self.skin;
 -      ClearMonsterSounds();
 -      //LoadMonsterSounds("sound/monsters/default.sounds", 1);
 -      if(!autocvar_g_debug_defaultsounds)
 -      if(!LoadMonsterSounds(get_monster_model_datafilename(mon.model, self.skin, "sounds"), 0))
 -              LoadMonsterSounds(get_monster_model_datafilename(mon.model, 0, "sounds"), 0);
 +      Monster_Sounds_Clear();
 +      if(!Monster_Sounds_Load(get_monster_model_datafilename(self.model, self.skin, "sounds"), 0))
 +              Monster_Sounds_Load(get_monster_model_datafilename(self.model, 0, "sounds"), 0);
  }
  
 -void MonsterSound(.string samplefield, float sound_delay, float delaytoo, float chan)
 +void Monster_Sound(.string samplefield, float sound_delay, float delaytoo, float chan)
  {
        if(!autocvar_g_monsters_sounds) { return; }
  
        self.msound_delay = time + sound_delay;
  }
  
 -void monster_makevectors(entity e)
 +
 +// =======================
 +// Monster attack handlers
 +// =======================
 +
 +float Monster_Attack_Melee(entity targ, float damg, vector anim, float er, float animtime, int deathtype, float dostop)
  {
 -      vector v;
 +      if(dostop && (self.flags & FL_MONSTER)) { self.state = MONSTER_ATTACK_MELEE; }
  
 -      v = e.origin + (e.mins + e.maxs) * 0.5;
 -      self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
 -      self.v_angle_x = -self.v_angle.x;
 +      setanim(self, anim, false, true, false);
  
 -      makevectors(self.v_angle);
 +      if(self.animstate_endtime > time && (self.flags & FL_MONSTER))
 +              self.attack_finished_single = self.anim_finished = self.animstate_endtime;
 +      else
 +              self.attack_finished_single = self.anim_finished = time + animtime;
 +
 +      monster_makevectors(targ);
 +
 +      traceline(self.origin + self.view_ofs, self.origin + v_forward * er, 0, self);
 +
 +      if(trace_ent.takedamage)
 +              Damage(trace_ent, self, self, damg * MONSTER_SKILLMOD(self), deathtype, trace_ent.origin, normalize(trace_ent.origin - self.origin));
 +
 +      return true;
  }
  
 -float monster_melee(entity targ, float damg, float anim, float er, float anim_finished, int deathtype, float dostop)
 +float Monster_Attack_Leap_Check(vector vel)
  {
 -      if (self.health <= 0)
 -              return false; // attacking while dead?!
 +      if(self.state && (self.flags & FL_MONSTER))
 +              return false; // already attacking
 +      if(!(self.flags & FL_ONGROUND))
 +              return false; // not on the ground
 +      if(self.health <= 0)
 +              return false; // called when dead?
 +      if(time < self.attack_finished_single)
 +              return false; // still attacking
 +
 +      vector old = self.velocity;
 +
 +      self.velocity = vel;
 +      tracetoss(self, self);
 +      self.velocity = old;
 +      if (trace_ent != self.enemy)
 +              return false;
 +
 +      return true;
 +}
 +
 +bool Monster_Attack_Leap(vector anm, void() touchfunc, vector vel, float animtime)
 +{
 +      if(!Monster_Attack_Leap_Check(vel))
 +              return false;
 +
 +      setanim(self, anm, false, true, false);
 +
 +      if(self.animstate_endtime > time && (self.flags & FL_MONSTER))
 +              self.attack_finished_single = self.anim_finished = self.animstate_endtime;
 +      else
 +              self.attack_finished_single = self.anim_finished = time + animtime;
  
 -      if(dostop)
 +      if(self.flags & FL_MONSTER)
 +              self.state = MONSTER_ATTACK_RANGED;
 +      self.touch = touchfunc;
 +      self.origin_z += 1;
 +      self.velocity = vel;
 +      self.flags &= ~FL_ONGROUND;
 +
 +      return true;
 +}
 +
 +void Monster_Attack_Check(entity e, entity targ)
 +{
 +      if((e == world || targ == world)
 +      || (!e.monster_attackfunc)
 +      || (time < e.attack_finished_single)
 +      ) { return; }
 +
 +      float targ_vlen = vlen(targ.origin - e.origin);
 +
 +      if(targ_vlen <= e.attack_range)
        {
 -              self.velocity_x = 0;
 -              self.velocity_y = 0;
 -              self.state = MONSTER_STATE_ATTACK_MELEE;
 +              float attack_success = e.monster_attackfunc(MONSTER_ATTACK_MELEE);
 +              if(attack_success == 1)
 +                      Monster_Sound(monstersound_melee, 0, false, CH_VOICE);
 +              else if(attack_success > 0)
 +                      return;
        }
  
 -      self.frame = anim;
 +      if(targ_vlen > e.attack_range)
 +      {
 +              float attack_success = e.monster_attackfunc(MONSTER_ATTACK_RANGED);
 +              if(attack_success == 1)
 +                      Monster_Sound(monstersound_melee, 0, false, CH_VOICE);
 +              else if(attack_success > 0)
 +                      return;
 +      }
 +}
  
 -      if(anim_finished != 0)
 -              self.attack_finished_single = time + anim_finished;
  
 -      monster_makevectors(targ);
 +// ======================
 +// Main monster functions
 +// ======================
  
 -      traceline(self.origin + self.view_ofs, self.origin + v_forward * er, 0, self);
 +void Monster_UpdateModel()
 +{
 +      // assume some defaults
 +      /*self.anim_idle   = animfixfps(self, '0 1 0.01', '0 0 0');
 +      self.anim_walk   = animfixfps(self, '1 1 0.01', '0 0 0');
 +      self.anim_run    = animfixfps(self, '2 1 0.01', '0 0 0');
 +      self.anim_fire1  = animfixfps(self, '3 1 0.01', '0 0 0');
 +      self.anim_fire2  = animfixfps(self, '4 1 0.01', '0 0 0');
 +      self.anim_melee  = animfixfps(self, '5 1 0.01', '0 0 0');
 +      self.anim_pain1  = animfixfps(self, '6 1 0.01', '0 0 0');
 +      self.anim_pain2  = animfixfps(self, '7 1 0.01', '0 0 0');
 +      self.anim_die1   = animfixfps(self, '8 1 0.01', '0 0 0');
 +      self.anim_die2   = animfixfps(self, '9 1 0.01', '0 0 0');*/
 +
 +      // then get the real values
 +      MON_ACTION(self.monsterid, MR_ANIM);
 +}
  
 -      if(trace_ent.takedamage)
 -              Damage(trace_ent, self, self, damg * Monster_SkillModifier(), deathtype, trace_ent.origin, normalize(trace_ent.origin - self.origin));
 +void Monster_Touch()
 +{
 +      if(other == world) { return; }
  
 -      return true;
 +      if(other.monster_attack)
 +      if(self.enemy != other)
 +      if(!IS_MONSTER(other))
 +      if(Monster_ValidTarget(self, other))
 +              self.enemy = other;
  }
  
 -void Monster_CheckMinibossFlag ()
 +void Monster_Miniboss_Check()
  {
        if(MUTATOR_CALLHOOK(MonsterCheckBossFlag))
                return;
        }
  }
  
 -float Monster_CanRespawn(entity ent)
 +bool Monster_Respawn_Check()
  {
 -      other = ent;
 -      if(ent.deadflag == DEAD_DEAD) // don't call when monster isn't dead
 -      if(MUTATOR_CALLHOOK(MonsterRespawn, ent))
 +      if(self.deadflag == DEAD_DEAD) // don't call when monster isn't dead
 +      if(MUTATOR_CALLHOOK(MonsterRespawn, self))
                return true; // enabled by a mutator
  
 -      if(ent.spawnflags & MONSTERFLAG_NORESPAWN)
 +      if(self.spawnflags & MONSTERFLAG_NORESPAWN)
                return false;
  
        if(!autocvar_g_monsters_respawn)
        return true;
  }
  
 -void monster_respawn()
 -{
 -      // is this function really needed?
 -      monster_initialize(self.monsterid);
 -}
 +void Monster_Respawn() { Monster_Spawn(self.monsterid); }
  
 -void Monster_Fade ()
 +void Monster_Dead_Fade()
  {
 -      if(Monster_CanRespawn(self))
 +      if(Monster_Respawn_Check())
        {
                self.spawnflags |= MONSTERFLAG_RESPAWNED;
 -              self.think = monster_respawn;
 +              self.think = Monster_Respawn;
                self.nextthink = time + self.respawntime;
                self.monster_lifetime = 0;
                self.deadflag = DEAD_RESPAWNING;
        }
  }
  
 -float Monster_CanJump (vector vel)
 +void Monster_Use()
  {
 -      if(self.state)
 -              return false; // already attacking
 -      if(!(self.flags & FL_ONGROUND))
 -              return false; // not on the ground
 -      if(self.health <= 0)
 -              return false; // called when dead?
 -      if(time < self.attack_finished_single)
 -              return false; // still attacking
 -
 -      vector old = self.velocity;
 -
 -      self.velocity = vel;
 -      tracetoss(self, self);
 -      self.velocity = old;
 -      if (trace_ent != self.enemy)
 -              return false;
 -
 -      return true;
 +      if(Monster_ValidTarget(self, activator)) { self.enemy = activator; }
  }
  
 -float monster_leap (float anm, void() touchfunc, vector vel, float anim_finished)
 -{
 -      if(!Monster_CanJump(vel))
 -              return false;
 -
 -      self.frame = anm;
 -      self.state = MONSTER_STATE_ATTACK_LEAP;
 -      self.touch = touchfunc;
 -      self.origin_z += 1;
 -      self.velocity = vel;
 -      self.flags &= ~FL_ONGROUND;
 -
 -      self.attack_finished_single = time + anim_finished;
 -
 -      return true;
 -}
 -
 -void monster_checkattack(entity e, entity targ)
 -{
 -      if(e == world)
 -              return;
 -      if(targ == world)
 -              return;
 -
 -      if(!e.monster_attackfunc)
 -              return;
 -
 -      if(time < e.attack_finished_single)
 -              return;
 -
 -      if(vlen(targ.origin - e.origin) <= e.attack_range)
 -      if(e.monster_attackfunc(MONSTER_ATTACK_MELEE))
 -      {
 -              MonsterSound(monstersound_melee, 0, false, CH_VOICE);
 -              return;
 -      }
 -
 -      if(vlen(targ.origin - e.origin) > e.attack_range)
 -      if(e.monster_attackfunc(MONSTER_ATTACK_RANGED))
 -      {
 -              MonsterSound(monstersound_ranged, 0, false, CH_VOICE);
 -              return;
 -      }
 -}
 -
 -void monster_use ()
 -{
 -      if(!self.enemy)
 -      if(self.health > 0)
 -      if(monster_isvalidtarget(activator, self))
 -              self.enemy = activator;
 -}
 -
 -.float last_trace;
 -.float last_enemycheck; // for checking enemy
 -vector monster_pickmovetarget(entity targ)
 +vector Monster_Move_Target(entity targ)
  {
        // enemy is always preferred target
        if(self.enemy)
                        || (self.enemy.deadflag != DEAD_NO || self.enemy.health < 1)
                        || (self.enemy.frozen)
                        || (self.enemy.flags & FL_NOTARGET)
 -                      || (self.enemy.alpha < 0.5)
 +                      || (self.enemy.alpha < 0.5 && self.enemy.alpha != 0)
                        || (self.enemy.takedamage == DAMAGE_NO)
                        || (vlen(self.origin - targ_origin) > self.target_range)
                        || ((trace_fraction < 1) && (trace_ent != self.enemy)))
  
                if(self.enemy)
                {
-                       /*WarpZone_TrailParticles(world, particleeffectnum("red_pass"), self.origin, targ_origin);
+                       /*WarpZone_TrailParticles(world, particleeffectnum(EFFECT_RED_PASS), self.origin, targ_origin);
                        print("Trace origin: ", vtos(targ_origin), "\n");
                        print("Target origin: ", vtos(self.enemy.origin), "\n");
                        print("My origin: ", vtos(self.origin), "\n"); */
  
                        self.monster_movestate = MONSTER_MOVE_ENEMY;
                        self.last_trace = time + 1.2;
 -                      return targ_origin;
 +                      if(self.monster_moveto)
 +                              return self.monster_moveto; // assumes code is properly setting this when monster has an enemy
 +                      else
 +                              return targ_origin;
                }
  
                /*makevectors(self.angles);
  
        switch(self.monster_moveflags)
        {
 -              case MONSTER_MOVE_OWNER:
 +              case MONSTER_MOVE_FOLLOW:
                {
 -                      self.monster_movestate = MONSTER_MOVE_OWNER;
 +                      self.monster_movestate = MONSTER_MOVE_FOLLOW;
                        self.last_trace = time + 0.3;
 -                      return (self.monster_owner) ? self.monster_owner.origin : self.origin;
 +                      return (self.monster_follow) ? self.monster_follow.origin : self.origin;
                }
                case MONSTER_MOVE_SPAWNLOC:
                {
                }
                case MONSTER_MOVE_NOMOVE:
                {
 -                      self.monster_movestate = MONSTER_MOVE_NOMOVE;
 -                      self.last_trace = time + 2;
 +                      if(self.monster_moveto)
 +                      {
 +                              self.last_trace = time + 0.5;
 +                              return self.monster_moveto;
 +                      }
 +                      else
 +                      {
 +                              self.monster_movestate = MONSTER_MOVE_NOMOVE;
 +                              self.last_trace = time + 2;
 +                      }
                        return self.origin;
                }
                default:
                        vector pos;
                        self.monster_movestate = MONSTER_MOVE_WANDER;
  
 -                      if(targ)
 +                      if(self.monster_moveto)
 +                      {
 +                              self.last_trace = time + 0.5;
 +                              pos = self.monster_moveto;
 +                      }
 +                      else if(targ)
                        {
                                self.last_trace = time + 0.5;
                                pos = targ.origin;
        }
  }
  
 -void monster_CalculateVelocity(entity mon, vector to, vector from, float turnrate, float movespeed)
 +void Monster_CalculateVelocity(entity mon, vector to, vector from, float turnrate, float movespeed)
  {
        float current_distance = vlen((('1 0 0' * to.x) + ('0 1 0' * to.y)) - (('1 0 0' * from.x) + ('0 1 0' * from.y))); // for the sake of this check, exclude Z axis
        float initial_height = 0; //min(50, (targ_distance * tanh(20)));
 -      float current_height = (initial_height * min(1, self.pass_distance ? (current_distance / self.pass_distance) : 0));
 +      float current_height = (initial_height * min(1, (self.pass_distance) ? (current_distance / self.pass_distance) : current_distance));
        //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n");
  
        vector targpos;
        //mon.angles = vectoangles(mon.velocity);
  }
  
 -void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_run, float manim_walk, float manim_idle)
 +void Monster_Move(float runspeed, float walkspeed, float stpspeed)
  {
 -      //fixedmakevectors(self.angles);
 -
 -      if(self.target2)
 -              self.goalentity = find(world, targetname, self.target2);
 +      if(self.target2) { self.goalentity = find(world, targetname, self.target2); }
  
        entity targ;
  
                self.health = max(1, self.revive_progress * self.max_health);
                self.iceblock.alpha = bound(0.2, 1 - self.revive_progress, 1);
  
 -              WaypointSprite_UpdateHealth(self.sprite, self.health);
 +              if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE) && self.sprite)
 +                      WaypointSprite_UpdateHealth(self.sprite, self.health);
  
 -              movelib_beak_simple(stopspeed);
 -              self.frame = manim_idle;
 +              movelib_beak_simple(stpspeed);
 +              setanim(self, self.anim_idle, true, false, false);
  
                self.enemy = world;
                self.nextthink = time + self.ticrate;
                self.revive_progress = bound(0, self.revive_progress - self.ticrate * self.revive_speed, 1);
                self.health = max(0, autocvar_g_nades_ice_health + (self.max_health-autocvar_g_nades_ice_health) * self.revive_progress );
  
 -              WaypointSprite_UpdateHealth(self.sprite, self.health);
 +              if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE) && self.sprite)
 +                      WaypointSprite_UpdateHealth(self.sprite, self.health);
  
 -              movelib_beak_simple(stopspeed);
 -              self.frame = manim_idle;
 +              movelib_beak_simple(stpspeed);
 +              setanim(self, self.anim_idle, true, false, false);
  
                self.enemy = world;
                self.nextthink = time + self.ticrate;
                {
                        Unfreeze(self);
                        self.health = 0;
 -                      self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
 +                      if(self.event_damage)
 +                              self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
                }
  
                else if ( self.revive_progress <= 0 )
                {
                        if(time >= self.last_trace)
                        {
 -                              self.fish_wasdrowning = true;
                                self.last_trace = time + 0.4;
  
                                Damage (self, world, world, 2, DEATH_DROWN, self.origin, '0 0 0');
  
                        return;
                }
 -              else if(self.fish_wasdrowning)
 +              else if(self.movetype == MOVETYPE_BOUNCE)
                {
 -                      self.fish_wasdrowning = false;
                        self.angles_x = 0;
                        self.movetype = MOVETYPE_WALK;
                }
        {
                runspeed = walkspeed = 0;
                if(time >= self.spawn_time)
 -                      self.frame = manim_idle;
 -              movelib_beak_simple(stopspeed);
 +                      setanim(self, self.anim_idle, true, false, false);
 +              movelib_beak_simple(stpspeed);
                return;
        }
  
 -      runspeed = bound(0, monster_speed_run * Monster_SkillModifier(), runspeed * 2); // limit maxspeed to prevent craziness
 -      walkspeed = bound(0, monster_speed_walk * Monster_SkillModifier(), walkspeed * 2); // limit maxspeed to prevent craziness
 +      targ = monster_target;
 +      runspeed = bound(0, monster_speed_run * MONSTER_SKILLMOD(self), runspeed * 2.5); // limit maxspeed to prevent craziness
 +      walkspeed = bound(0, monster_speed_walk * MONSTER_SKILLMOD(self), walkspeed * 2.5); // limit maxspeed to prevent craziness
  
        if(time < self.spider_slowness)
        {
  
        if(teamplay)
        if(autocvar_g_monsters_teams)
 -      if(DIFF_TEAM(self.monster_owner, self))
 -              self.monster_owner = world;
 +      if(DIFF_TEAM(self.monster_follow, self))
 +              self.monster_follow = world;
  
        if(time >= self.last_enemycheck)
        {
                if(!self.enemy)
                {
 -                      self.enemy = FindTarget(self);
 +                      self.enemy = Monster_FindTarget(self);
                        if(self.enemy)
                        {
                                WarpZone_RefSys_Copy(self.enemy, self);
                                WarpZone_RefSys_AddInverse(self.enemy, self); // wz1^-1 ... wzn^-1 receiver
                                self.moveto = WarpZone_RefSys_TransformOrigin(self.enemy, self, (0.5 * (self.enemy.absmin + self.enemy.absmax)));
 -
 -                              self.pass_distance = vlen((('1 0 0' * self.enemy.origin.x) + ('0 1 0' * self.enemy.origin.y)) - (('1 0 0' *  self.origin.x) + ('0 1 0' *  self.origin.y)));
 -                              MonsterSound(monstersound_sight, 0, false, CH_VOICE);
 +                              self.monster_moveto = '0 0 0';
 +                              self.monster_face = '0 0 0';
 +                              
 +                              self.pass_distance = vlen((('1 0 0' * self.enemy.origin_x) + ('0 1 0' * self.enemy.origin_y)) - (('1 0 0' *  self.origin_x) + ('0 1 0' *  self.origin_y)));
 +                              Monster_Sound(monstersound_sight, 0, false, CH_VOICE);
                        }
                }
  
                self.last_enemycheck = time + 1; // check for enemies every second
        }
  
 -      if(self.state == MONSTER_STATE_ATTACK_MELEE && time >= self.attack_finished_single)
 +      if(self.state == MONSTER_ATTACK_RANGED && (self.flags & FL_ONGROUND))
 +      {
                self.state = 0;
 +              self.touch = Monster_Touch;
 +      }
  
 -      if(self.state != MONSTER_STATE_ATTACK_MELEE) // don't move if set
 -      if(time >= self.last_trace || self.enemy) // update enemy instantly
 -              self.moveto = monster_pickmovetarget(targ);
 +      if(self.state && time >= self.attack_finished_single)
 +              self.state = 0; // attack is over
  
 -      if(!self.enemy)
 -              MonsterSound(monstersound_idle, 7, true, CH_VOICE);
 +      if(self.state != MONSTER_ATTACK_MELEE) // don't move if set
 +      if(time >= self.last_trace || self.enemy) // update enemy or rider instantly
 +              self.moveto = Monster_Move_Target(targ);
  
 -      if(self.state == MONSTER_STATE_ATTACK_LEAP && (self.flags & FL_ONGROUND))
 -      {
 -              self.state = 0;
 -              self.touch = MonsterTouch;
 -      }
 +      if(!self.enemy)
 +              Monster_Sound(monstersound_idle, 7, true, CH_VOICE);
  
 -      if(self.state == MONSTER_STATE_ATTACK_MELEE)
 +      if(self.state == MONSTER_ATTACK_MELEE)
                self.moveto = self.origin;
  
        if(self.enemy && self.enemy.vehicle)
                runspeed = 0;
  
 -      if(!(((self.flags & FL_FLY) && (self.spawnflags & MONSTERFLAG_FLY_VERTICAL)) || (self.flags & FL_SWIM)))
 -              //v_forward = normalize(self.moveto - self.origin);
 -      //else
 -              self.moveto_z = self.origin.z;
 +      if(!(self.spawnflags & MONSTERFLAG_FLY_VERTICAL) && !(self.flags & FL_SWIM))
 +              self.moveto_z = self.origin_z;
  
 -      if(vlen(self.origin - self.moveto) > 64)
 +      if(vlen(self.origin - self.moveto) > 100)
        {
 +              float do_run = !!(self.enemy);
                if((self.flags & FL_ONGROUND) || ((self.flags & FL_FLY) || (self.flags & FL_SWIM)))
 -                      monster_CalculateVelocity(self, self.moveto, self.origin, true, ((self.enemy) ? runspeed : walkspeed));
 -
 -              /*&if(self.flags & FL_FLY || self.flags & FL_SWIM)
 -                      movelib_move_simple(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
 -              else
 -                      movelib_move_simple_gravity(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6); */
 +                      Monster_CalculateVelocity(self, self.moveto, self.origin, true, ((do_run) ? runspeed : walkspeed));
  
 -              if(time > self.pain_finished)
 -              if(time > self.attack_finished_single)
 +              if(time > self.pain_finished) // TODO: use anim_finished instead!
 +              if(!self.state)
 +              if(time > self.anim_finished)
                if(vlen(self.velocity) > 10)
 -                      self.frame = ((self.enemy) ? manim_run : manim_walk);
 +                      setanim(self, ((do_run) ? self.anim_run : self.anim_walk), true, false, false);
                else
 -                      self.frame = manim_idle;
 +                      setanim(self, self.anim_idle, true, false, false);
        }
        else
        {
                else if(e.target)
                        self.target2 = e.target;
  
 -              movelib_beak_simple(stopspeed);
 -              if(time > self.attack_finished_single)
 +              movelib_beak_simple(stpspeed);
 +              if(time > self.anim_finished)
                if(time > self.pain_finished)
 -              if (vlen(self.velocity) <= 30)
 -                      self.frame = manim_idle;
 +              if(!self.state)
 +              if(vlen(self.velocity) <= 30)
 +                      setanim(self, self.anim_idle, true, false, false);
        }
  
 -      self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
 +      self.steerto = steerlib_attract2(((self.monster_face) ? self.monster_face : self.moveto), 0.5, 500, 0.95);
  
        vector real_angle = vectoangles(self.steerto) - self.angles;
        float turny = 25;
 -      if(self.state == MONSTER_STATE_ATTACK_MELEE)
 +      if(self.state == MONSTER_ATTACK_MELEE)
                turny = 0;
        if(turny)
        {
                self.angles_y += turny;
        }
  
 -      monster_checkattack(self, self.enemy);
 +      Monster_Attack_Check(self, self.enemy);
  }
  
 -void monster_remove(entity mon)
 +void Monster_Remove(entity mon)
  {
 -      if(!mon)
 -              return; // nothing to remove
 -
 -      Send_Effect(EFFECT_ITEM_PICKUP, mon.origin, '0 0 0', 1);
 +      if(!mon) { return; }
  
 -      if(mon.weaponentity)
 -              remove(mon.weaponentity);
 -
 -      if(mon.iceblock)
 -              remove(mon.iceblock);
 +      if(!MUTATOR_CALLHOOK(MonsterRemove, mon))
-               Send_Effect("item_pickup", mon.origin, '0 0 0', 1);
++              Send_Effect(EFFECT_ITEM_PICKUP, mon.origin, '0 0 0', 1);
  
 +      if(mon.weaponentity) { remove(mon.weaponentity); }
 +      if(mon.iceblock) { remove(mon.iceblock); }
        WaypointSprite_Kill(mon.sprite);
 -
        remove(mon);
  }
  
 -void monster_dead_think()
 +void Monster_Dead_Think()
  {
        self.nextthink = time + self.ticrate;
  
 -      CSQCMODEL_AUTOUPDATE();
 -
        if(self.monster_lifetime != 0)
        if(time >= self.monster_lifetime)
        {
 -              Monster_Fade();
 +              Monster_Dead_Fade();
                return;
        }
  }
  
 -void monsters_setstatus()
 -{
 -      self.stat_monsters_total = monsters_total;
 -      self.stat_monsters_killed = monsters_killed;
 -}
 -
  void Monster_Appear()
  {
        self.enemy = activator;
        self.spawnflags &= ~MONSTERFLAG_APPEAR; // otherwise, we get an endless loop
 -      monster_initialize(self.monsterid);
 +      Monster_Spawn(self.monsterid);
  }
  
 -float Monster_CheckAppearFlags(entity ent, float monster_id)
 +float Monster_Appear_Check(entity ent, float monster_id)
  {
        if(!(ent.spawnflags & MONSTERFLAG_APPEAR))
                return false;
        return true;
  }
  
 -void monsters_reset()
 +void Monster_Reset()
  {
        setorigin(self, self.pos1);
        self.angles = self.pos2;
        self.moveto = self.origin;
  }
  
 -void monsters_corpse_damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
 +void Monster_Dead_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
  {
        self.health -= damage;
  
        }
  }
  
 -void monster_die(entity attacker, float gibbed)
 +void Monster_Dead(entity attacker, float gibbed)
  {
 -      self.think = monster_dead_think;
 +      self.think = Monster_Dead_Think;
        self.nextthink = time;
        self.monster_lifetime = time + 5;
  
  
        monster_dropitem();
  
 -      MonsterSound(monstersound_death, 0, false, CH_VOICE);
 +      Monster_Sound(monstersound_death, 0, false, CH_VOICE);
  
        if(!(self.spawnflags & MONSTERFLAG_SPAWNED) && !(self.spawnflags & MONSTERFLAG_RESPAWNED))
                monsters_killed += 1;
                totalspawned -= 1;
        }
  
 -      if(self.candrop && self.weapon)
 -              W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325');
 -
 -      self.event_damage       = ((gibbed) ? func_null : monsters_corpse_damage);
 +      self.event_damage       = ((gibbed) ? func_null : Monster_Dead_Damage);
        self.solid                      = SOLID_CORPSE;
        self.takedamage         = DAMAGE_AIM;
        self.deadflag           = DEAD_DEAD;
        self.enemy                      = world;
        self.movetype           = MOVETYPE_TOSS;
        self.moveto                     = self.origin;
 -      self.touch                      = MonsterTouch; // reset incase monster was pouncing
 +      self.touch                      = Monster_Touch; // reset incase monster was pouncing
        self.reset                      = func_null;
        self.state                      = 0;
        self.attack_finished_single = 0;
 +      self.effects = 0;
  
        if(!((self.flags & FL_FLY) || (self.flags & FL_SWIM)))
                self.velocity = '0 0 0';
  
 +      CSQCModel_UnlinkEntity();
 +
        MON_ACTION(self.monsterid, MR_DEATH);
 +
 +      if(self.candrop && self.weapon)
 +              W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325');
  }
  
 -void monsters_damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
 +void Monster_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
  {
 -      if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
 -              return;
 -
        if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL)
                return;
  
 -      if(time < self.pain_finished && deathtype != DEATH_KILL)
 +      if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
                return;
  
 +      //if(time < self.pain_finished && deathtype != DEATH_KILL)
 +              //return;
 +
        if(time < self.spawnshieldtime && deathtype != DEATH_KILL)
                return;
  
        vector v;
        float take, save;
  
 -      v = healtharmor_applydamage(self.armorvalue, self.m_armor_blockpercent, deathtype, damage);
 -      take = v.x;
 -      save = v.y;
 +      v = healtharmor_applydamage(100, self.armorvalue / 100, deathtype, damage);
 +      take = v_x;
 +      save = v_y;
  
 -      self.health -= take;
 +      damage_take = take;
 +      frag_attacker = attacker;
 +      frag_deathtype = deathtype;
 +      MON_ACTION(self.monsterid, MR_PAIN);
 +      take = damage_take;
  
 -      WaypointSprite_UpdateHealth(self.sprite, self.health);
 +      if(take)
 +      {
 +              self.health -= take;
 +              Monster_Sound(monstersound_pain, 1.2, true, CH_PAIN);
 +      }
 +
 +      if(self.sprite)
 +              WaypointSprite_UpdateHealth(self.sprite, self.health);
  
        self.dmg_time = time;
  
  
        self.velocity += force * self.damageforcescale;
  
 -      if(deathtype != DEATH_DROWN)
 +      if(deathtype != DEATH_DROWN && take)
        {
                Violence_GibSplash_At(hitloc, force, 2, bound(0, take, 200) / 16, self, attacker);
                if (take > 50)
                SUB_UseTargets();
                self.target2 = self.oldtarget2; // reset to original target on death, incase we respawn
  
 -              monster_die(attacker, (self.health <= -100 || deathtype == DEATH_KILL));
 +              Monster_Dead(attacker, (self.health <= -100 || deathtype == DEATH_KILL));
  
                WaypointSprite_Kill(self.sprite);
  
        }
  }
  
 -void monster_setupcolors(entity mon)
 +// don't check for enemies, just keep walking in a straight line
 +void Monster_Move_2D(float mspeed, float allow_jumpoff)
  {
 -      if(IS_PLAYER(mon.monster_owner))
 -              mon.colormap = mon.monster_owner.colormap;
 -      else if(teamplay && mon.team)
 -              mon.colormap = 1024 + (mon.team - 1) * 17;
 -      else
 +      if(gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || self.draggedby != world || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < self.spawn_time)
        {
 -              if(mon.monster_skill <= MONSTER_SKILL_EASY)
 -                      mon.colormap = 1029;
 -              else if(mon.monster_skill <= MONSTER_SKILL_MEDIUM)
 -                      mon.colormap = 1027;
 -              else if(mon.monster_skill <= MONSTER_SKILL_HARD)
 -                      mon.colormap = 1038;
 -              else if(mon.monster_skill <= MONSTER_SKILL_INSANE)
 -                      mon.colormap = 1028;
 -              else if(mon.monster_skill <= MONSTER_SKILL_NIGHTMARE)
 -                      mon.colormap = 1032;
 -              else
 -                      mon.colormap = 1024;
 +              mspeed = 0;
 +              if(time >= self.spawn_time)
 +                      setanim(self, self.anim_idle, true, false, false);
 +              movelib_beak_simple(0.6);
 +              return;
        }
 -}
  
 -void monster_changeteam(entity ent, float newteam)
 -{
 -      if(!teamplay) { return; }
 +      float reverse = FALSE;
 +      vector a, b;
 +      
 +      makevectors(self.angles);
 +      a = self.origin + '0 0 16';
 +      b = self.origin + '0 0 16' + v_forward * 32;
 +      
 +      traceline(a, b, MOVE_NORMAL, self);
 +      
 +      if(trace_fraction != 1.0)
 +      {
 +              reverse = TRUE;
 +              
 +              if(trace_ent)
 +              if(IS_PLAYER(trace_ent) && !(trace_ent.items & IT_STRENGTH))
 +                      reverse = FALSE;
 +      }
 +      
 +      // TODO: fix this... tracing is broken if the floor is thin
 +      /*
 +      if(!allow_jumpoff)
 +      {
 +              a = b - '0 0 32';
 +              traceline(b, a, MOVE_WORLDONLY, self);
 +              if(trace_fraction == 1.0)
 +                      reverse = TRUE;
 +      } */
 +      
 +      if(reverse)
 +      {
 +              self.angles_y = anglemods(self.angles_y - 180);
 +              makevectors(self.angles);
 +      }
 +      
 +      movelib_move_simple_gravity(v_forward, mspeed, 1);
  
 -      ent.team = newteam;
 -      ent.monster_attack = true; // new team, activate attacking
 -      monster_setupcolors(ent);
 +      if(time > self.pain_finished)
 +      if(time > self.attack_finished_single)
 +      if(vlen(self.velocity) > 10)
 +              setanim(self, self.anim_walk, true, false, false);
 +      else
 +              setanim(self, self.anim_idle, true, false, false);
 +}
  
 -      if(ent.sprite)
 +void Monster_Anim()
 +{
 +      int deadbits = (self.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2));
 +      if(self.deadflag)
        {
 -              WaypointSprite_UpdateTeamRadar(ent.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0'));
 -
 -              ent.sprite.team = newteam;
 -              ent.sprite.SendFlags |= 1;
 +              if (!deadbits)
 +              {
 +                      // Decide on which death animation to use.
 +                      if(random() < 0.5)
 +                              deadbits = ANIMSTATE_DEAD1;
 +                      else
 +                              deadbits = ANIMSTATE_DEAD2;
 +              }
 +      }
 +      else
 +      {
 +              // Clear a previous death animation.
 +              deadbits = 0;
 +      }
 +      int animbits = deadbits;
 +      if(self.frozen)
 +              animbits |= ANIMSTATE_FROZEN;
 +      if(self.crouch)
 +              animbits |= ANIMSTATE_DUCK; // not that monsters can crouch currently...
 +      animdecide_setstate(self, animbits, false);
 +      animdecide_setimplicitstate(self, (self.flags & FL_ONGROUND));
 +
 +      /* // weapon entities for monsters?
 +      if (self.weaponentity)
 +      {
 +              updateanim(self.weaponentity);
 +              if (!self.weaponentity.animstate_override)
 +                      setanim(self.weaponentity, self.weaponentity.anim_idle, true, false, false);
        }
 +      */
  }
  
 -void monster_think()
 +void Monster_Think()
  {
 -      self.think = monster_think;
 +      self.think = Monster_Think;
        self.nextthink = self.ticrate;
  
        if(self.monster_lifetime)
                return;
        }
  
 -      MON_ACTION(self.monsterid, MR_THINK);
 +      if(MON_ACTION(self.monsterid, MR_THINK))
 +              Monster_Move(self.speed2, self.speed, self.stopspeed);
 +
 +      Monster_Anim();
  
        CSQCMODEL_AUTOUPDATE();
  }
  
 -float monster_spawn()
 +float Monster_Spawn_Setup()
  {
        MON_ACTION(self.monsterid, MR_SETUP);
  
 +      // ensure some basic needs are met
 +      if(!self.health) { self.health = 100; }
 +      if(!self.armorvalue) { self.armorvalue = bound(0.2, 0.5 * MONSTER_SKILLMOD(self), 0.9); }
 +      if(!self.target_range) { self.target_range = autocvar_g_monsters_target_range; }
 +      if(!self.respawntime) { self.respawntime = autocvar_g_monsters_respawn_delay; }
 +      if(!self.monster_moveflags) { self.monster_moveflags = MONSTER_MOVE_WANDER; }
 +      if(!self.attack_range) { self.attack_range = autocvar_g_monsters_attack_range; }
 +      if(!self.damageforcescale) { self.damageforcescale = autocvar_g_monsters_damageforcescale; }
 +
        if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
        {
 -              Monster_CheckMinibossFlag();
 -              self.health *= Monster_SkillModifier();
 +              Monster_Miniboss_Check();
 +              self.health *= MONSTER_SKILLMOD(self);
 +
 +              if(!self.skin)
 +                      self.skin = rint(random() * 4);
        }
  
        self.max_health = self.health;
        self.pain_finished = self.nextthink;
  
 -      if(IS_PLAYER(self.monster_owner))
 +      if(IS_PLAYER(self.monster_follow))
                self.effects |= EF_DIMLIGHT;
  
 -      if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
 -      if(!self.skin)
 -              self.skin = rint(random() * 4);
 -
 -      if(!self.attack_range)
 -              self.attack_range = autocvar_g_monsters_attack_range;
 -
        if(!self.wander_delay) { self.wander_delay = 2; }
        if(!self.wander_distance) { self.wander_distance = 600; }
  
 -      precache_monstersounds();
 -      UpdateMonsterSounds();
 +      Monster_Sounds_Precache();
 +      Monster_Sounds_Update();
  
        if(teamplay)
                self.monster_attack = true; // we can have monster enemies in team games
  
 -      MonsterSound(monstersound_spawn, 0, false, CH_VOICE);
 +      Monster_Sound(monstersound_spawn, 0, false, CH_VOICE);
  
 -      entity wp = WaypointSprite_Spawn(WP_Monster, 0, 1024, self, '0 0 1' * (self.maxs.z + 15), world, self.team, self, sprite, true, RADARICON_DANGER);
 -      wp.wp_extra = self.monsterid;
 -      wp.colormod = ((self.team) ? Team_ColorRGB(self.team) : '1 0 0');
 -      if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE))
 +      if(autocvar_g_monsters_healthbars)
        {
 -              WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
 -              WaypointSprite_UpdateHealth(self.sprite, self.health);
 +              entity wp = WaypointSprite_Spawn(WP_Monster, 0, 1024, self, '0 0 1' * (self.maxs.z + 15), world, self.team, self, sprite, true, RADARICON_DANGER);
 +              wp.wp_extra = self.monsterid;
 +              wp.colormod = ((self.team) ? Team_ColorRGB(self.team) : '1 0 0');
 +              if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE))
 +              {
 +                      WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
 +                      WaypointSprite_UpdateHealth(self.sprite, self.health);
 +              }
        }
  
 -      self.think = monster_think;
 +      self.think = Monster_Think;
        self.nextthink = time + self.ticrate;
  
        if(MUTATOR_CALLHOOK(MonsterSpawn))
        return true;
  }
  
 -float monster_initialize(float mon_id)
 +bool Monster_Spawn(int mon_id)
  {
 -      if(!autocvar_g_monsters) { return false; }
 -      if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) { MON_ACTION(mon_id, MR_PRECACHE); }
 -      if(Monster_CheckAppearFlags(self, mon_id)) { return true; } // return true so the monster isn't removed
 -
 +      // setup the basic required properties for a monster
        entity mon = get_monsterinfo(mon_id);
 +      if(!mon.monsterid) { return false; } // invalid monster
 +
 +      if(!autocvar_g_monsters) { Monster_Remove(self); return false; }
 +
 +      self.mdl = mon.model;
 +      if(Monster_Appear_Check(self, mon_id)) { return true; } // return true so the monster isn't removed
  
        if(!self.monster_skill)
                self.monster_skill = cvar("g_monsters_skill");
  
        // support for quake style removing monsters based on skill
 -      if(self.monster_skill == MONSTER_SKILL_EASY) if(self.spawnflags & MONSTERSKILL_NOTEASY) { return false; }
 -      if(self.monster_skill == MONSTER_SKILL_MEDIUM) if(self.spawnflags & MONSTERSKILL_NOTMEDIUM) { return false; }
 -      if(self.monster_skill == MONSTER_SKILL_HARD) if(self.spawnflags & MONSTERSKILL_NOTHARD) { return false; }
 +      if(self.monster_skill == MONSTER_SKILL_EASY) if(self.spawnflags & MONSTERSKILL_NOTEASY) { Monster_Remove(self); return false; }
 +      if(self.monster_skill == MONSTER_SKILL_MEDIUM) if(self.spawnflags & MONSTERSKILL_NOTMEDIUM) { Monster_Remove(self); return false; }
 +      if(self.monster_skill == MONSTER_SKILL_HARD) if(self.spawnflags & MONSTERSKILL_NOTHARD) { Monster_Remove(self); return false; }
  
        if(self.team && !teamplay)
                self.team = 0;
        if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) // don't count re-spawning monsters either
                monsters_total += 1;
  
 -      setmodel(self, mon.model);
 -      //setsize(self, mon.mins, mon.maxs);
 +      setmodel(self, self.mdl);
        self.flags                              = FL_MONSTER;
 +      self.classname                  = "monster";
        self.takedamage                 = DAMAGE_AIM;
        self.bot_attack                 = true;
        self.iscreature                 = true;
        self.teleportable               = true;
        self.damagedbycontents  = true;
        self.monsterid                  = mon_id;
 -      self.damageforcescale   = 0;
 -      self.event_damage               = monsters_damage;
 -      self.touch                              = MonsterTouch;
 -      self.use                                = monster_use;
 +      self.event_damage               = Monster_Damage;
 +      self.touch                              = Monster_Touch;
 +      self.use                                = Monster_Use;
        self.solid                              = SOLID_BBOX;
        self.movetype                   = MOVETYPE_WALK;
        self.spawnshieldtime    = time + autocvar_g_monsters_spawnshieldtime;
        self.moveto                             = self.origin;
        self.pos1                               = self.origin;
        self.pos2                               = self.angles;
 -      self.reset                              = monsters_reset;
 +      self.reset                              = Monster_Reset;
        self.netname                    = mon.netname;
 -      self.monster_name               = M_NAME(mon_id);
 +      self.monster_attackfunc = mon.monster_attackfunc;
 +      self.monster_name               = mon.monster_name;
        self.candrop                    = true;
 -      self.view_ofs                   = '0 0 1' * (self.maxs.z * 0.5);
 +      self.view_ofs                   = '0 0 0.7' * (self.maxs_z * 0.5);
        self.oldtarget2                 = self.target2;
        self.pass_distance              = 0;
        self.deadflag                   = DEAD_NO;
        self.spawn_time                 = time;
        self.spider_slowness    = 0;
        self.gravity                    = 1;
 +      self.monster_moveto             = '0 0 0';
 +      self.monster_face               = '0 0 0';
        self.dphitcontentsmask  = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
 -
 -      if(!self.scale)
 -              self.scale = 1;
 -
 -      if(autocvar_g_monsters_edit)
 -              self.grab = 1; // owner may carry their monster
 -
 -      if(autocvar_g_fullbrightplayers)
 -              self.effects |= EF_FULLBRIGHT;
 -
 -      if(autocvar_g_nodepthtestplayers)
 -              self.effects |= EF_NODEPTHTEST;
 -
 -      if(mon.spawnflags & MONSTER_TYPE_SWIM)
 -              self.flags |= FL_SWIM;
 +      
 +      if(!self.scale) { self.scale = 1; }
 +      if(autocvar_g_monsters_edit) { self.grab = 1; }
 +      if(autocvar_g_fullbrightplayers) { self.effects |= EF_FULLBRIGHT; }
 +      if(autocvar_g_nodepthtestplayers) { self.effects |= EF_NODEPTHTEST; }
 +      if(mon.spawnflags & MONSTER_TYPE_SWIM) { self.flags |= FL_SWIM; }
  
        if(mon.spawnflags & MONSTER_TYPE_FLY)
        {
  
        setsize(self, mon.mins * self.scale, mon.maxs * self.scale);
  
 -      if(!self.ticrate)
 -              self.ticrate = autocvar_g_monsters_think_delay;
 -
 -      self.ticrate = bound(sys_frametime, self.ticrate, 60);
 -
 -      if(!self.m_armor_blockpercent)
 -              self.m_armor_blockpercent = 0.5;
 -
 -      if(!self.target_range)
 -              self.target_range = autocvar_g_monsters_target_range;
 +      self.ticrate = bound(sys_frametime, ((!self.ticrate) ? autocvar_g_monsters_think_delay : self.ticrate), 60);
  
 -      if(!self.respawntime)
 -              self.respawntime = autocvar_g_monsters_respawn_delay;
 +      Monster_UpdateModel();
  
 -      if(!self.monster_moveflags)
 -              self.monster_moveflags = MONSTER_MOVE_WANDER;
 +      if(!Monster_Spawn_Setup())
 +      {
 +              Monster_Remove(self);
 +              return false;
 +      }
  
        if(!self.noalign)
        {
                setorigin(self, trace_endpos);
        }
  
 -      if(!monster_spawn())
 -              return false;
 -
        if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
                monster_setupcolors(self);
  
index 7906e5820f64de0ec40b175dbc4af1bdd51adbbf,08728a6403a5df75361c8c5a09da20eb31155c60..de36b375410b9e8426fe0e7eef7fe3319c8a0080
@@@ -175,6 -175,47 +175,6 @@@ void GameCommand_adminmsg(float request
        }
  }
  
 -void GameCommand_mobbutcher(float request)
 -{
 -      switch(request)
 -      {
 -              case CMD_REQUEST_COMMAND:
 -              {
 -                      if(autocvar_g_campaign) { LOG_INFO("This command doesn't work in campaign mode.\n"); return; }
 -                      if(g_invasion) { LOG_INFO("This command doesn't work during an invasion.\n"); return; }
 -
 -                      float removed_count = 0;
 -                      entity head;
 -
 -                      FOR_EACH_MONSTER(head)
 -                      {
 -                              monster_remove(head);
 -                              ++removed_count;
 -                      }
 -
 -                      monsters_total = 0; // reset stats?
 -                      monsters_killed = 0;
 -
 -                      totalspawned = 0;
 -
 -                      if(removed_count <= 0)
 -                              LOG_INFO("No monsters to kill\n");
 -                      else
 -                              LOG_INFOF("Killed %d monster%s\n", removed_count, ((removed_count == 1) ? "" : "s"));
 -
 -                      return; // never fall through to usage
 -              }
 -
 -              default:
 -              case CMD_REQUEST_USAGE:
 -              {
 -                      LOG_INFO("\nUsage:^3 sv_cmd mobbutcher\n");
 -                      LOG_INFO("  No arguments required.\n");
 -                      return;
 -              }
 -      }
 -}
 -
  void GameCommand_allready(float request)
  {
        switch(request)
@@@ -642,42 -683,42 +642,42 @@@ void GameCommand_effectindexdump(float 
  
                        d = db_create();
                        LOG_INFO("begin of effects list\n");
-                       db_put(d, "TE_GUNSHOT", "1"); LOG_INFO("effect TE_GUNSHOT is ", ftos(particleeffectnum("TE_GUNSHOT")), "\n");
-                       db_put(d, "TE_GUNSHOTQUAD", "1"); LOG_INFO("effect TE_GUNSHOTQUAD is ", ftos(particleeffectnum("TE_GUNSHOTQUAD")), "\n");
-                       db_put(d, "TE_SPIKE", "1"); LOG_INFO("effect TE_SPIKE is ", ftos(particleeffectnum("TE_SPIKE")), "\n");
-                       db_put(d, "TE_SPIKEQUAD", "1"); LOG_INFO("effect TE_SPIKEQUAD is ", ftos(particleeffectnum("TE_SPIKEQUAD")), "\n");
-                       db_put(d, "TE_SUPERSPIKE", "1"); LOG_INFO("effect TE_SUPERSPIKE is ", ftos(particleeffectnum("TE_SUPERSPIKE")), "\n");
-                       db_put(d, "TE_SUPERSPIKEQUAD", "1"); LOG_INFO("effect TE_SUPERSPIKEQUAD is ", ftos(particleeffectnum("TE_SUPERSPIKEQUAD")), "\n");
-                       db_put(d, "TE_WIZSPIKE", "1"); LOG_INFO("effect TE_WIZSPIKE is ", ftos(particleeffectnum("TE_WIZSPIKE")), "\n");
-                       db_put(d, "TE_KNIGHTSPIKE", "1"); LOG_INFO("effect TE_KNIGHTSPIKE is ", ftos(particleeffectnum("TE_KNIGHTSPIKE")), "\n");
-                       db_put(d, "TE_EXPLOSION", "1"); LOG_INFO("effect TE_EXPLOSION is ", ftos(particleeffectnum("TE_EXPLOSION")), "\n");
-                       db_put(d, "TE_EXPLOSIONQUAD", "1"); LOG_INFO("effect TE_EXPLOSIONQUAD is ", ftos(particleeffectnum("TE_EXPLOSIONQUAD")), "\n");
-                       db_put(d, "TE_TAREXPLOSION", "1"); LOG_INFO("effect TE_TAREXPLOSION is ", ftos(particleeffectnum("TE_TAREXPLOSION")), "\n");
-                       db_put(d, "TE_TELEPORT", "1"); LOG_INFO("effect TE_TELEPORT is ", ftos(particleeffectnum("TE_TELEPORT")), "\n");
-                       db_put(d, "TE_LAVASPLASH", "1"); LOG_INFO("effect TE_LAVASPLASH is ", ftos(particleeffectnum("TE_LAVASPLASH")), "\n");
-                       db_put(d, "TE_SMALLFLASH", "1"); LOG_INFO("effect TE_SMALLFLASH is ", ftos(particleeffectnum("TE_SMALLFLASH")), "\n");
-                       db_put(d, "TE_FLAMEJET", "1"); LOG_INFO("effect TE_FLAMEJET is ", ftos(particleeffectnum("TE_FLAMEJET")), "\n");
-                       db_put(d, "EF_FLAME", "1"); LOG_INFO("effect EF_FLAME is ", ftos(particleeffectnum("EF_FLAME")), "\n");
-                       db_put(d, "TE_BLOOD", "1"); LOG_INFO("effect TE_BLOOD is ", ftos(particleeffectnum("TE_BLOOD")), "\n");
-                       db_put(d, "TE_SPARK", "1"); LOG_INFO("effect TE_SPARK is ", ftos(particleeffectnum("TE_SPARK")), "\n");
-                       db_put(d, "TE_PLASMABURN", "1"); LOG_INFO("effect TE_PLASMABURN is ", ftos(particleeffectnum("TE_PLASMABURN")), "\n");
-                       db_put(d, "TE_TEI_G3", "1"); LOG_INFO("effect TE_TEI_G3 is ", ftos(particleeffectnum("TE_TEI_G3")), "\n");
-                       db_put(d, "TE_TEI_SMOKE", "1"); LOG_INFO("effect TE_TEI_SMOKE is ", ftos(particleeffectnum("TE_TEI_SMOKE")), "\n");
-                       db_put(d, "TE_TEI_BIGEXPLOSION", "1"); LOG_INFO("effect TE_TEI_BIGEXPLOSION is ", ftos(particleeffectnum("TE_TEI_BIGEXPLOSION")), "\n");
-                       db_put(d, "TE_TEI_PLASMAHIT", "1"); LOG_INFO("effect TE_TEI_PLASMAHIT is ", ftos(particleeffectnum("TE_TEI_PLASMAHIT")), "\n");
-                       db_put(d, "EF_STARDUST", "1"); LOG_INFO("effect EF_STARDUST is ", ftos(particleeffectnum("EF_STARDUST")), "\n");
-                       db_put(d, "TR_ROCKET", "1"); LOG_INFO("effect TR_ROCKET is ", ftos(particleeffectnum("TR_ROCKET")), "\n");
-                       db_put(d, "TR_GRENADE", "1"); LOG_INFO("effect TR_GRENADE is ", ftos(particleeffectnum("TR_GRENADE")), "\n");
-                       db_put(d, "TR_BLOOD", "1"); LOG_INFO("effect TR_BLOOD is ", ftos(particleeffectnum("TR_BLOOD")), "\n");
-                       db_put(d, "TR_WIZSPIKE", "1"); LOG_INFO("effect TR_WIZSPIKE is ", ftos(particleeffectnum("TR_WIZSPIKE")), "\n");
-                       db_put(d, "TR_SLIGHTBLOOD", "1"); LOG_INFO("effect TR_SLIGHTBLOOD is ", ftos(particleeffectnum("TR_SLIGHTBLOOD")), "\n");
-                       db_put(d, "TR_KNIGHTSPIKE", "1"); LOG_INFO("effect TR_KNIGHTSPIKE is ", ftos(particleeffectnum("TR_KNIGHTSPIKE")), "\n");
-                       db_put(d, "TR_VORESPIKE", "1"); LOG_INFO("effect TR_VORESPIKE is ", ftos(particleeffectnum("TR_VORESPIKE")), "\n");
-                       db_put(d, "TR_NEHAHRASMOKE", "1"); LOG_INFO("effect TR_NEHAHRASMOKE is ", ftos(particleeffectnum("TR_NEHAHRASMOKE")), "\n");
-                       db_put(d, "TR_NEXUIZPLASMA", "1"); LOG_INFO("effect TR_NEXUIZPLASMA is ", ftos(particleeffectnum("TR_NEXUIZPLASMA")), "\n");
-                       db_put(d, "TR_GLOWTRAIL", "1"); LOG_INFO("effect TR_GLOWTRAIL is ", ftos(particleeffectnum("TR_GLOWTRAIL")), "\n");
-                       db_put(d, "TR_SEEKER", "1"); LOG_INFO("effect TR_SEEKER is ", ftos(particleeffectnum("TR_SEEKER")), "\n");
-                       db_put(d, "SVC_PARTICLE", "1"); LOG_INFO("effect SVC_PARTICLE is ", ftos(particleeffectnum("SVC_PARTICLE")), "\n");
+                       db_put(d, "TE_GUNSHOT", "1"); LOG_INFO("effect TE_GUNSHOT is ", ftos(_particleeffectnum("TE_GUNSHOT")), "\n");
+                       db_put(d, "TE_GUNSHOTQUAD", "1"); LOG_INFO("effect TE_GUNSHOTQUAD is ", ftos(_particleeffectnum("TE_GUNSHOTQUAD")), "\n");
+                       db_put(d, "TE_SPIKE", "1"); LOG_INFO("effect TE_SPIKE is ", ftos(_particleeffectnum("TE_SPIKE")), "\n");
+                       db_put(d, "TE_SPIKEQUAD", "1"); LOG_INFO("effect TE_SPIKEQUAD is ", ftos(_particleeffectnum("TE_SPIKEQUAD")), "\n");
+                       db_put(d, "TE_SUPERSPIKE", "1"); LOG_INFO("effect TE_SUPERSPIKE is ", ftos(_particleeffectnum("TE_SUPERSPIKE")), "\n");
+                       db_put(d, "TE_SUPERSPIKEQUAD", "1"); LOG_INFO("effect TE_SUPERSPIKEQUAD is ", ftos(_particleeffectnum("TE_SUPERSPIKEQUAD")), "\n");
+                       db_put(d, "TE_WIZSPIKE", "1"); LOG_INFO("effect TE_WIZSPIKE is ", ftos(_particleeffectnum("TE_WIZSPIKE")), "\n");
+                       db_put(d, "TE_KNIGHTSPIKE", "1"); LOG_INFO("effect TE_KNIGHTSPIKE is ", ftos(_particleeffectnum("TE_KNIGHTSPIKE")), "\n");
+                       db_put(d, "TE_EXPLOSION", "1"); LOG_INFO("effect TE_EXPLOSION is ", ftos(_particleeffectnum("TE_EXPLOSION")), "\n");
+                       db_put(d, "TE_EXPLOSIONQUAD", "1"); LOG_INFO("effect TE_EXPLOSIONQUAD is ", ftos(_particleeffectnum("TE_EXPLOSIONQUAD")), "\n");
+                       db_put(d, "TE_TAREXPLOSION", "1"); LOG_INFO("effect TE_TAREXPLOSION is ", ftos(_particleeffectnum("TE_TAREXPLOSION")), "\n");
+                       db_put(d, "TE_TELEPORT", "1"); LOG_INFO("effect TE_TELEPORT is ", ftos(_particleeffectnum("TE_TELEPORT")), "\n");
+                       db_put(d, "TE_LAVASPLASH", "1"); LOG_INFO("effect TE_LAVASPLASH is ", ftos(_particleeffectnum("TE_LAVASPLASH")), "\n");
+                       db_put(d, "TE_SMALLFLASH", "1"); LOG_INFO("effect TE_SMALLFLASH is ", ftos(_particleeffectnum("TE_SMALLFLASH")), "\n");
+                       db_put(d, "TE_FLAMEJET", "1"); LOG_INFO("effect TE_FLAMEJET is ", ftos(_particleeffectnum("TE_FLAMEJET")), "\n");
+                       db_put(d, "EF_FLAME", "1"); LOG_INFO("effect EF_FLAME is ", ftos(_particleeffectnum("EF_FLAME")), "\n");
+                       db_put(d, "TE_BLOOD", "1"); LOG_INFO("effect TE_BLOOD is ", ftos(_particleeffectnum("TE_BLOOD")), "\n");
+                       db_put(d, "TE_SPARK", "1"); LOG_INFO("effect TE_SPARK is ", ftos(_particleeffectnum("TE_SPARK")), "\n");
+                       db_put(d, "TE_PLASMABURN", "1"); LOG_INFO("effect TE_PLASMABURN is ", ftos(_particleeffectnum("TE_PLASMABURN")), "\n");
+                       db_put(d, "TE_TEI_G3", "1"); LOG_INFO("effect TE_TEI_G3 is ", ftos(_particleeffectnum("TE_TEI_G3")), "\n");
+                       db_put(d, "TE_TEI_SMOKE", "1"); LOG_INFO("effect TE_TEI_SMOKE is ", ftos(_particleeffectnum("TE_TEI_SMOKE")), "\n");
+                       db_put(d, "TE_TEI_BIGEXPLOSION", "1"); LOG_INFO("effect TE_TEI_BIGEXPLOSION is ", ftos(_particleeffectnum("TE_TEI_BIGEXPLOSION")), "\n");
+                       db_put(d, "TE_TEI_PLASMAHIT", "1"); LOG_INFO("effect TE_TEI_PLASMAHIT is ", ftos(_particleeffectnum("TE_TEI_PLASMAHIT")), "\n");
+                       db_put(d, "EF_STARDUST", "1"); LOG_INFO("effect EF_STARDUST is ", ftos(_particleeffectnum("EF_STARDUST")), "\n");
+                       db_put(d, "TR_ROCKET", "1"); LOG_INFO("effect TR_ROCKET is ", ftos(_particleeffectnum("TR_ROCKET")), "\n");
+                       db_put(d, "TR_GRENADE", "1"); LOG_INFO("effect TR_GRENADE is ", ftos(_particleeffectnum("TR_GRENADE")), "\n");
+                       db_put(d, "TR_BLOOD", "1"); LOG_INFO("effect TR_BLOOD is ", ftos(_particleeffectnum("TR_BLOOD")), "\n");
+                       db_put(d, "TR_WIZSPIKE", "1"); LOG_INFO("effect TR_WIZSPIKE is ", ftos(_particleeffectnum("TR_WIZSPIKE")), "\n");
+                       db_put(d, "TR_SLIGHTBLOOD", "1"); LOG_INFO("effect TR_SLIGHTBLOOD is ", ftos(_particleeffectnum("TR_SLIGHTBLOOD")), "\n");
+                       db_put(d, "TR_KNIGHTSPIKE", "1"); LOG_INFO("effect TR_KNIGHTSPIKE is ", ftos(_particleeffectnum("TR_KNIGHTSPIKE")), "\n");
+                       db_put(d, "TR_VORESPIKE", "1"); LOG_INFO("effect TR_VORESPIKE is ", ftos(_particleeffectnum("TR_VORESPIKE")), "\n");
+                       db_put(d, "TR_NEHAHRASMOKE", "1"); LOG_INFO("effect TR_NEHAHRASMOKE is ", ftos(_particleeffectnum("TR_NEHAHRASMOKE")), "\n");
+                       db_put(d, "TR_NEXUIZPLASMA", "1"); LOG_INFO("effect TR_NEXUIZPLASMA is ", ftos(_particleeffectnum("TR_NEXUIZPLASMA")), "\n");
+                       db_put(d, "TR_GLOWTRAIL", "1"); LOG_INFO("effect TR_GLOWTRAIL is ", ftos(_particleeffectnum("TR_GLOWTRAIL")), "\n");
+                       db_put(d, "TR_SEEKER", "1"); LOG_INFO("effect TR_SEEKER is ", ftos(_particleeffectnum("TR_SEEKER")), "\n");
+                       db_put(d, "SVC_PARTICLE", "1"); LOG_INFO("effect SVC_PARTICLE is ", ftos(_particleeffectnum("SVC_PARTICLE")), "\n");
  
                        fh = fopen("effectinfo.txt", FILE_READ);
                        while((s = fgets(fh)))
                                {
                                        if(db_get(d, argv(1)) != "1")
                                        {
-                                               if(particleeffectnum(argv(1)) >= 0)
-                                                       LOG_INFO("effect ", argv(1), " is ", ftos(particleeffectnum(argv(1))), "\n");
+                                               int i = _particleeffectnum(argv(1));
+                                               if(i >= 0)
+                                                       LOG_INFO("effect ", argv(1), " is ", ftos(i), "\n");
                                                db_put(d, argv(1), "1");
                                        }
                                }
@@@ -1657,8 -1699,8 +1658,8 @@@ void GameCommand_trace(float request, f
                                                vv = stov(argv(2));
                                                dv = stov(argv(3));
                                                traceline(vv, dv, MOVE_NORMAL, world);
-                                               trailparticles(world, particleeffectnum("TR_NEXUIZPLASMA"), vv, trace_endpos);
-                                               trailparticles(world, particleeffectnum("TR_CRYLINKPLASMA"), trace_endpos, dv);
+                                               trailparticles(world, particleeffectnum(EFFECT_TR_NEXUIZPLASMA), vv, trace_endpos);
+                                               trailparticles(world, particleeffectnum(EFFECT_TR_CRYLINKPLASMA), trace_endpos, dv);
                                                return;
                                        }
                                }
@@@ -1774,6 -1816,7 +1775,6 @@@ void GameCommand_(float request
  // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
  #define SERVER_COMMANDS(request,arguments,command) \
        SERVER_COMMAND("adminmsg", GameCommand_adminmsg(request, arguments), "Send an admin message to a client directly") \
 -      SERVER_COMMAND("mobbutcher", GameCommand_mobbutcher(request), "Instantly removes all monsters on the map") \
        SERVER_COMMAND("allready", GameCommand_allready(request), "Restart the server and reset the players") \
        SERVER_COMMAND("allspec", GameCommand_allspec(request, arguments), "Force all players to spectate") \
        SERVER_COMMAND("anticheat", GameCommand_anticheat(request, arguments), "Create an anticheat report for a client") \