]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into TimePath/unified_weapons
authorTimePath <andrew.hardaker1995@gmail.com>
Sat, 3 Oct 2015 08:29:19 +0000 (18:29 +1000)
committerTimePath <andrew.hardaker1995@gmail.com>
Sat, 3 Oct 2015 08:29:38 +0000 (18:29 +1000)
# Conflicts:
# qcsrc/common/turrets/turret/ewheel.qc
# qcsrc/common/turrets/turret/hk_weapon.qc
# qcsrc/common/turrets/turret/walker.qc
# qcsrc/common/turrets/unit/flac.qc
# qcsrc/common/turrets/unit/fusionreactor.qc
# qcsrc/common/turrets/unit/hellion.qc
# qcsrc/common/turrets/unit/machinegun.qc
# qcsrc/common/turrets/unit/mlrs.qc
# qcsrc/common/turrets/unit/phaser.qc
# qcsrc/common/turrets/unit/plasma.qc
# qcsrc/common/turrets/unit/plasma_dual.qc
# qcsrc/common/turrets/unit/tesla.qc
# qcsrc/common/vehicles/vehicle/bumblebee.qc
# qcsrc/common/weapons/weapon/hook.qc
# qcsrc/common/weapons/weapon/vortex.qc
# qcsrc/server/mutators/mutator_overkill.qc

60 files changed:
1  2 
qcsrc/client/progs.inc
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/monster/zombie.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/itemstime.qc
qcsrc/common/turrets/turret/ewheel.qc
qcsrc/common/turrets/turret/flac.qc
qcsrc/common/turrets/turret/fusionreactor.qc
qcsrc/common/turrets/turret/hellion.qc
qcsrc/common/turrets/turret/hk.qc
qcsrc/common/turrets/turret/machinegun.qc
qcsrc/common/turrets/turret/mlrs.qc
qcsrc/common/turrets/turret/phaser.qc
qcsrc/common/turrets/turret/plasma.qc
qcsrc/common/turrets/turret/plasma_dual.qc
qcsrc/common/turrets/turret/tesla.qc
qcsrc/common/turrets/turret/walker.qc
qcsrc/common/vehicles/vehicle/bumblebee.qc
qcsrc/common/vehicles/vehicle/racer.qc
qcsrc/common/vehicles/vehicle/raptor.qc
qcsrc/common/vehicles/vehicle/spiderbot.qc
qcsrc/common/weapons/weapon/arc.qc
qcsrc/common/weapons/weapon/blaster.qc
qcsrc/common/weapons/weapon/crylink.qc
qcsrc/common/weapons/weapon/devastator.qc
qcsrc/common/weapons/weapon/electro.qc
qcsrc/common/weapons/weapon/fireball.qc
qcsrc/common/weapons/weapon/hagar.qc
qcsrc/common/weapons/weapon/hlac.qc
qcsrc/common/weapons/weapon/hmg.qc
qcsrc/common/weapons/weapon/hook.qc
qcsrc/common/weapons/weapon/machinegun.qc
qcsrc/common/weapons/weapon/minelayer.qc
qcsrc/common/weapons/weapon/mortar.qc
qcsrc/common/weapons/weapon/porto.qc
qcsrc/common/weapons/weapon/rifle.qc
qcsrc/common/weapons/weapon/rpc.qc
qcsrc/common/weapons/weapon/seeker.qc
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/common/weapons/weapon/shotgun.qc
qcsrc/common/weapons/weapon/tuba.qc
qcsrc/common/weapons/weapon/vaporizer.qc
qcsrc/common/weapons/weapon/vortex.qc
qcsrc/server/autocvars.qh
qcsrc/server/bot/havocbot/havocbot.qc
qcsrc/server/cl_client.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/mutators/gamemode_invasion.qc
qcsrc/server/mutators/gamemode_nexball.qc
qcsrc/server/mutators/mutator_instagib.qc
qcsrc/server/mutators/mutator_overkill.qc
qcsrc/server/progs.inc
qcsrc/server/t_items.qc

diff --combined qcsrc/client/progs.inc
index 449692d5f2fd1401c91b621a8d6027c4f79a7cff,3d742a624588ce6de0fc83ae2fef10896ea172ea..ef31d66d0b2cf31ce54097787c9288e8708af1e5
@@@ -38,6 -38,7 +38,7 @@@
  #include "../common/animdecide.qc"
  #include "../common/buffs.qc"
  #include "../common/effects/effects.qc"
+ #include "../common/effects/effectinfo.qc"
  #include "../common/mapinfo.qc"
  #include "../common/movetypes/include.qc"
  #include "../common/nades.qc"
  #include "../common/items/all.qc"
  #include "../common/monsters/all.qc"
  #include "../common/mutators/all.qc"
 +#include "../common/turrets/all.qc"
  #include "../common/vehicles/all.qc"
  #include "../common/weapons/all.qc"
  
  #include "../common/turrets/cl_turrets.qc"
 -#include "../common/turrets/all.qc"
  
  #include "../common/triggers/include.qc"
  
index 7194b7e844db521aa028b4dd1e2ec40879da7245,433fed6830e23c050bb1a9aa69678cb4f420ba7a..54d17d934e33030ba55c4f956a0fd66a9a2f025a
@@@ -1,75 -1,21 +1,75 @@@
 +#ifndef MAGE_H
 +#define MAGE_H
 +
  #ifndef MENUQC
 -bool M_Mage(int);
 +MODEL(MON_MAGE, "models/monsters/mage.dpm");
  #endif
 -REGISTER_MONSTER_SIMPLE(
 -/* MON_##id   */ MAGE,
 -/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED,
 -/* mins,maxs  */ '-36 -36 -24', '36 36 50',
 -/* model      */ "mage.dpm",
 -/* netname    */ "mage",
 -/* fullname   */ _("Mage")
 -) {
 +
 +CLASS(Mage, Monster)
 +    ATTRIB(Mage, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED);
 +    ATTRIB(Mage, mins, vector, '-36 -36 -24');
 +    ATTRIB(Mage, maxs, vector, '36 36 50');
  #ifndef MENUQC
 -    this.monster_func = M_Mage;
 -    this.monster_func(MR_PRECACHE);
 +    ATTRIB(Mage, m_model, Model, MDL_MON_MAGE);
 +#endif
 +    ATTRIB(Mage, netname, string, "mage");
 +    ATTRIB(Mage, monster_name, string, _("Mage"));
 +ENDCLASS(Mage)
 +
 +REGISTER_MONSTER(MAGE, NEW(Mage)) {
 +#ifndef MENUQC
 +    this.mr_precache(this);
  #endif
  }
  
 +#include "../../weapons/all.qh"
 +
 +CLASS(MageSpike, PortoLaunch)
 +/* flags     */ ATTRIB(MageSpike, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
 +/* impulse   */ ATTRIB(MageSpike, impulse, int, 9);
 +/* refname   */ ATTRIB(MageSpike, netname, string, "magespike");
 +/* wepname   */ ATTRIB(MageSpike, message, string, _("Mage spike"));
 +ENDCLASS(MageSpike)
 +REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
  #ifdef SVQC
 +
 +void M_Mage_Attack_Spike(vector dir);
 +void M_Mage_Attack_Push();
 +METHOD(MageSpike, wr_think, void(MageSpike thiswep, entity actor, bool fire1, bool fire2)) {
 +    if (fire1)
 +    if (!IS_PLAYER(actor) || weapon_prepareattack(actor, false, 0.2)) {
 +        if (!actor.target_range) actor.target_range = autocvar_g_monsters_target_range;
 +        actor.enemy = Monster_FindTarget(actor);
 +        W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
 +      if (!IS_PLAYER(actor)) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin);
 +        M_Mage_Attack_Spike(w_shotdir);
 +        weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
 +    }
 +    if (fire2)
 +    if (!IS_PLAYER(actor) || weapon_prepareattack(actor, true, 0.5)) {
 +        M_Mage_Attack_Push();
 +        weapon_thinkf(actor, WFRAME_FIRE2, 0, w_ready);
 +    }
 +}
 +
 +void M_Mage_Attack_Teleport();
 +
 +CLASS(OffhandMageTeleport, OffhandWeapon)
 +    .bool OffhandMageTeleport_key_pressed;
 +    METHOD(OffhandMageTeleport, offhand_think, void(OffhandMageTeleport this, entity player, bool key_pressed))
 +    {
 +        if (key_pressed && !player.OffhandMageTeleport_key_pressed)
 +            WITH(entity, self, player, M_Mage_Attack_Teleport());
 +        player.OffhandMageTeleport_key_pressed = key_pressed;
 +    }
 +ENDCLASS(OffhandMageTeleport)
 +OffhandMageTeleport OFFHAND_MAGE_TELEPORT; STATIC_INIT(OFFHAND_MAGE_TELEPORT) { OFFHAND_MAGE_TELEPORT = NEW(OffhandMageTeleport); }
 +
  float autocvar_g_monster_mage_health;
  float autocvar_g_monster_mage_damageforcescale = 0.5;
  float autocvar_g_monster_mage_attack_spike_damage;
@@@ -165,12 -111,19 +165,12 @@@ void M_Mage_Attack_Spike_Touch(
  // copied from W_Seeker_Think
  void M_Mage_Attack_Spike_Think()
  {SELFPARAM();
 -      entity e;
 -      vector desireddir, olddir, newdir, eorg;
 -      float turnrate;
 -      float dist;
 -      float spd;
 -
 -      if (time > self.ltime || self.enemy.health <= 0 || self.owner.health <= 0)
 -      {
 +      if (time > self.ltime || (self.enemy && self.enemy.health <= 0) || self.owner.health <= 0) {
                self.projectiledeathtype |= HITTYPE_SPLASH;
                M_Mage_Attack_Spike_Explode();
        }
  
 -      spd = vlen(self.velocity);
 +      float spd = vlen(self.velocity);
        spd = bound(
                spd - (autocvar_g_monster_mage_attack_spike_decel) * frametime,
                (autocvar_g_monster_mage_attack_spike_speed_max),
  
        if (self.enemy != world)
        {
 -              e                               = self.enemy;
 -              eorg                    = 0.5 * (e.absmin + e.absmax);
 -              turnrate                = (autocvar_g_monster_mage_attack_spike_turnrate); // how fast to turn
 -              desireddir              = normalize(eorg - self.origin);
 -              olddir                  = normalize(self.velocity); // get my current direction
 -              dist                    = vlen(eorg - self.origin);
 +              entity e = self.enemy;
 +              vector eorg = 0.5 * (e.absmin + e.absmax);
 +              float turnrate = (autocvar_g_monster_mage_attack_spike_turnrate); // how fast to turn
 +              vector desireddir = normalize(eorg - self.origin);
 +              vector olddir = normalize(self.velocity); // get my current direction
 +              float dist = vlen(eorg - self.origin);
  
                // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )
                if ((autocvar_g_monster_mage_attack_spike_smart) && (dist > (autocvar_g_monster_mage_attack_spike_smart_mindist)))
                        desireddir = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5);
                }
  
 -              newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy
 +              vector newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy
                self.velocity = newdir * spd; // make me fly in the new direction at my flight speed
        }
 -      else
 -              dist = 0;
  
        ///////////////
  
        UpdateCSQCProjectile(self);
  }
  
 -void M_Mage_Attack_Spike()
 -{SELFPARAM();
 -      entity missile;
 -      vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
 -
 +void M_Mage_Attack_Spike(vector dir)
 +{
 +      SELFPARAM();
        makevectors(self.angles);
  
 -      missile = spawn ();
 +      entity missile = spawn();
        missile.owner = missile.realowner = self;
        missile.think = M_Mage_Attack_Spike_Think;
        missile.ltime = time + 7;
        missile.movetype = MOVETYPE_FLYMISSILE;
        missile.flags = FL_PROJECTILE;
        setorigin(missile, self.origin + v_forward * 14 + '0 0 30' + v_right * -14);
 -      setsize (missile, '0 0 0', '0 0 0');
 +      setsize(missile, '0 0 0', '0 0 0');
        missile.velocity = dir * 400;
        missile.avelocity = '300 300 300';
        missile.enemy = self.enemy;
@@@ -311,25 -268,17 +311,25 @@@ void M_Mage_Attack_Push(
  
  void M_Mage_Attack_Teleport()
  {SELFPARAM();
 -      if(vlen(self.enemy.origin - self.origin) >= 500)
 -              return;
 +      entity targ = self.enemy;
 +      if (!targ) return;
 +      if (vlen(targ.origin - self.origin) > 1500) return;
  
 -      makevectors(self.enemy.angles);
 -      tracebox(self.enemy.origin + ((v_forward * -1) * 200), self.mins, self.maxs, self.origin, MOVE_NOMONSTERS, self);
 +      makevectors(targ.angles);
 +      tracebox(targ.origin + ((v_forward * -1) * 200), self.mins, self.maxs, self.origin, MOVE_NOMONSTERS, self);
  
        if(trace_fraction < 1)
                return;
  
        Send_Effect(EFFECT_SPAWN_NEUTRAL, self.origin, '0 0 0', 1);
 -      setorigin(self, self.enemy.origin + ((v_forward * -1) * 200));
 +      setorigin(self, targ.origin + ((v_forward * -1) * 200));
 +
 +      vector a = vectoangles(targ.origin - self.origin);
 +      a.x = -a.x;
 +      self.angles_x = a.x;
 +      self.angles_y = a.y;
 +      self.fixangle = true;
 +      self.velocity *= 0.5;
  
        self.attack_finished_single = time + 0.2;
  }
@@@ -351,7 -300,7 +351,7 @@@ void M_Mage_Defend_Shield(
        self.anim_finished = time + 1;
  }
  
 -float M_Mage_Attack(float attack_type)
 +float M_Mage_Attack(float attack_type, entity targ)
  {SELFPARAM();
        switch(attack_type)
        {
                {
                        if(random() <= 0.7)
                        {
 -                              M_Mage_Attack_Push();
 +                              Weapon wep = WEP_MAGE_SPIKE;
 +                              wep.wr_think(wep, self, false, true);
                                return true;
                        }
  
                        {
                                if(random() <= 0.4)
                                {
 -                                      M_Mage_Attack_Teleport();
 +                                      OffhandWeapon off = OFFHAND_MAGE_TELEPORT;
 +                                      off.offhand_think(off, self, true);
                                        return true;
                                }
                                else
                                        setanim(self, self.anim_shoot, true, true, true);
                                        self.attack_finished_single = time + (autocvar_g_monster_mage_attack_spike_delay);
                                        self.anim_finished = time + 1;
 -                                      Monster_Delay(1, 0, 0.2, M_Mage_Attack_Spike);
 +                                      Weapon wep = WEP_MAGE_SPIKE;
 +                                      wep.wr_think(wep, self, true, false);
                                        return true;
                                }
                        }
        return false;
  }
  
void spawnfunc_monster_mage() { Monster_Spawn(MON_MAGE.monsterid); }
spawnfunc(monster_mage) { Monster_Spawn(MON_MAGE.monsterid); }
  
  #endif // SVQC
  
 -bool M_Mage(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
                #ifdef SVQC
 -              case MR_THINK:
 +              METHOD(Mage, mr_think, bool(Monster thismon))
                {
 +                      SELFPARAM();
                        entity head;
                        bool need_help = false;
  
  
                        return true;
                }
 -              case MR_PAIN:
 +              METHOD(Mage, mr_pain, bool(Monster thismon))
                {
                        return true;
                }
 -              case MR_DEATH:
 +              METHOD(Mage, mr_death, bool(Monster thismon))
                {
 +                      SELFPARAM();
                        setanim(self, self.anim_die1, false, true, true);
                        return true;
                }
                #endif
                #ifndef MENUQC
 -              case MR_ANIM:
 +              METHOD(Mage, mr_anim, bool(Monster thismon))
                {
 +                      SELFPARAM();
                        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);
                }
                #endif
                #ifdef SVQC
 -              case MR_SETUP:
 +              METHOD(Mage, mr_setup, bool(Monster thismon))
                {
 +                      SELFPARAM();
                        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); }
  
                        return true;
                }
 -              case MR_PRECACHE:
 +              METHOD(Mage, mr_precache, bool(Monster thismon))
                {
                        return true;
                }
                #endif
 -      }
  
 -      return true;
 -}
 +#endif
index 40d0ef6a3f1274982d0eae2352ea2f256d8e93bc,fe6078303cb5cacd1b488e0bccb38d0b8f6e6490..937c83c9edbe868b7f2f3c25f5d3d4caaab628cf
@@@ -1,31 -1,20 +1,31 @@@
 +#ifndef SHAMBLER_H
 +#define SHAMBLER_H
 +
  #ifndef MENUQC
 -bool M_Shambler(int);
 +MODEL(MON_SHAMBLER, "models/monsters/shambler.mdl");
  #endif
 -REGISTER_MONSTER_SIMPLE(
 -/* MON_##id   */ SHAMBLER,
 -/* spawnflags */ MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED,
 -/* mins,maxs  */ '-41 -41 -31', '41 41 65',
 -/* model      */ "shambler.mdl",
 -/* netname    */ "shambler",
 -/* fullname   */ _("Shambler")
 -) {
 +
 +CLASS(Shambler, Monster)
 +    ATTRIB(Shambler, spawnflags, int, MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED);
 +    ATTRIB(Shambler, mins, vector, '-41 -41 -31');
 +    ATTRIB(Shambler, maxs, vector, '41 41 65');
  #ifndef MENUQC
 -      this.monster_func = M_Shambler;
 -      this.monster_func(MR_PRECACHE);
 +    ATTRIB(Shambler, m_model, Model, MDL_MON_SHAMBLER);
 +#endif
 +    ATTRIB(Shambler, netname, string, "shambler");
 +    ATTRIB(Shambler, monster_name, string, _("Shambler"));
 +ENDCLASS(Shambler)
 +
 +REGISTER_MONSTER(SHAMBLER, NEW(Shambler)) {
 +#ifndef MENUQC
 +    this.mr_precache(this);
  #endif
  }
  
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
  #ifdef SVQC
  float autocvar_g_monster_shambler_health;
  float autocvar_g_monster_shambler_damageforcescale = 0.1;
@@@ -179,7 -168,7 +179,7 @@@ void M_Shambler_Attack_Lightning(
        CSQCProjectile(gren, true, PROJECTILE_SHAMBLER_LIGHTNING, true);
  }
  
 -float M_Shambler_Attack(float attack_type)
 +float M_Shambler_Attack(float attack_type, entity targ)
  {SELFPARAM();
        switch(attack_type)
        {
        return false;
  }
  
void spawnfunc_monster_shambler() { Monster_Spawn(MON_SHAMBLER.monsterid); }
spawnfunc(monster_shambler) { Monster_Spawn(MON_SHAMBLER.monsterid); }
  #endif // SVQC
  
 -bool M_Shambler(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
                #ifdef SVQC
 -              case MR_THINK:
 +              METHOD(Shambler, mr_think, bool(Shambler thismon))
                {
                        return true;
                }
 -              case MR_PAIN:
 +              METHOD(Shambler, mr_pain, bool(Shambler thismon))
                {
 +                      SELFPARAM();
                        self.pain_finished = time + 0.5;
                        setanim(self, self.anim_pain1, true, true, false);
                        return true;
                }
 -              case MR_DEATH:
 +              METHOD(Shambler, mr_death, bool(Shambler thismon))
                {
 +                      SELFPARAM();
                        setanim(self, self.anim_die1, false, true, true);
                        return true;
                }
                #endif
                #ifndef MENUQC
 -              case MR_ANIM:
 +              METHOD(Shambler, mr_anim, bool(Shambler thismon))
                {
 +                      SELFPARAM();
                        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);
                }
                #endif
                #ifdef SVQC
 -              case MR_SETUP:
 +              METHOD(Shambler, mr_setup, bool(Shambler thismon))
                {
 +                      SELFPARAM();
                        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); }
  
                        return true;
                }
 -              case MR_PRECACHE:
 +              METHOD(Shambler, mr_precache, bool(Shambler thismon))
                {
                        return true;
                }
                #endif
 -      }
  
 -      return true;
 -}
 +#endif
index 8f3bc11782aae7c026449209e76b616f5971eb3b,ba2b12695d8d9a2eb85273814b162cc161a6ac00..e262ca87dbb9593d47a72162df73ef4f54bf4b00
@@@ -1,85 -1,29 +1,85 @@@
 +#ifndef SPIDER_H
 +#define SPIDER_H
 +
  #ifndef MENUQC
 -bool M_Spider(int);
 +MODEL(MON_SPIDER, "models/monsters/spider.dpm");
  #endif
 -REGISTER_MONSTER_SIMPLE(
 -/* MON_##id   */ SPIDER,
 -/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE,
 -/* mins,maxs  */ '-18 -18 -25', '18 18 30',
 -/* model      */ "spider.dpm",
 -/* netname    */ "spider",
 -/* fullname   */ _("Spider")
 -) {
 +
 +CLASS(Spider, Monster)
 +    ATTRIB(Spider, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE);
 +    ATTRIB(Spider, mins, vector, '-18 -18 -25');
 +    ATTRIB(Spider, maxs, vector, '18 18 30');
 +#ifndef MENUQC
 +    ATTRIB(Spider, m_model, Model, MDL_MON_SPIDER);
 +#endif
 +    ATTRIB(Spider, netname, string, "spider");
 +    ATTRIB(Spider, monster_name, string, _("Spider"));
 +ENDCLASS(Spider)
 +
 +REGISTER_MONSTER(SPIDER, NEW(Spider)) {
  #ifndef MENUQC
 -      this.monster_func = M_Spider;
 -      this.monster_func(MR_PRECACHE);
 +    this.mr_precache(this);
  #endif
  }
  
 +#include "../../weapons/all.qh"
 +
 +CLASS(SpiderAttack, PortoLaunch)
 +/* flags     */ ATTRIB(SpiderAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
 +/* impulse   */ ATTRIB(SpiderAttack, impulse, int, 9);
 +/* refname   */ ATTRIB(SpiderAttack, netname, string, "spider");
 +/* wepname   */ ATTRIB(SpiderAttack, message, string, _("Spider attack"));
 +ENDCLASS(SpiderAttack)
 +REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
  #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 spider_web_delay;
 +
  float autocvar_g_monster_spider_attack_web_damagetime;
  float autocvar_g_monster_spider_attack_web_speed;
  float autocvar_g_monster_spider_attack_web_speed_up;
  float autocvar_g_monster_spider_attack_web_delay;
 +
 +float autocvar_g_monster_spider_attack_bite_damage;
 +float autocvar_g_monster_spider_attack_bite_delay;
 +
 +void M_Spider_Attack_Web();
 +
 +METHOD(SpiderAttack, wr_think, void(SpiderAttack thiswep, entity actor, bool fire1, bool fire2)) {
 +    bool isPlayer = IS_PLAYER(actor);
 +    if (fire1)
 +    if ((!isPlayer && time >= actor.spider_web_delay) || weapon_prepareattack(actor, false, autocvar_g_monster_spider_attack_web_delay)) {
 +              if (!isPlayer) {
 +                      actor.spider_web_delay = time + 3;
 +                      setanim(actor, actor.anim_shoot, true, true, true);
 +                      actor.attack_finished_single = time + (autocvar_g_monster_spider_attack_web_delay);
 +                      actor.anim_finished = time + 1;
 +              }
 +        if (isPlayer) actor.enemy = Monster_FindTarget(actor);
 +        W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
 +      if (!isPlayer) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin);
 +              M_Spider_Attack_Web();
 +        weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
 +        return;
 +    }
 +    if (fire2)
 +    if (!isPlayer || weapon_prepareattack(actor, true, 0.5)) {
 +      if (isPlayer) {
 +              actor.enemy = Monster_FindTarget(actor);
 +              actor.attack_range = 60;
 +      }
 +      Monster_Attack_Melee(actor.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);
 +        weapon_thinkf(actor, WFRAME_FIRE2, 0, w_ready);
 +    }
 +}
 +
 +float autocvar_g_monster_spider_health;
 +float autocvar_g_monster_spider_damageforcescale = 0.6;
  float autocvar_g_monster_spider_speed_stop;
  float autocvar_g_monster_spider_speed_run;
  float autocvar_g_monster_spider_speed_walk;
@@@ -91,6 -35,8 +91,6 @@@ const float spider_anim_attack                = 2
  const float spider_anim_attack2               = 3;
  */
  
 -.float spider_web_delay;
 -
  void M_Spider_Attack_Web_Explode()
  {SELFPARAM();
        entity e;
@@@ -151,50 -97,59 +151,50 @@@ void M_Spider_Attack_Web(
        CSQCProjectile(proj, true, PROJECTILE_ELECTRO, true);
  }
  
 -bool M_Spider_Attack(int attack_type)
 +bool M_Spider_Attack(int attack_type, entity targ)
  {SELFPARAM();
        switch(attack_type)
        {
 +              Weapon wep = WEP_SPIDER_ATTACK;
                case MONSTER_ATTACK_MELEE:
                {
 -                      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);
 +                      wep.wr_think(wep, self, false, true);
 +                      return true;
                }
                case MONSTER_ATTACK_RANGED:
                {
 -                      if(time >= self.spider_web_delay)
 -                      {
 -                              setanim(self, self.anim_shoot, true, true, true);
 -                              self.attack_finished_single = time + (autocvar_g_monster_spider_attack_web_delay);
 -                              self.anim_finished = time + 1;
 -                              M_Spider_Attack_Web();
 -                              self.spider_web_delay = time + 3;
 -                              return true;
 -                      }
 -
 -                      return false;
 +                      wep.wr_think(wep, self, true, false);
 +                      return true;
                }
        }
  
        return false;
  }
  
void spawnfunc_monster_spider() { Monster_Spawn(MON_SPIDER.monsterid); }
spawnfunc(monster_spider) { Monster_Spawn(MON_SPIDER.monsterid); }
  #endif // SVQC
  
 -bool M_Spider(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
                #ifdef SVQC
 -              case MR_THINK:
 +              METHOD(Spider, mr_think, bool(Spider thismon))
                {
                        return true;
                }
 -              case MR_PAIN:
 +              METHOD(Spider, mr_pain, bool(Spider thismon))
                {
                        return true;
                }
 -              case MR_DEATH:
 +              METHOD(Spider, mr_death, bool(Spider thismon))
                {
 +                      SELFPARAM();
                        setanim(self, self.anim_melee, false, true, true);
                        self.angles_x = 180;
                        return true;
                }
                #endif
                #ifndef MENUQC
 -              case MR_ANIM:
 +              METHOD(Spider, mr_anim, bool(Spider thismon))
                {
 +                      SELFPARAM();
                        vector none = '0 0 0';
                        self.anim_walk = animfixfps(self, '1 1 1', none);
                        self.anim_idle = animfixfps(self, '0 1 1', none);
                }
                #endif
                #ifdef SVQC
 -              case MR_SETUP:
 +              METHOD(Spider, mr_setup, bool(Spider thismon))
                {
 +                      SELFPARAM();
                        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); }
  
                        return true;
                }
 -              case MR_PRECACHE:
 +              METHOD(Spider, mr_precache, bool(Spider thismon))
                {
                        return true;
                }
                #endif
 -      }
  
 -      return true;
 -}
 +#endif
index 2b1c035edd002b12c22e47fe2d1fc30190c5a897,d65bcf648da3ce62286f1d852f9ec2b858d87d01..c901e3ae3e669ad0a0ab69cd1439b0ff4b0474d1
@@@ -1,88 -1,29 +1,88 @@@
 +#ifndef WYVERN_H
 +#define WYVERN_H
 +
  #ifndef MENUQC
 -bool M_Wyvern(int);
 +MODEL(MON_WYVERN, "models/monsters/wizard.mdl");
  #endif
 -REGISTER_MONSTER_SIMPLE(
 -/* MON_##id   */ WYVERN,
 -/* spawnflags */ MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE,
 -/* mins,maxs  */ '-20 -20 -58', '20 20 20',
 -/* model      */ "wizard.mdl",
 -/* netname    */ "wyvern",
 -/* fullname   */ _("Wyvern")
 -) {
 +
 +CLASS(Wyvern, Monster)
 +    ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE);
 +    ATTRIB(Wyvern, mins, vector, '-20 -20 -58');
 +    ATTRIB(Wyvern, maxs, vector, '20 20 20');
 +#ifndef MENUQC
 +    ATTRIB(Wyvern, m_model, Model, MDL_MON_WYVERN);
 +#endif
 +    ATTRIB(Wyvern, netname, string, "wyvern");
 +    ATTRIB(Wyvern, monster_name, string, _("Wyvern"));
 +ENDCLASS(Wyvern)
 +
 +REGISTER_MONSTER(WYVERN, NEW(Wyvern)) {
  #ifndef MENUQC
 -      this.monster_func = M_Wyvern;
 -      this.monster_func(MR_PRECACHE);
 +    this.mr_precache(this);
  #endif
  }
  
 +#include "../../weapons/all.qh"
 +
 +CLASS(WyvernAttack, PortoLaunch)
 +/* flags     */ ATTRIB(WyvernAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
 +/* impulse   */ ATTRIB(WyvernAttack, impulse, int, 9);
 +/* refname   */ ATTRIB(WyvernAttack, netname, string, "wyvern");
 +/* wepname   */ ATTRIB(WyvernAttack, message, string, _("Wyvern attack"));
 +ENDCLASS(WyvernAttack)
 +REGISTER_WEAPON(WYVERN_ATTACK, NEW(WyvernAttack));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
  #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;
  float autocvar_g_monster_wyvern_attack_fireball_force;
  float autocvar_g_monster_wyvern_attack_fireball_radius;
  float autocvar_g_monster_wyvern_attack_fireball_speed;
 +
 +void M_Wyvern_Attack_Fireball_Explode();
 +void M_Wyvern_Attack_Fireball_Touch();
 +
 +METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, bool fire1, bool fire2)) {
 +    if (fire1)
 +    if (time > actor.attack_finished_single || weapon_prepareattack(actor, false, 1.2)) {
 +        if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
 +              if (IS_MONSTER(actor)) {
 +                      actor.attack_finished_single = time + 1.2;
 +                      actor.anim_finished = time + 1.2;
 +                      monster_makevectors(actor.enemy);
 +              }
 +
 +              entity missile = spawn();
 +              missile.owner = missile.realowner = actor;
 +              missile.solid = SOLID_TRIGGER;
 +              missile.movetype = MOVETYPE_FLYMISSILE;
 +              missile.projectiledeathtype = DEATH_MONSTER_WYVERN;
 +              setsize(missile, '-6 -6 -6', '6 6 6');
 +              setorigin(missile, actor.origin + actor.view_ofs + v_forward * 14);
 +              missile.flags = FL_PROJECTILE;
 +              missile.velocity = w_shotdir * (autocvar_g_monster_wyvern_attack_fireball_speed);
 +              missile.avelocity = '300 300 300';
 +              missile.nextthink = time + 5;
 +              missile.think = M_Wyvern_Attack_Fireball_Explode;
 +              missile.touch = M_Wyvern_Attack_Fireball_Touch;
 +              CSQCProjectile(missile, true, PROJECTILE_FIREMINE, true);
 +
 +        weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
 +    }
 +}
 +
 +METHOD(WyvernAttack, wr_checkammo1, bool(WyvernAttack thiswep)) {
 +      return true;
 +}
 +
 +float autocvar_g_monster_wyvern_health;
 +float autocvar_g_monster_wyvern_damageforcescale = 0.6;
  float autocvar_g_monster_wyvern_speed_stop;
  float autocvar_g_monster_wyvern_speed_run;
  float autocvar_g_monster_wyvern_speed_walk;
@@@ -96,19 -37,19 +96,19 @@@ const float wyvern_anim_death      = 4
  */
  
  void M_Wyvern_Attack_Fireball_Explode()
 -{SELFPARAM();
 -      entity e;
 -      if(self)
 -      {
 -              Send_Effect(EFFECT_FIREBALL_EXPLODE, self.origin, '0 0 0', 1);
 +{
 +      SELFPARAM();
 +      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);
 +      entity owner = self.realowner;
  
 -              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_SKILLMOD(self), (autocvar_g_monster_wyvern_attack_fireball_damagetime), self.projectiledeathtype);
 +      RadiusDamage(self, owner, 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);
  
 -              remove(self);
 -      }
 +      for (entity e = world; (e = findfloat(e, takedamage, DAMAGE_AIM)); )
 +      if (vlen(e.origin - self.origin) <= (autocvar_g_monster_wyvern_attack_fireball_radius))
 +              Fire_AddDamage(e, owner, 5 * MONSTER_SKILLMOD(owner), (autocvar_g_monster_wyvern_attack_fireball_damagetime), self.projectiledeathtype);
 +
 +      remove(self);
  }
  
  void M_Wyvern_Attack_Fireball_Touch()
        M_Wyvern_Attack_Fireball_Explode();
  }
  
 -void M_Wyvern_Attack_Fireball()
 -{SELFPARAM();
 -      entity missile = spawn();
 -      vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
 -
 -      monster_makevectors(self.enemy);
 -
 -      missile.owner = missile.realowner = self;
 -      missile.solid = SOLID_TRIGGER;
 -      missile.movetype = MOVETYPE_FLYMISSILE;
 -      missile.projectiledeathtype = DEATH_MONSTER_WYVERN;
 -      setsize(missile, '-6 -6 -6', '6 6 6');
 -      setorigin(missile, self.origin + self.view_ofs + v_forward * 14);
 -      missile.flags = FL_PROJECTILE;
 -      missile.velocity = dir * (autocvar_g_monster_wyvern_attack_fireball_speed);
 -      missile.avelocity = '300 300 300';
 -      missile.nextthink = time + 5;
 -      missile.think = M_Wyvern_Attack_Fireball_Explode;
 -      missile.enemy = self.enemy;
 -      missile.touch = M_Wyvern_Attack_Fireball_Touch;
 -      CSQCProjectile(missile, true, PROJECTILE_FIREMINE, true);
 -}
 -
 -float M_Wyvern_Attack(float attack_type)
 -{SELFPARAM();
 +float M_Wyvern_Attack(float attack_type, entity targ)
 +{
 +      SELFPARAM();
        switch(attack_type)
        {
                case MONSTER_ATTACK_MELEE:
                case MONSTER_ATTACK_RANGED:
                {
 -                      self.attack_finished_single = time + 1.2;
 -                      self.anim_finished = time + 1.2;
 -
 -                      M_Wyvern_Attack_Fireball();
 -
 +                      w_shotdir = normalize((self.enemy.origin + '0 0 10') - self.origin);
 +                      Weapon wep = WEP_WYVERN_ATTACK;
 +                      wep.wr_think(wep, self, true, false);
                        return true;
                }
        }
        return false;
  }
  
void spawnfunc_monster_wyvern() { Monster_Spawn(MON_WYVERN.monsterid); }
spawnfunc(monster_wyvern) { Monster_Spawn(MON_WYVERN.monsterid); }
  #endif // SVQC
  
 -bool M_Wyvern(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
                #ifdef SVQC
 -              case MR_THINK:
 +              METHOD(Wyvern, mr_think, bool(Wyvern thismon))
                {
                        return true;
                }
 -              case MR_PAIN:
 +              METHOD(Wyvern, mr_pain, bool(Wyvern thismon))
                {
 +                      SELFPARAM();
                        self.pain_finished = time + 0.5;
                        setanim(self, self.anim_pain1, true, true, false);
                        return true;
                }
 -              case MR_DEATH:
 +              METHOD(Wyvern, mr_death, bool(Wyvern thismon))
                {
 +                      SELFPARAM();
                        setanim(self, self.anim_die1, false, true, true);
                        self.velocity_x = -200 + 400 * random();
                        self.velocity_y = -200 + 400 * random();
                }
                #endif
                #ifndef MENUQC
 -              case MR_ANIM:
 +              METHOD(Wyvern, mr_anim, bool(Wyvern thismon))
                {
 +                      SELFPARAM();
                        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);
                }
                #endif
                #ifdef SVQC
 -              case MR_SETUP:
 +              METHOD(Wyvern, mr_setup, bool(Wyvern thismon))
                {
 +                      SELFPARAM();
                        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); }
  
                        return true;
                }
 -              case MR_PRECACHE:
 +              METHOD(Wyvern, mr_precache, bool(Wyvern thismon))
                {
                        return true;
                }
                #endif
 -      }
  
 -      return true;
 -}
 +#endif
index b9619be19ad5958c9797f8de4b1fd674f4ed2fd3,6242a757d081af4610645932402fa44c1e703b8f..8425a123add60d29469b219c2d0f3b191a34b7de
@@@ -1,31 -1,20 +1,31 @@@
 +#ifndef ZOMBIE_H
 +#define ZOMBIE_H
 +
  #ifndef MENUQC
 -bool M_Zombie(int);
 +MODEL(MON_ZOMBIE, "models/monsters/zombie.dpm");
  #endif
 -REGISTER_MONSTER_SIMPLE(
 -/* MON_##id   */ ZOMBIE,
 -/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RIDE,
 -/* mins,maxs  */ '-18 -18 -25', '18 18 47',
 -/* model      */ "zombie.dpm",
 -/* netname    */ "zombie",
 -/* fullname   */ _("Zombie")
 -) {
 +
 +CLASS(Zombie, Monster)
 +    ATTRIB(Zombie, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RIDE);
 +    ATTRIB(Zombie, mins, vector, '-18 -18 -25');
 +    ATTRIB(Zombie, maxs, vector, '18 18 47');
  #ifndef MENUQC
 -      this.monster_func = M_Zombie;
 -      this.monster_func(MR_PRECACHE);
 +    ATTRIB(Zombie, m_model, Model, MDL_MON_ZOMBIE);
 +#endif
 +    ATTRIB(Zombie, netname, string, "zombie");
 +    ATTRIB(Zombie, monster_name, string, _("Zombie"));
 +ENDCLASS(Zombie)
 +
 +REGISTER_MONSTER(ZOMBIE, NEW(Zombie)) {
 +#ifndef MENUQC
 +    this.mr_precache(this);
  #endif
  }
  
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
  #ifdef SVQC
  float autocvar_g_monster_zombie_health;
  float autocvar_g_monster_zombie_damageforcescale = 0.55;
@@@ -118,7 -107,7 +118,7 @@@ float M_Zombie_Defend_Block(
        return true;
  }
  
 -float M_Zombie_Attack(float attack_type)
 +float M_Zombie_Attack(float attack_type, entity targ)
  {SELFPARAM();
        switch(attack_type)
        {
        return false;
  }
  
void spawnfunc_monster_zombie() { Monster_Spawn(MON_ZOMBIE.monsterid); }
spawnfunc(monster_zombie) { Monster_Spawn(MON_ZOMBIE.monsterid); }
  #endif // SVQC
  
 -bool M_Zombie(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
                #ifdef SVQC
 -              case MR_THINK:
 +              METHOD(Zombie, mr_think, bool(Zombie thismon))
                {
 +                      SELFPARAM();
                        if(time >= self.spawn_time)
                                self.damageforcescale = autocvar_g_monster_zombie_damageforcescale;
                        return true;
                }
 -              case MR_PAIN:
 +              METHOD(Zombie, mr_pain, bool(Zombie thismon))
                {
 +                      SELFPARAM();
                        self.pain_finished = time + 0.34;
                        setanim(self, ((random() > 0.5) ? self.anim_pain1 : self.anim_pain2), true, true, false);
                        return true;
                }
 -              case MR_DEATH:
 +              METHOD(Zombie, mr_death, bool(Zombie thismon))
                {
 +                      SELFPARAM();
                        self.armorvalue = autocvar_g_monsters_armor_blockpercent;
  
                        setanim(self, ((random() > 0.5) ? self.anim_die1 : self.anim_die2), false, true, true);
                }
                #endif
                #ifndef MENUQC
 -              case MR_ANIM:
 +              METHOD(Zombie, mr_anim, bool(Zombie thismon))
                {
 +                      SELFPARAM();
                        vector none = '0 0 0';
                        self.anim_die1 = animfixfps(self, '9 1 0.5', none); // 2 seconds
                        self.anim_die2 = animfixfps(self, '12 1 0.5', none); // 2 seconds
                }
                #endif
                #ifdef SVQC
 -              case MR_SETUP:
 +              METHOD(Zombie, mr_setup, bool(Zombie thismon))
                {
                        if(!self.health) self.health = (autocvar_g_monster_zombie_health);
                        if(!self.speed) { self.speed = (autocvar_g_monster_zombie_speed_walk); }
  
                        return true;
                }
 -              case MR_PRECACHE:
 +              METHOD(Zombie, mr_precache, bool(Zombie thismon))
                {
                        return true;
                }
                #endif
 -      }
  
 -      return true;
 -}
 +#endif
index 34bd6e5aa4cffb64c6de7793a873143f9c2d89a1,18bf6af2a9a64f238b39d94b3316337e56c862b6..ad4ce9f9447bf60ab537ed01938edb7643065876
@@@ -49,7 -49,7 +49,7 @@@ void monster_dropitem(
        {
                setself(e);
                e.noalign = true;
-               e.monster_loot();
+               e.monster_loot(e);
                e.gravity = 1;
                e.movetype = MOVETYPE_TOSS;
                e.reset = SUB_Remove;
@@@ -118,7 -118,7 +118,7 @@@ bool Monster_ValidTarget(entity mon, en
                makevectors (mon.angles);
                dot = normalize (player.origin - mon.origin) * v_forward;
  
-               if(dot <= 0.3) { return false; }
+               if(dot <= autocvar_g_monsters_target_infront_range) { return false; }
        }
  
        return true; // this target is valid!
@@@ -267,7 -267,7 +267,7 @@@ void Monster_Sound_Precache(string f
  
  void Monster_Sounds_Precache()
  {SELFPARAM();
 -      string m = (get_monsterinfo(self.monsterid)).model;
 +      string m = (get_monsterinfo(self.monsterid)).m_model.model_str();
        float globhandle, n, i;
        string f;
  
@@@ -434,7 -434,7 +434,7 @@@ void Monster_Attack_Check(entity e, ent
  
        if(targ_vlen <= e.attack_range)
        {
 -              float attack_success = e.monster_attackfunc(MONSTER_ATTACK_MELEE);
 +              float attack_success = e.monster_attackfunc(MONSTER_ATTACK_MELEE, targ);
                if(attack_success == 1)
                        Monster_Sound(monstersound_melee, 0, false, CH_VOICE);
                else if(attack_success > 0)
  
        if(targ_vlen > e.attack_range)
        {
 -              float attack_success = e.monster_attackfunc(MONSTER_ATTACK_RANGED);
 +              float attack_success = e.monster_attackfunc(MONSTER_ATTACK_RANGED, targ);
                if(attack_success == 1)
                        Monster_Sound(monstersound_melee, 0, false, CH_VOICE);
                else if(attack_success > 0)
@@@ -471,8 -471,7 +471,8 @@@ void Monster_UpdateModel(
        self.anim_die2   = animfixfps(self, '9 1 0.01', '0 0 0');*/
  
        // then get the real values
 -      MON_ACTION(self.monsterid, MR_ANIM);
 +      Monster mon = get_monsterinfo(self.monsterid);
 +      mon.mr_anim(mon);
  }
  
  void Monster_Touch()
@@@ -1038,8 -1037,7 +1038,8 @@@ void Monster_Dead(entity attacker, floa
  
        CSQCModel_UnlinkEntity();
  
 -      MON_ACTION(self.monsterid, MR_DEATH);
 +      Monster mon = get_monsterinfo(self.monsterid);
 +      mon.mr_death(mon);
  
        if(self.candrop && self.weapon)
                W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325');
  
  void Monster_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
  {SELFPARAM();
-       if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL)
+       if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL && !ITEM_DAMAGE_NEEDKILL(deathtype))
                return;
  
        if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
        damage_take = take;
        frag_attacker = attacker;
        frag_deathtype = deathtype;
 -      MON_ACTION(self.monsterid, MR_PAIN);
 +      Monster mon = get_monsterinfo(self.monsterid);
 +      mon.mr_pain(mon);
        take = damage_take;
  
        if(take)
@@@ -1234,8 -1231,7 +1234,8 @@@ void Monster_Think(
                return;
        }
  
 -      if(MON_ACTION(self.monsterid, MR_THINK))
 +      Monster mon = get_monsterinfo(self.monsterid);
 +      if(mon.mr_think(mon))
                Monster_Move(self.speed2, self.speed, self.stopspeed);
  
        Monster_Anim();
  
  float Monster_Spawn_Setup()
  {SELFPARAM();
 -      MON_ACTION(self.monsterid, MR_SETUP);
 +      Monster mon = get_monsterinfo(self.monsterid);
 +      mon.mr_setup(mon);
  
        // ensure some basic needs are met
        if(!self.health) { self.health = 100; }
@@@ -1312,6 -1307,7 +1312,6 @@@ bool Monster_Spawn(int mon_id
  
        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)
        if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) // don't count re-spawning monsters either
                monsters_total += 1;
  
 -      _setmodel(self, self.mdl);
 +      setmodel(self, mon.m_model);
        self.flags                              = FL_MONSTER;
        self.classname                  = "monster";
        self.takedamage                 = DAMAGE_AIM;
index 868a0af92929a9c4dcf736b3913ac4151ec4b9af,4d859b1fcab1438db82826be497093395af67a11..3cdb7b69978ad6ef98c85787bc733bf0913378f9
@@@ -118,7 -118,7 +118,7 @@@ float Item_ItemsTime_UpdateTime(entity 
      {
          for (entity head = world; (head = nextent(head)); )
          {
 -            if (clienttype(head) != CLIENTTYPE_NOTACLIENT || !(head.weapons & WEPSET_SUPERWEAPONS) || head.classname == "weapon_info")
 +            if (clienttype(head) != CLIENTTYPE_NOTACLIENT || !(head.weapons & WEPSET_SUPERWEAPONS) || head.instanceOfWeapon)
                  continue;
              if (e == head)
                  continue;
@@@ -232,10 -232,15 +232,15 @@@ void DrawItemsTimeItem(vector myPos, ve
          HUD_Panel_DrawProgressBar(p_pos, p_size, autocvar_hud_panel_itemstime_progressbar_name, t/autocvar_hud_panel_itemstime_progressbar_maxtime, 0, autocvar_hud_panel_itemstime_iconalign, color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
      }
  
-     if (t > 0 && autocvar_hud_panel_itemstime_text)
-         drawstring_aspect(numpos, ftos(t), eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
-     else
-         picpos.x = myPos.x + mySize.x / 2 - mySize.y / 2;
+     if(autocvar_hud_panel_itemstime_text)
+     {
+         if(t > 0)
+             drawstring_aspect(numpos, ftos(t), eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+         else if(precache_pic("gfx/hud/default/checkmark")) // COMPAT: check if this image exists, as 0.8.1 clients lack it
+             drawpic_aspect_skin(numpos, "checkmark", eX * (ar - 1) * mySize_y + eY * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL);
+         else // legacy code, if the image is missing just center the icon
+             picpos.x = myPos.x + mySize.x / 2 - mySize.y / 2;
+     }
      if (item_availableTime)
          drawpic_aspect_skin_expanding(picpos, item.m_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL, item_availableTime);
      drawpic_aspect_skin(picpos, item.m_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL);
index 6dab6172758f869fd12b9236d92bd3b6ee143281,0000000000000000000000000000000000000000..cdb9a605c2c37d46c328a79f22bc9166a22b1a63
mode 100644,000000..100644
--- /dev/null
@@@ -1,270 -1,0 +1,270 @@@
- void spawnfunc_turret_ewheel() { SELFPARAM(); if(!turret_initialize(TUR_EWHEEL)) remove(self); }
 +#ifndef TURRET_EWHEEL_H
 +#define TURRET_EWHEEL_H
 +
 +//#define EWHEEL_FANCYPATH
 +
 +#include "ewheel_weapon.qc"
 +
 +CLASS(EWheel, Turret)
 +/* spawnflags */ ATTRIB(EWheel, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM);
 +/* mins       */ ATTRIB(EWheel, mins, vector, '-32 -32 0');
 +/* maxs       */ ATTRIB(EWheel, maxs, vector, '32 32 48');
 +/* modelname  */ ATTRIB(EWheel, mdl, string, "ewheel-base2.md3");
 +/* model      */ ATTRIB(EWheel, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(EWheel, head_model, string, strzone(strcat("models/turrets/", "ewheel-gun1.md3")));
 +/* netname    */ ATTRIB(EWheel, netname, string, "ewheel");
 +/* fullname   */ ATTRIB(EWheel, turret_name, string, _("eWheel Turret"));
 +    ATTRIB(EWheel, m_weapon, Weapon, WEP_EWHEEL);
 +ENDCLASS(EWheel)
 +REGISTER_TURRET(EWHEEL, NEW(EWheel));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "ewheel_weapon.qc"
 +
 +#ifdef SVQC
 +
 +float autocvar_g_turrets_unit_ewheel_speed_fast;
 +float autocvar_g_turrets_unit_ewheel_speed_slow;
 +float autocvar_g_turrets_unit_ewheel_speed_slower;
 +float autocvar_g_turrets_unit_ewheel_speed_stop;
 +float autocvar_g_turrets_unit_ewheel_turnrate;
 +
 +const float ewheel_anim_stop = 0;
 +const float ewheel_anim_fwd_slow = 1;
 +const float ewheel_anim_fwd_fast = 2;
 +const float ewheel_anim_bck_slow = 3;
 +const float ewheel_anim_bck_fast = 4;
 +
 +void ewheel_move_path()
 +{SELFPARAM();
 +#ifdef EWHEEL_FANCYPATH
 +    // Are we close enougth to a path node to switch to the next?
 +    if (vlen(self.origin  - self.pathcurrent.origin) < 64)
 +        if (self.pathcurrent.path_next == world)
 +        {
 +            // Path endpoint reached
 +            pathlib_deletepath(self.pathcurrent.owner);
 +            self.pathcurrent = world;
 +
 +            if (self.pathgoal)
 +            {
 +                if (self.pathgoal.use)
 +                    self.pathgoal.use();
 +
 +                if (self.pathgoal.enemy)
 +                {
 +                    self.pathcurrent = pathlib_astar(self.pathgoal.origin,self.pathgoal.enemy.origin);
 +                    self.pathgoal = self.pathgoal.enemy;
 +                }
 +            }
 +            else
 +                self.pathgoal = world;
 +        }
 +        else
 +            self.pathcurrent = self.pathcurrent.path_next;
 +
 +#else
 +    if (vlen(self.origin - self.pathcurrent.origin) < 64)
 +        self.pathcurrent = self.pathcurrent.enemy;
 +#endif
 +
 +    if (self.pathcurrent)
 +    {
 +
 +        self.moveto = self.pathcurrent.origin;
 +        self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
 +
 +        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4);
 +    }
 +}
 +
 +void ewheel_move_enemy()
 +{SELFPARAM();
 +    float newframe;
 +
 +    self.steerto = steerlib_arrive(self.enemy.origin,self.target_range_optimal);
 +
 +    self.moveto  = self.origin + self.steerto * 128;
 +
 +    if (self.tur_dist_enemy > self.target_range_optimal)
 +    {
 +        if ( self.tur_head.spawnshieldtime < 1 )
 +        {
 +            newframe = ewheel_anim_fwd_fast;
 +            movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4);
 +        }
 +        else if (self.tur_head.spawnshieldtime < 2)
 +        {
 +
 +            newframe = ewheel_anim_fwd_slow;
 +            movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4);
 +       }
 +        else
 +        {
 +            newframe = ewheel_anim_fwd_slow;
 +            movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slower), 0.4);
 +        }
 +    }
 +    else if (self.tur_dist_enemy < self.target_range_optimal * 0.5)
 +    {
 +        newframe = ewheel_anim_bck_slow;
 +        movelib_move_simple(v_forward * -1, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4);
 +    }
 +    else
 +    {
 +        newframe = ewheel_anim_stop;
 +        movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop));
 +    }
 +
 +    turrets_setframe(newframe, false);
 +}
 +
 +void ewheel_move_idle()
 +{SELFPARAM();
 +    if(self.frame != 0)
 +    {
 +        self.SendFlags |= TNSF_ANIM;
 +        self.anim_start_time = time;
 +    }
 +
 +    self.frame = 0;
 +    if (vlen(self.velocity))
 +        movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop));
 +}
 +
++spawnfunc(turret_ewheel) { if(!turret_initialize(TUR_EWHEEL)) remove(self); }
 +
 +        METHOD(EWheel, tr_think, void(EWheel thistur))
 +        {
 +            SELFPARAM();
 +            float vz;
 +            vector wish_angle, real_angle;
 +
 +            vz = self.velocity_z;
 +
 +            self.angles_x = anglemods(self.angles_x);
 +            self.angles_y = anglemods(self.angles_y);
 +
 +            fixedmakevectors(self.angles);
 +
 +            wish_angle = normalize(self.steerto);
 +            wish_angle = vectoangles(wish_angle);
 +            real_angle = wish_angle - self.angles;
 +            real_angle = shortangle_vxy(real_angle, self.tur_head.angles);
 +
 +            self.tur_head.spawnshieldtime = fabs(real_angle_y);
 +            real_angle_y  = bound(-self.tur_head.aim_speed, real_angle_y, self.tur_head.aim_speed);
 +            self.angles_y = (self.angles_y + real_angle_y);
 +
 +            if(self.enemy)
 +                ewheel_move_enemy();
 +            else if(self.pathcurrent)
 +                ewheel_move_path();
 +            else
 +                ewheel_move_idle();
 +
 +            self.velocity_z = vz;
 +
 +            if(vlen(self.velocity))
 +                self.SendFlags |= TNSF_MOVE;
 +        }
 +        METHOD(EWheel, tr_death, void(EWheel this, entity it))
 +        {
 +            it.velocity = '0 0 0';
 +
 +#ifdef EWHEEL_FANCYPATH
 +            if (self.pathcurrent)
 +                pathlib_deletepath(it.pathcurrent.owner);
 +#endif
 +            it.pathcurrent = NULL;
 +        }
 +        METHOD(EWheel, tr_setup, void(EWheel this, entity it))
 +        {
 +            entity e;
 +
 +            if(it.movetype == MOVETYPE_WALK)
 +            {
 +                it.velocity = '0 0 0';
 +                it.enemy = world;
 +
 +                setorigin(it, it.pos1);
 +
 +                if (it.target != "")
 +                {
 +                    e = find(world, targetname, it.target);
 +                    if (!e)
 +                    {
 +                        LOG_TRACE("Initital waypoint for ewheel does NOT exsist, fix your map!\n");
 +                        it.target = "";
 +                    }
 +
 +                    if (e.classname != "turret_checkpoint")
 +                        LOG_TRACE("Warning: not a turrret path\n");
 +                    else
 +                    {
 +
 +#ifdef EWHEEL_FANCYPATH
 +                        it.pathcurrent = WALKER_PATH(it.origin,e.origin);
 +                        it.pathgoal = e;
 +#else
 +                        it.pathcurrent  = e;
 +#endif
 +                    }
 +                }
 +            }
 +
 +            it.iscreature                             = true;
 +            it.teleportable                   = TELEPORT_NORMAL;
 +            it.damagedbycontents              = true;
 +            it.movetype                               = MOVETYPE_WALK;
 +            it.solid                                  = SOLID_SLIDEBOX;
 +            it.takedamage                             = DAMAGE_AIM;
 +            it.idle_aim                               = '0 0 0';
 +            it.pos1                                   = it.origin;
 +            it.target_select_flags    = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
 +            it.target_validate_flags  = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
 +            it.frame                                  = it.tur_head.frame = 1;
 +            it.ammo_flags                             = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
 +
 +            // Convert from dgr / sec to dgr / tic
 +            it.tur_head.aim_speed = (autocvar_g_turrets_unit_ewheel_turnrate);
 +            it.tur_head.aim_speed = it.tur_head.aim_speed / (1 / it.ticrate);
 +        }
 +
 +#endif // SVQC
 +#ifdef CSQC
 +
 +void ewheel_draw()
 +{SELFPARAM();
 +    float dt;
 +
 +    dt = time - self.move_time;
 +    self.move_time = time;
 +    if(dt <= 0)
 +        return;
 +
 +    fixedmakevectors(self.angles);
 +    setorigin(self, self.origin + self.velocity * dt);
 +    self.tur_head.angles += dt * self.tur_head.move_avelocity;
 +    self.angles_y = self.move_angles_y;
 +
 +    if (self.health < 127)
 +    if(random() < 0.05)
 +        te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16);
 +}
 +
 +        METHOD(EWheel, tr_setup, void(EWheel this, entity it))
 +        {
 +            it.gravity                = 1;
 +            it.movetype               = MOVETYPE_BOUNCE;
 +            it.move_movetype  = MOVETYPE_BOUNCE;
 +            it.move_origin    = it.origin;
 +            it.move_time              = time;
 +            it.draw                   = ewheel_draw;
 +        }
 +
 +#endif // CSQC
 +#endif
index a2de5cac4bb7289fe05eb8f341100f9528329c16,0000000000000000000000000000000000000000..1c9122f6733670c3841336c03a0693e760ace6d5
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,39 @@@
- void spawnfunc_turret_flac() { SELFPARAM(); if (!turret_initialize(TUR_FLAC)) remove(self); }
 +#ifndef TURRET_FLAC_H
 +#define TURRET_FLAC_H
 +
 +#include "flac_weapon.qc"
 +
 +CLASS(Flac, Turret)
 +/* spawnflags */ ATTRIB(Flac, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE);
 +/* mins       */ ATTRIB(Flac, mins, vector, '-32 -32 0');
 +/* maxs       */ ATTRIB(Flac, maxs, vector, '32 32 64');
 +/* modelname  */ ATTRIB(Flac, mdl, string, "base.md3");
 +/* model      */ ATTRIB(Flac, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(Flac, head_model, string, strzone(strcat("models/turrets/", "flac.md3")));
 +/* netname    */ ATTRIB(Flac, netname, string, "flac");
 +/* fullname   */ ATTRIB(Flac, turret_name, string, _("FLAC Cannon"));
 +    ATTRIB(Flac, m_weapon, Weapon, WEP_FLAC);
 +ENDCLASS(Flac)
 +REGISTER_TURRET(FLAC, NEW(Flac));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "flac_weapon.qc"
 +
 +#ifdef SVQC
 +
++spawnfunc(turret_flac) { if (!turret_initialize(TUR_FLAC)) remove(self); }
 +
 +METHOD(Flac, tr_setup, void(Flac this, entity it))
 +{
 +    it.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
 +    it.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
 +    it.damage_flags |= TFL_DMG_HEADSHAKE;
 +    it.target_select_flags |= TFL_TARGETSELECT_NOTURRETS | TFL_TARGETSELECT_MISSILESONLY;
 +}
 +
 +#endif
 +
 +#endif
index fcd6113779d986a66184d11834e7863a53c533f9,0000000000000000000000000000000000000000..1077d5c635bf7848dc34af3f2765aa0fda130877
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,77 @@@
- void spawnfunc_turret_fusionreactor() { SELFPARAM(); if (!turret_initialize(TUR_FUSIONREACTOR)) remove(self); }
 +#ifndef TURRET_FUSIONREACTOR_H
 +#define TURRET_FUSIONREACTOR_H
 +
 +CLASS(FusionReactor, Turret)
 +/* spawnflags */ ATTRIB(FusionReactor, spawnflags, int, TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE);
 +/* mins       */ ATTRIB(FusionReactor, mins, vector, '-34 -34 0');
 +/* maxs       */ ATTRIB(FusionReactor, maxs, vector, '34 34 90');
 +/* modelname  */ ATTRIB(FusionReactor, mdl, string, "base.md3");
 +/* model      */ ATTRIB(FusionReactor, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(FusionReactor, head_model, string, strzone(strcat("models/turrets/", "reactor.md3")));
 +/* netname    */ ATTRIB(FusionReactor, netname, string, "fusionreactor");
 +/* fullname   */ ATTRIB(FusionReactor, turret_name, string, _("Fusion Reactor"));
 +ENDCLASS(FusionReactor)
 +REGISTER_TURRET(FUSIONREACTOR, NEW(FusionReactor));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +#ifdef SVQC
 +bool turret_fusionreactor_firecheck()
 +{SELFPARAM();
 +    if (self.attack_finished_single > time)
 +        return false;
 +
 +    if (self.enemy.deadflag != DEAD_NO)
 +        return false;
 +
 +    if (self.enemy == world)
 +        return false;
 +
 +    if (self.ammo < self.shot_dmg)
 +        return false;
 +
 +    if (self.enemy.ammo >= self.enemy.ammo_max)
 +        return false;
 +
 +    if (vlen(self.enemy.origin - self.origin) > self.target_range)
 +        return false;
 +
 +    if(self.team != self.enemy.team)
 +        return false;
 +
 +    if(!(self.enemy.ammo_flags & TFL_AMMO_ENERGY))
 +        return false;
 +
 +    return true;
 +}
 +
++spawnfunc(turret_fusionreactor) { if (!turret_initialize(TUR_FUSIONREACTOR)) remove(self); }
 +
 +METHOD(FusionReactor, tr_attack, void(FusionReactor this))
 +{
 +    self.enemy.ammo = min(self.enemy.ammo + self.shot_dmg,self.enemy.ammo_max);
 +    vector fl_org = 0.5 * (self.enemy.absmin + self.enemy.absmax);
 +    te_smallflash(fl_org);
 +}
 +METHOD(FusionReactor, tr_think, void(FusionReactor thistur))
 +{
 +    self.tur_head.avelocity = '0 250 0' * (self.ammo / self.ammo_max);
 +}
 +METHOD(FusionReactor, tr_setup, void(FusionReactor this, entity it))
 +{
 +    it.ammo_flags                             = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE;
 +    it.target_select_flags    = TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_OWNTEAM | TFL_TARGETSELECT_RANGELIMITS;
 +    it.firecheck_flags                = TFL_FIRECHECK_AMMO_OWN | TFL_FIRECHECK_AMMO_OTHER | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_DEAD;
 +    it.shoot_flags                    = TFL_SHOOT_HITALLVALID;
 +    it.aim_flags                              = TFL_AIM_NO;
 +    it.track_flags                    = TFL_TRACK_NO;
 +
 +    it.tur_head.scale = 0.75;
 +    it.tur_head.avelocity = '0 50 0';
 +
 +    it.turret_firecheckfunc = turret_fusionreactor_firecheck;
 +}
 +
 +#endif
 +#endif
index f2c6f7fc914061d214d86adfe5d6d17fd07d3b55,0000000000000000000000000000000000000000..203be93dbe20cd86c0c7afc54137f7f958d7bfae
mode 100644,000000..100644
--- /dev/null
@@@ -1,46 -1,0 +1,46 @@@
- void spawnfunc_turret_hellion() { SELFPARAM(); if (!turret_initialize(TUR_HELLION)) remove(self); }
 +#ifndef TURRET_HELLION_H
 +#define TURRET_HELLION_H
 +
 +#include "hellion_weapon.qc"
 +
 +CLASS(Hellion, Turret)
 +/* spawnflags */ ATTRIB(Hellion, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE);
 +/* mins       */ ATTRIB(Hellion, mins, vector, '-32 -32 0');
 +/* maxs       */ ATTRIB(Hellion, maxs, vector, '32 32 64');
 +/* modelname  */ ATTRIB(Hellion, mdl, string, "base.md3");
 +/* model      */ ATTRIB(Hellion, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(Hellion, head_model, string, strzone(strcat("models/turrets/", "hellion.md3")));
 +/* netname    */ ATTRIB(Hellion, netname, string, "hellion");
 +/* fullname   */ ATTRIB(Hellion, turret_name, string, _("Hellion Missile Turret"));
 +    ATTRIB(Hellion, m_weapon, Weapon, WEP_HELLION);
 +ENDCLASS(Hellion)
 +REGISTER_TURRET(HELLION, NEW(Hellion));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "hellion_weapon.qc"
 +
 +#ifdef SVQC
 +
++spawnfunc(turret_hellion) { if (!turret_initialize(TUR_HELLION)) remove(self); }
 +
 +METHOD(Hellion, tr_think, void(Hellion thistur))
 +{
 +    if (self.tur_head.frame != 0)
 +        self.tur_head.frame += 1;
 +
 +    if (self.tur_head.frame >= 7)
 +        self.tur_head.frame = 0;
 +}
 +METHOD(Hellion, tr_setup, void(Hellion this, entity it))
 +{
 +    it.aim_flags = TFL_AIM_SIMPLE;
 +    it.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK ;
 +    it.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF | TFL_FIRECHECK_AMMO_OWN;
 +    it.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
 +}
 +
 +#endif
 +#endif
index 0d7dc6dd0af8103632f35d0741e1ba1e838f2778,0000000000000000000000000000000000000000..61d8318db89eb29a12bb47ee393f01b17af74d19
mode 100644,000000..100644
--- /dev/null
@@@ -1,72 -1,0 +1,72 @@@
- void spawnfunc_turret_hk() { SELFPARAM(); if(!turret_initialize(TUR_HK)) remove(self); }
 +#ifndef TURRET_HK_H
 +#define TURRET_HK_H
 +
 +//#define TURRET_DEBUG_HK
 +
 +#include "hk_weapon.qc"
 +
 +CLASS(HunterKiller, Turret)
 +/* spawnflags */ ATTRIB(HunterKiller, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS);
 +/* mins       */ ATTRIB(HunterKiller, mins, vector, '-32 -32 0');
 +/* maxs       */ ATTRIB(HunterKiller, maxs, vector, '32 32 64');
 +/* modelname  */ ATTRIB(HunterKiller, mdl, string, "base.md3");
 +/* model      */ ATTRIB(HunterKiller, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(HunterKiller, head_model, string, strzone(strcat("models/turrets/", "hk.md3")));
 +/* netname    */ ATTRIB(HunterKiller, netname, string, "hk");
 +/* fullname   */ ATTRIB(HunterKiller, turret_name, string, _("Hunter-Killer Turret"));
 +    ATTRIB(HunterKiller, m_weapon, Weapon, WEP_HK);
 +ENDCLASS(HunterKiller)
 +REGISTER_TURRET(HK, NEW(HunterKiller));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "hk_weapon.qc"
 +
 +#ifdef SVQC
 +
 +#ifdef TURRET_DEBUG_HK
 +.float atime;
 +#endif
 +
++spawnfunc(turret_hk) { if(!turret_initialize(TUR_HK)) remove(self); }
 +
 +METHOD(HunterKiller, tr_think, void(HunterKiller thistur))
 +{
 +    if (self.tur_head.frame != 0)
 +        self.tur_head.frame = self.tur_head.frame + 1;
 +
 +    if (self.tur_head.frame > 5)
 +        self.tur_head.frame = 0;
 +}
 +
 +float turret_hk_addtarget(entity e_target,entity e_sender);
 +METHOD(HunterKiller, tr_setup, void(HunterKiller this, entity it))
 +{
 +    it.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
 +    it.aim_flags = TFL_AIM_SIMPLE;
 +    it.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
 +    it.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCHECK  | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF;
 +    it.shoot_flags = TFL_SHOOT_CLEARTARGET;
 +    it.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK;
 +
 +    it.turret_addtarget = turret_hk_addtarget;
 +}
 +
 +float turret_hk_addtarget(entity e_target,entity e_sender)
 +{SELFPARAM();
 +    if (e_target)
 +    {
 +        if (turret_validate_target(self,e_target,self.target_validate_flags) > 0)
 +        {
 +            self.enemy = e_target;
 +            return 1;
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +#endif // SVQC
 +#endif
index 8fc1f4251c78f9b1f6096f7afc27c2f4fe4840c5,0000000000000000000000000000000000000000..9911b97c6e21d86311b3e4d416607ec0c0a41630
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,39 @@@
- void spawnfunc_turret_machinegun() { SELFPARAM(); if (!turret_initialize(TUR_MACHINEGUN)) remove(self); }
 +#ifndef TURRET_MACHINEGUN_H
 +#define TURRET_MACHINEGUN_H
 +
 +#include "machinegun_weapon.qc"
 +
 +CLASS(MachineGunTurret, Turret)
 +/* spawnflags */ ATTRIB(MachineGunTurret, spawnflags, int, TUR_FLAG_PLAYER);
 +/* mins       */ ATTRIB(MachineGunTurret, mins, vector, '-32 -32 0');
 +/* maxs       */ ATTRIB(MachineGunTurret, maxs, vector, '32 32 64');
 +/* modelname  */ ATTRIB(MachineGunTurret, mdl, string, "base.md3");
 +/* model      */ ATTRIB(MachineGunTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(MachineGunTurret, head_model, string, strzone(strcat("models/turrets/", "machinegun.md3")));
 +/* netname    */ ATTRIB(MachineGunTurret, netname, string, "machinegun");
 +/* fullname   */ ATTRIB(MachineGunTurret, turret_name, string, _("Machinegun Turret"));
 +    ATTRIB(MachineGunTurret, m_weapon, Weapon, WEP_TUR_MACHINEGUN);
 +ENDCLASS(MachineGunTurret)
 +REGISTER_TURRET(MACHINEGUN, NEW(MachineGunTurret));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "machinegun_weapon.qc"
 +
 +#ifdef SVQC
 +
++spawnfunc(turret_machinegun) { if (!turret_initialize(TUR_MACHINEGUN)) remove(self); }
 +
 +METHOD(MachineGunTurret, tr_setup, void(MachineGunTurret this, entity it))
 +{
 +    it.damage_flags |= TFL_DMG_HEADSHAKE;
 +    it.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
 +    it.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
 +    it.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
 +    it.turret_flags |= TUR_FLAG_HITSCAN;
 +}
 +
 +#endif // SVQC
 +#endif
index 7920c1a02d3da2a25130cd79cc0335579abb122f,0000000000000000000000000000000000000000..ae2d1d47ccc2a9753b43f260c19c5a6f5e2ef81b
mode 100644,000000..100644
--- /dev/null
@@@ -1,50 -1,0 +1,50 @@@
- void spawnfunc_turret_mlrs() { SELFPARAM(); if (!turret_initialize(TUR_MLRS)) remove(self); }
 +#ifndef TURRET_MLRS_H
 +#define TURRET_MLRS_H
 +
 +#include "mlrs_weapon.qc"
 +
 +CLASS(MLRSTurret, Turret)
 +/* spawnflags */ ATTRIB(MLRSTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
 +/* mins       */ ATTRIB(MLRSTurret, mins, vector, '-32 -32 0');
 +/* maxs       */ ATTRIB(MLRSTurret, maxs, vector, '32 32 64');
 +/* modelname  */ ATTRIB(MLRSTurret, mdl, string, "base.md3");
 +/* model      */ ATTRIB(MLRSTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(MLRSTurret, head_model, string, strzone(strcat("models/turrets/", "mlrs.md3")));
 +/* netname    */ ATTRIB(MLRSTurret, netname, string, "mlrs");
 +/* fullname   */ ATTRIB(MLRSTurret, turret_name, string, _("MLRS Turret"));
 +    ATTRIB(MLRSTurret, m_weapon, Weapon, WEP_TUR_MLRS);
 +ENDCLASS(MLRSTurret)
 +REGISTER_TURRET(MLRS, NEW(MLRSTurret));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "mlrs_weapon.qc"
 +
 +#ifdef SVQC
 +
++spawnfunc(turret_mlrs) { if (!turret_initialize(TUR_MLRS)) remove(self); }
 +
 +METHOD(MLRSTurret, tr_think, void(MLRSTurret thistur))
 +{
 +    // 0 = full, 6 = empty
 +    self.tur_head.frame = bound(0, 6 - floor(0.1 + self.ammo / self.shot_dmg), 6);
 +    if(self.tur_head.frame < 0)
 +    {
 +        LOG_TRACE("ammo:",ftos(self.ammo),"\n");
 +        LOG_TRACE("shot_dmg:",ftos(self.shot_dmg),"\n");
 +    }
 +}
 +METHOD(MLRSTurret, tr_setup, void(MLRSTurret this, entity it))
 +{
 +    it.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
 +    it.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
 +
 +    it.damage_flags |= TFL_DMG_HEADSHAKE;
 +    it.shoot_flags  |= TFL_SHOOT_VOLLYALWAYS;
 +    it.volly_counter = it.shot_volly;
 +}
 +
 +#endif // SVQC
 +#endif
index 7fcb0131f6fa7f41e3697be632aeccb5e9e61180,0000000000000000000000000000000000000000..24d4d30c59791f777b8a8421c5d951246c08e7c0
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,67 @@@
- void spawnfunc_turret_phaser() { SELFPARAM(); if (!turret_initialize(TUR_PHASER)) remove(self); }
 +#ifndef TURRET_PHASER_H
 +#define TURRET_PHASER_H
 +
 +#include "phaser_weapon.qc"
 +
 +CLASS(PhaserTurret, Turret)
 +/* spawnflags */ ATTRIB(PhaserTurret, spawnflags, int, TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER);
 +/* mins       */ ATTRIB(PhaserTurret, mins, vector, '-32 -32 0');
 +/* maxs       */ ATTRIB(PhaserTurret, maxs, vector, '32 32 64');
 +/* modelname  */ ATTRIB(PhaserTurret, mdl, string, "base.md3");
 +/* model      */ ATTRIB(PhaserTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(PhaserTurret, head_model, string, strzone(strcat("models/turrets/", "phaser.md3")));
 +/* netname    */ ATTRIB(PhaserTurret, netname, string, "phaser");
 +/* fullname   */ ATTRIB(PhaserTurret, turret_name, string, _("Phaser Cannon"));
 +    ATTRIB(PhaserTurret, m_weapon, Weapon, WEP_PHASER);
 +ENDCLASS(PhaserTurret)
 +REGISTER_TURRET(PHASER, NEW(PhaserTurret));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "phaser_weapon.qc"
 +
 +#ifdef SVQC
 +
++spawnfunc(turret_phaser) { if (!turret_initialize(TUR_PHASER)) remove(self); }
 +
 +METHOD(PhaserTurret, tr_think, void(PhaserTurret thistur))
 +{
 +    if (self.tur_head.frame != 0)
 +    {
 +        if (self.fireflag == 1)
 +        {
 +            if (self.tur_head.frame == 10)
 +                self.tur_head.frame = 1;
 +            else
 +                self.tur_head.frame = self.tur_head.frame +1;
 +        }
 +        else if (self.fireflag == 2 )
 +        {
 +            self.tur_head.frame = self.tur_head.frame +1;
 +            if (self.tur_head.frame == 15)
 +            {
 +                self.tur_head.frame = 0;
 +                self.fireflag = 0;
 +            }
 +        }
 +    }
 +}
 +float turret_phaser_firecheck();
 +METHOD(PhaserTurret, tr_setup, void(PhaserTurret this, entity it))
 +{
 +    it.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
 +    it.aim_flags = TFL_AIM_LEAD;
 +
 +    it.turret_firecheckfunc = turret_phaser_firecheck;
 +}
 +float turret_phaser_firecheck()
 +{
 +    SELFPARAM();
 +    if (self.fireflag != 0) return 0;
 +    return turret_firecheck();
 +}
 +
 +#endif
 +#endif
index 04f81cbc0d2851db5c15042312bf90111f990e0b,0000000000000000000000000000000000000000..7c40299aa3a55ea139aedec8bc23b721384dbaf1
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,68 @@@
- void spawnfunc_turret_plasma() { SELFPARAM(); if (!turret_initialize(TUR_PLASMA)) remove(self); }
 +#ifndef TURRET_PLASMA_H
 +#define TURRET_PLASMA_H
 +
 +#include "plasma_weapon.qc"
 +
 +CLASS(PlasmaTurret, Turret)
 +/* spawnflags */ ATTRIB(PlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
 +/* mins       */ ATTRIB(PlasmaTurret, mins, vector, '-32 -32 0');
 +/* maxs       */ ATTRIB(PlasmaTurret, maxs, vector, '32 32 64');
 +/* modelname  */ ATTRIB(PlasmaTurret, mdl, string, "base.md3");
 +/* model      */ ATTRIB(PlasmaTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(PlasmaTurret, head_model, string, strzone(strcat("models/turrets/", "plasma.md3")));
 +/* netname    */ ATTRIB(PlasmaTurret, netname, string, "plasma");
 +/* fullname   */ ATTRIB(PlasmaTurret, turret_name, string, _("Plasma Cannon"));
 +    ATTRIB(PlasmaTurret, m_weapon, Weapon, WEP_PLASMA);
 +ENDCLASS(PlasmaTurret)
 +REGISTER_TURRET(PLASMA, NEW(PlasmaTurret));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "plasma_weapon.qc"
 +
 +#ifdef SVQC
 +
++spawnfunc(turret_plasma) { if (!turret_initialize(TUR_PLASMA)) remove(self); }
 +
 +METHOD(PlasmaTurret, tr_attack, void(PlasmaTurret this))
 +{
 +    if(g_instagib)
 +    {
 +        FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000,
 +                           800, 0, 0, 0, 0, DEATH_TURRET_PLASMA);
 +
 +        Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
 +
 +        // teamcolor / hit beam effect
 +        vector v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
 +        WarpZone_TrailParticles(world, particleeffectnum(EFFECT_VAPORIZER(self.team)), self.tur_shotorg, v);
 +    }
 +    else
 +    {
 +        super.tr_attack(this);
 +    }
 +    if (self.tur_head.frame == 0)
 +        self.tur_head.frame = 1;
 +}
 +METHOD(PlasmaTurret, tr_think, void(PlasmaTurret thistur))
 +{
 +    if (self.tur_head.frame != 0)
 +        self.tur_head.frame = self.tur_head.frame + 1;
 +
 +    if (self.tur_head.frame > 5)
 +        self.tur_head.frame = 0;
 +}
 +METHOD(PlasmaTurret, tr_setup, void(PlasmaTurret this, entity it))
 +{
 +    it.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
 +    it.damage_flags |= TFL_DMG_HEADSHAKE;
 +    it.firecheck_flags |= TFL_FIRECHECK_AFF;
 +    it.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_SPLASH;
 +
 +    turret_do_updates(it);
 +}
 +
 +#endif
 +#endif
index faac00d26dce98cfb77b7cbe21e9503e26c6465f,0000000000000000000000000000000000000000..e1143bc04803ea6877599567d76ad94e97e3ffcb
mode 100644,000000..100644
--- /dev/null
@@@ -1,58 -1,0 +1,58 @@@
- void spawnfunc_turret_plasma_dual() { SELFPARAM(); if (!turret_initialize(TUR_PLASMA_DUAL)) remove(self); }
 +#ifndef TURRET_PLASMA_DUAL_H
 +#define TURRET_PLASMA_DUAL_H
 +
 +CLASS(PlasmaDualAttack, PlasmaAttack)
 +/* refname   */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual");
 +/* wepname   */ ATTRIB(PlasmaDualAttack, message, string, _("Dual plasma"));
 +ENDCLASS(PlasmaDualAttack)
 +REGISTER_WEAPON(PLASMA_DUAL, NEW(PlasmaDualAttack));
 +
 +CLASS(DualPlasmaTurret, PlasmaTurret)
 +/* spawnflags */ ATTRIB(DualPlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER);
 +/* mins       */ ATTRIB(DualPlasmaTurret, mins, vector, '-32 -32 0');
 +/* maxs       */ ATTRIB(DualPlasmaTurret, maxs, vector, '32 32 64');
 +/* modelname  */ ATTRIB(DualPlasmaTurret, mdl, string, "base.md3");
 +/* model      */ ATTRIB(DualPlasmaTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(DualPlasmaTurret, head_model, string, strzone(strcat("models/turrets/", "plasmad.md3")));
 +/* netname    */ ATTRIB(DualPlasmaTurret, netname, string, "plasma_dual");
 +/* fullname   */ ATTRIB(DualPlasmaTurret, turret_name, string, _("Dual Plasma Cannon"));
 +    ATTRIB(DualPlasmaTurret, m_weapon, Weapon, WEP_PLASMA_DUAL);
 +ENDCLASS(DualPlasmaTurret)
 +REGISTER_TURRET(PLASMA_DUAL, NEW(DualPlasmaTurret));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#ifdef SVQC
 +
++spawnfunc(turret_plasma_dual) { if (!turret_initialize(TUR_PLASMA_DUAL)) remove(self); }
 +
 +METHOD(DualPlasmaTurret, tr_attack, void(DualPlasmaTurret this))
 +{
 +    if (g_instagib) {
 +        FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000,
 +                           800, 0, 0, 0, 0, DEATH_TURRET_PLASMA);
 +
 +
 +        Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
 +
 +        // teamcolor / hit beam effect
 +        vector v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
 +        WarpZone_TrailParticles(world, particleeffectnum(EFFECT_VAPORIZER(self.team)), self.tur_shotorg, v);
 +    } else {
 +        super.vtblbase.tr_attack(this);
 +    }
 +    self.tur_head.frame += 1;
 +}
 +METHOD(DualPlasmaTurret, tr_think, void(DualPlasmaTurret thistur))
 +{
 +    if ((self.tur_head.frame != 0) && (self.tur_head.frame != 3))
 +        self.tur_head.frame = self.tur_head.frame + 1;
 +
 +    if (self.tur_head.frame > 6)
 +        self.tur_head.frame = 0;
 +}
 +
 +#endif
 +#endif
index 65733a8cbe72405fec3418d9228469fa8b46f268,0000000000000000000000000000000000000000..3ae039c3ccbb3f1f6cf00f65c6787565a00dff20
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,111 @@@
- void spawnfunc_turret_tesla() { SELFPARAM(); if (!turret_initialize(TUR_TESLA)) remove(self); }
 +#ifndef TURRET_TESLA_H
 +#define TURRET_TESLA_H
 +
 +#include "tesla_weapon.qc"
 +
 +CLASS(TeslaCoil, Turret)
 +/* spawnflags */ ATTRIB(TeslaCoil, spawnflags, int, TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE);
 +/* mins       */ ATTRIB(TeslaCoil, mins, vector, '-60 -60 0');
 +/* maxs       */ ATTRIB(TeslaCoil, maxs, vector, '60 60 128');
 +/* modelname  */ ATTRIB(TeslaCoil, mdl, string, "tesla_base.md3");
 +/* model      */ ATTRIB(TeslaCoil, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(TeslaCoil, head_model, string, strzone(strcat("models/turrets/", "tesla_head.md3")));
 +/* netname    */ ATTRIB(TeslaCoil, netname, string, "tesla");
 +/* fullname   */ ATTRIB(TeslaCoil, turret_name, string, _("Tesla Coil"));
 +    ATTRIB(TeslaCoil, m_weapon, Weapon, WEP_TESLA);
 +ENDCLASS(TeslaCoil)
 +REGISTER_TURRET(TESLA, NEW(TeslaCoil));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "tesla_weapon.qc"
 +
 +#ifdef SVQC
 +
++spawnfunc(turret_tesla) { if (!turret_initialize(TUR_TESLA)) remove(self); }
 +
 +METHOD(TeslaCoil, tr_think, void(TeslaCoil thistur))
 +{
 +    if(!self.active)
 +    {
 +        self.tur_head.avelocity = '0 0 0';
 +        return;
 +    }
 +
 +    if(self.ammo < self.shot_dmg)
 +    {
 +        self.tur_head.avelocity = '0 45 0' * (self.ammo / self.shot_dmg);
 +    }
 +    else
 +    {
 +        self.tur_head.avelocity = '0 180 0' * (self.ammo / self.shot_dmg);
 +
 +        if(self.attack_finished_single > time)
 +            return;
 +
 +        float f;
 +        f = (self.ammo / self.ammo_max);
 +        f = f * f;
 +        if(f > random())
 +            if(random() < 0.1)
 +                te_csqc_lightningarc(self.tur_shotorg,self.tur_shotorg + (randomvec() * 350));
 +    }
 +}
 +
 +float turret_tesla_firecheck();
 +METHOD(TeslaCoil, tr_setup, void(TeslaCoil this, entity it))
 +{
 +    it.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
 +                         TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
 +
 +    it.turret_firecheckfunc = turret_tesla_firecheck;
 +    it.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
 +                       TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
 +
 +    it.firecheck_flags        = TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AMMO_OWN;
 +    it.shoot_flags            = TFL_SHOOT_CUSTOM;
 +    it.ammo_flags                     = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
 +    it.aim_flags                      = TFL_AIM_NO;
 +    it.track_flags            = TFL_TRACK_NO;
 +}
 +
 +float turret_tesla_firecheck()
 +{SELFPARAM();
 +    // g_turrets_targetscan_maxdelay forces a target re-scan at least this often
 +    float do_target_scan = 0;
 +
 +    if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time)
 +        do_target_scan = 1;
 +
 +    // Old target (if any) invalid?
 +    if(self.target_validate_time < time)
 +    if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0)
 +    {
 +        self.enemy = world;
 +        self.target_validate_time = time + 0.5;
 +        do_target_scan = 1;
 +    }
 +
 +    // But never more often then g_turrets_targetscan_mindelay!
 +    if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time)
 +        do_target_scan = 0;
 +
 +    if(do_target_scan)
 +    {
 +        self.enemy = turret_select_target();
 +        self.target_select_time = time;
 +    }
 +
 +    if(!turret_firecheck())
 +        return 0;
 +
 +    if(self.enemy)
 +        return 1;
 +
 +    return 0;
 +}
 +
 +#endif
 +#endif
index 49e93c09d0ae49de24921ad644d7736fa5a7d64d,0000000000000000000000000000000000000000..1feace46ae2e5e404702283e72ff4b263932cb1e
mode 100644,000000..100644
--- /dev/null
@@@ -1,667 -1,0 +1,667 @@@
- void spawnfunc_turret_walker() { SELFPARAM(); if(!turret_initialize(TUR_WALKER)) remove(self); }
 +#ifndef TURRET_WALKER_H
 +#define TURRET_WALKER_H
 +
 +//#define WALKER_FANCYPATHING
 +
 +#include "walker_weapon.qc"
 +
 +CLASS(WalkerTurret, Turret)
 +/* spawnflags */ ATTRIB(WalkerTurret, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE);
 +/* mins       */ ATTRIB(WalkerTurret, mins, vector, '-70 -70 0');
 +/* maxs       */ ATTRIB(WalkerTurret, maxs, vector, '70 70 95');
 +/* modelname  */ ATTRIB(WalkerTurret, mdl, string, "walker_body.md3");
 +/* model      */ ATTRIB(WalkerTurret, model, string, strzone(strcat("models/turrets/", this.mdl)));
 +/* head_model */ ATTRIB(WalkerTurret, head_model, string, strzone(strcat("models/turrets/", "walker_head_minigun.md3")));
 +/* netname    */ ATTRIB(WalkerTurret, netname, string, "walker");
 +/* fullname   */ ATTRIB(WalkerTurret, turret_name, string, _("Walker Turret"));
 +    ATTRIB(WalkerTurret, m_weapon, Weapon, WEP_WALKER);
 +ENDCLASS(WalkerTurret)
 +REGISTER_TURRET(WALKER, NEW(WalkerTurret));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "walker_weapon.qc"
 +
 +#ifdef SVQC
 +
 +float autocvar_g_turrets_unit_walker_melee_damage;
 +float autocvar_g_turrets_unit_walker_melee_force;
 +float autocvar_g_turrets_unit_walker_melee_range;
 +float autocvar_g_turrets_unit_walker_rocket_damage;
 +float autocvar_g_turrets_unit_walker_rocket_radius;
 +float autocvar_g_turrets_unit_walker_rocket_force;
 +float autocvar_g_turrets_unit_walker_rocket_speed;
 +float autocvar_g_turrets_unit_walker_rocket_range;
 +float autocvar_g_turrets_unit_walker_rocket_range_min;
 +float autocvar_g_turrets_unit_walker_rocket_refire;
 +float autocvar_g_turrets_unit_walker_rocket_turnrate;
 +float autocvar_g_turrets_unit_walker_speed_stop;
 +float autocvar_g_turrets_unit_walker_speed_walk;
 +float autocvar_g_turrets_unit_walker_speed_run;
 +float autocvar_g_turrets_unit_walker_speed_jump;
 +float autocvar_g_turrets_unit_walker_speed_swim;
 +float autocvar_g_turrets_unit_walker_speed_roam;
 +float autocvar_g_turrets_unit_walker_turn;
 +float autocvar_g_turrets_unit_walker_turn_walk;
 +float autocvar_g_turrets_unit_walker_turn_strafe;
 +float autocvar_g_turrets_unit_walker_turn_swim;
 +float autocvar_g_turrets_unit_walker_turn_run;
 +
 +const int ANIM_NO         = 0;
 +const int ANIM_TURN       = 1;
 +const int ANIM_WALK       = 2;
 +const int ANIM_RUN        = 3;
 +const int ANIM_STRAFE_L   = 4;
 +const int ANIM_STRAFE_R   = 5;
 +const int ANIM_JUMP       = 6;
 +const int ANIM_LAND       = 7;
 +const int ANIM_PAIN       = 8;
 +const int ANIM_MELEE      = 9;
 +const int ANIM_SWIM       = 10;
 +const int ANIM_ROAM       = 11;
 +
 +.float animflag;
 +.float idletime;
 +
 +#define WALKER_PATH(s,e) pathlib_astar(s,e)
 +
 +float walker_firecheck()
 +{SELFPARAM();
 +    if (self.animflag == ANIM_MELEE)
 +        return 0;
 +
 +    return turret_firecheck();
 +}
 +
 +void walker_melee_do_dmg()
 +{SELFPARAM();
 +    vector where;
 +    entity e;
 +
 +    makevectors(self.angles);
 +    where = self.origin + v_forward * 128;
 +
 +    e = findradius(where,32);
 +    while (e)
 +    {
 +        if (turret_validate_target(self, e, self.target_validate_flags))
 +            if (e != self && e.owner != self)
 +                Damage(e, self, self, (autocvar_g_turrets_unit_walker_melee_damage), DEATH_TURRET_WALK_MELEE, '0 0 0', v_forward * (autocvar_g_turrets_unit_walker_melee_force));
 +
 +        e = e.chain;
 +    }
 +}
 +
 +void walker_setnoanim()
 +{SELFPARAM();
 +    turrets_setframe(ANIM_NO, false);
 +    self.animflag = self.frame;
 +}
 +void walker_rocket_explode()
 +{SELFPARAM();
 +    RadiusDamage (self, self.owner, (autocvar_g_turrets_unit_walker_rocket_damage), 0, (autocvar_g_turrets_unit_walker_rocket_radius), self, world, (autocvar_g_turrets_unit_walker_rocket_force), DEATH_TURRET_WALK_ROCKET, world);
 +    remove (self);
 +}
 +
 +void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
 +{SELFPARAM();
 +    self.health = self.health - damage;
 +    self.velocity = self.velocity + vforce;
 +
 +    if (self.health <= 0)
 +        W_PrepareExplosionByDamage(self.owner, walker_rocket_explode);
 +}
 +
 +#define WALKER_ROCKET_MOVE movelib_move_simple(newdir, (autocvar_g_turrets_unit_walker_rocket_speed), (autocvar_g_turrets_unit_walker_rocket_turnrate)); UpdateCSQCProjectile(self)
 +void walker_rocket_loop();
 +void walker_rocket_think()
 +{SELFPARAM();
 +    vector newdir;
 +    float edist;
 +    float itime;
 +    float m_speed;
 +
 +    self.nextthink = time;
 +
 +    edist = vlen(self.enemy.origin - self.origin);
 +
 +    // Simulate crude guidance
 +    if (self.cnt < time)
 +    {
 +        if (edist < 1000)
 +            self.tur_shotorg = randomvec() * min(edist, 64);
 +        else
 +            self.tur_shotorg = randomvec() * min(edist, 256);
 +
 +        self.cnt = time + 0.5;
 +    }
 +
 +    if (edist < 128)
 +        self.tur_shotorg = '0 0 0';
 +
 +    if (self.max_health < time)
 +    {
 +        self.think      = walker_rocket_explode;
 +        self.nextthink  = time;
 +        return;
 +    }
 +
 +    if (self.shot_dmg != 1337 && random() < 0.01)
 +    {
 +        walker_rocket_loop();
 +        return;
 +    }
 +
 +    m_speed = vlen(self.velocity);
 +
 +    // Enemy dead? just keep on the current heading then.
 +    if (self.enemy == world || self.enemy.deadflag != DEAD_NO)
 +        self.enemy = world;
 +
 +    if (self.enemy)
 +    {
 +        itime = max(edist / m_speed, 1);
 +        newdir = steerlib_pull(self.enemy.origin + self.tur_shotorg);
 +    }
 +    else
 +        newdir  = normalize(self.velocity);
 +
 +    WALKER_ROCKET_MOVE;
 +}
 +
 +void walker_rocket_loop3()
 +{SELFPARAM();
 +    vector newdir;
 +    self.nextthink = time;
 +
 +    if (self.max_health < time)
 +    {
 +        self.think = walker_rocket_explode;
 +        return;
 +    }
 +
 +    if (vlen(self.origin - self.tur_shotorg) < 100 )
 +    {
 +        self.think = walker_rocket_think;
 +        return;
 +    }
 +
 +    newdir = steerlib_pull(self.tur_shotorg);
 +    WALKER_ROCKET_MOVE;
 +
 +    self.angles = vectoangles(self.velocity);
 +}
 +
 +void walker_rocket_loop2()
 +{SELFPARAM();
 +    vector newdir;
 +
 +    self.nextthink = time;
 +
 +    if (self.max_health < time)
 +    {
 +        self.think = walker_rocket_explode;
 +        return;
 +    }
 +
 +    if (vlen(self.origin - self.tur_shotorg) < 100 )
 +    {
 +        self.tur_shotorg = self.origin - '0 0 200';
 +        self.think = walker_rocket_loop3;
 +        return;
 +    }
 +
 +    newdir = steerlib_pull(self.tur_shotorg);
 +    WALKER_ROCKET_MOVE;
 +}
 +
 +void walker_rocket_loop()
 +{SELFPARAM();
 +    self.nextthink = time;
 +    self.tur_shotorg = self.origin + '0 0 300';
 +    self.think = walker_rocket_loop2;
 +    self.shot_dmg = 1337;
 +}
 +
 +void walker_fire_rocket(vector org)
 +{SELFPARAM();
 +    entity rocket;
 +
 +    fixedmakevectors(self.angles);
 +
 +    te_explosion (org);
 +
 +    rocket = spawn ();
 +    setorigin(rocket, org);
 +
 +    sound (self, CH_WEAPON_A, SND_HAGAR_FIRE, VOL_BASE, ATTEN_NORM);
 +    setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
 +
 +    rocket.classname            = "walker_rocket";
 +    rocket.owner                        = self;
 +    rocket.bot_dodge            = true;
 +    rocket.bot_dodgerating    = 50;
 +    rocket.takedamage          = DAMAGE_YES;
 +    rocket.damageforcescale   = 2;
 +    rocket.health                      = 25;
 +    rocket.tur_shotorg                = randomvec() * 512;
 +    rocket.cnt                                = time + 1;
 +    rocket.enemy                        = self.enemy;
 +
 +    if (random() < 0.01)
 +        rocket.think            = walker_rocket_loop;
 +    else
 +        rocket.think            = walker_rocket_think;
 +
 +    rocket.event_damage          = walker_rocket_damage;
 +
 +    rocket.nextthink            = time;
 +    rocket.movetype              = MOVETYPE_FLY;
 +    rocket.velocity              = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * (autocvar_g_turrets_unit_walker_rocket_speed);
 +    rocket.angles                      = vectoangles(rocket.velocity);
 +    rocket.touch                        = walker_rocket_explode;
 +    rocket.flags                        = FL_PROJECTILE;
 +    rocket.solid                        = SOLID_BBOX;
 +    rocket.max_health          = time + 9;
 +    rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT;
 +
 +    CSQCProjectile(rocket, false, PROJECTILE_ROCKET, false); // no culling, has fly sound
 +}
 +
 +.vector enemy_last_loc;
 +.float enemy_last_time;
 +void walker_move_to(vector _target, float _dist)
 +{SELFPARAM();
 +    switch (self.waterlevel)
 +    {
 +        case WATERLEVEL_NONE:
 +            if (_dist > 500)
 +                self.animflag = ANIM_RUN;
 +            else
 +                self.animflag = ANIM_WALK;
 +        case WATERLEVEL_WETFEET:
 +        case WATERLEVEL_SWIMMING:
 +            if (self.animflag != ANIM_SWIM)
 +                self.animflag = ANIM_WALK;
 +            else
 +                self.animflag = ANIM_SWIM;
 +            break;
 +        case WATERLEVEL_SUBMERGED:
 +            self.animflag = ANIM_SWIM;
 +    }
 +
 +    self.moveto = _target;
 +    self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
 +
 +    if(self.enemy)
 +    {
 +        self.enemy_last_loc = _target;
 +        self.enemy_last_time = time;
 +    }
 +}
 +
 +void walker_move_path()
 +{SELFPARAM();
 +#ifdef WALKER_FANCYPATHING
 +    // Are we close enougth to a path node to switch to the next?
 +    if (vlen(self.origin  - self.pathcurrent.origin) < 64)
 +        if (self.pathcurrent.path_next == world)
 +        {
 +            // Path endpoint reached
 +            pathlib_deletepath(self.pathcurrent.owner);
 +            self.pathcurrent = world;
 +
 +            if (self.pathgoal)
 +            {
 +                if (self.pathgoal.use)
 +                    self.pathgoal.use();
 +
 +                if (self.pathgoal.enemy)
 +                {
 +                    self.pathcurrent = WALKER_PATH(self.pathgoal.origin,self.pathgoal.enemy.origin);
 +                    self.pathgoal = self.pathgoal.enemy;
 +                }
 +            }
 +            else
 +                self.pathgoal = world;
 +        }
 +        else
 +            self.pathcurrent = self.pathcurrent.path_next;
 +
 +    self.moveto = self.pathcurrent.origin;
 +    self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95);
 +    walker_move_to(self.moveto, 0);
 +
 +#else
 +    if (vlen(self.origin - self.pathcurrent.origin) < 64)
 +        self.pathcurrent = self.pathcurrent.enemy;
 +
 +    if(!self.pathcurrent)
 +        return;
 +
 +    self.moveto = self.pathcurrent.origin;
 +    self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
 +    walker_move_to(self.moveto, 0);
 +#endif
 +}
 +
++spawnfunc(turret_walker) { if(!turret_initialize(TUR_WALKER)) remove(self); }
 +
 +        METHOD(WalkerTurret, tr_think, void(WalkerTurret thistur))
 +        {
 +            fixedmakevectors(self.angles);
 +
 +            if (self.spawnflags & TSF_NO_PATHBREAK && self.pathcurrent)
 +                walker_move_path();
 +            else if (self.enemy == world)
 +            {
 +                if(self.pathcurrent)
 +                    walker_move_path();
 +                else
 +                {
 +                    if(self.enemy_last_time != 0)
 +                    {
 +                        if(vlen(self.origin - self.enemy_last_loc) < 128 || time - self.enemy_last_time > 10)
 +                            self.enemy_last_time = 0;
 +                        else
 +                            walker_move_to(self.enemy_last_loc, 0);
 +                    }
 +                    else
 +                    {
 +                        if(self.animflag != ANIM_NO)
 +                        {
 +                            traceline(self.origin + '0 0 64', self.origin + '0 0 64' + v_forward * 128, MOVE_NORMAL, self);
 +
 +                            if(trace_fraction != 1.0)
 +                                self.tur_head.idletime = -1337;
 +                            else
 +                            {
 +                                traceline(trace_endpos, trace_endpos - '0 0 256', MOVE_NORMAL, self);
 +                                if(trace_fraction == 1.0)
 +                                    self.tur_head.idletime = -1337;
 +                            }
 +
 +                            if(self.tur_head.idletime == -1337)
 +                            {
 +                                self.moveto = self.origin + randomvec() * 256;
 +                                self.tur_head.idletime = 0;
 +                            }
 +
 +                            self.moveto = self.moveto * 0.9 + ((self.origin + v_forward * 500) + randomvec() * 400) * 0.1;
 +                            self.moveto_z = self.origin_z + 64;
 +                            walker_move_to(self.moveto, 0);
 +                        }
 +
 +                        if(self.idletime < time)
 +                        {
 +                            if(random() < 0.5 || !(self.spawnflags & TSL_ROAM))
 +                            {
 +                                self.idletime = time + 1 + random() * 5;
 +                                self.moveto = self.origin;
 +                                self.animflag = ANIM_NO;
 +                            }
 +                            else
 +                            {
 +                                self.animflag = ANIM_WALK;
 +                                self.idletime = time + 4 + random() * 2;
 +                                self.moveto = self.origin + randomvec() * 256;
 +                                self.tur_head.moveto = self.moveto;
 +                                self.tur_head.idletime = 0;
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_melee_range) && self.animflag != ANIM_MELEE)
 +                {
 +                    vector wish_angle;
 +
 +                    wish_angle = angleofs(self, self.enemy);
 +                    if (self.animflag != ANIM_SWIM)
 +                    if (fabs(wish_angle_y) < 15)
 +                    {
 +                        self.moveto   = self.enemy.origin;
 +                        self.steerto  = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
 +                        self.animflag = ANIM_MELEE;
 +                    }
 +                }
 +                else if (self.tur_head.attack_finished_single < time)
 +                {
 +                    if(self.tur_head.shot_volly)
 +                    {
 +                        self.animflag = ANIM_NO;
 +
 +                        self.tur_head.shot_volly = self.tur_head.shot_volly -1;
 +                        if(self.tur_head.shot_volly == 0)
 +                            self.tur_head.attack_finished_single = time + (autocvar_g_turrets_unit_walker_rocket_refire);
 +                        else
 +                            self.tur_head.attack_finished_single = time + 0.2;
 +
 +                        if(self.tur_head.shot_volly > 1)
 +                            walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01")));
 +                        else
 +                            walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket02")));
 +                    }
 +                    else
 +                    {
 +                        if (self.tur_dist_enemy > (autocvar_g_turrets_unit_walker_rocket_range_min))
 +                        if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_rocket_range))
 +                            self.tur_head.shot_volly = 4;
 +                    }
 +                }
 +                else
 +                {
 +                    if (self.animflag != ANIM_MELEE)
 +                        walker_move_to(self.enemy.origin, self.tur_dist_enemy);
 +                }
 +            }
 +
 +            {
 +                vector real_angle;
 +                float turny = 0, turnx = 0;
 +                float vz;
 +
 +                real_angle = vectoangles(self.steerto) - self.angles;
 +                vz = self.velocity_z;
 +
 +                switch (self.animflag)
 +                {
 +                    case ANIM_NO:
 +                        movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
 +                        break;
 +
 +                    case ANIM_TURN:
 +                        turny = (autocvar_g_turrets_unit_walker_turn);
 +                        movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
 +                        break;
 +
 +                    case ANIM_WALK:
 +                        turny = (autocvar_g_turrets_unit_walker_turn_walk);
 +                        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_walk), 0.6);
 +                        break;
 +
 +                    case ANIM_RUN:
 +                        turny = (autocvar_g_turrets_unit_walker_turn_run);
 +                        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_run), 0.6);
 +                        break;
 +
 +                    case ANIM_STRAFE_L:
 +                        turny = (autocvar_g_turrets_unit_walker_turn_strafe);
 +                        movelib_move_simple(v_right * -1, (autocvar_g_turrets_unit_walker_speed_walk), 0.8);
 +                        break;
 +
 +                    case ANIM_STRAFE_R:
 +                        turny = (autocvar_g_turrets_unit_walker_turn_strafe);
 +                        movelib_move_simple(v_right, (autocvar_g_turrets_unit_walker_speed_walk), 0.8);
 +                        break;
 +
 +                    case ANIM_JUMP:
 +                        self.velocity += '0 0 1' * (autocvar_g_turrets_unit_walker_speed_jump);
 +                        break;
 +
 +                    case ANIM_LAND:
 +                        break;
 +
 +                    case ANIM_PAIN:
 +                        if(self.frame != ANIM_PAIN)
 +                            defer(0.25, walker_setnoanim);
 +
 +                        break;
 +
 +                    case ANIM_MELEE:
 +                        if(self.frame != ANIM_MELEE)
 +                        {
 +                            defer(0.41, walker_setnoanim);
 +                            defer(0.21, walker_melee_do_dmg);
 +                        }
 +
 +                        movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
 +                        break;
 +
 +                    case ANIM_SWIM:
 +                        turny = (autocvar_g_turrets_unit_walker_turn_swim);
 +                        turnx = (autocvar_g_turrets_unit_walker_turn_swim);
 +
 +                        self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10);
 +                        movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_swim), 0.3);
 +                        vz = self.velocity_z + sin(time * 4) * 8;
 +                        break;
 +
 +                    case ANIM_ROAM:
 +                        turny = (autocvar_g_turrets_unit_walker_turn_walk);
 +                        movelib_move_simple(v_forward ,(autocvar_g_turrets_unit_walker_speed_roam), 0.5);
 +                        break;
 +                }
 +
 +                if(turny)
 +                {
 +                    turny = bound( turny * -1, shortangle_f(real_angle_y, self.angles_y), turny );
 +                    self.angles_y += turny;
 +                }
 +
 +                if(turnx)
 +                {
 +                    turnx = bound( turnx * -1, shortangle_f(real_angle_x, self.angles_x), turnx );
 +                    self.angles_x += turnx;
 +                }
 +
 +                self.velocity_z = vz;
 +            }
 +
 +
 +            if(self.origin != self.oldorigin)
 +                self.SendFlags |= TNSF_MOVE;
 +
 +            self.oldorigin = self.origin;
 +            turrets_setframe(self.animflag, false);
 +        }
 +        METHOD(WalkerTurret, tr_death, void(WalkerTurret this, entity it))
 +        {
 +#ifdef WALKER_FANCYPATHING
 +            if (it.pathcurrent)
 +                pathlib_deletepath(it.pathcurrent.owner);
 +#endif
 +            it.pathcurrent = NULL;
 +        }
 +        METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it))
 +        {
 +            it.ticrate = 0.05;
 +
 +            entity e;
 +
 +            // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn.
 +            if(it.movetype == MOVETYPE_WALK)
 +            {
 +                if(it.pos1)
 +                    setorigin(it, it.pos1);
 +                if(it.pos2)
 +                    it.angles = it.pos2;
 +            }
 +
 +            it.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
 +            it.aim_flags = TFL_AIM_LEAD;
 +            it.turret_flags |= TUR_FLAG_HITSCAN;
 +
 +            it.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
 +            it.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
 +            it.iscreature = true;
 +            it.teleportable = TELEPORT_NORMAL;
 +            it.damagedbycontents = true;
 +            it.solid = SOLID_SLIDEBOX;
 +            it.takedamage = DAMAGE_AIM;
 +            if(it.movetype != MOVETYPE_WALK)
 +            {
 +                setorigin(it, it.origin);
 +                tracebox(it.origin + '0 0 128', it.mins, it.maxs, it.origin - '0 0 10000', MOVE_NORMAL, it);
 +                setorigin(it, trace_endpos + '0 0 4');
 +                it.pos1 = it.origin;
 +                it.pos2 = it.angles;
 +            }
 +            it.movetype = MOVETYPE_WALK;
 +            it.idle_aim = '0 0 0';
 +            it.turret_firecheckfunc = walker_firecheck;
 +
 +            if (it.target != "")
 +            {
 +                e = find(world, targetname, it.target);
 +                if (!e)
 +                {
 +                    LOG_TRACE("Initital waypoint for walker does NOT exsist, fix your map!\n");
 +                    it.target = "";
 +                }
 +
 +                if (e.classname != "turret_checkpoint")
 +                    LOG_TRACE("Warning: not a turrret path\n");
 +                else
 +                {
 +#ifdef WALKER_FANCYPATHING
 +                    it.pathcurrent = WALKER_PATH(it.origin, e.origin);
 +                    it.pathgoal = e;
 +#else
 +                    it.pathcurrent = e;
 +#endif
 +                }
 +            }
 +        }
 +
 +#endif // SVQC
 +#ifdef CSQC
 +
 +#include "../../../client/movelib.qh"
 +
 +void walker_draw()
 +{SELFPARAM();
 +    float dt;
 +
 +    dt = time - self.move_time;
 +    self.move_time = time;
 +    if(dt <= 0)
 +        return;
 +
 +    fixedmakevectors(self.angles);
 +    movelib_groundalign4point(300, 100, 0.25, 45);
 +    setorigin(self, self.origin + self.velocity * dt);
 +    self.tur_head.angles += dt * self.tur_head.move_avelocity;
 +    self.angles_y = self.move_angles_y;
 +
 +    if (self.health < 127)
 +    if(random() < 0.15)
 +        te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16);
 +}
 +
 +        METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it))
 +        {
 +            it.gravity                = 1;
 +            it.movetype               = MOVETYPE_BOUNCE;
 +            it.move_movetype  = MOVETYPE_BOUNCE;
 +            it.move_origin    = it.origin;
 +            it.move_time              = time;
 +            it.draw                   = walker_draw;
 +        }
 +
 +#endif // CSQC
 +#endif
index ee276f2b24a6b0611390cba50ab95e68e7a84c81,0000000000000000000000000000000000000000..011fdf1658e14f99911e6c21d91d1d1de20e3ce6
mode 100644,000000..100644
--- /dev/null
@@@ -1,975 -1,0 +1,975 @@@
- void spawnfunc_vehicle_bumblebee()
- {SELFPARAM();
 +#ifndef VEHICLE_BUMBLEBEE
 +#define VEHICLE_BUMBLEBEE
 +#include "bumblebee.qh"
 +
 +#include "bumblebee_weapons.qc"
 +
 +CLASS(Bumblebee, Vehicle)
 +/* spawnflags */ ATTRIB(Bumblebee, spawnflags, int, VHF_DMGSHAKE);
 +/* mins       */ ATTRIB(Bumblebee, mins, vector, '-245 -130 -130');
 +/* maxs       */ ATTRIB(Bumblebee, maxs, vector, '230 130 130');
 +/* model        */ ATTRIB(Bumblebee, mdl, string, "models/vehicles/bumblebee_body.dpm");
 +/* model        */ ATTRIB(Bumblebee, model, string, "models/vehicles/bumblebee_body.dpm");
 +/* head_model */ ATTRIB(Bumblebee, head_model, string, "");
 +/* hud_model  */ ATTRIB(Bumblebee, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm");
 +/* tags       */ ATTRIB(Bumblebee, tag_head, string, "");
 +/* tags       */ ATTRIB(Bumblebee, tag_hud, string, "");
 +/* tags       */ ATTRIB(Bumblebee, tag_view, string, "tag_viewport");
 +/* netname    */ ATTRIB(Bumblebee, netname, string, "bumblebee");
 +/* fullname   */ ATTRIB(Bumblebee, vehicle_name, string, _("Bumblebee"));
 +/* icon       */ ATTRIB(Bumblebee, m_icon, string, "vehicle_bumble");
 +ENDCLASS(Bumblebee)
 +REGISTER_VEHICLE(BUMBLEBEE, NEW(Bumblebee));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +const float BRG_SETUP = 2;
 +const float BRG_START = 4;
 +const float BRG_END = 8;
 +
 +#include "bumblebee_weapons.qc"
 +
 +#ifdef SVQC
 +float autocvar_g_vehicle_bumblebee_speed_forward;
 +float autocvar_g_vehicle_bumblebee_speed_strafe;
 +float autocvar_g_vehicle_bumblebee_speed_up;
 +float autocvar_g_vehicle_bumblebee_speed_down;
 +float autocvar_g_vehicle_bumblebee_turnspeed;
 +float autocvar_g_vehicle_bumblebee_pitchspeed;
 +float autocvar_g_vehicle_bumblebee_pitchlimit;
 +float autocvar_g_vehicle_bumblebee_friction;
 +
 +float autocvar_g_vehicle_bumblebee_energy;
 +float autocvar_g_vehicle_bumblebee_energy_regen;
 +float autocvar_g_vehicle_bumblebee_energy_regen_pause;
 +
 +float autocvar_g_vehicle_bumblebee_health;
 +float autocvar_g_vehicle_bumblebee_health_regen;
 +float autocvar_g_vehicle_bumblebee_health_regen_pause;
 +
 +float autocvar_g_vehicle_bumblebee_shield;
 +float autocvar_g_vehicle_bumblebee_shield_regen;
 +float autocvar_g_vehicle_bumblebee_shield_regen_pause;
 +
 +float autocvar_g_vehicle_bumblebee_cannon_ammo;
 +float autocvar_g_vehicle_bumblebee_cannon_ammo_regen;
 +float autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause;
 +
 +float autocvar_g_vehicle_bumblebee_cannon_lock = 0;
 +
 +float autocvar_g_vehicle_bumblebee_cannon_turnspeed;
 +float autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down;
 +float autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up;
 +float autocvar_g_vehicle_bumblebee_cannon_turnlimit_in;
 +float autocvar_g_vehicle_bumblebee_cannon_turnlimit_out;
 +
 +
 +float autocvar_g_vehicle_bumblebee_raygun_turnspeed;
 +float autocvar_g_vehicle_bumblebee_raygun_pitchlimit_down;
 +float autocvar_g_vehicle_bumblebee_raygun_pitchlimit_up;
 +float autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides;
 +
 +float autocvar_g_vehicle_bumblebee_raygun_range;
 +float autocvar_g_vehicle_bumblebee_raygun_dps;
 +float autocvar_g_vehicle_bumblebee_raygun_aps;
 +float autocvar_g_vehicle_bumblebee_raygun_fps;
 +
 +float autocvar_g_vehicle_bumblebee_raygun;
 +float autocvar_g_vehicle_bumblebee_healgun_hps;
 +float autocvar_g_vehicle_bumblebee_healgun_hmax;
 +float autocvar_g_vehicle_bumblebee_healgun_aps;
 +float autocvar_g_vehicle_bumblebee_healgun_amax;
 +float autocvar_g_vehicle_bumblebee_healgun_sps;
 +float autocvar_g_vehicle_bumblebee_healgun_locktime;
 +
 +float autocvar_g_vehicle_bumblebee_respawntime;
 +
 +float autocvar_g_vehicle_bumblebee_blowup_radius;
 +float autocvar_g_vehicle_bumblebee_blowup_coredamage;
 +float autocvar_g_vehicle_bumblebee_blowup_edgedamage;
 +float autocvar_g_vehicle_bumblebee_blowup_forceintensity;
 +vector autocvar_g_vehicle_bumblebee_bouncepain;
 +
 +bool autocvar_g_vehicle_bumblebee = 0;
 +
 +float bumblebee_gunner_frame()
 +{SELFPARAM();
 +      entity vehic    = self.vehicle.owner;
 +      entity gun      = self.vehicle;
 +      entity gunner   = self;
 +      setself(vehic);
 +
 +      vehic.solid = SOLID_NOT;
 +      //setorigin(gunner, vehic.origin);
 +      gunner.velocity = vehic.velocity;
 +
 +      float _in, _out;
 +      vehic.angles_x *= -1;
 +      makevectors(vehic.angles);
 +      vehic.angles_x *= -1;
 +      if(gun == vehic.gun1)
 +      {
 +              _in = autocvar_g_vehicle_bumblebee_cannon_turnlimit_in;
 +              _out = autocvar_g_vehicle_bumblebee_cannon_turnlimit_out;
 +              setorigin(gunner, vehic.origin + v_up * -16 + v_forward * -16 + v_right * 128);
 +      }
 +      else
 +      {
 +              _in = autocvar_g_vehicle_bumblebee_cannon_turnlimit_out;
 +              _out = autocvar_g_vehicle_bumblebee_cannon_turnlimit_in;
 +              setorigin(gunner, vehic.origin + v_up * -16 + v_forward * -16 + v_right * -128);
 +      }
 +
 +      crosshair_trace(gunner);
 +      vector _ct = trace_endpos;
 +      vector ad;
 +
 +      if(autocvar_g_vehicle_bumblebee_cannon_lock)
 +      {
 +              if(gun.lock_time < time)
 +                      gun.enemy = world;
 +
 +              if(trace_ent)
 +                      if(trace_ent.movetype)
 +                              if(trace_ent.takedamage)
 +                                      if(!trace_ent.deadflag)
 +                                      {
 +                                              if(DIFF_TEAM(trace_ent, gunner))
 +                                              {
 +                                                      gun.enemy = trace_ent;
 +                                                      gun.lock_time = time + 5;
 +                                              }
 +                                      }
 +      }
 +
 +      if(gun.enemy)
 +      {
 +              float distance, impact_time;
 +
 +              vector vf = real_origin(gun.enemy);
 +              vector _vel = gun.enemy.velocity;
 +              if(gun.enemy.movetype == MOVETYPE_WALK)
 +                      _vel.z *= 0.1;
 +
 +
 +              ad = vf;
 +              distance = vlen(ad - gunner.origin);
 +              impact_time = distance / autocvar_g_vehicle_bumblebee_cannon_speed;
 +              ad = vf + _vel * impact_time;
 +              trace_endpos = ad;
 +
 +
 +              UpdateAuxiliaryXhair(gunner, ad, '1 0 1', 1);
 +              vehicle_aimturret(vehic, trace_endpos, gun, "fire",
 +                                                autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up,
 +                                                _out * -1,  _in,  autocvar_g_vehicle_bumblebee_cannon_turnspeed);
 +
 +      }
 +      else
 +              vehicle_aimturret(vehic, _ct, gun, "fire",
 +                                                autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up,
 +                                                _out * -1,  _in,  autocvar_g_vehicle_bumblebee_cannon_turnspeed);
 +
 +      if(!forbidWeaponUse(gunner))
 +      if(gunner.BUTTON_ATCK)
 +              if(time > gun.attack_finished_single)
 +                      if(gun.vehicle_energy >= autocvar_g_vehicle_bumblebee_cannon_cost)
 +                      {
 +                              gun.vehicle_energy -= autocvar_g_vehicle_bumblebee_cannon_cost;
 +                              bumblebee_fire_cannon(gun, "fire", gunner);
 +                              gun.delay = time;
 +                              gun.attack_finished_single = time + autocvar_g_vehicle_bumblebee_cannon_refire;
 +                      }
 +
 +      VEHICLE_UPDATE_PLAYER(gunner, health, bumblebee);
 +
 +      if(vehic.vehicle_flags & VHF_HASSHIELD)
 +              VEHICLE_UPDATE_PLAYER(gunner, shield, bumblebee);
 +
 +      ad = gettaginfo(gun, gettagindex(gun, "fire"));
 +      traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, gun);
 +
 +      UpdateAuxiliaryXhair(gunner, trace_endpos, ('1 0 0' * gunner.vehicle_reload1) + ('0 1 0' *(1 - gunner.vehicle_reload1)), 0);
 +
 +      if(vehic.owner)
 +              UpdateAuxiliaryXhair(vehic.owner, trace_endpos, ('1 0 0' * gunner.vehicle_reload1) + ('0 1 0' *(1 - gunner.vehicle_reload1)), ((gunner == vehic.gunner1) ? 1 : 2));
 +
 +      vehic.solid = SOLID_BBOX;
 +      gunner.BUTTON_ATCK = gunner.BUTTON_ATCK2 = gunner.BUTTON_CROUCH = 0;
 +      gunner.vehicle_energy = (gun.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100;
 +
 +      setself(gunner);
 +      return 1;
 +}
 +
 +vector bumblebee_gunner_findgoodexit(vector prefer_spot, entity gunner, entity player)
 +{
 +      //vector exitspot;
 +      float mysize;
 +
 +      tracebox(gunner.origin + '0 0 32', PL_MIN, PL_MAX, prefer_spot, MOVE_NORMAL, player);
 +      if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
 +              return prefer_spot;
 +
 +      mysize = 1.5 * vlen(PL_MAX - PL_MIN); // can't use gunner's size, as they don't have a size
 +      float i;
 +      vector v, v2;
 +      v2 = 0.5 * (gunner.absmin + gunner.absmax);
 +      for(i = 0; i < 100; ++i)
 +      {
 +              v = randomvec();
 +              v_z = 0;
 +              v = v2 + normalize(v) * mysize;
 +              tracebox(v2, PL_MIN, PL_MAX, v, MOVE_NORMAL, player);
 +              if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
 +                      return v;
 +      }
 +
 +      return prefer_spot; // this should be considered a fallback?!
 +}
 +
 +void bumblebee_gunner_exit(int _exitflag)
 +{SELFPARAM();
 +      entity player = self;
 +      entity gunner = player.vehicle;
 +      entity vehic = gunner.owner;
 +
 +      if(IS_REAL_CLIENT(player))
 +      {
 +              msg_entity = player;
 +              WriteByte(MSG_ONE, SVC_SETVIEWPORT);
 +              WriteEntity(MSG_ONE, player);
 +
 +              WriteByte(MSG_ONE, SVC_SETVIEWANGLES);
 +              WriteAngle(MSG_ONE, 0);
 +              WriteAngle(MSG_ONE, vehic.angles.y);
 +              WriteAngle(MSG_ONE, 0);
 +      }
 +
 +      CSQCVehicleSetup(player, HUD_NORMAL);
 +      setsize(player, PL_MIN, PL_MAX);
 +
 +      player.takedamage     = DAMAGE_AIM;
 +      player.solid          = SOLID_SLIDEBOX;
 +      player.movetype       = MOVETYPE_WALK;
 +      player.effects       &= ~EF_NODRAW;
 +      player.alpha          = 1;
 +      player.PlayerPhysplug = func_null;
 +      player.view_ofs       = PL_VIEW_OFS;
 +      player.event_damage   = PlayerDamage;
 +      player.hud            = HUD_NORMAL;
 +      player.teleportable       = TELEPORT_NORMAL;
 +      player.switchweapon   = gunner.switchweapon;
 +      player.vehicle_enter_delay = time + 2;
 +
 +      fixedmakevectors(vehic.angles);
 +
 +      if(player == vehic.gunner1) { vehic.gunner1 = world; }
 +      if(player == vehic.gunner2) { vehic.gunner2 = world; v_right *= -1; }
 +
 +      vector spot = real_origin(gunner);
 +      spot = spot + v_up * 128 + v_forward * 300 + v_right * 150;
 +      spot = bumblebee_gunner_findgoodexit(spot, gunner, player);
 +
 +      // TODO: figure a way to move player out of the gunner
 +
 +      player.velocity = 0.75 * vehic.velocity + normalize(spot - vehic.origin) * 200;
 +      player.velocity_z += 10;
 +
 +      gunner.phase = time + 5;
 +      gunner.vehicle_hudmodel.viewmodelforclient = gunner;
 +
 +      MUTATOR_CALLHOOK(VehicleExit, player, gunner);
 +
 +      player.vehicle = world;
 +}
 +
 +bool bumblebee_gunner_enter()
 +{SELFPARAM();
 +      entity vehic = self;
 +      entity player = other;
 +      entity gunner = world;
 +
 +      if(!vehic.gunner1 && !vehic.gunner2 && ((time >= vehic.gun1.phase) + (time >= vehic.gun2.phase)) == 2)
 +      {
 +              // we can have some fun
 +              if(vlen(real_origin(vehic.gun2) - player.origin) < vlen(real_origin(vehic.gun1) - player.origin))
 +              {
 +                      gunner = vehic.gun2;
 +                      vehic.gunner2 = player;
 +              }
 +              else
 +              {
 +                      gunner = vehic.gun1;
 +                      vehic.gunner1 = player;
 +              }
 +      }
 +      else if(!vehic.gunner1 && time >= vehic.gun1.phase)     { gunner = vehic.gun1; vehic.gunner1 = player; }
 +      else if(!vehic.gunner2 && time >= vehic.gun2.phase)             { gunner = vehic.gun2; vehic.gunner2 = player; }
 +      else { LOG_TRACE("Vehicle is full, fail\n"); return false; }
 +
 +      player.vehicle                  = gunner;
 +      player.angles                   = vehic.angles;
 +      player.takedamage               = DAMAGE_NO;
 +      player.solid                    = SOLID_NOT;
 +      player.alpha                    = -1;
 +      player.movetype                 = MOVETYPE_NOCLIP;
 +      player.event_damage     = func_null;
 +      player.view_ofs                 = '0 0 0';
 +      player.hud                              = gunner.hud;
 +      player.teleportable     = false;
 +      player.PlayerPhysplug   = gunner.PlayerPhysplug;
 +      player.vehicle_ammo1    = vehic.vehicle_ammo1;
 +      player.vehicle_ammo2    = vehic.vehicle_ammo2;
 +      player.vehicle_reload1  = vehic.vehicle_reload1;
 +      player.vehicle_reload2  = vehic.vehicle_reload2;
 +      player.vehicle_energy   = vehic.vehicle_energy;
 +      player.flags               &= ~FL_ONGROUND;
 +
 +      RemoveGrapplingHook(player);
 +
 +      gunner.switchweapon = player.switchweapon;
 +      gunner.vehicle_exit = bumblebee_gunner_exit;
 +      gunner.vehicle_hudmodel.viewmodelforclient = player;
 +
 +      if(IS_REAL_CLIENT(player))
 +      {
 +              msg_entity = player;
 +              WriteByte(MSG_ONE,              SVC_SETVIEWPORT);
 +              WriteEntity(MSG_ONE,    gunner.vehicle_viewport);
 +
 +              WriteByte(MSG_ONE,              SVC_SETVIEWANGLES);
 +              WriteAngle(MSG_ONE,     gunner.angles_x + vehic.angles_x); // tilt
 +              WriteAngle(MSG_ONE,     gunner.angles_y + vehic.angles_y); // yaw
 +              WriteAngle(MSG_ONE,     0); // roll
 +      }
 +
 +      CSQCVehicleSetup(player, player.hud);
 +
 +      MUTATOR_CALLHOOK(VehicleEnter, player, gunner);
 +
 +      return true;
 +}
 +
 +bool vehicles_valid_pilot()
 +{SELFPARAM();
 +      if(IS_BOT_CLIENT(other) && !autocvar_g_vehicles_allow_bots)
 +              return false;
 +
 +      if((!IS_PLAYER(other))
 +      || (other.deadflag != DEAD_NO)
 +      || (other.vehicle)
 +      || (DIFF_TEAM(other, self))
 +      ) { return false; }
 +
 +      return true;
 +}
 +
 +void bumblebee_touch()
 +{SELFPARAM();
 +      if(autocvar_g_vehicles_enter) { return; }
 +
 +      if(self.gunner1 != world && self.gunner2 != world)
 +      {
 +              vehicles_touch();
 +              return;
 +      }
 +
 +      if(vehicles_valid_pilot())
 +      {
 +              float phase_time = (time >= self.gun1.phase) + (time >= self.gun2.phase);
 +
 +              if(time >= other.vehicle_enter_delay && phase_time)
 +              if(bumblebee_gunner_enter())
 +                      return;
 +      }
 +
 +      vehicles_touch();
 +}
 +
 +void bumblebee_regen()
 +{SELFPARAM();
 +      if(self.gun1.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time)
 +              self.gun1.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo,
 +                                                                         self.gun1.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime);
 +
 +      if(self.gun2.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time)
 +              self.gun2.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo,
 +                                                                         self.gun2.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime);
 +
 +      if(self.vehicle_flags  & VHF_SHIELDREGEN)
 +              vehicles_regen(self.dmg_time, vehicle_shield, autocvar_g_vehicle_bumblebee_shield, autocvar_g_vehicle_bumblebee_shield_regen_pause, autocvar_g_vehicle_bumblebee_shield_regen, frametime, true);
 +
 +      if(self.vehicle_flags  & VHF_HEALTHREGEN)
 +              vehicles_regen(self.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, frametime, false);
 +
 +      if(self.vehicle_flags  & VHF_ENERGYREGEN)
 +              vehicles_regen(self.wait, vehicle_energy, autocvar_g_vehicle_bumblebee_energy, autocvar_g_vehicle_bumblebee_energy_regen_pause, autocvar_g_vehicle_bumblebee_energy_regen, frametime, false);
 +
 +}
 +
 +float bumblebee_pilot_frame()
 +{SELFPARAM();
 +      entity pilot, vehic;
 +      vector newvel;
 +
 +      if(intermission_running)
 +      {
 +              self.vehicle.velocity = '0 0 0';
 +              self.vehicle.avelocity = '0 0 0';
 +              return 1;
 +      }
 +
 +      pilot = self;
 +      vehic = self.vehicle;
 +      setself(vehic);
 +
 +      if(vehic.deadflag != DEAD_NO)
 +      {
 +              setself(pilot);
 +              pilot.BUTTON_ATCK = pilot.BUTTON_ATCK2 = 0;
 +              return 1;
 +      }
 +
 +      bumblebee_regen();
 +
 +      crosshair_trace(pilot);
 +
 +      vector vang;
 +      float ftmp;
 +
 +      vang = vehic.angles;
 +      newvel = vectoangles(normalize(trace_endpos - self.origin + '0 0 32'));
 +      vang.x *= -1;
 +      newvel.x *= -1;
 +      if(newvel.x > 180)  newvel.x -= 360;
 +      if(newvel.x < -180) newvel.x += 360;
 +      if(newvel.y > 180)  newvel.y -= 360;
 +      if(newvel.y < -180) newvel.y += 360;
 +
 +      ftmp = shortangle_f(pilot.v_angle.y - vang.y, vang.y);
 +      if(ftmp > 180)  ftmp -= 360;
 +      if(ftmp < -180) ftmp += 360;
 +      vehic.avelocity_y = bound(-autocvar_g_vehicle_bumblebee_turnspeed, ftmp + vehic.avelocity.y * 0.9, autocvar_g_vehicle_bumblebee_turnspeed);
 +
 +      // Pitch
 +      ftmp = 0;
 +      if(pilot.movement.x > 0 && vang.x < autocvar_g_vehicle_bumblebee_pitchlimit)
 +              ftmp = 4;
 +      else if(pilot.movement.x < 0 && vang.x > -autocvar_g_vehicle_bumblebee_pitchlimit)
 +              ftmp = -8;
 +
 +      newvel.x = bound(-autocvar_g_vehicle_bumblebee_pitchlimit, newvel.x , autocvar_g_vehicle_bumblebee_pitchlimit);
 +      ftmp = vang.x - bound(-autocvar_g_vehicle_bumblebee_pitchlimit, newvel.x + ftmp, autocvar_g_vehicle_bumblebee_pitchlimit);
 +      vehic.avelocity_x = bound(-autocvar_g_vehicle_bumblebee_pitchspeed, ftmp + vehic.avelocity.x * 0.9, autocvar_g_vehicle_bumblebee_pitchspeed);
 +
 +      vehic.angles_x = anglemods(vehic.angles.x);
 +      vehic.angles_y = anglemods(vehic.angles.y);
 +      vehic.angles_z = anglemods(vehic.angles.z);
 +
 +      makevectors('0 1 0' * vehic.angles.y);
 +      newvel = vehic.velocity * -autocvar_g_vehicle_bumblebee_friction;
 +
 +      if(pilot.movement.x != 0)
 +      {
 +              if(pilot.movement.x > 0)
 +                      newvel += v_forward  * autocvar_g_vehicle_bumblebee_speed_forward;
 +              else if(pilot.movement.x < 0)
 +                      newvel -= v_forward  * autocvar_g_vehicle_bumblebee_speed_forward;
 +      }
 +
 +      if(pilot.movement.y != 0)
 +      {
 +              if(pilot.movement.y < 0)
 +                      newvel -= v_right * autocvar_g_vehicle_bumblebee_speed_strafe;
 +              else if(pilot.movement.y > 0)
 +                      newvel += v_right * autocvar_g_vehicle_bumblebee_speed_strafe;
 +              ftmp = newvel * v_right;
 +              ftmp *= frametime * 0.1;
 +              vehic.angles_z = bound(-15, vehic.angles.z + ftmp, 15);
 +      }
 +      else
 +      {
 +              vehic.angles_z *= 0.95;
 +              if(vehic.angles.z >= -1 && vehic.angles.z <= -1)
 +                      vehic.angles_z = 0;
 +      }
 +
 +      if(pilot.BUTTON_CROUCH)
 +              newvel -=   v_up * autocvar_g_vehicle_bumblebee_speed_down;
 +      else if(pilot.BUTTON_JUMP)
 +              newvel +=  v_up * autocvar_g_vehicle_bumblebee_speed_up;
 +
 +      vehic.velocity  += newvel * frametime;
 +      pilot.velocity = pilot.movement  = vehic.velocity;
 +
 +
 +      if(autocvar_g_vehicle_bumblebee_healgun_locktime)
 +      {
 +              if(vehic.tur_head.lock_time < time || vehic.tur_head.enemy.deadflag)
 +                      vehic.tur_head.enemy = world;
 +
 +              if(trace_ent)
 +              if(trace_ent.movetype)
 +              if(trace_ent.takedamage)
 +              if(!trace_ent.deadflag)
 +              {
 +                      if(teamplay)
 +                      {
 +                              if(trace_ent.team == pilot.team)
 +                              {
 +                                      vehic.tur_head.enemy = trace_ent;
 +                                      vehic.tur_head.lock_time = time + autocvar_g_vehicle_bumblebee_healgun_locktime;
 +                              }
 +                      }
 +                      else
 +                      {
 +                              vehic.tur_head.enemy = trace_ent;
 +                              vehic.tur_head.lock_time = time + autocvar_g_vehicle_bumblebee_healgun_locktime;
 +                      }
 +              }
 +
 +              if(vehic.tur_head.enemy)
 +              {
 +                      trace_endpos = real_origin(vehic.tur_head.enemy);
 +                      UpdateAuxiliaryXhair(pilot, trace_endpos, '0 0.75 0', 0);
 +              }
 +      }
 +
 +      vang = vehicle_aimturret(vehic, trace_endpos, self.gun3, "fire",
 +                                        autocvar_g_vehicle_bumblebee_raygun_pitchlimit_down * -1,  autocvar_g_vehicle_bumblebee_raygun_pitchlimit_up,
 +                                        autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides * -1,  autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides,  autocvar_g_vehicle_bumblebee_raygun_turnspeed);
 +
 +      if(!forbidWeaponUse(pilot))
 +      if((pilot.BUTTON_ATCK || pilot.BUTTON_ATCK2) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime || autocvar_g_vehicle_bumblebee_raygun == 0))
 +      {
 +              vehic.gun3.enemy.realowner = pilot;
 +              vehic.gun3.enemy.effects &= ~EF_NODRAW;
 +
 +              vehic.gun3.enemy.hook_start = gettaginfo(vehic.gun3, gettagindex(vehic.gun3, "fire"));
 +              vehic.gun3.enemy.SendFlags |= BRG_START;
 +
 +              traceline(vehic.gun3.enemy.hook_start, vehic.gun3.enemy.hook_start + v_forward * autocvar_g_vehicle_bumblebee_raygun_range, MOVE_NORMAL, vehic);
 +
 +              if(trace_ent)
 +              {
 +                      if(autocvar_g_vehicle_bumblebee_raygun)
 +                      {
 +                              Damage(trace_ent, vehic, pilot, autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime, DEATH_GENERIC, trace_endpos, v_forward * autocvar_g_vehicle_bumblebee_raygun_fps * sys_frametime);
 +                              vehic.vehicle_energy -= autocvar_g_vehicle_bumblebee_raygun_aps * sys_frametime;
 +                      }
 +                      else
 +                      {
 +                              if(trace_ent.deadflag == DEAD_NO)
 +                                      if((teamplay && trace_ent.team == pilot.team) || !teamplay)
 +                                      {
 +
 +                                              if(trace_ent.vehicle_flags & VHF_ISVEHICLE)
 +                                              {
 +                                                      if(autocvar_g_vehicle_bumblebee_healgun_sps && trace_ent.vehicle_health <= trace_ent.max_health)
 +                                                              trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * frametime, trace_ent.tur_head.max_health);
 +
 +                                                      if(autocvar_g_vehicle_bumblebee_healgun_hps)
 +                                                              trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.max_health);
 +                                              }
 +                                              else if(IS_CLIENT(trace_ent))
 +                                              {
 +                                                      if(trace_ent.health <= autocvar_g_vehicle_bumblebee_healgun_hmax && autocvar_g_vehicle_bumblebee_healgun_hps)
 +                                                              trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax);
 +
 +                                                      if(trace_ent.armorvalue <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps)
 +                                                              trace_ent.armorvalue = min(trace_ent.armorvalue + autocvar_g_vehicle_bumblebee_healgun_aps * frametime, autocvar_g_vehicle_bumblebee_healgun_amax);
 +
 +                                                      trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax);
 +                                              }
 +                                              else if(IS_TURRET(trace_ent))
 +                                              {
 +                                                      if(trace_ent.health  <= trace_ent.max_health && autocvar_g_vehicle_bumblebee_healgun_hps)
 +                                                              trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.max_health);
 +                                                      //else ..hmmm what? ammo?
 +
 +                                                      trace_ent.SendFlags |= TNSF_STATUS;
 +                                              }
 +                                      }
 +                      }
 +              }
 +
 +              vehic.gun3.enemy.hook_end = trace_endpos;
 +              setorigin(vehic.gun3.enemy, trace_endpos);
 +              vehic.gun3.enemy.SendFlags |= BRG_END;
 +
 +              vehic.wait = time + 1;
 +      }
 +      else
 +              vehic.gun3.enemy.effects |= EF_NODRAW;
 +      /*{
 +              if(vehic.gun3.enemy)
 +                      remove(vehic.gun3.enemy);
 +
 +              vehic.gun3.enemy = world;
 +      }
 +      */
 +
 +      VEHICLE_UPDATE_PLAYER(pilot, health, bumblebee);
 +      VEHICLE_UPDATE_PLAYER(pilot, energy, bumblebee);
 +
 +      pilot.vehicle_ammo1 = (vehic.gun1.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100;
 +      pilot.vehicle_ammo2 = (vehic.gun2.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100;
 +
 +      if(vehic.vehicle_flags & VHF_HASSHIELD)
 +              VEHICLE_UPDATE_PLAYER(pilot, shield, bumblebee);
 +
 +      vehic.angles_x *= -1;
 +      makevectors(vehic.angles);
 +      vehic.angles_x *= -1;
 +      setorigin(pilot, vehic.origin + v_up * 48 + v_forward * 160);
 +
 +      pilot.BUTTON_ATCK = pilot.BUTTON_ATCK2 = pilot.BUTTON_CROUCH = 0;
 +      setself(pilot);
 +
 +      return 1;
 +}
 +
 +void bumblebee_land()
 +{SELFPARAM();
 +      float hgt;
 +
 +      hgt = raptor_altitude(512);
 +      self.velocity = (self.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime);
 +      self.angles_x *= 0.95;
 +      self.angles_z *= 0.95;
 +
 +      if(hgt < 16)
 +              self.think      = vehicles_think;
 +
 +      self.nextthink = time;
 +
 +      CSQCMODEL_AUTOUPDATE(self);
 +}
 +
 +void bumblebee_exit(float eject)
 +{SELFPARAM();
 +      if(self.owner.vehicleid == VEH_BUMBLEBEE.vehicleid)
 +      {
 +              bumblebee_gunner_exit(eject);
 +              return;
 +      }
 +
 +      self.touch = vehicles_touch;
 +
 +      if(self.deadflag == DEAD_NO)
 +      {
 +              self.think = bumblebee_land;
 +              self.nextthink  = time;
 +      }
 +
 +      self.movetype = MOVETYPE_TOSS;
 +
 +      if(!self.owner)
 +              return;
 +
 +      fixedmakevectors(self.angles);
 +      vector spot;
 +      if(vlen(self.velocity) > autocvar_g_vehicle_bumblebee_speed_forward * 0.5)
 +              spot = self.origin + v_up * 128 + v_forward * 300;
 +      else
 +              spot = self.origin + v_up * 128 - v_forward * 300;
 +
 +      spot = vehicles_findgoodexit(spot);
 +
 +      // Hide beam
 +      if(self.gun3.enemy || !wasfreed(self.gun3.enemy)) {
 +              self.gun3.enemy.effects |= EF_NODRAW;
 +      }
 +
 +      self.owner.velocity = 0.75 * self.vehicle.velocity + normalize(spot - self.vehicle.origin) * 200;
 +      self.owner.velocity_z += 10;
 +      setorigin(self.owner, spot);
 +
 +      antilag_clear(self.owner);
 +      self.owner = world;
 +}
 +
 +void bumblebee_blowup()
 +{SELFPARAM();
 +      RadiusDamage(self, self.enemy, autocvar_g_vehicle_bumblebee_blowup_coredamage,
 +                               autocvar_g_vehicle_bumblebee_blowup_edgedamage,
 +                               autocvar_g_vehicle_bumblebee_blowup_radius, self, world,
 +                               autocvar_g_vehicle_bumblebee_blowup_forceintensity,
 +                               DEATH_VH_BUMB_DEATH, world);
 +
 +      sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
 +      Send_Effect(EFFECT_EXPLOSION_BIG, (self.origin + '0 0 100') + (randomvec() * 80), '0 0 0', 1);
 +
 +      if(self.owner.deadflag == DEAD_DYING)
 +              self.owner.deadflag = DEAD_DEAD;
 +
 +      remove(self);
 +}
 +
 +void bumblebee_diethink()
 +{SELFPARAM();
 +      if(time >= self.wait)
 +              self.think = bumblebee_blowup;
 +
 +      if(random() < 0.1)
 +      {
 +              sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
 +              Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
 +      }
 +
 +      self.nextthink = time + 0.1;
 +}
 +
++spawnfunc(vehicle_bumblebee)
++{
 +      if(!autocvar_g_vehicle_bumblebee) { remove(self); return; }
 +      if(!vehicle_initialize(VEH_BUMBLEBEE, false)) { remove(self); return; }
 +}
 +
 +              METHOD(Bumblebee, vr_impact, void(Bumblebee thisveh))
 +              {
 +                      if(autocvar_g_vehicle_bumblebee_bouncepain)
 +                              vehicles_impact(autocvar_g_vehicle_bumblebee_bouncepain_x, autocvar_g_vehicle_bumblebee_bouncepain_y, autocvar_g_vehicle_bumblebee_bouncepain_z);
 +              }
 +              METHOD(Bumblebee, vr_enter, void(Bumblebee thisveh))
 +              {
 +                      SELFPARAM();
 +                      self.touch = bumblebee_touch;
 +                      self.nextthink = 0;
 +                      self.movetype = MOVETYPE_BOUNCEMISSILE;
 +              }
 +              METHOD(Bumblebee, vr_think, void(Bumblebee thisveh))
 +              {
 +                      SELFPARAM();
 +                      self.angles_z *= 0.8;
 +                      self.angles_x *= 0.8;
 +
 +                      self.nextthink = time;
 +
 +                      if(!self.owner)
 +                      {
 +                              entity oldself = self;
 +                              if(self.gunner1)
 +                              {
 +                                      setself(self.gunner1);
 +                                      oldself.gun1.vehicle_exit(VHEF_EJECT);
 +                                      entity oldother = other;
 +                                      other = self;
 +                                      setself(oldself);
 +                                      self.phase = 0;
 +                                      self.touch();
 +                                      other = oldother;
 +                                      return;
 +                              }
 +
 +                              if(self.gunner2)
 +                              {
 +                                      setself(self.gunner2);
 +                                      oldself.gun2.vehicle_exit(VHEF_EJECT);
 +                                      entity oldother = other;
 +                                      other = self;
 +                                      setself(oldself);
 +                                      self.phase = 0;
 +                                      self.touch();
 +                                      other = oldother;
 +                                      return;
 +                              }
 +                      }
 +              }
 +              METHOD(Bumblebee, vr_death, void(Bumblebee thisveh))
 +              {
 +                      SELFPARAM();
 +                      entity oldself = self;
 +
 +                      CSQCModel_UnlinkEntity();
 +
 +                      // Hide beam
 +                      if(self.gun3.enemy || !wasfreed(self.gun3.enemy))
 +                              self.gun3.enemy.effects |= EF_NODRAW;
 +
 +                      if(self.gunner1)
 +                      {
 +                              setself(self.gunner1);
 +                              oldself.gun1.vehicle_exit(VHEF_EJECT);
 +                              setself(oldself);
 +                      }
 +
 +                      if(self.gunner2)
 +                      {
 +                              setself(self.gunner2);
 +                              oldself.gun2.vehicle_exit(VHEF_EJECT);
 +                              setself(oldself);
 +                      }
 +
 +                      self.vehicle_exit(VHEF_EJECT);
 +
 +                      fixedmakevectors(self.angles);
 +                      vehicle_tossgib(self.gun1, self.velocity + v_right * 300 + v_up * 100 + randomvec() * 200, "cannon_right", rint(random()), rint(random()), 6, randomvec() * 200);
 +                      vehicle_tossgib(self.gun2, self.velocity + v_right * -300 + v_up * 100 + randomvec() * 200, "cannon_left", rint(random()), rint(random()), 6, randomvec() * 200);
 +                      vehicle_tossgib(self.gun3, self.velocity + v_forward * 300 + v_up * -100 + randomvec() * 200, "raygun", rint(random()), rint(random()), 6, randomvec() * 300);
 +
 +                      entity _body = vehicle_tossgib(self, self.velocity + randomvec() * 200, "", rint(random()), rint(random()), 6, randomvec() * 100);
 +
 +                      if(random() > 0.5)
 +                              _body.touch = bumblebee_blowup;
 +                      else
 +                              _body.touch = func_null;
 +
 +                      _body.think = bumblebee_diethink;
 +                      _body.nextthink = time;
 +                      _body.wait = time + 2 + (random() * 8);
 +                      _body.owner = self;
 +                      _body.enemy = self.enemy;
 +                      _body.scale = 1.5;
 +                      _body.angles = self.angles;
 +
 +                      Send_Effect(EFFECT_EXPLOSION_MEDIUM, findbetterlocation(self.origin, 16), '0 0 0', 1);
 +
 +                      self.health                     = 0;
 +                      self.event_damage       = func_null;
 +                      self.solid                      = SOLID_NOT;
 +                      self.takedamage         = DAMAGE_NO;
 +                      self.deadflag           = DEAD_DYING;
 +                      self.movetype           = MOVETYPE_NONE;
 +                      self.effects            = EF_NODRAW;
 +                      self.colormod           = '0 0 0';
 +                      self.avelocity          = '0 0 0';
 +                      self.velocity           = '0 0 0';
 +                      self.touch                      = func_null;
 +                      self.nextthink          = 0;
 +
 +                      setorigin(self, self.pos1);
 +              }
 +              METHOD(Bumblebee, vr_spawn, void(Bumblebee thisveh))
 +              {
 +                      SELFPARAM();
 +                      if(!self.gun1)
 +                      {
 +                              // for some reason, autosizing of the shield entity refuses to work for this one so set it up in advance.
 +                              self.vehicle_shieldent = spawn();
 +                              self.vehicle_shieldent.effects = EF_LOWPRECISION;
 +                              setmodel(self.vehicle_shieldent, MDL_VEH_BUMBLEBEE_SHIELD);
 +                              setattachment(self.vehicle_shieldent, self, "");
 +                              setorigin(self.vehicle_shieldent, real_origin(self) - self.origin);
 +                              self.vehicle_shieldent.scale       = 512 / vlen(self.maxs - self.mins);
 +                              self.vehicle_shieldent.think       = shieldhit_think;
 +                              self.vehicle_shieldent.alpha = -1;
 +                              self.vehicle_shieldent.effects = EF_LOWPRECISION | EF_NODRAW;
 +
 +                              self.gun1 = spawn();
 +                              self.gun2 = spawn();
 +                              self.gun3 = spawn();
 +
 +                              self.vehicle_flags |= VHF_MULTISLOT;
 +
 +                              self.gun1.owner = self;
 +                              self.gun2.owner = self;
 +                              self.gun3.owner = self;
 +
 +                              self.gun1.classname = self.gun2.classname = "vehicle_playerslot";
 +
 +                              setmodel(self.gun1, MDL_VEH_BUMBLEBEE_CANNON_RIGHT);
 +                              setmodel(self.gun2, MDL_VEH_BUMBLEBEE_CANNON_LEFT);
 +                              setmodel(self.gun3, MDL_VEH_BUMBLEBEE_CANNON_CENTER);
 +
 +                              setattachment(self.gun1, self, "cannon_right");
 +                              setattachment(self.gun2, self, "cannon_left");
 +
 +                              // Angled bones are no fun, messes up gun-aim; so work arround it.
 +                              self.gun3.pos1 = self.angles;
 +                              self.angles = '0 0 0';
 +                              vector ofs = gettaginfo(self, gettagindex(self, "raygun"));
 +                              ofs -= self.origin;
 +                              setattachment(self.gun3, self, "");
 +                              setorigin(self.gun3, ofs);
 +                              self.angles = self.gun3.pos1;
 +
 +                              vehicle_addplayerslot(self, self.gun1, HUD_BUMBLEBEE_GUN, "models/vehicles/wakizashi_cockpit.dpm", bumblebee_gunner_frame, bumblebee_gunner_exit, bumblebee_gunner_enter);
 +                              vehicle_addplayerslot(self, self.gun2, HUD_BUMBLEBEE_GUN, "models/vehicles/wakizashi_cockpit.dpm", bumblebee_gunner_frame, bumblebee_gunner_exit, bumblebee_gunner_enter);
 +
 +                              setorigin(self.vehicle_hudmodel, '50 0 -5');    // Move cockpit forward - down.
 +                              setorigin(self.vehicle_viewport, '5 0 2');    // Move camera forward up
 +
 +                              //fixme-model-bones
 +                              setorigin(self.gun1.vehicle_hudmodel, '90 -27 -23');
 +                              setorigin(self.gun1.vehicle_viewport, '-85 0 50');
 +                              //fixme-model-bones
 +                              setorigin(self.gun2.vehicle_hudmodel, '90 27 -23');
 +                              setorigin(self.gun2.vehicle_viewport, '-85 0 50');
 +
 +                              self.scale = 1.5;
 +
 +                              // Raygun beam
 +                              if(self.gun3.enemy == world)
 +                              {
 +                                      self.gun3.enemy = spawn();
 +                                      Net_LinkEntity(self.gun3.enemy, true, 0, bumble_raygun_send);
 +                                      self.gun3.enemy.SendFlags = BRG_SETUP;
 +                                      self.gun3.enemy.cnt = autocvar_g_vehicle_bumblebee_raygun;
 +                                      self.gun3.enemy.effects = EF_NODRAW | EF_LOWPRECISION;
 +                              }
 +                      }
 +
 +                      self.vehicle_health = autocvar_g_vehicle_bumblebee_health;
 +                      self.vehicle_shield = autocvar_g_vehicle_bumblebee_shield;
 +                      self.solid = SOLID_BBOX;
 +                      self.movetype = MOVETYPE_TOSS;
 +                      self.damageforcescale = 0.025;
 +
 +                      self.PlayerPhysplug = bumblebee_pilot_frame;
 +
 +                      setorigin(self, self.origin + '0 0 25');
 +              }
 +              METHOD(Bumblebee, vr_setup, void(Bumblebee thisveh))
 +              {
 +                      SELFPARAM();
 +                      if(autocvar_g_vehicle_bumblebee_energy)
 +                      if(autocvar_g_vehicle_bumblebee_energy_regen)
 +                              self.vehicle_flags |= VHF_ENERGYREGEN;
 +
 +                      if(autocvar_g_vehicle_bumblebee_shield)
 +                              self.vehicle_flags |= VHF_HASSHIELD;
 +
 +                      if(autocvar_g_vehicle_bumblebee_shield_regen)
 +                              self.vehicle_flags |= VHF_SHIELDREGEN;
 +
 +                      if(autocvar_g_vehicle_bumblebee_health_regen)
 +                              self.vehicle_flags |= VHF_HEALTHREGEN;
 +
 +                      self.vehicle_exit = bumblebee_exit;
 +                      self.respawntime = autocvar_g_vehicle_bumblebee_respawntime;
 +                      self.vehicle_health = autocvar_g_vehicle_bumblebee_health;
 +                      self.max_health = self.vehicle_health;
 +                      self.vehicle_shield = autocvar_g_vehicle_bumblebee_shield;
 +              }
 +
 +#endif // SVQC
 +#ifdef CSQC
 +
 +void CSQC_BUMBLE_GUN_HUD()
 +{
 +      Vehicles_drawHUD("vehicle_gunner", "vehicle_gunner_weapon1", string_null,
 +                                       "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
 +                                       string_null, '0 0 0',
 +                                       string_null);
 +}
 +
 +              METHOD(Bumblebee, vr_hud, void(Bumblebee thisveh))
 +              {
 +                      Vehicles_drawHUD(VEH_BUMBLEBEE.m_icon, "vehicle_bumble_weapon1", "vehicle_bumble_weapon2",
 +                                                       "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
 +                                                       "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
 +                                                       vCROSS_HEAL);
 +              }
 +              METHOD(Bumblebee, vr_setup, void(Bumblebee thisveh))
 +              {
 +                      AuxiliaryXhair[0].axh_image = vCROSS_LOCK;  // Raygun-locked
 +                      AuxiliaryXhair[1].axh_image = vCROSS_BURST; // Gunner1
 +                      AuxiliaryXhair[2].axh_image = vCROSS_BURST; // Gunner2
 +              }
 +
 +#endif
 +#endif
index e9211d54507163f9fd25ba328574e561ce4dcb73,0000000000000000000000000000000000000000..b5501806c14a8aedaa266ef0e3a81dfaa683418d
mode 100644,000000..100644
--- /dev/null
@@@ -1,693 -1,0 +1,693 @@@
- void spawnfunc_vehicle_racer()
- {SELFPARAM();
 +#ifndef VEHICLE_RACER
 +#define VEHICLE_RACER
 +
 +#include "racer_weapon.qc"
 +
 +CLASS(Racer, Vehicle)
 +/* spawnflags */ ATTRIB(Racer, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL);
 +/* mins       */ ATTRIB(Racer, mins, vector, '-120 -120 -40' * 0.5);
 +/* maxs       */ ATTRIB(Racer, maxs, vector, '120 120 40' * 0.5);
 +/* model        */ ATTRIB(Racer, mdl, string, "models/vehicles/wakizashi.dpm");
 +/* model        */ ATTRIB(Racer, model, string, "models/vehicles/wakizashi.dpm");
 +/* head_model */ ATTRIB(Racer, head_model, string, "null");
 +/* hud_model  */ ATTRIB(Racer, hud_model, string, "models/vehicles/wakizashi_cockpit.dpm");
 +/* tags       */ ATTRIB(Racer, tag_head, string, "");
 +/* tags       */ ATTRIB(Racer, tag_hud, string, "");
 +/* tags       */ ATTRIB(Racer, tag_view, string, "tag_viewport");
 +/* netname    */ ATTRIB(Racer, netname, string, "racer");
 +/* fullname   */ ATTRIB(Racer, vehicle_name, string, _("Racer"));
 +/* icon       */ ATTRIB(Racer, m_icon, string, "vehicle_racer");
 +ENDCLASS(Racer)
 +REGISTER_VEHICLE(RACER, NEW(Racer));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "racer_weapon.qc"
 +
 +#ifdef SVQC
 +#include "../../effects/effects.qh"
 +#include "../../triggers/trigger/impulse.qh"
 +
 +bool autocvar_g_vehicle_racer;
 +
 +float autocvar_g_vehicle_racer_speed_afterburn;
 +float autocvar_g_vehicle_racer_afterburn_cost;
 +
 +float autocvar_g_vehicle_racer_waterburn_cost;
 +float autocvar_g_vehicle_racer_waterburn_speed;
 +
 +float autocvar_g_vehicle_racer_water_speed_forward;
 +float autocvar_g_vehicle_racer_water_speed_strafe;
 +
 +float autocvar_g_vehicle_racer_pitchlimit = 30;
 +
 +float autocvar_g_vehicle_racer_water_downforce = 0.03;
 +float autocvar_g_vehicle_racer_water_upforcedamper = 15;
 +
 +float autocvar_g_vehicle_racer_anglestabilizer;
 +float autocvar_g_vehicle_racer_downforce;
 +
 +float autocvar_g_vehicle_racer_speed_forward;
 +float autocvar_g_vehicle_racer_speed_strafe;
 +float autocvar_g_vehicle_racer_springlength;
 +float autocvar_g_vehicle_racer_upforcedamper;
 +float autocvar_g_vehicle_racer_friction;
 +
 +float autocvar_g_vehicle_racer_water_time = 5;
 +
 +float autocvar_g_vehicle_racer_hovertype;
 +float autocvar_g_vehicle_racer_hoverpower;
 +
 +float autocvar_g_vehicle_racer_turnroll;
 +float autocvar_g_vehicle_racer_turnspeed;
 +float autocvar_g_vehicle_racer_pitchspeed;
 +
 +float autocvar_g_vehicle_racer_energy;
 +float autocvar_g_vehicle_racer_energy_regen;
 +float autocvar_g_vehicle_racer_energy_regen_pause;
 +
 +float autocvar_g_vehicle_racer_health;
 +float autocvar_g_vehicle_racer_health_regen;
 +float autocvar_g_vehicle_racer_health_regen_pause;
 +
 +float autocvar_g_vehicle_racer_shield;
 +float autocvar_g_vehicle_racer_shield_regen;
 +float autocvar_g_vehicle_racer_shield_regen_pause;
 +
 +float autocvar_g_vehicle_racer_rocket_locktarget;
 +float autocvar_g_vehicle_racer_rocket_locking_time;
 +float autocvar_g_vehicle_racer_rocket_locking_releasetime;
 +float autocvar_g_vehicle_racer_rocket_locked_time;
 +
 +float autocvar_g_vehicle_racer_respawntime;
 +
 +float autocvar_g_vehicle_racer_blowup_radius;
 +float autocvar_g_vehicle_racer_blowup_coredamage;
 +float autocvar_g_vehicle_racer_blowup_edgedamage;
 +float autocvar_g_vehicle_racer_blowup_forceintensity;
 +
 +float autocvar_g_vehicle_racer_bouncefactor;
 +float autocvar_g_vehicle_racer_bouncestop;
 +vector autocvar_g_vehicle_racer_bouncepain;
 +
 +.float racer_watertime;
 +
 +var vector racer_force_from_tag(string tag_name, float spring_length, float max_power);
 +
 +void racer_align4point(float _delta)
 +{SELFPARAM();
 +      vector push_vector;
 +      float fl_push, fr_push, bl_push, br_push;
 +
 +      push_vector  = racer_force_from_tag("tag_engine_fr", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
 +      fr_push   = force_fromtag_normpower;
 +      //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
 +
 +      push_vector += racer_force_from_tag("tag_engine_fl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
 +      fl_push   = force_fromtag_normpower;
 +      //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
 +
 +      push_vector += racer_force_from_tag("tag_engine_br", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
 +      br_push   = force_fromtag_normpower;
 +      //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
 +
 +      push_vector += racer_force_from_tag("tag_engine_bl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
 +      bl_push   = force_fromtag_normpower;
 +      //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
 +
 +      self.velocity += push_vector * _delta;
 +
 +      float uforce = autocvar_g_vehicle_racer_upforcedamper;
 +
 +      int cont = pointcontents(self.origin - '0 0 64');
 +      if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
 +      {
 +              uforce = autocvar_g_vehicle_racer_water_upforcedamper;
 +
 +              if(self.owner.BUTTON_CROUCH && time < self.air_finished)
 +                      self.velocity_z += 30;
 +              else
 +                      self.velocity_z += 200;
 +      }
 +
 +
 +      // Anti ocilation
 +      if(self.velocity_z > 0)
 +              self.velocity_z *= 1 - uforce * _delta;
 +
 +      push_vector_x =  (fl_push - bl_push);
 +      push_vector_x += (fr_push - br_push);
 +      push_vector_x *= 360;
 +
 +      push_vector_z = (fr_push - fl_push);
 +      push_vector_z += (br_push - bl_push);
 +      push_vector_z *= 360;
 +
 +      // Apply angle diffrance
 +      self.angles_z += push_vector_z * _delta;
 +      self.angles_x += push_vector_x * _delta;
 +
 +      // Apply stabilizer
 +      self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta);
 +      self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta);
 +}
 +
 +void racer_fire_rocket_aim(string tagname, entity trg)
 +{
 +      SELFPARAM();
 +      vector v = gettaginfo(self, gettagindex(self, tagname));
 +      racer_fire_rocket(v, v_forward, trg);
 +}
 +
 +float racer_frame()
 +{SELFPARAM();
 +      entity player, racer;
 +      vector df;
 +      float ftmp;
 +
 +      if(intermission_running)
 +      {
 +              self.vehicle.velocity = '0 0 0';
 +              self.vehicle.avelocity = '0 0 0';
 +              return 1;
 +      }
 +
 +      player  = self;
 +      racer   = self.vehicle;
 +      setself(racer);
 +
 +      vehicles_painframe();
 +
 +      if(pointcontents(racer.origin) != CONTENT_WATER)
 +              racer.air_finished = time + autocvar_g_vehicle_racer_water_time;
 +
 +      if(racer.deadflag != DEAD_NO)
 +      {
 +              setself(player);
 +              player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
 +              return 1;
 +      }
 +
 +      racer_align4point(PHYS_INPUT_TIMELENGTH);
 +
 +      player.BUTTON_ZOOM = player.BUTTON_CROUCH = 0;
 +
 +      crosshair_trace(player);
 +
 +      racer.angles_x *= -1;
 +
 +      // Yaw
 +      ftmp = autocvar_g_vehicle_racer_turnspeed * PHYS_INPUT_TIMELENGTH;
 +      ftmp = bound(-ftmp, shortangle_f(player.v_angle_y - racer.angles_y, racer.angles_y), ftmp);
 +      racer.angles_y = anglemods(racer.angles_y + ftmp);
 +
 +      // Roll
 +      racer.angles_z += -ftmp * autocvar_g_vehicle_racer_turnroll * PHYS_INPUT_TIMELENGTH;
 +
 +      // Pitch
 +      ftmp = autocvar_g_vehicle_racer_pitchspeed  * PHYS_INPUT_TIMELENGTH;
 +      ftmp = bound(-ftmp, shortangle_f(player.v_angle_x - racer.angles_x, racer.angles_x), ftmp);
 +      racer.angles_x = bound(-autocvar_g_vehicle_racer_pitchlimit, anglemods(racer.angles_x + ftmp), autocvar_g_vehicle_racer_pitchlimit);
 +
 +      makevectors(racer.angles);
 +      racer.angles_x *= -1;
 +
 +      //ftmp = racer.velocity_z;
 +      df = racer.velocity * -autocvar_g_vehicle_racer_friction;
 +      //racer.velocity_z = ftmp;
 +
 +      int cont = pointcontents(racer.origin);
 +      if(vlen(player.movement) != 0)
 +      {
 +              if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
 +              {
 +                      if(player.movement_x) { df += v_forward * ((player.movement_x > 0) ? autocvar_g_vehicle_racer_water_speed_forward : -autocvar_g_vehicle_racer_water_speed_forward); }
 +                      if(player.movement_y) { df += v_right * ((player.movement_y > 0) ? autocvar_g_vehicle_racer_water_speed_strafe : -autocvar_g_vehicle_racer_water_speed_strafe); }
 +              }
 +              else
 +              {
 +                      if(player.movement_x) { df += v_forward * ((player.movement_x > 0) ? autocvar_g_vehicle_racer_speed_forward : -autocvar_g_vehicle_racer_speed_forward); }
 +                      if(player.movement_y) { df += v_right * ((player.movement_y > 0) ? autocvar_g_vehicle_racer_speed_strafe : -autocvar_g_vehicle_racer_speed_strafe); }
 +              }
 +
 +#ifdef SVQC
 +              if(self.sound_nexttime < time || self.sounds != 1)
 +              {
 +                      self.sounds = 1;
 +                      self.sound_nexttime = time + 10.922667; //soundlength("vehicles/racer_move.wav");
 +                      sound (self, CH_TRIGGER_SINGLE, SND_VEH_RACER_MOVE, VOL_VEHICLEENGINE, ATTEN_NORM);
 +              }
 +#endif
 +      }
 +#ifdef SVQC
 +      else
 +      {
 +              if(self.sound_nexttime < time || self.sounds != 0)
 +              {
 +                      self.sounds = 0;
 +                      self.sound_nexttime = time + 11.888604; //soundlength("vehicles/racer_idle.wav");
 +                      sound (self, CH_TRIGGER_SINGLE, SND_VEH_RACER_IDLE, VOL_VEHICLEENGINE, ATTEN_NORM);
 +              }
 +      }
 +#endif
 +
 +      // Afterburn
 +      if (PHYS_INPUT_BUTTON_JUMP(player) && racer.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * PHYS_INPUT_TIMELENGTH))
 +      {
 +#ifdef SVQC
 +              if(time - racer.wait > 0.2)
 +                      pointparticles(particleeffectnum(EFFECT_RACER_BOOSTER), self.origin - v_forward * 32, v_forward  * vlen(self.velocity), 1);
 +#endif
 +
 +              racer.wait = time;
 +
 +              if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
 +              {
 +                      racer.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * PHYS_INPUT_TIMELENGTH;
 +                      df += (v_forward * autocvar_g_vehicle_racer_waterburn_speed);
 +              }
 +              else
 +              {
 +                      racer.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * PHYS_INPUT_TIMELENGTH;
 +                      df += (v_forward * autocvar_g_vehicle_racer_speed_afterburn);
 +              }
 +
 +#ifdef SVQC
 +              if(racer.invincible_finished < time)
 +              {
 +                      traceline(racer.origin, racer.origin - '0 0 256', MOVE_NORMAL, self);
 +                      if(trace_fraction != 1.0)
 +                              pointparticles(particleeffectnum(EFFECT_SMOKE_SMALL), trace_endpos, '0 0 0', 1);
 +
 +                      racer.invincible_finished = time + 0.1 + (random() * 0.1);
 +              }
 +
 +              if(racer.strength_finished < time)
 +              {
 +                      racer.strength_finished = time + 10.922667; //soundlength("vehicles/racer_boost.wav");
 +                      sound (racer.tur_head, CH_TRIGGER_SINGLE, SND_VEH_RACER_BOOST, VOL_VEHICLEENGINE, ATTEN_NORM);
 +              }
 +#endif
 +      }
 +      else
 +      {
 +              racer.strength_finished = 0;
 +              sound (racer.tur_head, CH_TRIGGER_SINGLE, SND_Null, VOL_VEHICLEENGINE, ATTEN_NORM);
 +      }
 +
 +      if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
 +              racer.racer_watertime = time;
 +
 +      float dforce = autocvar_g_vehicle_racer_downforce;
 +      if(time - racer.racer_watertime <= 3)
 +              dforce = autocvar_g_vehicle_racer_water_downforce;
 +
 +      df -= v_up * (vlen(racer.velocity) * dforce);
 +      player.movement = racer.velocity += df * PHYS_INPUT_TIMELENGTH;
 +
 +#ifdef SVQC
 +      Weapon wep1 = WEP_RACER;
 +      if (!forbidWeaponUse(player))
 +      if (player.BUTTON_ATCK)
 +      if (wep1.wr_checkammo1(wep1))
 +      {
 +              string tagname = (racer.cnt)
 +                  ? (racer.cnt = 0, "tag_fire1")
 +                  : (racer.cnt = 1, "tag_fire2");
 +              vector org = gettaginfo(self, gettagindex(self, tagname));
 +              w_shotorg = org;
 +              w_shotdir = v_forward;
 +              // Fix z-aim (for chase mode)
 +              crosshair_trace(player);
 +              w_shotdir.z = normalize(trace_endpos - org).z * 0.5;
 +              wep1.wr_think(wep1, self, true, false);
 +      }
 +
 +      if(autocvar_g_vehicle_racer_rocket_locktarget)
 +      {
 +              vehicles_locktarget((1 / autocvar_g_vehicle_racer_rocket_locking_time) * frametime,
 +                                               (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * frametime,
 +                                               autocvar_g_vehicle_racer_rocket_locked_time);
 +
 +              if(self.lock_target)
 +              {
 +                      if(racer.lock_strength == 1)
 +                              UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '1 0 0', 0);
 +                      else if(self.lock_strength > 0.5)
 +                              UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '0 1 0', 0);
 +                      else if(self.lock_strength < 0.5)
 +                              UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '0 0 1', 0);
 +              }
 +      }
 +
 +      if(!forbidWeaponUse(player))
 +      if(time > racer.delay)
 +      if(player.BUTTON_ATCK2)
 +      {
 +              racer.misc_bulletcounter += 1;
 +              racer.delay = time + 0.3;
 +
 +              if(racer.misc_bulletcounter == 1)
 +              {
 +                      racer_fire_rocket_aim("tag_rocket_r", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world);
 +                      player.vehicle_ammo2 = 50;
 +              }
 +              else if(racer.misc_bulletcounter == 2)
 +              {
 +                      racer_fire_rocket_aim("tag_rocket_l", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world);
 +                      racer.lock_strength  = 0;
 +                      racer.lock_target       = world;
 +                      racer.misc_bulletcounter = 0;
 +                      racer.delay = time + autocvar_g_vehicle_racer_rocket_refire;
 +                      racer.lip = time;
 +                      player.vehicle_ammo2 = 0;
 +              }
 +      }
 +      else if(racer.misc_bulletcounter == 0)
 +              player.vehicle_ammo2 = 100;
 +
 +      player.vehicle_reload2 = bound(0, 100 * ((time - racer.lip) / (racer.delay - racer.lip)), 100);
 +
 +      if(racer.vehicle_flags  & VHF_SHIELDREGEN)
 +              vehicles_regen(racer.dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, frametime, true);
 +
 +      if(racer.vehicle_flags  & VHF_HEALTHREGEN)
 +              vehicles_regen(racer.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, frametime, false);
 +
 +      if(racer.vehicle_flags  & VHF_ENERGYREGEN)
 +              vehicles_regen(racer.wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, frametime, false);
 +
 +
 +      VEHICLE_UPDATE_PLAYER(player, health, racer);
 +      VEHICLE_UPDATE_PLAYER(player, energy, racer);
 +
 +      if(racer.vehicle_flags & VHF_HASSHIELD)
 +              VEHICLE_UPDATE_PLAYER(player, shield, racer);
 +
 +      player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
 +#endif
 +
 +      setorigin(player,racer.origin + '0 0 32');
 +      player.velocity = racer.velocity;
 +
 +      setself(player);
 +      return 1;
 +}
 +
 +void racer_think()
 +{SELFPARAM();
 +      self.nextthink = time;
 +
 +      float pushdeltatime = time - self.lastpushtime;
 +      if (pushdeltatime > 0.15) pushdeltatime = 0;
 +      self.lastpushtime = time;
 +      if(!pushdeltatime) return;
 +
 +      tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * autocvar_g_vehicle_racer_springlength), MOVE_NOMONSTERS, self);
 +
 +      vector df = self.velocity * -autocvar_g_vehicle_racer_friction;
 +      df_z += (1 - trace_fraction) * autocvar_g_vehicle_racer_hoverpower + sin(time * 2) * (autocvar_g_vehicle_racer_springlength * 2);
 +
 +      float forced = autocvar_g_vehicle_racer_upforcedamper;
 +
 +      int cont = pointcontents(self.origin - '0 0 64');
 +      if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
 +      {
 +              forced = autocvar_g_vehicle_racer_water_upforcedamper;
 +              self.velocity_z += 200;
 +      }
 +
 +      self.velocity += df * pushdeltatime;
 +      if(self.velocity_z > 0)
 +              self.velocity_z *= 1 - forced * pushdeltatime;
 +
 +      self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime);
 +      self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime);
 +
 +      CSQCMODEL_AUTOUPDATE(self);
 +}
 +
 +void racer_exit(float eject)
 +{SELFPARAM();
 +      vector spot;
 +
 +      self.think        = racer_think;
 +      self.nextthink  = time;
 +      self.movetype   = MOVETYPE_BOUNCE;
 +      sound (self.tur_head, CH_TRIGGER_SINGLE, SND_Null, VOL_VEHICLEENGINE, ATTEN_NORM);
 +
 +      if(!self.owner)
 +              return;
 +
 +      makevectors(self.angles);
 +      if(eject)
 +      {
 +              spot = self.origin + v_forward * 100 + '0 0 64';
 +              spot = vehicles_findgoodexit(spot);
 +              setorigin(self.owner , spot);
 +              self.owner.velocity = (v_up + v_forward * 0.25) * 750;
 +              self.owner.oldvelocity = self.owner.velocity;
 +      }
 +      else
 +      {
 +              if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed)
 +              {
 +                      self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2;
 +                      self.owner.velocity_z += 200;
 +                      spot = self.origin + v_forward * 32 + '0 0 32';
 +                      spot = vehicles_findgoodexit(spot);
 +              }
 +              else
 +              {
 +                      self.owner.velocity = self.velocity * 0.5;
 +                      self.owner.velocity_z += 10;
 +                      spot = self.origin - v_forward * 200 + '0 0 32';
 +                      spot = vehicles_findgoodexit(spot);
 +              }
 +              self.owner.oldvelocity = self.owner.velocity;
 +              setorigin(self.owner , spot);
 +      }
 +      antilag_clear(self.owner);
 +      self.owner = world;
 +}
 +
 +void racer_blowup()
 +{SELFPARAM();
 +      self.deadflag   = DEAD_DEAD;
 +      self.vehicle_exit(VHEF_NORMAL);
 +
 +      RadiusDamage (self, self.enemy, autocvar_g_vehicle_racer_blowup_coredamage,
 +                                      autocvar_g_vehicle_racer_blowup_edgedamage,
 +                                      autocvar_g_vehicle_racer_blowup_radius, world, world,
 +                                      autocvar_g_vehicle_racer_blowup_forceintensity,
 +                                      DEATH_VH_WAKI_DEATH, world);
 +
 +      self.nextthink  = time + autocvar_g_vehicle_racer_respawntime;
 +      self.think        = vehicles_spawn;
 +      self.movetype   = MOVETYPE_NONE;
 +      self.effects    = EF_NODRAW;
 +
 +      self.colormod  = '0 0 0';
 +      self.avelocity = '0 0 0';
 +      self.velocity  = '0 0 0';
 +
 +      setorigin(self, self.pos1);
 +}
 +
 +void racer_blowup_think()
 +{SELFPARAM();
 +      self.nextthink = time;
 +
 +      if(time >= self.delay)
 +              racer_blowup();
 +
 +      CSQCMODEL_AUTOUPDATE(self);
 +}
 +
 +void racer_deadtouch()
 +{SELFPARAM();
 +      self.avelocity_x *= 0.7;
 +      self.cnt -= 1;
 +      if(self.cnt <= 0)
 +              racer_blowup();
 +}
 +
++spawnfunc(vehicle_racer)
++{
 +      if(!autocvar_g_vehicle_racer) { remove(self); return; }
 +      if(!vehicle_initialize(VEH_RACER, false)) { remove(self); return; }
 +}
 +
 +#endif // SVQC
 +
 +#ifdef CSQC
 +#if 0
 +void racer_draw()
 +{SELFPARAM();
 +      float pushdeltatime = time - self.lastpushtime;
 +      if (pushdeltatime > 0.15) pushdeltatime = 0;
 +      self.lastpushtime = time;
 +      if(!pushdeltatime) return;
 +
 +      tracebox(self.move_origin, self.mins, self.maxs, self.move_origin - ('0 0 1' * getstatf(STAT_VEH_RACER_SPRINGLENGTH)), MOVE_NOMONSTERS, self);
 +
 +      vector df = self.move_velocity * -getstatf(STAT_VEH_RACER_FRICTION);
 +      df_z += (1 - trace_fraction) * getstatf(STAT_VEH_RACER_HOVERPOWER) + sin(time * 2) * (getstatf(STAT_VEH_RACER_SPRINGLENGTH) * 2);
 +
 +      float forced = getstatf(STAT_VEH_RACER_UPFORCEDAMPER);
 +
 +      int cont = pointcontents(self.move_origin - '0 0 64');
 +      if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
 +      {
 +              forced = getstatf(STAT_VEH_RACER_WATER_UPFORCEDAMPER);
 +              self.move_velocity_z += 200;
 +      }
 +
 +      self.move_velocity += df * pushdeltatime;
 +      if(self.move_velocity_z > 0)
 +              self.move_velocity_z *= 1 - forced * pushdeltatime;
 +
 +      self.move_angles_x *= 1 - (getstatf(STAT_VEH_RACER_ANGLESTABILIZER) * pushdeltatime);
 +      self.move_angles_z *= 1 - (getstatf(STAT_VEH_RACER_ANGLESTABILIZER) * pushdeltatime);
 +
 +      Movetype_Physics_MatchServer(false);
 +}
 +#endif
 +#endif
 +
 +              METHOD(Racer, vr_impact, void(Racer thisveh))
 +              {
 +              #ifdef SVQC
 +                      if(autocvar_g_vehicle_racer_bouncepain)
 +                              vehicles_impact(autocvar_g_vehicle_racer_bouncepain_x, autocvar_g_vehicle_racer_bouncepain_y, autocvar_g_vehicle_racer_bouncepain_z);
 +              #endif
 +              }
 +
 +              METHOD(Racer, vr_enter, void(Racer thisveh))
 +              {
 +              #ifdef SVQC
 +                      self.movetype = MOVETYPE_BOUNCE;
 +                      self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_racer_health)  * 100;
 +                      self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_racer_shield)  * 100;
 +
 +                      if(self.owner.flagcarried)
 +                         setorigin(self.owner.flagcarried, '-190 0 96');
 +              #elif defined(CSQC)
 +
 +                      self.move_movetype = MOVETYPE_BOUNCE;
 +              #endif
 +              }
 +
 +              METHOD(Racer, vr_spawn, void(Racer thisveh))
 +              {
 +              #ifdef SVQC
 +                      if(self.scale != 0.5)
 +                      {
 +                              if(autocvar_g_vehicle_racer_hovertype != 0)
 +                                      racer_force_from_tag = vehicles_force_fromtag_maglev;
 +                              else
 +                                      racer_force_from_tag = vehicles_force_fromtag_hover;
 +
 +                              // FIXME: this be hakkz, fix the models insted (scale body, add tag_viewport to the hudmodel).
 +                              self.scale = 0.5;
 +                              setattachment(self.vehicle_hudmodel, self, "");
 +                              setattachment(self.vehicle_viewport, self, "tag_viewport");
 +
 +                              self.mass                          = 900;
 +                      }
 +
 +                      self.think                = racer_think;
 +                      self.nextthink    = time;
 +                      self.vehicle_health = autocvar_g_vehicle_racer_health;
 +                      self.vehicle_shield = autocvar_g_vehicle_racer_shield;
 +
 +                      self.movetype     = MOVETYPE_TOSS;
 +                      self.solid                = SOLID_SLIDEBOX;
 +                      self.delay                = time;
 +                      self.scale                = 0.5;
 +
 +                      self.PlayerPhysplug = racer_frame;
 +
 +                      self.bouncefactor = autocvar_g_vehicle_racer_bouncefactor;
 +                      self.bouncestop = autocvar_g_vehicle_racer_bouncestop;
 +                      self.damageforcescale = 0.5;
 +                      self.vehicle_health = autocvar_g_vehicle_racer_health;
 +                      self.vehicle_shield = autocvar_g_vehicle_racer_shield;
 +              #endif
 +              }
 +
 +              METHOD(Racer, vr_death, void(Racer thisveh))
 +              {
 +              #ifdef SVQC
 +                      self.SendEntity         = func_null; // stop networking this racer (for now)
 +                      self.health                     = 0;
 +                      self.event_damage       = func_null;
 +                      self.solid                      = SOLID_CORPSE;
 +                      self.takedamage         = DAMAGE_NO;
 +                      self.deadflag           = DEAD_DYING;
 +                      self.movetype           = MOVETYPE_BOUNCE;
 +                      self.wait                       = time;
 +                      self.delay                      = 2 + time + random() * 3;
 +                      self.cnt                        = 1 + random() * 2;
 +                      self.touch                      = racer_deadtouch;
 +
 +                      Send_Effect(EFFECT_EXPLOSION_MEDIUM, self.origin, '0 0 0', 1);
 +
 +                      if(random() < 0.5)
 +                              self.avelocity_z = 32;
 +                      else
 +                              self.avelocity_z = -32;
 +
 +                      self.avelocity_x = -vlen(self.velocity) * 0.2;
 +                      self.velocity += '0 0 700';
 +                      self.colormod = '-0.5 -0.5 -0.5';
 +
 +                      self.think = racer_blowup_think;
 +                      self.nextthink = time;
 +              #endif
 +              }
 +
 +#ifdef CSQC
 +              METHOD(Racer, vr_hud, void(Racer thisveh))
 +              {
 +                      Vehicles_drawHUD(VEH_RACER.m_icon, "vehicle_racer_weapon1", "vehicle_racer_weapon2",
 +                                                       "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
 +                                                       "vehicle_icon_ammo2", autocvar_hud_progressbar_vehicles_ammo2_color,
 +                                                       vCROSS_GUIDE);
 +              }
 +#endif
 +              METHOD(Racer, vr_setup, void(Racer thisveh))
 +              {
 +              #ifdef SVQC
 +                      self.vehicle_exit = racer_exit;
 +              #endif
 +
 +              #ifdef SVQC
 +                      // we have no need to network energy
 +                      if(autocvar_g_vehicle_racer_energy)
 +                      if(autocvar_g_vehicle_racer_energy_regen)
 +                              self.vehicle_flags |= VHF_ENERGYREGEN;
 +
 +                      if(autocvar_g_vehicle_racer_shield)
 +                              self.vehicle_flags |= VHF_HASSHIELD;
 +
 +                      if(autocvar_g_vehicle_racer_shield_regen)
 +                              self.vehicle_flags |= VHF_SHIELDREGEN;
 +
 +                      if(autocvar_g_vehicle_racer_health_regen)
 +                              self.vehicle_flags |= VHF_HEALTHREGEN;
 +
 +                      self.respawntime = autocvar_g_vehicle_racer_respawntime;
 +                      self.vehicle_health = autocvar_g_vehicle_racer_health;
 +                      self.vehicle_shield = autocvar_g_vehicle_racer_shield;
 +                      self.max_health = self.vehicle_health;
 +              #endif
 +
 +              #ifdef CSQC
 +                      AuxiliaryXhair[0].axh_image = vCROSS_LOCK; // Rocket
 +              #endif
 +              }
 +
 +#endif
index db3d880229022b4f960ed2d0159a81838e563c30,0000000000000000000000000000000000000000..1f623379e55d1c1d50224bb0e66278e655caadea
mode 100644,000000..100644
--- /dev/null
@@@ -1,797 -1,0 +1,797 @@@
- void spawnfunc_vehicle_raptor()
- {SELFPARAM();
 +#ifndef VEHICLE_RAPTOR
 +#define VEHICLE_RAPTOR
 +#include "raptor.qh"
 +
 +#include "raptor_weapons.qc"
 +
 +CLASS(Raptor, Vehicle)
 +/* spawnflags */ ATTRIB(Raptor, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL);
 +/* mins       */ ATTRIB(Raptor, mins, vector, '-80 -80 0');
 +/* maxs       */ ATTRIB(Raptor, maxs, vector, '80 80 70');
 +/* model        */ ATTRIB(Raptor, mdl, string, "models/vehicles/raptor.dpm");
 +/* model        */ ATTRIB(Raptor, model, string, "models/vehicles/raptor.dpm");
 +/* head_model */ ATTRIB(Raptor, head_model, string, "");
 +/* hud_model  */ ATTRIB(Raptor, hud_model, string, "models/vehicles/raptor_cockpit.dpm");
 +/* tags       */ ATTRIB(Raptor, tag_head, string, "");
 +/* tags       */ ATTRIB(Raptor, tag_hud, string, "tag_hud");
 +/* tags       */ ATTRIB(Raptor, tag_view, string, "tag_camera");
 +/* netname    */ ATTRIB(Raptor, netname, string, "raptor");
 +/* fullname   */ ATTRIB(Raptor, vehicle_name, string, _("Raptor"));
 +/* icon       */ ATTRIB(Raptor, m_icon, string, "vehicle_raptor");
 +ENDCLASS(Raptor)
 +REGISTER_VEHICLE(RAPTOR, NEW(Raptor));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +#include "raptor_weapons.qc"
 +
 +#ifdef SVQC
 +
 +bool autocvar_g_vehicle_raptor;
 +
 +float autocvar_g_vehicle_raptor_respawntime;
 +float autocvar_g_vehicle_raptor_takeofftime;
 +
 +float autocvar_g_vehicle_raptor_movestyle;
 +float autocvar_g_vehicle_raptor_turnspeed;
 +float autocvar_g_vehicle_raptor_pitchspeed;
 +float autocvar_g_vehicle_raptor_pitchlimit;
 +
 +float autocvar_g_vehicle_raptor_speed_forward;
 +float autocvar_g_vehicle_raptor_speed_strafe;
 +float autocvar_g_vehicle_raptor_speed_up;
 +float autocvar_g_vehicle_raptor_speed_down;
 +float autocvar_g_vehicle_raptor_friction;
 +
 +float autocvar_g_vehicle_raptor_cannon_turnspeed;
 +float autocvar_g_vehicle_raptor_cannon_turnlimit;
 +float autocvar_g_vehicle_raptor_cannon_pitchlimit_up;
 +float autocvar_g_vehicle_raptor_cannon_pitchlimit_down;
 +
 +float autocvar_g_vehicle_raptor_cannon_locktarget;
 +float autocvar_g_vehicle_raptor_cannon_locking_time;
 +float autocvar_g_vehicle_raptor_cannon_locking_releasetime;
 +float autocvar_g_vehicle_raptor_cannon_locked_time;
 +float autocvar_g_vehicle_raptor_cannon_predicttarget;
 +
 +float autocvar_g_vehicle_raptor_energy;
 +float autocvar_g_vehicle_raptor_energy_regen;
 +float autocvar_g_vehicle_raptor_energy_regen_pause;
 +
 +float autocvar_g_vehicle_raptor_health;
 +float autocvar_g_vehicle_raptor_health_regen;
 +float autocvar_g_vehicle_raptor_health_regen_pause;
 +
 +float autocvar_g_vehicle_raptor_shield;
 +float autocvar_g_vehicle_raptor_shield_regen;
 +float autocvar_g_vehicle_raptor_shield_regen_pause;
 +
 +float autocvar_g_vehicle_raptor_bouncefactor;
 +float autocvar_g_vehicle_raptor_bouncestop;
 +vector autocvar_g_vehicle_raptor_bouncepain;
 +
 +.entity bomb1;
 +.entity bomb2;
 +
 +float raptor_altitude(float amax)
 +{SELFPARAM();
 +      tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * amax), MOVE_WORLDONLY, self);
 +      return vlen(self.origin - trace_endpos);
 +}
 +
 +void raptor_land()
 +{SELFPARAM();
 +      float hgt;
 +
 +      hgt = raptor_altitude(512);
 +      self.velocity = (self.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime);
 +      self.angles_x *= 0.95;
 +      self.angles_z *= 0.95;
 +
 +      if(hgt < 128)
 +      if(hgt > 0)
 +              self.frame = (hgt / 128) * 25;
 +
 +      self.bomb1.gun1.avelocity_y = 90 + ((self.frame / 25) * 2000);
 +      self.bomb1.gun2.avelocity_y = -self.bomb1.gun1.avelocity_y;
 +
 +      if(hgt < 16)
 +      {
 +              self.movetype = MOVETYPE_TOSS;
 +              self.think      = vehicles_think;
 +              self.frame      = 0;
 +      }
 +
 +      self.nextthink  = time;
 +
 +      CSQCMODEL_AUTOUPDATE(self);
 +}
 +
 +void raptor_exit(float eject)
 +{SELFPARAM();
 +      vector spot;
 +      self.tur_head.exteriormodeltoclient = world;
 +
 +      if(self.deadflag == DEAD_NO)
 +      {
 +              self.think        = raptor_land;
 +              self.nextthink  = time;
 +      }
 +
 +      if(!self.owner)
 +              return;
 +
 +      makevectors(self.angles);
 +      if(eject)
 +      {
 +              spot = self.origin + v_forward * 100 + '0 0 64';
 +              spot = vehicles_findgoodexit(spot);
 +              setorigin(self.owner , spot);
 +              self.owner.velocity = (v_up + v_forward * 0.25) * 750;
 +              self.owner.oldvelocity = self.owner.velocity;
 +      }
 +      else
 +      {
 +              if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed)
 +              {
 +                      self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2;
 +                      self.owner.velocity_z += 200;
 +                      spot = self.origin + v_forward * 32 + '0 0 64';
 +                      spot = vehicles_findgoodexit(spot);
 +              }
 +              else
 +              {
 +                      self.owner.velocity = self.velocity * 0.5;
 +                      self.owner.velocity_z += 10;
 +                      spot = self.origin - v_forward * 200 + '0 0 64';
 +                      spot = vehicles_findgoodexit(spot);
 +              }
 +              self.owner.oldvelocity = self.owner.velocity;
 +              setorigin(self.owner , spot);
 +      }
 +
 +      antilag_clear(self.owner);
 +      self.owner = world;
 +}
 +
 +float raptor_frame()
 +{SELFPARAM();
 +      entity player, raptor;
 +      float ftmp = 0;
 +      vector df;
 +
 +      if(intermission_running)
 +      {
 +              self.vehicle.velocity = '0 0 0';
 +              self.vehicle.avelocity = '0 0 0';
 +              return 1;
 +      }
 +
 +      player = self;
 +      raptor = self.vehicle;
 +      setself(raptor);
 +
 +      vehicles_painframe();
 +      /*
 +      ftmp = vlen(self.velocity);
 +      if(ftmp > autocvar_g_vehicle_raptor_speed_forward)
 +              ftmp = 1;
 +      else
 +              ftmp = ftmp / autocvar_g_vehicle_raptor_speed_forward;
 +      */
 +
 +      if(self.sound_nexttime < time)
 +      {
 +              self.sound_nexttime = time + 7.955812;
 +              //sound (self.tur_head, CH_TRIGGER_SINGLE, SND_VEH_RAPTOR_FLY, 1 - ftmp,   ATTEN_NORM );
 +              sound (self, CH_TRIGGER_SINGLE, SND_VEH_RAPTOR_SPEED, 1, ATTEN_NORM);
 +              self.wait = ftmp;
 +      }
 +      /*
 +      else if(fabs(ftmp - self.wait) > 0.2)
 +      {
 +              sound (self.tur_head, CH_TRIGGER_SINGLE, SND_Null, 1 - ftmp,   ATTEN_NORM );
 +              sound (self, CH_TRIGGER_SINGLE, SND_Null, ftmp, ATTEN_NORM);
 +              self.wait = ftmp;
 +      }
 +      */
 +
 +      if(raptor.deadflag != DEAD_NO)
 +      {
 +              setself(player);
 +              player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
 +              return 1;
 +      }
 +      crosshair_trace(player);
 +
 +      //if(time - self.lastteleporttime < 1)
 +      //{
 +              if(raptor.angles_z > 50 || raptor.angles_z < -50)
 +              {
 +                      if(player.BUTTON_JUMP)
 +                      {
 +                              player.BUTTON_CROUCH = true;
 +                              player.BUTTON_JUMP = false;
 +                      }
 +              }
 +      //}
 +
 +      vector vang;
 +      vang = raptor.angles;
 +      df = vectoangles(normalize(trace_endpos - self.origin + '0 0 32'));
 +      vang_x *= -1;
 +      df_x *= -1;
 +      if(df_x > 180)  df_x -= 360;
 +      if(df_x < -180) df_x += 360;
 +      if(df_y > 180)  df_y -= 360;
 +      if(df_y < -180) df_y += 360;
 +
 +      ftmp = shortangle_f(player.v_angle_y - vang_y, vang_y);
 +      if(ftmp > 180)  ftmp -= 360; if(ftmp < -180) ftmp += 360;
 +      raptor.avelocity_y = bound(-autocvar_g_vehicle_raptor_turnspeed, ftmp + raptor.avelocity_y * 0.9, autocvar_g_vehicle_raptor_turnspeed);
 +
 +      // Pitch
 +      ftmp = 0;
 +      if(player.movement_x > 0 && vang_x < autocvar_g_vehicle_raptor_pitchlimit) ftmp = 5;
 +      else if(player.movement_x < 0 && vang_x > -autocvar_g_vehicle_raptor_pitchlimit) ftmp = -20;
 +
 +      df_x = bound(-autocvar_g_vehicle_raptor_pitchlimit, df_x , autocvar_g_vehicle_raptor_pitchlimit);
 +      ftmp = vang_x - bound(-autocvar_g_vehicle_raptor_pitchlimit, df_x + ftmp, autocvar_g_vehicle_raptor_pitchlimit);
 +      raptor.avelocity_x = bound(-autocvar_g_vehicle_raptor_pitchspeed, ftmp + raptor.avelocity_x * 0.9, autocvar_g_vehicle_raptor_pitchspeed);
 +
 +      raptor.angles_x = anglemods(raptor.angles_x);
 +      raptor.angles_y = anglemods(raptor.angles_y);
 +      raptor.angles_z = anglemods(raptor.angles_z);
 +
 +      if(autocvar_g_vehicle_raptor_movestyle == 1)
 +              makevectors('0 1 0' * raptor.angles_y);
 +      else
 +              makevectors(player.v_angle);
 +
 +      df = raptor.velocity * -autocvar_g_vehicle_raptor_friction;
 +
 +      if(player.movement_x != 0)
 +      {
 +              if(player.movement_x > 0)
 +                      df += v_forward  * autocvar_g_vehicle_raptor_speed_forward;
 +              else if(player.movement_x < 0)
 +                      df -= v_forward  * autocvar_g_vehicle_raptor_speed_forward;
 +      }
 +
 +      if(player.movement_y != 0)
 +      {
 +              if(player.movement_y < 0)
 +                      df -= v_right * autocvar_g_vehicle_raptor_speed_strafe;
 +              else if(player.movement_y > 0)
 +                      df += v_right * autocvar_g_vehicle_raptor_speed_strafe;
 +
 +              raptor.angles_z = bound(-30,raptor.angles_z + (player.movement_y / autocvar_g_vehicle_raptor_speed_strafe),30);
 +      }
 +      else
 +      {
 +              raptor.angles_z *= 0.95;
 +              if(raptor.angles_z >= -1 && raptor.angles_z <= -1)
 +                      raptor.angles_z = 0;
 +      }
 +
 +      if(player.BUTTON_CROUCH)
 +              df -=   v_up * autocvar_g_vehicle_raptor_speed_down;
 +      else if (player.BUTTON_JUMP)
 +              df +=  v_up * autocvar_g_vehicle_raptor_speed_up;
 +
 +      raptor.velocity  += df * frametime;
 +      player.velocity = player.movement  = raptor.velocity;
 +      setorigin(player, raptor.origin + '0 0 32');
 +
 +      player.vehicle_weapon2mode = raptor.vehicle_weapon2mode;
 +
 +      vector vf, ad;
 +      // Target lock & predict
 +      if(autocvar_g_vehicle_raptor_cannon_locktarget == 2)
 +      {
 +              if(raptor.gun1.lock_time < time || raptor.gun1.enemy.deadflag)
 +                      raptor.gun1.enemy = world;
 +
 +              if(trace_ent)
 +              if(trace_ent.movetype)
 +              if(trace_ent.takedamage)
 +              if(!trace_ent.deadflag)
 +              {
 +                      if(teamplay)
 +                      {
 +                              if(trace_ent.team != player.team)
 +                              {
 +                                      raptor.gun1.enemy = trace_ent;
 +                                      raptor.gun1.lock_time = time + 5;
 +                              }
 +                      }
 +                      else
 +                      {
 +                              raptor.gun1.enemy = trace_ent;
 +                              raptor.gun1.lock_time = time + 0.5;
 +                      }
 +              }
 +
 +              if(raptor.gun1.enemy)
 +              {
 +                      float distance, impact_time;
 +
 +                      vf = real_origin(raptor.gun1.enemy);
 +                      UpdateAuxiliaryXhair(player, vf, '1 0 0', 1);
 +                      vector _vel = raptor.gun1.enemy.velocity;
 +                      if(raptor.gun1.enemy.movetype == MOVETYPE_WALK)
 +                              _vel_z *= 0.1;
 +
 +                      if(autocvar_g_vehicle_raptor_cannon_predicttarget)
 +                      {
 +                              ad = vf;
 +                              distance = vlen(ad - player.origin);
 +                              impact_time = distance / autocvar_g_vehicle_raptor_cannon_speed;
 +                              ad = vf + _vel * impact_time;
 +                              trace_endpos = ad;
 +                      }
 +                      else
 +                              trace_endpos = vf;
 +              }
 +      }
 +      else if(autocvar_g_vehicle_raptor_cannon_locktarget == 1)
 +      {
 +
 +              vehicles_locktarget((1 / autocvar_g_vehicle_raptor_cannon_locking_time) * frametime,
 +                                                       (1 / autocvar_g_vehicle_raptor_cannon_locking_releasetime) * frametime,
 +                                                       autocvar_g_vehicle_raptor_cannon_locked_time);
 +
 +              if(self.lock_target != world)
 +              if(autocvar_g_vehicle_raptor_cannon_predicttarget)
 +              if(self.lock_strength == 1)
 +              {
 +                      float i, distance, impact_time;
 +
 +                      vf = real_origin(raptor.lock_target);
 +                      ad = vf;
 +                      for(i = 0; i < 4; ++i)
 +                      {
 +                              distance = vlen(ad - raptor.origin);
 +                              impact_time = distance / autocvar_g_vehicle_raptor_cannon_speed;
 +                              ad = vf + raptor.lock_target.velocity * impact_time;
 +                      }
 +                      trace_endpos = ad;
 +              }
 +
 +              if(self.lock_target)
 +              {
 +                      if(raptor.lock_strength == 1)
 +                              UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '1 0 0', 1);
 +                      else if(self.lock_strength > 0.5)
 +                              UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '0 1 0', 1);
 +                      else if(self.lock_strength < 0.5)
 +                              UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '0 0 1', 1);
 +              }
 +      }
 +
 +
 +      vehicle_aimturret(raptor, trace_endpos, raptor.gun1, "fire1",
 +                                                autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1,  autocvar_g_vehicle_raptor_cannon_pitchlimit_up,
 +                                                autocvar_g_vehicle_raptor_cannon_turnlimit * -1,  autocvar_g_vehicle_raptor_cannon_turnlimit,  autocvar_g_vehicle_raptor_cannon_turnspeed);
 +
 +      vehicle_aimturret(raptor, trace_endpos, raptor.gun2, "fire1",
 +                                                autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1,  autocvar_g_vehicle_raptor_cannon_pitchlimit_up,
 +                                                autocvar_g_vehicle_raptor_cannon_turnlimit * -1,  autocvar_g_vehicle_raptor_cannon_turnlimit,  autocvar_g_vehicle_raptor_cannon_turnspeed);
 +
 +      /*
 +      ad = ad * 0.5;
 +      v_forward = vf * 0.5;
 +      traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, raptor);
 +      UpdateAuxiliaryXhair(player, trace_endpos, '0 1 0', 0);
 +      */
 +
 +      Weapon wep1 = WEP_RAPTOR;
 +      if(!forbidWeaponUse(player))
 +      if(player.BUTTON_ATCK)
 +      if (wep1.wr_checkammo1(wep1))
 +      {
 +              wep1.wr_think(wep1, self, true, false);
 +      }
 +
 +      if(self.vehicle_flags  & VHF_SHIELDREGEN)
 +              vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true);
 +
 +      if(self.vehicle_flags  & VHF_HEALTHREGEN)
 +              vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false);
 +
 +      if(self.vehicle_flags  & VHF_ENERGYREGEN)
 +              vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false);
 +
 +      Weapon wep2a = WEP_RAPTOR_BOMB;
 +      if(!forbidWeaponUse(player))
 +      if(raptor.vehicle_weapon2mode == RSM_BOMB)
 +      {
 +              if(time > raptor.lip + autocvar_g_vehicle_raptor_bombs_refire)
 +              if(player.BUTTON_ATCK2)
 +              {
 +                      wep2a.wr_think(wep2a, self, false, true);
 +                      raptor.delay = time + autocvar_g_vehicle_raptor_bombs_refire;
 +                      raptor.lip   = time;
 +              }
 +      }
 +      else
 +      {
 +              Weapon wep2b = WEP_RAPTOR_FLARE;
 +              if(time > raptor.lip + autocvar_g_vehicle_raptor_flare_refire)
 +              if(player.BUTTON_ATCK2)
 +              {
 +                      wep2b.wr_think(wep2b, self, false, true);
 +                      raptor.delay = time + autocvar_g_vehicle_raptor_flare_refire;
 +                      raptor.lip   = time;
 +              }
 +      }
 +
 +      raptor.bomb1.alpha = raptor.bomb2.alpha = (time - raptor.lip) / (raptor.delay - raptor.lip);
 +      player.vehicle_reload2 = bound(0, raptor.bomb1.alpha * 100, 100);
 +      player.vehicle_ammo2 = (player.vehicle_reload2 == 100) ? 100 : 0;
 +
 +      if(self.bomb1.cnt < time)
 +      {
 +              entity _missile = findchainentity(enemy, raptor);
 +              float _incomming = 0;
 +              while(_missile)
 +              {
 +                      if(_missile.flags & FL_PROJECTILE)
 +                      if(MISSILE_IS_TRACKING(_missile))
 +                      if(vlen(self.origin - _missile.origin) < 2 * autocvar_g_vehicle_raptor_flare_range)
 +                              ++_incomming;
 +
 +                      _missile = _missile.chain;
 +              }
 +
 +              if(_incomming)
 +                      sound(self, CH_PAIN_SINGLE, SND_VEH_MISSILE_ALARM, VOL_BASE, ATTEN_NONE);
 +
 +              self.bomb1.cnt = time + 1;
 +      }
 +
 +
 +      VEHICLE_UPDATE_PLAYER(player, health, raptor);
 +      VEHICLE_UPDATE_PLAYER(player, energy, raptor);
 +      if(self.vehicle_flags & VHF_HASSHIELD)
 +              VEHICLE_UPDATE_PLAYER(player, shield, raptor);
 +
 +      player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
 +
 +      setself(player);
 +      return 1;
 +}
 +
 +float raptor_takeoff()
 +{SELFPARAM();
 +      entity player, raptor;
 +
 +      player = self;
 +      raptor = self.vehicle;
 +      setself(raptor);
 +
 +      self.nextthink = time;
 +      CSQCMODEL_AUTOUPDATE(self);
 +      self.nextthink = 0; // will this work?
 +
 +      if(self.sound_nexttime < time)
 +      {
 +              self.sound_nexttime = time + 7.955812; //soundlength("vehicles/raptor_fly.wav");
 +              sound (self, CH_TRIGGER_SINGLE, SND_VEH_RAPTOR_SPEED, VOL_VEHICLEENGINE, ATTEN_NORM);
 +      }
 +
 +      // Takeoff sequense
 +      if(raptor.frame < 25)
 +      {
 +              raptor.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / sys_frametime);
 +              raptor.velocity_z = min(raptor.velocity_z * 1.5, 256);
 +              self.bomb1.gun1.avelocity_y = 90 + ((raptor.frame / 25) * 25000);
 +              self.bomb1.gun2.avelocity_y = -self.bomb1.gun1.avelocity_y;
 +              player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
 +
 +              setorigin(player, raptor.origin + '0 0 32');
 +      }
 +      else
 +              player.PlayerPhysplug = raptor_frame;
 +
 +      if(self.vehicle_flags  & VHF_SHIELDREGEN)
 +              vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true);
 +
 +      if(self.vehicle_flags  & VHF_HEALTHREGEN)
 +              vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false);
 +
 +      if(self.vehicle_flags  & VHF_ENERGYREGEN)
 +              vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false);
 +
 +
 +      raptor.bomb1.alpha = raptor.bomb2.alpha = (time - raptor.lip) / (raptor.delay - raptor.lip);
 +      player.vehicle_reload2 = bound(0, raptor.bomb1.alpha * 100, 100);
 +      player.vehicle_ammo2 = (player.vehicle_reload2 == 100) ? 100 : 0;
 +
 +      VEHICLE_UPDATE_PLAYER(player, health, raptor);
 +      VEHICLE_UPDATE_PLAYER(player, energy, raptor);
 +      if(self.vehicle_flags & VHF_HASSHIELD)
 +              VEHICLE_UPDATE_PLAYER(player, shield, raptor);
 +
 +      player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
 +      setself(player);
 +      return 1;
 +}
 +
 +void raptor_blowup()
 +{SELFPARAM();
 +      self.deadflag   = DEAD_DEAD;
 +      self.vehicle_exit(VHEF_NORMAL);
 +      RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_RAPT_DEATH, world);
 +
 +      self.alpha                = -1;
 +      self.movetype      = MOVETYPE_NONE;
 +      self.effects            = EF_NODRAW;
 +      self.colormod      = '0 0 0';
 +      self.avelocity    = '0 0 0';
 +      self.velocity      = '0 0 0';
 +
 +      setorigin(self, self.pos1);
 +      self.touch = func_null;
 +      self.nextthink = 0;
 +}
 +
 +void raptor_diethink()
 +{SELFPARAM();
 +      if(time >= self.wait)
 +              self.think = raptor_blowup;
 +
 +      if(random() < 0.05)
 +      {
 +              sound (self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
 +              Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
 +      }
 +      self.nextthink = time;
 +
 +      CSQCMODEL_AUTOUPDATE(self);
 +}
 +
 +// If we dont do this ever now and then, the raptors rotors
 +// stop working, presumably due to angle overflow. cute.
 +void raptor_rotor_anglefix()
 +{SELFPARAM();
 +      self.gun1.angles_y = anglemods(self.gun1.angles_y);
 +      self.gun2.angles_y = anglemods(self.gun2.angles_y);
 +      self.nextthink = time + 15;
 +}
 +
 +float raptor_impulse(float _imp)
 +{SELFPARAM();
 +      switch(_imp)
 +      {
 +              case 1:
 +                      self.vehicle.vehicle_weapon2mode = RSM_BOMB;
 +                      CSQCVehicleSetup(self, 0);
 +                      return true;
 +              case 2:
 +                      self.vehicle.vehicle_weapon2mode = RSM_FLARE;
 +                      CSQCVehicleSetup(self, 0);
 +                      return true;
 +
 +              case 10:
 +              case 15:
 +              case 18:
 +                      self.vehicle.vehicle_weapon2mode += 1;
 +                      if(self.vehicle.vehicle_weapon2mode > RSM_LAST)
 +                              self.vehicle.vehicle_weapon2mode = RSM_FIRST;
 +
 +                      CSQCVehicleSetup(self, 0);
 +                      return true;
 +              case 11:
 +              case 12:
 +              case 16:
 +              case 19:
 +                      self.vehicle.vehicle_weapon2mode -= 1;
 +                      if(self.vehicle.vehicle_weapon2mode < RSM_FIRST)
 +                              self.vehicle.vehicle_weapon2mode = RSM_LAST;
 +
 +                      CSQCVehicleSetup(self, 0);
 +                      return true;
 +
 +              /*
 +              case 17: // toss gun, could be used to exit?
 +                      break;
 +              case 20: // Manual minigun reload?
 +                      break;
 +              */
 +      }
 +      return false;
 +}
 +
++spawnfunc(vehicle_raptor)
++{
 +      if(!autocvar_g_vehicle_raptor) { remove(self); return; }
 +      if(!vehicle_initialize(VEH_RAPTOR, false)) { remove(self); return; }
 +}
 +
 +              METHOD(Raptor, vr_impact, void(Raptor thisveh))
 +              {
 +                      if(autocvar_g_vehicle_raptor_bouncepain)
 +                              vehicles_impact(autocvar_g_vehicle_raptor_bouncepain_x, autocvar_g_vehicle_raptor_bouncepain_y, autocvar_g_vehicle_raptor_bouncepain_z);
 +              }
 +              METHOD(Raptor, vr_enter, void(Raptor thisveh))
 +              {
 +                      self.vehicle_weapon2mode = RSM_BOMB;
 +                      self.owner.PlayerPhysplug = raptor_takeoff;
 +                      self.movetype      = MOVETYPE_BOUNCEMISSILE;
 +                      self.solid                = SOLID_SLIDEBOX;
 +                      self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_raptor_health) * 100;
 +                      self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_raptor_shield) * 100;
 +                      self.velocity_z = 1; // Nudge upwards to takeoff sequense can work.
 +                      self.tur_head.exteriormodeltoclient = self.owner;
 +
 +                      self.delay = time + autocvar_g_vehicle_raptor_bombs_refire;
 +                      self.lip   = time;
 +
 +                      if(self.owner.flagcarried)
 +                         setorigin(self.owner.flagcarried, '-20 0 96');
 +
 +                      CSQCVehicleSetup(self.owner, 0);
 +              }
 +              METHOD(Raptor, vr_death, void(Raptor thisveh))
 +              {
 +                      self.health                             = 0;
 +                      self.event_damage               = func_null;
 +                      self.solid                              = SOLID_CORPSE;
 +                      self.takedamage                 = DAMAGE_NO;
 +                      self.deadflag                   = DEAD_DYING;
 +                      self.movetype                   = MOVETYPE_BOUNCE;
 +                      self.think                              = raptor_diethink;
 +                      self.nextthink                  = time;
 +                      self.wait                               = time + 5 + (random() * 5);
 +
 +                      Send_Effect(EFFECT_EXPLOSION_MEDIUM, findbetterlocation (self.origin, 16), '0 0 0', 1);
 +
 +                      self.velocity_z += 600;
 +
 +                      self.avelocity = '0 0.5 1' * (random() * 400);
 +                      self.avelocity -= '0 0.5 1' * (random() * 400);
 +
 +                      self.colormod = '-0.5 -0.5 -0.5';
 +                      self.touch = raptor_blowup;
 +              }
 +              METHOD(Raptor, vr_spawn, void(Raptor thisveh))
 +              {
 +                      if(!self.gun1)
 +                      {
 +                              entity spinner;
 +                              vector ofs;
 +
 +                              //FIXME: Camera is in a bad place in HUD model.
 +                              //setorigin(self.vehicle_viewport, '25 0 5');
 +
 +                              self.vehicles_impulse   = raptor_impulse;
 +
 +                              self.frame = 0;
 +
 +                              self.bomb1 = spawn();
 +                              self.bomb2 = spawn();
 +                              self.gun1  = spawn();
 +                              self.gun2  = spawn();
 +
 +                              setmodel(self.bomb1, MDL_VEH_RAPTOR_CB_FOLDED);
 +                              setmodel(self.bomb2, MDL_VEH_RAPTOR_CB_FOLDED);
 +                              setmodel(self.gun1, MDL_VEH_RAPTOR_GUN);
 +                              setmodel(self.gun2, MDL_VEH_RAPTOR_GUN);
 +                              setmodel(self.tur_head, MDL_VEH_RAPTOR_TAIL);
 +
 +                              setattachment(self.bomb1, self, "bombmount_left");
 +                              setattachment(self.bomb2, self, "bombmount_right");
 +                              setattachment(self.tur_head, self,"root");
 +
 +                              // FIXMODEL Guns mounts to angled bones
 +                              self.bomb1.angles = self.angles;
 +                              self.angles = '0 0 0';
 +                              // This messes up gun-aim, so work arround it.
 +                              //setattachment(self.gun1, self, "gunmount_left");
 +                              ofs = gettaginfo(self, gettagindex(self, "gunmount_left"));
 +                              ofs -= self.origin;
 +                              setattachment(self.gun1, self, "");
 +                              setorigin(self.gun1, ofs);
 +
 +                              //setattachment(self.gun2, self, "gunmount_right");
 +                              ofs = gettaginfo(self, gettagindex(self, "gunmount_right"));
 +                              ofs -= self.origin;
 +                              setattachment(self.gun2, self, "");
 +                              setorigin(self.gun2, ofs);
 +
 +                              self.angles = self.bomb1.angles;
 +                              self.bomb1.angles = '0 0 0';
 +
 +                              spinner = spawn();
 +                              spinner.owner = self;
 +                              setmodel(spinner, MDL_VEH_RAPTOR_PROP);
 +                              setattachment(spinner, self, "engine_left");
 +                              spinner.movetype = MOVETYPE_NOCLIP;
 +                              spinner.avelocity = '0 90 0';
 +                              self.bomb1.gun1 = spinner;
 +
 +                              spinner = spawn();
 +                              spinner.owner = self;
 +                              setmodel(spinner, MDL_VEH_RAPTOR_PROP);
 +                              setattachment(spinner, self, "engine_right");
 +                              spinner.movetype = MOVETYPE_NOCLIP;
 +                              spinner.avelocity = '0 -90 0';
 +                              self.bomb1.gun2 = spinner;
 +
 +                              // Sigh.
 +                              self.bomb1.think = raptor_rotor_anglefix;
 +                              self.bomb1.nextthink = time;
 +
 +                              self.mass                          = 1 ;
 +                      }
 +
 +                      self.frame                = 0;
 +                      self.vehicle_health = autocvar_g_vehicle_raptor_health;
 +                      self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
 +                      self.movetype      = MOVETYPE_TOSS;
 +                      self.solid                = SOLID_SLIDEBOX;
 +                      self.vehicle_energy = 1;
 +
 +                      self.PlayerPhysplug = raptor_frame;
 +
 +                      self.bomb1.gun1.avelocity_y = 90;
 +                      self.bomb1.gun2.avelocity_y = -90;
 +
 +                      self.delay = time;
 +
 +                      self.bouncefactor = autocvar_g_vehicle_raptor_bouncefactor;
 +                      self.bouncestop = autocvar_g_vehicle_raptor_bouncestop;
 +                      self.damageforcescale = 0.25;
 +                      self.vehicle_health = autocvar_g_vehicle_raptor_health;
 +                      self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
 +              }
 +              METHOD(Raptor, vr_setup, void(Raptor thisveh))
 +              {
 +                      if(autocvar_g_vehicle_raptor_shield)
 +                              self.vehicle_flags |= VHF_HASSHIELD;
 +
 +                      if(autocvar_g_vehicle_raptor_shield_regen)
 +                              self.vehicle_flags |= VHF_SHIELDREGEN;
 +
 +                      if(autocvar_g_vehicle_raptor_health_regen)
 +                              self.vehicle_flags |= VHF_HEALTHREGEN;
 +
 +                      if(autocvar_g_vehicle_raptor_energy_regen)
 +                              self.vehicle_flags |= VHF_ENERGYREGEN;
 +
 +                      self.vehicle_exit = raptor_exit;
 +                      self.respawntime = autocvar_g_vehicle_raptor_respawntime;
 +                      self.vehicle_health = autocvar_g_vehicle_raptor_health;
 +                      self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
 +                      self.max_health = self.vehicle_health;
 +              }
 +
 +#endif
 +#ifdef CSQC
 +
 +              METHOD(Raptor, vr_hud, void(Raptor thisveh))
 +              {
 +                      string crosshair;
 +
 +                      switch(weapon2mode)
 +                      {
 +                              case RSM_FLARE: crosshair = vCROSS_RAIN;  break;
 +                              case RSM_BOMB:  crosshair = vCROSS_BURST; break;
 +                              default:        crosshair = vCROSS_BURST;
 +                      }
 +
 +                      Vehicles_drawHUD(VEH_RAPTOR.m_icon, "vehicle_raptor_weapon1", "vehicle_raptor_weapon2",
 +                                                       "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
 +                                                       "vehicle_icon_ammo2", autocvar_hud_progressbar_vehicles_ammo2_color,
 +                                                       crosshair);
 +              }
 +              METHOD(Raptor, vr_setup, void(Raptor thisveh))
 +              {
 +                      AuxiliaryXhair[1].axh_image = vCROSS_LOCK;
 +              }
 +
 +#endif
 +#endif
index ede09c5c3d2825094bf299d95f69b5b76a81729d,0000000000000000000000000000000000000000..9d524ef0a0acedf3161e9db0aa73a747766dd543
mode 100644,000000..100644
--- /dev/null
@@@ -1,671 -1,0 +1,671 @@@
- void spawnfunc_vehicle_spiderbot()
- {SELFPARAM();
 +#ifndef VEHICLE_SPIDERBOT
 +#define VEHICLE_SPIDERBOT
 +
 +#include "spiderbot_weapons.qc"
 +
 +CLASS(Spiderbot, Vehicle)
 +/* spawnflags */ ATTRIB(Spiderbot, spawnflags, int, VHF_DMGSHAKE);
 +/* mins       */ ATTRIB(Spiderbot, mins, vector, '-75 -75 10');
 +/* maxs       */ ATTRIB(Spiderbot, maxs, vector, '75 75 125');
 +/* model        */ ATTRIB(Spiderbot, mdl, string, "models/vehicles/spiderbot.dpm");
 +/* model        */ ATTRIB(Spiderbot, model, string, "models/vehicles/spiderbot.dpm");
 +/* head_model */ ATTRIB(Spiderbot, head_model, string, "models/vehicles/spiderbot_top.dpm");
 +/* hud_model  */ ATTRIB(Spiderbot, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm");
 +/* tags       */ ATTRIB(Spiderbot, tag_head, string, "tag_head");
 +/* tags       */ ATTRIB(Spiderbot, tag_hud, string, "tag_hud");
 +/* tags       */ ATTRIB(Spiderbot, tag_view, string, "");
 +/* netname    */ ATTRIB(Spiderbot, netname, string, "spiderbot");
 +/* fullname   */ ATTRIB(Spiderbot, vehicle_name, string, _("Spiderbot"));
 +/* icon       */ ATTRIB(Spiderbot, m_icon, string, "vehicle_spider");
 +ENDCLASS(Spiderbot)
 +
 +REGISTER_VEHICLE(SPIDERBOT, NEW(Spiderbot));
 +
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +const int SBRM_FIRST = 1;
 +const int SBRM_VOLLY = 1;
 +const int SBRM_GUIDE = 2;
 +const int SBRM_ARTILLERY = 3;
 +const int SBRM_LAST = 3;
 +
 +#include "spiderbot_weapons.qc"
 +
 +#ifdef SVQC
 +bool autocvar_g_vehicle_spiderbot;
 +
 +float autocvar_g_vehicle_spiderbot_respawntime;
 +
 +float autocvar_g_vehicle_spiderbot_speed_stop;
 +float autocvar_g_vehicle_spiderbot_speed_strafe;
 +float autocvar_g_vehicle_spiderbot_speed_walk;
 +float autocvar_g_vehicle_spiderbot_speed_run = 700;
 +float autocvar_g_vehicle_spiderbot_turnspeed;
 +float autocvar_g_vehicle_spiderbot_turnspeed_strafe;
 +float autocvar_g_vehicle_spiderbot_movement_inertia;
 +
 +float autocvar_g_vehicle_spiderbot_springlength;
 +float autocvar_g_vehicle_spiderbot_springup;
 +float autocvar_g_vehicle_spiderbot_springblend;
 +float autocvar_g_vehicle_spiderbot_tiltlimit;
 +
 +float autocvar_g_vehicle_spiderbot_head_pitchlimit_down;
 +float autocvar_g_vehicle_spiderbot_head_pitchlimit_up;
 +float autocvar_g_vehicle_spiderbot_head_turnlimit;
 +float autocvar_g_vehicle_spiderbot_head_turnspeed;
 +
 +int autocvar_g_vehicle_spiderbot_health;
 +float autocvar_g_vehicle_spiderbot_health_regen;
 +float autocvar_g_vehicle_spiderbot_health_regen_pause;
 +
 +int autocvar_g_vehicle_spiderbot_shield;
 +float autocvar_g_vehicle_spiderbot_shield_regen;
 +float autocvar_g_vehicle_spiderbot_shield_regen_pause;
 +
 +vector autocvar_g_vehicle_spiderbot_bouncepain;
 +
 +.float jump_delay;
 +float spiderbot_frame()
 +{SELFPARAM();
 +      vector ad, vf;
 +      entity player, spider;
 +      float ftmp;
 +
 +      if(intermission_running)
 +      {
 +              self.vehicle.velocity = '0 0 0';
 +              self.vehicle.avelocity = '0 0 0';
 +              return 1;
 +      }
 +
 +      player = self;
 +      spider = self.vehicle;
 +      setself(spider);
 +
 +      vehicles_painframe();
 +
 +      player.BUTTON_ZOOM        = 0;
 +      player.BUTTON_CROUCH    = 0;
 +      player.switchweapon      = 0;
 +      player.vehicle_weapon2mode = spider.vehicle_weapon2mode;
 +
 +
 +#if 1 // 0 to enable per-gun impact aux crosshairs
 +      // Avarage gun impact point's -> aux cross
 +      ad = gettaginfo(spider.tur_head, gettagindex(spider.tur_head, "tag_hardpoint01"));
 +      vf = v_forward;
 +      ad += gettaginfo(spider.tur_head, gettagindex(spider.tur_head, "tag_hardpoint02"));
 +      vf += v_forward;
 +      ad = ad * 0.5;
 +      v_forward = vf * 0.5;
 +      traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, spider);
 +      UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 0);
 +#else
 +      ad = gettaginfo(spider.gun1, gettagindex(spider.gun1, "barrels"));
 +      traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, spider);
 +      UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 0);
 +      vf = ad;
 +      ad = gettaginfo(spider.gun2, gettagindex(spider.gun2, "barrels"));
 +      traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, spider);
 +      UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 1);
 +      ad = 0.5 * (ad + vf);
 +#endif
 +
 +      crosshair_trace(player);
 +      ad = vectoangles(normalize(trace_endpos - ad));
 +      ad = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(spider.angles), AnglesTransform_FromAngles(ad))) - spider.tur_head.angles;
 +      ad = AnglesTransform_Normalize(ad, true);
 +      //UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload2) + ('0 1 0' * (1 - player.vehicle_reload2)), 2);
 +
 +      // Rotate head
 +      ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * sys_frametime;
 +      ad_y = bound(-ftmp, ad_y, ftmp);
 +      spider.tur_head.angles_y = bound(autocvar_g_vehicle_spiderbot_head_turnlimit * -1, spider.tur_head.angles_y + ad_y, autocvar_g_vehicle_spiderbot_head_turnlimit);
 +
 +      // Pitch head
 +      ad_x = bound(ftmp * -1, ad_x, ftmp);
 +      spider.tur_head.angles_x = bound(autocvar_g_vehicle_spiderbot_head_pitchlimit_down, spider.tur_head.angles_x + ad_x, autocvar_g_vehicle_spiderbot_head_pitchlimit_up);
 +
 +
 +      //fixedmakevectors(spider.angles);
 +      makevectors(spider.angles + '-2 0 0' * spider.angles_x);
 +
 +      movelib_groundalign4point(autocvar_g_vehicle_spiderbot_springlength, autocvar_g_vehicle_spiderbot_springup, autocvar_g_vehicle_spiderbot_springblend, autocvar_g_vehicle_spiderbot_tiltlimit);
 +
 +      if(spider.flags & FL_ONGROUND)
 +              spider.jump_delay = time; // reset now so movement can begin
 +
 +      //if(spider.flags & FL_ONGROUND)
 +      {
 +              if(spider.flags & FL_ONGROUND)
 +              if(spider.frame == 4 && self.tur_head.wait != 0)
 +              {
 +                      sound (self, CH_TRIGGER_SINGLE, SND_VEH_SPIDERBOT_LAND, VOL_VEHICLEENGINE, ATTEN_NORM);
 +                      spider.frame = 5;
 +              }
 +
 +              if(!player.BUTTON_JUMP)
 +                      spider.BUTTON_JUMP = 0;
 +
 +              if((spider.flags & FL_ONGROUND) && player.BUTTON_JUMP && !spider.BUTTON_JUMP && self.tur_head.wait < time)
 +              {
 +                      sound (self, CH_TRIGGER_SINGLE, SND_VEH_SPIDERBOT_JUMP, VOL_VEHICLEENGINE, ATTEN_NORM);
 +                      //dprint("spiderbot_jump:", ftos(soundlength("vehicles/spiderbot_jump.wav")), "\n");
 +                      self.delay = 0;
 +
 +                      self.tur_head.wait = time + 2;
 +                      spider.jump_delay = time + 2;
 +                      spider.BUTTON_JUMP = 1; // set spider's jump
 +                      //player.BUTTON_JUMP = 0;
 +
 +                      vector movefix = '0 0 0';
 +                      if(player.movement_x > 0) movefix_x = 1;
 +                      if(player.movement_x < 0) movefix_x = -1;
 +                      if(player.movement_y > 0) movefix_y = 1;
 +                      if(player.movement_y < 0) movefix_y = -1;
 +
 +                      vector rt = movefix_y * v_right;
 +                      vector sd = movefix_x * v_forward;
 +                      if(movefix_y == 0 && movefix_x == 0)
 +                              sd = v_forward; // always do forward
 +
 +                      spider.flags &= ~FL_ONGROUND;
 +
 +                      spider.velocity = sd * 700 + rt * 600 + v_up * 600;
 +                      spider.frame = 4;
 +              }
 +              else if(time >= spider.jump_delay)
 +              {
 +                      if(vlen(player.movement) == 0)
 +                      {
 +                              if(spider.flags & FL_ONGROUND)
 +                              {
 +                                      if(self.sound_nexttime < time || self.delay != 3)
 +                                      {
 +                                              self.delay = 3;
 +                                              self.sound_nexttime = time + 6.486500; //soundlength("vehicles/spiderbot_idle.wav");
 +                                              //dprint("spiderbot_idle:", ftos(soundlength("vehicles/spiderbot_idle.wav")), "\n");
 +                                              sound (self, CH_TRIGGER_SINGLE, SND_VEH_SPIDERBOT_IDLE, VOL_VEHICLEENGINE, ATTEN_NORM);
 +                                      }
 +                                      movelib_beak_simple(autocvar_g_vehicle_spiderbot_speed_stop);
 +                                      spider.frame = 5;
 +                              }
 +                      }
 +                      else
 +                      {
 +                              // Turn Body
 +                              if(player.movement_x == 0 && player.movement_y != 0)
 +                                      ftmp = autocvar_g_vehicle_spiderbot_turnspeed_strafe * sys_frametime;
 +                              else
 +                                      ftmp = autocvar_g_vehicle_spiderbot_turnspeed * sys_frametime;
 +
 +                              ftmp = bound(-ftmp, spider.tur_head.angles_y, ftmp);
 +                              spider.angles_y = anglemods(spider.angles_y + ftmp);
 +                              spider.tur_head.angles_y -= ftmp;
 +
 +                              if(player.movement_x != 0)
 +                              {
 +                                      if(player.movement_x > 0)
 +                                      {
 +                                              player.movement_x = 1;
 +                                              if(spider.flags & FL_ONGROUND)
 +                                                      spider.frame = 0;
 +                                      }
 +                                      else if(player.movement_x < 0)
 +                                      {
 +                                              player.movement_x = -1;
 +                                              if(spider.flags & FL_ONGROUND)
 +                                                      spider.frame = 1;
 +                                      }
 +                                      player.movement_y = 0;
 +                                      float oldvelz = spider.velocity_z;
 +                                      movelib_move_simple(normalize(v_forward * player.movement_x),((player.BUTTON_JUMP) ? autocvar_g_vehicle_spiderbot_speed_run : autocvar_g_vehicle_spiderbot_speed_walk),autocvar_g_vehicle_spiderbot_movement_inertia);
 +                                      spider.velocity_z = oldvelz;
 +                                      float g = ((autocvar_sv_gameplayfix_gravityunaffectedbyticrate) ? 0.5 : 1);
 +                                      if(spider.velocity_z <= 20) // not while jumping
 +                                              spider.velocity_z -= g * sys_frametime * autocvar_sv_gravity;
 +                                      if(spider.flags & FL_ONGROUND)
 +                                      if(self.sound_nexttime < time || self.delay != 1)
 +                                      {
 +                                              self.delay = 1;
 +                                              self.sound_nexttime = time + 6.486500; //soundlength("vehicles/spiderbot_walk.wav");
 +                                              sound (self, CH_TRIGGER_SINGLE, SND_VEH_SPIDERBOT_WALK, VOL_VEHICLEENGINE, ATTEN_NORM);
 +                                              //dprint("spiderbot_walk:", ftos(soundlength("vehicles/spiderbot_walk.wav")), "\n");
 +                                      }
 +                              }
 +                              else if(player.movement_y != 0)
 +                              {
 +                                      if(player.movement_y < 0)
 +                                      {
 +                                              player.movement_y = -1;
 +                                              if(spider.flags & FL_ONGROUND)
 +                                                      spider.frame = 2;
 +                                      }
 +                                      else if(player.movement_y > 0)
 +                                      {
 +                                              player.movement_y = 1;
 +                                              if(spider.flags & FL_ONGROUND)
 +                                                      spider.frame = 3;
 +                                      }
 +
 +                                      float oldvelz = spider.velocity_z;
 +                                      movelib_move_simple(normalize(v_right * player.movement_y),autocvar_g_vehicle_spiderbot_speed_strafe,autocvar_g_vehicle_spiderbot_movement_inertia);
 +                                      spider.velocity_z = oldvelz;
 +                                      float g = ((autocvar_sv_gameplayfix_gravityunaffectedbyticrate) ? 0.5 : 1);
 +                                      if(spider.velocity_z <= 20) // not while jumping
 +                                              spider.velocity_z -= g * sys_frametime * autocvar_sv_gravity;
 +                                      if(spider.flags & FL_ONGROUND)
 +                                      if(self.sound_nexttime < time || self.delay != 2)
 +                                      {
 +                                              self.delay = 2;
 +                                              self.sound_nexttime = time + 6.486500; //soundlength("vehicles/spiderbot_strafe.wav");
 +                                              sound (self, CH_TRIGGER_SINGLE, SND_VEH_SPIDERBOT_STRAFE, VOL_VEHICLEENGINE, ATTEN_NORM);
 +                                              //dprint("spiderbot_strafe:", ftos(soundlength("vehicles/spiderbot_strafe.wav")), "\n");
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      self.angles_x = bound(-autocvar_g_vehicle_spiderbot_tiltlimit, self.angles_x, autocvar_g_vehicle_spiderbot_tiltlimit);
 +      self.angles_z = bound(-autocvar_g_vehicle_spiderbot_tiltlimit, self.angles_z, autocvar_g_vehicle_spiderbot_tiltlimit);
 +
 +      if(!forbidWeaponUse(player))
 +      if(player.BUTTON_ATCK)
 +      {
 +              spider.cnt = time;
 +              if(spider.vehicle_ammo1 >= autocvar_g_vehicle_spiderbot_minigun_ammo_cost && spider.tur_head.attack_finished_single <= time)
 +              {
 +                      entity gun;
 +                      vector v;
 +                      spider.misc_bulletcounter += 1;
 +
 +                      setself(player);
 +
 +                      gun = (spider.misc_bulletcounter % 2) ? spider.gun1 : spider.gun2;
 +
 +                      v = gettaginfo(gun, gettagindex(gun, "barrels"));
 +                      v_forward = normalize(v_forward);
 +                      v += v_forward * 50;
 +
 +                      fireBullet(v, v_forward, autocvar_g_vehicle_spiderbot_minigun_spread, autocvar_g_vehicle_spiderbot_minigun_solidpenetration,
 +                                autocvar_g_vehicle_spiderbot_minigun_damage, autocvar_g_vehicle_spiderbot_minigun_force, DEATH_VH_SPID_MINIGUN, 0);
 +
 +                      sound (gun, CH_WEAPON_A, SND_UZI_FIRE, VOL_BASE, ATTEN_NORM);
 +                      //trailparticles(self, _particleeffectnum("spiderbot_minigun_trail"), v, trace_endpos);
 +                      pointparticles(particleeffectnum(EFFECT_SPIDERBOT_MINIGUN_MUZZLEFLASH), v, v_forward * 2500, 1);
 +
 +                      setself(spider);
 +
 +                      spider.vehicle_ammo1 -= autocvar_g_vehicle_spiderbot_minigun_ammo_cost;
 +                      spider.tur_head.attack_finished_single = time + autocvar_g_vehicle_spiderbot_minigun_refire;
 +                      player.vehicle_ammo1 = (spider.vehicle_ammo1 / autocvar_g_vehicle_spiderbot_minigun_ammo_max) * 100;
 +                      spider.gun1.angles_z += 45;
 +                      spider.gun2.angles_z -= 45;
 +                      if(spider.gun1.angles_z >= 360)
 +                      {
 +                              spider.gun1.angles_z = 0;
 +                              spider.gun2.angles_z = 0;
 +                      }
 +              }
 +      }
 +      else
 +              vehicles_regen(spider.cnt, vehicle_ammo1, autocvar_g_vehicle_spiderbot_minigun_ammo_max,
 +                                                                                 autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause,
 +                                                                                 autocvar_g_vehicle_spiderbot_minigun_ammo_regen, frametime, false);
 +
 +
 +      spiderbot_rocket_do();
 +
 +      if(self.vehicle_flags  & VHF_SHIELDREGEN)
 +              vehicles_regen(spider.dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, frametime, true);
 +
 +      if(self.vehicle_flags  & VHF_HEALTHREGEN)
 +              vehicles_regen(spider.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, frametime, false);
 +
 +      player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
 +      //player.vehicle_ammo2 = spider.tur_head.frame;
 +      player.vehicle_ammo2 = (9 - spider.tur_head.frame) / 8 * 100; // Percentage, like ammo1
 +
 +      if(spider.gun2.cnt <= time)
 +              player.vehicle_reload2 = 100;
 +      else
 +              player.vehicle_reload2 = 100 - ((spider.gun2.cnt - time) / spider.attack_finished_single) * 100;
 +
 +      setorigin(player, spider.origin + '0 0 1' * spider.maxs_z);
 +      player.velocity = spider.velocity;
 +
 +      VEHICLE_UPDATE_PLAYER(player, health, spiderbot);
 +
 +      if(self.vehicle_flags & VHF_HASSHIELD)
 +              VEHICLE_UPDATE_PLAYER(player, shield, spiderbot);
 +
 +      setself(player);
 +      return 1;
 +}
 +
 +void spiderbot_exit(float eject)
 +{SELFPARAM();
 +      entity e;
 +      vector spot;
 +
 +      e = findchain(classname,"spiderbot_rocket");
 +      while(e)
 +      {
 +              if(e.owner == self.owner)
 +              {
 +                      e.realowner = self.owner;
 +                      e.owner = world;
 +              }
 +              e = e.chain;
 +      }
 +
 +      self.think = vehicles_think;
 +      self.nextthink = time;
 +      self.frame = 5;
 +      self.movetype = MOVETYPE_WALK;
 +
 +      if(!self.owner)
 +              return;
 +
 +      makevectors(self.angles);
 +      if(eject)
 +      {
 +              spot = self.origin + v_forward * 100 + '0 0 64';
 +              spot = vehicles_findgoodexit(spot);
 +              setorigin(self.owner , spot);
 +              self.owner.velocity = (v_up + v_forward * 0.25) * 750;
 +              self.owner.oldvelocity = self.owner.velocity;
 +      }
 +      else
 +      {
 +              if(vlen(self.velocity) > autocvar_g_vehicle_spiderbot_speed_strafe)
 +              {
 +                      self.owner.velocity = normalize(self.velocity) * vlen(self.velocity);
 +                      self.owner.velocity_z += 200;
 +                      spot = self.origin + v_forward * 128 + '0 0 64';
 +                      spot = vehicles_findgoodexit(spot);
 +              }
 +              else
 +              {
 +                      self.owner.velocity = self.velocity * 0.5;
 +                      self.owner.velocity_z += 10;
 +                      spot = self.origin + v_forward * 256 + '0 0 64';
 +                      spot = vehicles_findgoodexit(spot);
 +              }
 +              self.owner.oldvelocity = self.owner.velocity;
 +              setorigin(self.owner , spot);
 +      }
 +
 +      antilag_clear(self.owner);
 +      self.owner = world;
 +}
 +
 +void spiderbot_headfade()
 +{SELFPARAM();
 +      self.think = spiderbot_headfade;
 +      self.nextthink = self.fade_time;
 +      self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
 +
 +      if(self.cnt < time || self.alpha < 0.1)
 +      {
 +              if(self.alpha > 0.1)
 +              {
 +                      sound (self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
 +                      Send_Effect(EFFECT_EXPLOSION_BIG, self.origin + '0 0 100', '0 0 0', 1);
 +              }
 +              remove(self);
 +      }
 +}
 +
 +void spiderbot_blowup()
 +{SELFPARAM();
 +      if(self.cnt > time)
 +      {
 +              if(random() < 0.1)
 +              {
 +                      sound (self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
 +                      Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
 +              }
 +              self.nextthink = time + 0.1;
 +              return;
 +      }
 +
 +      entity h, g1, g2, b;
 +      b = spawn();
 +      h = spawn();
 +      g1 = spawn();
 +      g2 = spawn();
 +
 +      setmodel(b, MDL_VEH_SPIDERBOT_BODY);
 +      setmodel(h, MDL_VEH_SPIDERBOT_TOP);
 +      setmodel(g1, MDL_VEH_SPIDERBOT_GUN);
 +      setmodel(g2, MDL_VEH_SPIDERBOT_GUN);
 +
 +      setorigin(b, self.origin);
 +      b.frame = 11;
 +      b.angles = self.angles;
 +      setsize(b, self.mins, self.maxs);
 +
 +      setorigin(h, gettaginfo(self, gettagindex(self, "tag_head")));
 +      h.movetype = MOVETYPE_BOUNCE;
 +      h.solid = SOLID_BBOX;
 +      h.velocity = v_up * (500 + random() * 500) + randomvec() * 128;
 +      h.modelflags = MF_ROCKET;
 +      h.effects = EF_FLAME | EF_LOWPRECISION;
 +      h.avelocity = randomvec() * 360;
 +
 +      h.alpha = 1;
 +      h.cnt = time + (3.5 * random());
 +      h.fade_rate = 1 / min(self.respawntime, 10);
 +      h.fade_time = time;
 +      h.think = spiderbot_headfade;
 +      h.nextthink = time;
 +
 +      setorigin(g1, gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_hardpoint01")));
 +      g1.movetype = MOVETYPE_TOSS;
 +      g1.solid = SOLID_CORPSE;
 +      g1.velocity = v_forward * 700 + (randomvec() * 32);
 +      g1.avelocity = randomvec() * 180;
 +
 +      setorigin(g2, gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_hardpoint02")));
 +      g2.movetype = MOVETYPE_TOSS;
 +      g2.solid = SOLID_CORPSE;
 +      g2.velocity = v_forward * 700 + (randomvec() * 32);
 +      g2.avelocity = randomvec() * 180;
 +
 +      h.colormod = b.colormod = g1.colormod = g2.colormod = '-2 -2 -2';
 +
 +      SUB_SetFade(b,  time + 5, min(self.respawntime, 1));
 +      //SUB_SetFade(h,  time, min(self.respawntime, 10));
 +      SUB_SetFade(g1, time, min(self.respawntime, 10));
 +      SUB_SetFade(g2, time, min(self.respawntime, 10));
 +
 +      RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_SPID_DEATH, world);
 +
 +      self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = -1;
 +      self.movetype = MOVETYPE_NONE;
 +      self.deadflag = DEAD_DEAD;
 +      self.solid = SOLID_NOT;
 +      self.tur_head.effects &= ~EF_FLAME;
 +      self.vehicle_hudmodel.viewmodelforclient = self;
 +}
 +
 +bool spiderbot_impulse(int _imp)
 +{SELFPARAM();
 +      switch(_imp)
 +      {
 +              case 1:
 +                      self.vehicle.vehicle_weapon2mode = SBRM_VOLLY;
 +                      CSQCVehicleSetup(self, 0);
 +                      return true;
 +              case 2:
 +                      self.vehicle.vehicle_weapon2mode = SBRM_GUIDE;
 +                      CSQCVehicleSetup(self, 0);
 +                      return true;
 +              case 3:
 +                      self.vehicle.vehicle_weapon2mode = SBRM_ARTILLERY;
 +                      CSQCVehicleSetup(self, 0);
 +                      return true;
 +
 +              case 10:
 +              case 15:
 +              case 18:
 +                      self.vehicle.vehicle_weapon2mode += 1;
 +                      if(self.vehicle.vehicle_weapon2mode > SBRM_LAST)
 +                              self.vehicle.vehicle_weapon2mode = SBRM_FIRST;
 +
 +                      //centerprint(self, strcat("Rocket mode is ", ftos(self.vehicle.vehicle_weapon2mode)));
 +                      CSQCVehicleSetup(self, 0);
 +                      return true;
 +              case 11:
 +              case 12:
 +              case 16:
 +              case 19:
 +                      self.vehicle.vehicle_weapon2mode -= 1;
 +                      if(self.vehicle.vehicle_weapon2mode < SBRM_FIRST)
 +                              self.vehicle.vehicle_weapon2mode = SBRM_LAST;
 +
 +                      //centerprint(self, strcat("Rocket mode is ", ftos(self.vehicle.vehicle_weapon2mode)));
 +                      CSQCVehicleSetup(self, 0);
 +                      return true;
 +
 +              /*
 +              case 17: // toss gun, could be used to exit?
 +                      break;
 +              case 20: // Manual minigun reload?
 +                      break;
 +              */
 +      }
 +      return false;
 +}
 +
++spawnfunc(vehicle_spiderbot)
++{
 +      if(!autocvar_g_vehicle_spiderbot) { remove(self); return; }
 +      if(!vehicle_initialize(VEH_SPIDERBOT, false)) { remove(self); return; }
 +}
 +
 +              METHOD(Spiderbot, vr_impact, void(Spiderbot thisveh))
 +              {
 +                      if(autocvar_g_vehicle_spiderbot_bouncepain)
 +                              vehicles_impact(autocvar_g_vehicle_spiderbot_bouncepain_x, autocvar_g_vehicle_spiderbot_bouncepain_y, autocvar_g_vehicle_spiderbot_bouncepain_z);
 +              }
 +              METHOD(Spiderbot, vr_enter, void(Spiderbot thisveh))
 +              {
 +                      self.vehicle_weapon2mode = SBRM_GUIDE;
 +                      self.movetype = MOVETYPE_WALK;
 +                      CSQCVehicleSetup(self.owner, 0);
 +                      self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_spiderbot_health) * 100;
 +                      self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_spiderbot_shield) * 100;
 +
 +                      if(self.owner.flagcarried)
 +                      {
 +                              setattachment(self.owner.flagcarried, self.tur_head, "");
 +                              setorigin(self.owner.flagcarried, '-20 0 120');
 +                      }
 +              }
 +              METHOD(Spiderbot, vr_think, void(Spiderbot thisveh))
 +              {
 +                      if(self.flags & FL_ONGROUND)
 +                              movelib_beak_simple(autocvar_g_vehicle_spiderbot_speed_stop);
 +              }
 +              METHOD(Spiderbot, vr_death, void(Spiderbot thisveh))
 +              {
 +                      self.health                             = 0;
 +                      self.event_damage               = func_null;
 +                      self.takedamage                 = DAMAGE_NO;
 +                      self.touch                              = func_null;
 +                      self.cnt                                = 3.4 + time + random() * 2;
 +                      self.think                              = spiderbot_blowup;
 +                      self.nextthink                  = time;
 +                      self.deadflag                   = DEAD_DYING;
 +                      self.frame                              = 5;
 +                      self.tur_head.effects  |= EF_FLAME;
 +                      self.colormod                   = self.tur_head.colormod = '-1 -1 -1';
 +                      self.frame                              = 10;
 +                      self.movetype                   = MOVETYPE_TOSS;
 +
 +                      CSQCModel_UnlinkEntity(); // networking the death scene would be a nightmare
 +              }
 +              METHOD(Spiderbot, vr_spawn, void(Spiderbot thisveh))
 +              {
 +                      if(!self.gun1)
 +                      {
 +                              self.vehicles_impulse = spiderbot_impulse;
 +                              self.gun1 = spawn();
 +                              self.gun2 = spawn();
 +                              setmodel(self.gun1, MDL_VEH_SPIDERBOT_GUN);
 +                              setmodel(self.gun2, MDL_VEH_SPIDERBOT_GUN);
 +                              setattachment(self.gun1, self.tur_head, "tag_hardpoint01");
 +                              setattachment(self.gun2, self.tur_head, "tag_hardpoint02");
 +                              self.gravity = 2;
 +                              self.mass = 5000;
 +                      }
 +
 +                      self.frame = 5;
 +                      self.tur_head.frame = 1;
 +                      self.movetype = MOVETYPE_WALK;
 +                      self.solid = SOLID_SLIDEBOX;
 +                      self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = 1;
 +                      self.tur_head.angles = '0 0 0';
 +                      self.vehicle_exit = spiderbot_exit;
 +
 +                      setorigin(self, self.pos1 + '0 0 128');
 +                      self.angles = self.pos2;
 +                      self.damageforcescale = 0.03;
 +                      self.vehicle_health = autocvar_g_vehicle_spiderbot_health;
 +                      self.vehicle_shield = autocvar_g_vehicle_spiderbot_shield;
 +
 +                      self.PlayerPhysplug = spiderbot_frame;
 +              }
 +              METHOD(Spiderbot, vr_setup, void(Spiderbot thisveh))
 +              {
 +                      if(autocvar_g_vehicle_spiderbot_shield)
 +                              self.vehicle_flags |= VHF_HASSHIELD;
 +
 +                      if(autocvar_g_vehicle_spiderbot_shield_regen)
 +                              self.vehicle_flags |= VHF_SHIELDREGEN;
 +
 +                      if(autocvar_g_vehicle_spiderbot_health_regen)
 +                              self.vehicle_flags |= VHF_HEALTHREGEN;
 +
 +                      self.respawntime = autocvar_g_vehicle_spiderbot_respawntime;
 +                      self.vehicle_health = autocvar_g_vehicle_spiderbot_health;
 +                      self.vehicle_shield = autocvar_g_vehicle_spiderbot_shield;
 +                      self.max_health = self.vehicle_health;
 +                      self.pushable = true; // spiderbot can use jumppads
 +              }
 +
 +#endif // SVQC
 +#ifdef CSQC
 +float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6;
 +float autocvar_cl_vehicle_spiderbot_cross_size = 1;
 +
 +              METHOD(Spiderbot, vr_hud, void(Spiderbot thisveh))
 +              {
 +                      string crosshair;
 +
 +                      switch(weapon2mode)
 +                      {
 +                              case SBRM_VOLLY:     crosshair = vCROSS_BURST; break;
 +                              case SBRM_GUIDE:     crosshair = vCROSS_GUIDE; break;
 +                              case SBRM_ARTILLERY: crosshair = vCROSS_RAIN;  break;
 +                              default:             crosshair = vCROSS_BURST;
 +                      }
 +
 +                      Vehicles_drawHUD(VEH_SPIDERBOT.m_icon, "vehicle_spider_weapon1", "vehicle_spider_weapon2",
 +                                                       "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
 +                                                       "vehicle_icon_ammo2", autocvar_hud_progressbar_vehicles_ammo2_color,
 +                                                       crosshair);
 +              }
 +              METHOD(Spiderbot, vr_setup, void(Spiderbot thisveh))
 +              {
 +                      AuxiliaryXhair[0].axh_image = vCROSS_HINT; // Minigun1
 +                      AuxiliaryXhair[1].axh_image = vCROSS_HINT; // Minigun2
 +              }
 +
 +#endif
 +#endif
index b3fe1b1ef69a19bc259aa3ae90dea9c875f2c4d8,6e1e2c7cb6ebdec188eab9e383154d5bbb825822..0ec1d7a09c4a005bb99b482df3524d5d7dd7d141
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ ARC,
 -/* function  */ W_Arc,
 -/* ammotype  */ ammo_cells,
 -/* impulse   */ 3,
 -/* flags     */ WEP_FLAG_NORMAL,
 -/* rating    */ BOT_PICKUP_RATING_HIGH,
 -/* color     */ '1 1 1',
 -/* modelname */ "arc",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairhlac 0.7",
 -/* wepimg    */ "weaponarc",
 -/* refname   */ "arc",
 -/* wepname   */ _("Arc")
 -);
 +CLASS(Arc, Weapon)
 +/* ammotype  */ ATTRIB(Arc, ammo_field, .int, ammo_cells)
 +/* impulse   */ ATTRIB(Arc, impulse, int, 3)
 +/* flags     */ ATTRIB(Arc, spawnflags, int, WEP_FLAG_NORMAL);
 +/* rating    */ ATTRIB(Arc, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 +/* color     */ ATTRIB(Arc, wpcolor, vector, '1 1 1');
 +/* modelname */ ATTRIB(Arc, mdl, string, "arc");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Arc, m_model, Model, MDL_ARC_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Arc, w_crosshair, string, "gfx/crosshairhlac");
 +/* crosshair */ ATTRIB(Arc, w_crosshair_size, float, 0.7);
 +/* wepimg    */ ATTRIB(Arc, model2, string, "weaponarc");
 +/* refname   */ ATTRIB(Arc, netname, string, "arc");
 +/* wepname   */ ATTRIB(Arc, message, string, _("Arc"));
 +ENDCLASS(Arc)
 +REGISTER_WEAPON(ARC, NEW(Arc));
  
  #define ARC_SETTINGS(w_cvar,w_prop) ARC_SETTINGS_LIST(w_cvar, w_prop, ARC, arc)
  #define ARC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -84,7 -82,7 +84,7 @@@ const int ARC_SF_LOCALMASK =   14
  #ifdef SVQC
  ARC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
  .entity arc_beam;
 -.float arc_BUTTON_ATCK_prev; // for better animation control
 +.bool arc_BUTTON_ATCK_prev; // for better animation control
  .float beam_prev;
  .float beam_initialized;
  .float beam_bursting;
@@@ -128,7 -126,7 +128,7 @@@ vector Draw_ArcBeam_callback_last_botto
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_arc(void) { weapon_defaultspawnfunc(WEP_ARC.m_id); }
spawnfunc(weapon_arc) { weapon_defaultspawnfunc(WEP_ARC.m_id); }
  
  float W_Arc_Beam_Send(entity to, int sf)
  {SELFPARAM();
@@@ -275,8 -273,7 +275,8 @@@ void W_Arc_Beam_Think(void
  
                if(self == self.owner.arc_beam) { self.owner.arc_beam = world; }
                setself(self.owner);
 -              if(!WEP_ACTION(WEP_ARC.m_id, WR_CHECKAMMO1) && !WEP_ACTION(WEP_ARC.m_id, WR_CHECKAMMO2))
 +              Weapon w = WEP_ARC;
 +              if(!w.wr_checkammo1(w) && !w.wr_checkammo2(w))
                if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
                {
                        // note: this doesn't force the switch
@@@ -634,9 -631,12 +634,9 @@@ void Arc_Smoke(
        }
  }
  
 -bool W_Arc(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Arc, wr_aim, void(entity thiswep))
                {
 +                      SELFPARAM();
                        if(WEP_CVAR(arc, beam_botaimspeed))
                        {
                                self.BUTTON_ATCK = bot_aim(
                                        false
                                );
                        }
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Arc, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      Arc_Player_SetHeat(self);
 +                      Arc_Player_SetHeat(actor);
                        Arc_Smoke();
  
 -                      if ( self.arc_overheat <= time )
 -                      if(self.BUTTON_ATCK || self.BUTTON_ATCK2 || self.arc_beam.beam_bursting )
 +                      if (time >= actor.arc_overheat)
 +                      if (fire1 || fire2 || actor.arc_beam.beam_bursting)
                        {
  
 -                              if(self.arc_BUTTON_ATCK_prev)
 +                              if(actor.arc_BUTTON_ATCK_prev)
                                {
                                        #if 0
 -                                      if(self.animstate_startframe == self.anim_shoot.x && self.animstate_numframes == self.anim_shoot.y)
 -                                              weapon_thinkf(WFRAME_DONTCHANGE, autocvar_g_balance_arc_primary_animtime, w_ready);
 +                                      if(actor.animstate_startframe == actor.anim_shoot.x && actor.animstate_numframes == actor.anim_shoot.y)
 +                                              weapon_thinkf(actor, WFRAME_DONTCHANGE, autocvar_g_balance_arc_primary_animtime, w_ready);
                                        else
                                        #endif
 -                                              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
 +                                              weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
                                }
  
 -                              if((!self.arc_beam) || wasfreed(self.arc_beam))
 +                              if((!actor.arc_beam) || wasfreed(actor.arc_beam))
                                {
 -                                      if(weapon_prepareattack(!!self.BUTTON_ATCK2, 0))
 +                                      if(weapon_prepareattack(actor, fire2, 0))
                                        {
 -                                              W_Arc_Beam(!!self.BUTTON_ATCK2);
 +                                              W_Arc_Beam(fire2);
  
 -                                              if(!self.arc_BUTTON_ATCK_prev)
 +                                              if(!actor.arc_BUTTON_ATCK_prev)
                                                {
 -                                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
 -                                                      self.arc_BUTTON_ATCK_prev = 1;
 +                                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
 +                                                      actor.arc_BUTTON_ATCK_prev = true;
                                                }
                                        }
                                }
  
 -                              return true;
 +                              return;
                        }
  
 -                      if(self.arc_BUTTON_ATCK_prev != 0)
 +                      if(actor.arc_BUTTON_ATCK_prev)
                        {
 -                              sound(self, CH_WEAPON_A, SND_ARC_STOP, VOL_BASE, ATTN_NORM);
 -                              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
 -                              ATTACK_FINISHED(self) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor();
 +                              sound(actor, CH_WEAPON_A, SND_ARC_STOP, VOL_BASE, ATTN_NORM);
 +                              weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
 +                              ATTACK_FINISHED(actor) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor();
                        }
 -                      self.arc_BUTTON_ATCK_prev = 0;
 +                      actor.arc_BUTTON_ATCK_prev = false;
  
                        #if 0
 -                      if(self.BUTTON_ATCK2)
 -                      if(weapon_prepareattack(1, autocvar_g_balance_arc_secondary_refire))
 +                      if(fire2)
 +                      if(weapon_prepareattack(actor, true, autocvar_g_balance_arc_secondary_refire))
                        {
                                W_Arc_Attack2();
 -                              self.arc_count = autocvar_g_balance_arc_secondary_count;
 -                              weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_arc_secondary_animtime, w_arc_checkattack);
 -                              self.arc_secondarytime = time + autocvar_g_balance_arc_secondary_refire2 * W_WeaponRateFactor();
 +                              actor.arc_count = autocvar_g_balance_arc_secondary_count;
 +                              weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_balance_arc_secondary_animtime, w_arc_checkattack);
 +                              actor.arc_secondarytime = time + autocvar_g_balance_arc_secondary_refire2 * W_WeaponRateFactor();
                        }
                        #endif
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Arc, wr_init, void(entity thiswep))
                {
                        if(!arc_shotorigin[0])
                        {
                                arc_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC.m_id), false, false, 4);
                        }
                        ARC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Arc, wr_checkammo1, bool(entity thiswep))
                {
 +                      SELFPARAM();
                        return ((!WEP_CVAR(arc, beam_ammo)) || (self.WEP_AMMO(ARC) > 0));
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Arc, wr_checkammo2, bool(entity thiswep))
                {
 +                      SELFPARAM();
                        return WEP_CVAR(arc, overheat_max) > 0 &&
                                ((!WEP_CVAR(arc, burst_ammo)) || (self.WEP_AMMO(ARC) > 0));
                }
 -              case WR_CONFIG:
 +              METHOD(Arc, wr_config, void(entity thiswep))
                {
                        ARC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Arc, wr_killmessage, int(entity thiswep))
                {
                        return WEAPON_ARC_MURDER;
                }
 -              case WR_DROP:
 +              METHOD(Arc, wr_drop, void(entity thiswep))
                {
                        weapon_dropevent_item.arc_overheat = self.arc_overheat;
                        weapon_dropevent_item.arc_cooldown = self.arc_cooldown;
                        self.arc_overheat = 0;
                        self.arc_cooldown = 0;
 -                      return true;
                }
 -              case WR_PICKUP:
 +              METHOD(Arc, wr_pickup, void(entity thiswep))
                {
                        if ( !client_hasweapon(self, WEP_ARC.m_id, false, false) &&
                                weapon_dropevent_item.arc_overheat > time )
                                self.arc_overheat = weapon_dropevent_item.arc_overheat;
                                self.arc_cooldown = weapon_dropevent_item.arc_cooldown;
                        }
 -                      return true;
                }
 -      }
 -      return false;
 -}
  #endif
  #ifdef CSQC
  void Draw_ArcBeam_callback(vector start, vector hit, vector end)
@@@ -1501,5 -1509,26 +1501,5 @@@ void Ent_ReadArcBeam(float isnew
        }
  }
  
 -bool W_Arc(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 -              {
 -                      // todo
 -                      return true;
 -              }
 -              case WR_INIT:
 -              {
 -                      return true;
 -              }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
  #endif
  #endif
index 32310d033c1a48d9737913e6d7709b8c94dcb054,10e55990a5594f645aeec8859e357905cf8d6135..5d636674d0419fa28ecbaaa12ba4c483c8899618
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ BLASTER,
 -/* function  */ W_Blaster,
 -/* ammotype  */ ammo_none,
 -/* impulse   */ 1,
 -/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
 -/* rating    */ 0,
 -/* color     */ '1 0.5 0.5',
 -/* modelname */ "laser",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairlaser 0.5",
 -/* wepimg    */ "weaponlaser",
 -/* refname   */ "blaster",
 -/* wepname   */ _("Blaster")
 -);
 +CLASS(Blaster, Weapon)
 +/* ammotype  */ //ATTRIB(Blaster, ammo_field, .int, ammo_none)
 +/* impulse   */ ATTRIB(Blaster, impulse, int, 1)
 +/* flags     */ ATTRIB(Blaster, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(Blaster, bot_pickupbasevalue, float, 0);
 +/* color     */ ATTRIB(Blaster, wpcolor, vector, '1 0.5 0.5');
 +/* modelname */ ATTRIB(Blaster, mdl, string, "laser");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Blaster, m_model, Model, MDL_BLASTER_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Blaster, w_crosshair, string, "gfx/crosshairlaser");
 +/* crosshair */ ATTRIB(Blaster, w_crosshair_size, float, 0.5);
 +/* wepimg    */ ATTRIB(Blaster, model2, string, "weaponlaser");
 +/* refname   */ ATTRIB(Blaster, netname, string, "blaster");
 +/* wepname   */ ATTRIB(Blaster, message, string, _("Blaster"));
 +ENDCLASS(Blaster)
 +REGISTER_WEAPON(BLASTER, NEW(Blaster));
  
  #define BLASTER_SETTINGS(w_cvar,w_prop) BLASTER_SETTINGS_LIST(w_cvar, w_prop, BLASTER, blaster)
  #define BLASTER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -50,8 -48,8 +50,8 @@@ BLASTER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_blaster(void) { weapon_defaultspawnfunc(WEP_BLASTER.m_id); }
void spawnfunc_weapon_laser(void) { spawnfunc_weapon_blaster(); }
spawnfunc(weapon_blaster) { weapon_defaultspawnfunc(WEP_BLASTER.m_id); }
spawnfunc(weapon_laser) { spawnfunc_weapon_blaster(this); }
  
  void W_Blaster_Touch(void)
  {SELFPARAM();
@@@ -84,7 -82,6 +84,7 @@@ void W_Blaster_Think(void
  }
  
  void W_Blaster_Attack(
 +      entity actor,
        float atk_deathtype,
        float atk_shotangle,
        float atk_damage,
  {SELFPARAM();
        vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD);
  
 -      W_SetupShot_Dir(self, s_forward, false, 3, SND(LASERGUN_FIRE), CH_WEAPON_B, atk_damage);
 +      W_SetupShot_Dir(actor, s_forward, false, 3, SND(LASERGUN_FIRE), CH_WEAPON_B, atk_damage);
        Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
  
        entity missile = spawn();
 -      missile.owner = missile.realowner = self;
 +      missile.owner = missile.realowner = actor;
        missile.classname = "blasterbolt";
        missile.bot_dodge = true;
        missile.bot_dodgerating = atk_damage;
        missile.think = W_Blaster_Think;
        missile.nextthink = time + atk_delay;
  
 -      MUTATOR_CALLHOOK(EditProjectile, self, missile);
 +      MUTATOR_CALLHOOK(EditProjectile, actor, missile);
  
 -      if(time >= missile.nextthink)
 +      if (time >= missile.nextthink)
        {
                WITH(entity, self, missile, missile.think());
        }
  }
 -bool W_Blaster(int request)
 -{SELFPARAM();
 -      switch(request)
 -      {
 -              case WR_AIM:
 +
 +              METHOD(Blaster, wr_aim, void(entity thiswep))
                {
                        if(WEP_CVAR(blaster, secondary))
                        {
                        }
                        else
                                { self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), false); }
 -
 -                      return true;
                }
  
 -              case WR_THINK:
 +              METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(self.BUTTON_ATCK)
 +                      if(fire1)
                        {
 -                              if(weapon_prepareattack(0, WEP_CVAR_PRI(blaster, refire)))
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(blaster, refire)))
                                {
                                        W_Blaster_Attack(
 +                                              actor,
                                                WEP_BLASTER.m_id,
                                                WEP_CVAR_PRI(blaster, shotangle),
                                                WEP_CVAR_PRI(blaster, damage),
                                                WEP_CVAR_PRI(blaster, delay),
                                                WEP_CVAR_PRI(blaster, lifetime)
                                        );
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(blaster, animtime), w_ready);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(blaster, animtime), w_ready);
                                }
                        }
 -                      else if(self.BUTTON_ATCK2)
 +                      else if(fire2)
                        {
                                switch(WEP_CVAR(blaster, secondary))
                                {
                                        case 0: // switch to last used weapon
                                        {
 -                                              if(self.switchweapon == WEP_BLASTER.m_id) // don't do this if already switching
 +                                              if(actor.switchweapon == WEP_BLASTER.m_id) // don't do this if already switching
                                                        W_LastWeapon();
                                                break;
                                        }
  
                                        case 1: // normal projectile secondary
                                        {
 -                                              if(weapon_prepareattack(1, WEP_CVAR_SEC(blaster, refire)))
 +                                              if(weapon_prepareattack(actor, true, WEP_CVAR_SEC(blaster, refire)))
                                                {
                                                        W_Blaster_Attack(
 +                                                              actor,
                                                                WEP_BLASTER.m_id | HITTYPE_SECONDARY,
                                                                WEP_CVAR_SEC(blaster, shotangle),
                                                                WEP_CVAR_SEC(blaster, damage),
                                                                WEP_CVAR_SEC(blaster, delay),
                                                                WEP_CVAR_SEC(blaster, lifetime)
                                                        );
 -                                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready);
 +                                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready);
                                                }
  
                                                break;
                                        }
                                }
                        }
 -                      return true;
                }
  
 -              case WR_INIT:
 +              METHOD(Blaster, wr_init, void(entity thiswep))
                {
                        BLASTER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
  
 -              case WR_SETUP:
 +              METHOD(Blaster, wr_setup, void(entity thiswep))
                {
                        self.ammo_field = ammo_none;
 -                      return true;
                }
  
 -              case WR_CHECKAMMO1:
 -              case WR_CHECKAMMO2:
 +              METHOD(Blaster, wr_checkammo1, bool(entity thiswep))
                {
 -                      return true; // laser has infinite ammo
 +                      return true; // infinite ammo
                }
  
 -              case WR_CONFIG:
 +              METHOD(Blaster, wr_checkammo2, bool(entity thiswep))
 +              {
 +                      return true; // blaster has infinite ammo
 +              }
 +
 +              METHOD(Blaster, wr_config, void(entity thiswep))
                {
                        BLASTER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
  
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Blaster, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_BLASTER_SUICIDE;
                }
  
 -              case WR_KILLMESSAGE:
 +              METHOD(Blaster, wr_killmessage, int(entity thiswep))
                {
                        return WEAPON_BLASTER_MURDER;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_Blaster(int request)
 -{SELFPARAM();
 -      switch(request)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Blaster, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 6;
                        pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1);
                        if(!w_issilent) { sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); }
 -                      return true;
                }
  
 -              case WR_INIT:
 -              {
 -                      return true;
 -              }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
  #endif
  #endif
index 0ed21c80f2a5509745180785d3992965473a3f2b,bf6c0795f5985b9f1ae274bf94f76019929e4fd2..d72320cf2d131b3ffcef7fa9d7fd1aa199577991
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ CRYLINK,
 -/* function  */ W_Crylink,
 -/* ammotype  */ ammo_cells,
 -/* impulse   */ 6,
 -/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
 -/* rating    */ BOT_PICKUP_RATING_MID,
 -/* color     */ '1 0.5 1',
 -/* modelname */ "crylink",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshaircrylink 0.5",
 -/* wepimg    */ "weaponcrylink",
 -/* refname   */ "crylink",
 -/* wepname   */ _("Crylink")
 -);
 +CLASS(Crylink, Weapon)
 +/* ammotype  */ ATTRIB(Crylink, ammo_field, .int, ammo_cells)
 +/* impulse   */ ATTRIB(Crylink, impulse, int, 6)
 +/* flags     */ ATTRIB(Crylink, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(Crylink, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 +/* color     */ ATTRIB(Crylink, wpcolor, vector, '1 0.5 1');
 +/* modelname */ ATTRIB(Crylink, mdl, string, "crylink");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Crylink, m_model, Model, MDL_CRYLINK_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Crylink, w_crosshair, string, "gfx/crosshaircrylink");
 +/* crosshair */ ATTRIB(Crylink, w_crosshair_size, float, 0.5);
 +/* wepimg    */ ATTRIB(Crylink, model2, string, "weaponcrylink");
 +/* refname   */ ATTRIB(Crylink, netname, string, "crylink");
 +/* wepname   */ ATTRIB(Crylink, message, string, _("Crylink"));
 +ENDCLASS(Crylink)
 +REGISTER_WEAPON(CRYLINK, NEW(Crylink));
  
  #define CRYLINK_SETTINGS(w_cvar,w_prop) CRYLINK_SETTINGS_LIST(w_cvar, w_prop, CRYLINK, crylink)
  #define CRYLINK_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -66,7 -64,7 +66,7 @@@ CRYLINK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_crylink(void) { weapon_defaultspawnfunc(WEP_CRYLINK.m_id); }
spawnfunc(weapon_crylink) { weapon_defaultspawnfunc(WEP_CRYLINK.m_id); }
  
  void W_Crylink_CheckLinks(entity e)
  {
@@@ -341,7 -339,7 +341,7 @@@ void W_Crylink_Fadethink(void
        remove(self);
  }
  
 -void W_Crylink_Attack(void)
 +void W_Crylink_Attack(Weapon thiswep)
  {SELFPARAM();
        float counter, shots;
        entity proj, prevproj, firstproj;
        vector forward, right, up;
        float maxdmg;
  
 -      W_DecreaseAmmo(WEP_CVAR_PRI(crylink, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_PRI(crylink, ammo));
  
        maxdmg = WEP_CVAR_PRI(crylink, damage) * WEP_CVAR_PRI(crylink, shots);
        maxdmg *= 1 + WEP_CVAR_PRI(crylink, bouncedamagefactor) * WEP_CVAR_PRI(crylink, bounces);
        }
  }
  
 -void W_Crylink_Attack2(void)
 +void W_Crylink_Attack2(Weapon thiswep)
  {SELFPARAM();
        float counter, shots;
        entity proj, prevproj, firstproj;
        vector forward, right, up;
        float maxdmg;
  
 -      W_DecreaseAmmo(WEP_CVAR_SEC(crylink, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(crylink, ammo));
  
        maxdmg = WEP_CVAR_SEC(crylink, damage) * WEP_CVAR_SEC(crylink, shots);
        maxdmg *= 1 + WEP_CVAR_SEC(crylink, bouncedamagefactor) * WEP_CVAR_SEC(crylink, bounces);
        }
  }
  
 -bool W_Crylink(int req)
 -{SELFPARAM();
 -      float ammo_amount;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Crylink, wr_aim, void(entity thiswep))
                {
 +                      SELFPARAM();
                        if(random() < 0.10)
                                self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(crylink, speed), 0, WEP_CVAR_PRI(crylink, middle_lifetime), false);
                        else
                                self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(crylink, speed), 0, WEP_CVAR_SEC(crylink, middle_lifetime), false);
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Crylink, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(autocvar_g_balance_crylink_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo))) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 +                      if(autocvar_g_balance_crylink_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo))) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      }
  
 -                      if(self.BUTTON_ATCK)
 +                      if(fire1)
                        {
 -                              if(self.crylink_waitrelease != 1)
 -                              if(weapon_prepareattack(0, WEP_CVAR_PRI(crylink, refire)))
 +                              if(actor.crylink_waitrelease != 1)
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(crylink, refire)))
                                {
 -                                      W_Crylink_Attack();
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready);
 +                                      W_Crylink_Attack(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready);
                                }
                        }
  
 -                      if(self.BUTTON_ATCK2 && autocvar_g_balance_crylink_secondary)
 +                      if(fire2 && autocvar_g_balance_crylink_secondary)
                        {
 -                              if(self.crylink_waitrelease != 2)
 -                              if(weapon_prepareattack(1, WEP_CVAR_SEC(crylink, refire)))
 +                              if(actor.crylink_waitrelease != 2)
 +                              if(weapon_prepareattack(actor, true, WEP_CVAR_SEC(crylink, refire)))
                                {
 -                                      W_Crylink_Attack2();
 -                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready);
 +                                      W_Crylink_Attack2(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready);
                                }
                        }
  
 -                      if((self.crylink_waitrelease == 1 && !self.BUTTON_ATCK) || (self.crylink_waitrelease == 2 && !self.BUTTON_ATCK2))
 +                      if((actor.crylink_waitrelease == 1 && !fire1) || (actor.crylink_waitrelease == 2 && !fire2))
                        {
 -                              if(!self.crylink_lastgroup || time > self.crylink_lastgroup.teleport_time)
 +                              if(!actor.crylink_lastgroup || time > actor.crylink_lastgroup.teleport_time)
                                {
                                        // fired and released now!
 -                                      if(self.crylink_lastgroup)
 +                                      if(actor.crylink_lastgroup)
                                        {
                                                vector pos;
                                                entity linkjoineffect;
 -                                              float isprimary = (self.crylink_waitrelease == 1);
 +                                              float isprimary = (actor.crylink_waitrelease == 1);
  
 -                                              pos = W_Crylink_LinkJoin(self.crylink_lastgroup, WEP_CVAR_BOTH(crylink, isprimary, joinspread) * WEP_CVAR_BOTH(crylink, isprimary, speed));
 +                                              pos = W_Crylink_LinkJoin(actor.crylink_lastgroup, WEP_CVAR_BOTH(crylink, isprimary, joinspread) * WEP_CVAR_BOTH(crylink, isprimary, speed));
  
                                                linkjoineffect = spawn();
                                                linkjoineffect.think = W_Crylink_LinkJoinEffect_Think;
                                                linkjoineffect.classname = "linkjoineffect";
                                                linkjoineffect.nextthink = time + w_crylink_linkjoin_time;
 -                                              linkjoineffect.owner = self;
 +                                              linkjoineffect.owner = actor;
                                                setorigin(linkjoineffect, pos);
                                        }
 -                                      self.crylink_waitrelease = 0;
 -                                      if(!W_Crylink(WR_CHECKAMMO1) && !W_Crylink(WR_CHECKAMMO2))
 -                                      if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +                                      actor.crylink_waitrelease = 0;
 +                                      if(!thiswep.wr_checkammo1(thiswep) && !thiswep.wr_checkammo2(thiswep))
 +                                      if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
                                        {
                                                // ran out of ammo!
 -                                              self.cnt = WEP_CRYLINK.m_id;
 -                                              self.switchweapon = w_getbestweapon(self);
 +                                              actor.cnt = WEP_CRYLINK.m_id;
 +                                              actor.switchweapon = w_getbestweapon(actor);
                                        }
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Crylink, wr_init, void(entity thiswep))
                {
                        CRYLINK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Crylink, wr_checkammo1, bool(entity thiswep))
                {
 +                      SELFPARAM();
                        // don't "run out of ammo" and switch weapons while waiting for release
                        if(self.crylink_lastgroup && self.crylink_waitrelease)
                                return true;
  
 -                      ammo_amount = self.WEP_AMMO(CRYLINK) >= WEP_CVAR_PRI(crylink, ammo);
 +                      float ammo_amount = self.WEP_AMMO(CRYLINK) >= WEP_CVAR_PRI(crylink, ammo);
                        ammo_amount += self.(weapon_load[WEP_CRYLINK.m_id]) >= WEP_CVAR_PRI(crylink, ammo);
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Crylink, wr_checkammo2, bool(entity thiswep))
                {
 +                      SELFPARAM();
                        // don't "run out of ammo" and switch weapons while waiting for release
                        if(self.crylink_lastgroup && self.crylink_waitrelease)
                                return true;
  
 -                      ammo_amount = self.WEP_AMMO(CRYLINK) >= WEP_CVAR_SEC(crylink, ammo);
 +                      float ammo_amount = self.WEP_AMMO(CRYLINK) >= WEP_CVAR_SEC(crylink, ammo);
                        ammo_amount += self.(weapon_load[WEP_CRYLINK.m_id]) >= WEP_CVAR_SEC(crylink, ammo);
                        return ammo_amount;
                }
 -              case WR_CONFIG:
 +              METHOD(Crylink, wr_config, void(entity thiswep))
                {
                        CRYLINK_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Crylink, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Crylink, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_CRYLINK_SUICIDE;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Crylink, wr_killmessage, int(entity thiswep))
                {
                        return WEAPON_CRYLINK_MURDER;
                }
 -      }
 -      return false;
 -}
  #endif
  #ifdef CSQC
 -bool W_Crylink(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +              METHOD(Crylink, wr_impacteffect, void(entity thiswep))
                {
 +                      SELFPARAM();
                        vector org2;
                        org2 = w_org + w_backoff * 2;
                        if(w_deathtype & HITTYPE_SECONDARY)
                                if(!w_issilent)
                                        sound(self, CH_SHOTS, SND_CRYLINK_IMPACT, VOL_BASE, ATTN_NORM);
                        }
 -
 -                      return true;
 -              }
 -              case WR_INIT:
 -              {
 -                      return true;
                }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
  #endif
  #endif
index 3e142d5ecc3fe864d656919d6ba4abb7f024e25a,0735f79ab4eda876d2da26f947a37ebbb3c1aba2..3d0b46dcdad5788b6e2f380160ece453658ea354
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ DEVASTATOR,
 -/* function  */ W_Devastator,
 -/* ammotype  */ ammo_rockets,
 -/* impulse   */ 9,
 -/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
 -/* rating    */ BOT_PICKUP_RATING_HIGH,
 -/* color     */ '1 1 0',
 -/* modelname */ "rl",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairrocketlauncher 0.7",
 -/* wepimg    */ "weaponrocketlauncher",
 -/* refname   */ "devastator",
 -/* wepname   */ _("Devastator")
 -);
 +CLASS(Devastator, Weapon)
 +/* ammotype  */ ATTRIB(Devastator, ammo_field, .int, ammo_rockets)
 +/* impulse   */ ATTRIB(Devastator, impulse, int, 9)
 +/* flags     */ ATTRIB(Devastator, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(Devastator, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 +/* color     */ ATTRIB(Devastator, wpcolor, vector, '1 1 0');
 +/* modelname */ ATTRIB(Devastator, mdl, string, "rl");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Devastator, m_model, Model, MDL_DEVASTATOR_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Devastator, w_crosshair, string, "gfx/crosshairrocketlauncher");
 +/* crosshair */ ATTRIB(Devastator, w_crosshair_size, float, 0.7);
 +/* wepimg    */ ATTRIB(Devastator, model2, string, "weaponrocketlauncher");
 +/* refname   */ ATTRIB(Devastator, netname, string, "devastator");
 +/* wepname   */ ATTRIB(Devastator, message, string, _("Devastator"));
 +ENDCLASS(Devastator)
 +REGISTER_WEAPON(DEVASTATOR, NEW(Devastator));
  
  #define DEVASTATOR_SETTINGS(w_cvar,w_prop) DEVASTATOR_SETTINGS_LIST(w_cvar, w_prop, DEVASTATOR, devastator)
  #define DEVASTATOR_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -64,8 -62,8 +64,8 @@@ DEVASTATOR_SETTINGS(WEP_ADD_CVAR, WEP_A
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_devastator(void) { weapon_defaultspawnfunc(WEP_DEVASTATOR.m_id); }
void spawnfunc_weapon_rocketlauncher(void) { spawnfunc_weapon_devastator(); }
spawnfunc(weapon_devastator) { weapon_defaultspawnfunc(WEP_DEVASTATOR.m_id); }
spawnfunc(weapon_rocketlauncher) { spawnfunc_weapon_devastator(this); }
  
  void W_Devastator_Unregister(void)
  {SELFPARAM();
@@@ -337,12 -335,12 +337,12 @@@ void W_Devastator_Damage(entity inflict
                W_PrepareExplosionByDamage(attacker, W_Devastator_Explode);
  }
  
 -void W_Devastator_Attack(void)
 +void W_Devastator_Attack(Weapon thiswep)
  {SELFPARAM();
        entity missile;
        entity flash;
  
 -      W_DecreaseAmmo(WEP_CVAR(devastator, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR(devastator, ammo));
  
        W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', false, 5, SND(ROCKET_FIRE), CH_WEAPON_A, WEP_CVAR(devastator, damage));
        Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
        setmodel(flash, MDL_DEVASTATOR_MUZZLEFLASH); // precision set below
        SUB_SetFade(flash, time, 0.1);
        flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
 -      W_AttachToShotorg(flash, '5 0 0');
 +      W_AttachToShotorg(self, flash, '5 0 0');
  
        // common properties
        MUTATOR_CALLHOOK(EditProjectile, self, missile);
  }
  
 -bool W_Devastator(int req)
 -{SELFPARAM();
 -      entity rock;
 -      float rockfound;
 -      float ammo_amount;
 -      switch(req)
 -      {
                #if 0
 -              case WR_AIM:
 +              METHOD(Devastator, wr_aim, void(entity thiswep))
                {
                        // aim and decide to fire if appropriate
                        self.BUTTON_ATCK = bot_aim(WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false);
  
                                if(self.BUTTON_ATCK2) self.BUTTON_ATCK = false;
                        }
 -
 -                      return true;
                }
                #else
 -              case WR_AIM:
 +              METHOD(Devastator, wr_aim, void(entity thiswep))
                {
                        // aim and decide to fire if appropriate
                        self.BUTTON_ATCK = bot_aim(WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false);
                                //      dprint(ftos(desirabledamage),"\n");
                                if(self.BUTTON_ATCK2 == true) self.BUTTON_ATCK = false;
                        }
 -
 -                      return true;
                }
                #endif
 -              case WR_THINK:
 +              METHOD(Devastator, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(WEP_CVAR(devastator, reload_ammo) && self.clip_load < WEP_CVAR(devastator, ammo)) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      else
 -                      {
 -                              if(self.BUTTON_ATCK)
 +                      if(WEP_CVAR(devastator, reload_ammo) && actor.clip_load < WEP_CVAR(devastator, ammo)) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else {
 +                              if(fire1)
                                {
 -                                      if(self.rl_release || WEP_CVAR(devastator, guidestop))
 -                                      if(weapon_prepareattack(0, WEP_CVAR(devastator, refire)))
 +                                      if(actor.rl_release || WEP_CVAR(devastator, guidestop))
 +                                      if(weapon_prepareattack(actor, false, WEP_CVAR(devastator, refire)))
                                        {
 -                                              W_Devastator_Attack();
 -                                              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready);
 -                                              self.rl_release = 0;
 +                                              W_Devastator_Attack(thiswep);
 +                                              weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready);
 +                                              actor.rl_release = 0;
                                        }
                                }
                                else
 -                                      self.rl_release = 1;
 +                                      actor.rl_release = 1;
  
 -                              if(self.BUTTON_ATCK2)
 -                              if(self.switchweapon == WEP_DEVASTATOR.m_id)
 +                              if(fire2)
 +                              if(actor.switchweapon == WEP_DEVASTATOR.m_id)
                                {
 -                                      rockfound = 0;
 -                                      for(rock = world; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == self)
 +                                      entity rock;
 +                                      bool rockfound = false;
 +                                      for(rock = world; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == actor)
                                        {
                                                if(!rock.rl_detonate_later)
                                                {
                                                        rock.rl_detonate_later = true;
 -                                                      rockfound = 1;
 +                                                      rockfound = true;
                                                }
                                        }
                                        if(rockfound)
 -                                              sound(self, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM);
 +                                              sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM);
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Devastator, wr_init, void(entity thiswep))
                {
                        DEVASTATOR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_SETUP:
 +              METHOD(Devastator, wr_setup, void(entity thiswep))
                {
                        self.rl_release = 1;
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Devastator, wr_checkammo1, bool(entity thiswep))
                {
                        #if 0
                        // don't switch while guiding a missile
                                return ammo_amount;
                        }
                        #else
 -                      ammo_amount = self.WEP_AMMO(DEVASTATOR) >= WEP_CVAR(devastator, ammo);
 +                      float ammo_amount = self.WEP_AMMO(DEVASTATOR) >= WEP_CVAR(devastator, ammo);
                        ammo_amount += self.(weapon_load[WEP_DEVASTATOR.m_id]) >= WEP_CVAR(devastator, ammo);
                        return ammo_amount;
                        #endif
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Devastator, wr_checkammo2, bool(entity thiswep))
                {
                        return false;
                }
 -              case WR_CONFIG:
 +              METHOD(Devastator, wr_config, void(entity thiswep))
                {
                        DEVASTATOR_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RESETPLAYER:
 +              METHOD(Devastator, wr_resetplayer, void(entity thiswep))
                {
                        self.rl_release = 0;
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Devastator, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(WEP_CVAR(devastator, ammo), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, WEP_CVAR(devastator, ammo), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Devastator, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_DEVASTATOR_SUICIDE;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Devastator, wr_killmessage, int(entity thiswep))
                {
                        if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
                                return WEAPON_DEVASTATOR_MURDER_SPLASH;
                        else
                                return WEAPON_DEVASTATOR_MURDER_DIRECT;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_Devastator(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Devastator, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 12;
                        pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), org2, '0 0 0', 1);
                        if(!w_issilent)
                                sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTN_NORM);
 -
 -                      return true;
                }
 -              case WR_INIT:
 -              {
 -                      return true;
 -              }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index b155632252f75f68f23a79984bd04f3b605b3e3b,85a75ed9c5b9f483ba6d45a3fd5f3fef45feae9f..8b643e57c0c6517df230eb70b9891b44bceef2fd
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ ELECTRO,
 -/* function  */ W_Electro,
 -/* ammotype  */ ammo_cells,
 -/* impulse   */ 5,
 -/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
 -/* rating    */ BOT_PICKUP_RATING_MID,
 -/* color     */ '0 0.5 1',
 -/* modelname */ "electro",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairelectro 0.6",
 -/* wepimg    */ "weaponelectro",
 -/* refname   */ "electro",
 -/* wepname   */ _("Electro")
 -);
 +CLASS(Electro, Weapon)
 +/* ammotype  */ ATTRIB(Electro, ammo_field, .int, ammo_cells)
 +/* impulse   */ ATTRIB(Electro, impulse, int, 5)
 +/* flags     */ ATTRIB(Electro, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(Electro, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 +/* color     */ ATTRIB(Electro, wpcolor, vector, '0 0.5 1');
 +/* modelname */ ATTRIB(Electro, mdl, string, "electro");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Electro, m_model, Model, MDL_ELECTRO_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Electro, w_crosshair, string, "gfx/crosshairelectro");
 +/* crosshair */ ATTRIB(Electro, w_crosshair_size, float, 0.6);
 +/* wepimg    */ ATTRIB(Electro, model2, string, "weaponelectro");
 +/* refname   */ ATTRIB(Electro, netname, string, "electro");
 +/* wepname   */ ATTRIB(Electro, message, string, _("Electro"));
 +ENDCLASS(Electro)
 +REGISTER_WEAPON(ELECTRO, NEW(Electro));
  
  #define ELECTRO_SETTINGS(w_cvar,w_prop) ELECTRO_SETTINGS_LIST(w_cvar, w_prop, ELECTRO, electro)
  #define ELECTRO_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -69,7 -67,7 +69,7 @@@ void W_Electro_ExplodeCombo(void)
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_electro(void) { weapon_defaultspawnfunc(WEP_ELECTRO.m_id); }
spawnfunc(weapon_electro) { weapon_defaultspawnfunc(WEP_ELECTRO.m_id); }
  
  void W_Electro_TriggerCombo(vector org, float rad, entity own)
  {
@@@ -245,11 -243,11 +245,11 @@@ void W_Electro_Bolt_Think(void
        else { self.nextthink = self.ltime; }
  }
  
 -void W_Electro_Attack_Bolt(void)
 +void W_Electro_Attack_Bolt(Weapon thiswep)
  {SELFPARAM();
        entity proj;
  
 -      W_DecreaseAmmo(WEP_CVAR_PRI(electro, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_PRI(electro, ammo));
  
        W_SetupShot_ProjectileSize(
                self,
@@@ -345,9 -343,9 +345,9 @@@ void W_Electro_Orb_Damage(entity inflic
        }
  }
  
 -void W_Electro_Attack_Orb(void)
 +void W_Electro_Attack_Orb(Weapon thiswep)
  {SELFPARAM();
 -      W_DecreaseAmmo(WEP_CVAR_SEC(electro, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(electro, ammo));
  
        W_SetupShot_ProjectileSize(
                self,
        MUTATOR_CALLHOOK(EditProjectile, self, proj);
  }
  
 -void W_Electro_CheckAttack(void)
 +void W_Electro_CheckAttack(Weapon thiswep, entity actor, bool fire1, bool fire2)
  {SELFPARAM();
        if(self.electro_count > 1)
        if(self.BUTTON_ATCK2)
 -      if(weapon_prepareattack(1, -1))
 +      if(weapon_prepareattack(actor, true, -1))
        {
 -              W_Electro_Attack_Orb();
 +              W_Electro_Attack_Orb(WEP_ELECTRO);
                self.electro_count -= 1;
 -              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
 +              weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
                return;
        }
        // WEAPONTODO: when the player releases the button, cut down the length of refire2?
 -      w_ready();
 +      w_ready(thiswep, actor, fire1, fire2);
  }
  
  .float bot_secondary_electromooth;
 -bool W_Electro(int req)
 -{SELFPARAM();
 -      float ammo_amount;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +
 +              METHOD(Electro, wr_aim, void(entity thiswep))
                {
                        self.BUTTON_ATCK = self.BUTTON_ATCK2 = false;
                        if(vlen(self.origin-self.enemy.origin) > 1000) { self.bot_secondary_electromooth = 0; }
                                        if(random() < 0.03) self.bot_secondary_electromooth = 0;
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Electro, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
                        if(autocvar_g_balance_electro_reload_ammo) // forced reload // WEAPONTODO
                        {
 -                              ammo_amount = 0;
 -                              if(self.clip_load >= WEP_CVAR_PRI(electro, ammo))
 +                              float ammo_amount = 0;
 +                              if(actor.clip_load >= WEP_CVAR_PRI(electro, ammo))
                                        ammo_amount = 1;
 -                              if(self.clip_load >= WEP_CVAR_SEC(electro, ammo))
 +                              if(actor.clip_load >= WEP_CVAR_SEC(electro, ammo))
                                        ammo_amount += 1;
  
                                if(!ammo_amount)
                                {
 -                                      WEP_ACTION(self.weapon, WR_RELOAD);
 -                                      return false;
 +                                      Weapon w = get_weaponinfo(actor.weapon);
 +                                      w.wr_reload(w);
 +                                      return;
                                }
 -
 -                              return true;
                        }
  
 -                      if(self.BUTTON_ATCK)
 +                      if(fire1)
                        {
 -                              if(weapon_prepareattack(0, WEP_CVAR_PRI(electro, refire)))
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(electro, refire)))
                                {
 -                                              W_Electro_Attack_Bolt();
 -                                              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
 +                                              W_Electro_Attack_Bolt(thiswep);
 +                                              weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
                                }
                        }
 -                      else if(self.BUTTON_ATCK2)
 +                      else if(fire2)
                        {
 -                              if(time >= self.electro_secondarytime)
 -                              if(weapon_prepareattack(1, WEP_CVAR_SEC(electro, refire)))
 +                              if(time >= actor.electro_secondarytime)
 +                              if(weapon_prepareattack(actor, true, WEP_CVAR_SEC(electro, refire)))
                                {
 -                                      W_Electro_Attack_Orb();
 -                                      self.electro_count = WEP_CVAR_SEC(electro, count);
 -                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
 -                                      self.electro_secondarytime = time + WEP_CVAR_SEC(electro, refire2) * W_WeaponRateFactor();
 +                                      W_Electro_Attack_Orb(thiswep);
 +                                      actor.electro_count = WEP_CVAR_SEC(electro, count);
 +                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
 +                                      actor.electro_secondarytime = time + WEP_CVAR_SEC(electro, refire2) * W_WeaponRateFactor();
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Electro, wr_init, void(entity thiswep))
                {
                        ELECTRO_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Electro, wr_checkammo1, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(ELECTRO) >= WEP_CVAR_PRI(electro, ammo);
 +                      float ammo_amount = self.WEP_AMMO(ELECTRO) >= WEP_CVAR_PRI(electro, ammo);
                        ammo_amount += self.(weapon_load[WEP_ELECTRO.m_id]) >= WEP_CVAR_PRI(electro, ammo);
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Electro, wr_checkammo2, bool(entity thiswep))
                {
 +                      float ammo_amount;
                        if(WEP_CVAR(electro, combo_safeammocheck)) // true if you can fire at least one secondary blob AND one primary shot after it, otherwise false.
                        {
                                ammo_amount = self.WEP_AMMO(ELECTRO) >= WEP_CVAR_SEC(electro, ammo) + WEP_CVAR_PRI(electro, ammo);
                        }
                        return ammo_amount;
                }
 -              case WR_CONFIG:
 +              METHOD(Electro, wr_config, void(entity thiswep))
                {
                        ELECTRO_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RESETPLAYER:
 +              METHOD(Electro, wr_resetplayer, void(entity thiswep))
                {
                        self.electro_secondarytime = time;
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Electro, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(min(WEP_CVAR_PRI(electro, ammo), WEP_CVAR_SEC(electro, ammo)), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, min(WEP_CVAR_PRI(electro, ammo), WEP_CVAR_SEC(electro, ammo)), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Electro, wr_suicidemessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_ELECTRO_SUICIDE_ORBS;
                        else
                                return WEAPON_ELECTRO_SUICIDE_BOLT;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Electro, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                        {
                                        return WEAPON_ELECTRO_MURDER_BOLT;
                        }
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_Electro(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Electro, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 6;
                                                sound(self, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
                                }
                        }
 -
 -                      return true;
 -              }
 -              case WR_INIT:
 -              {
 -                      return true;
                }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index fd10eb2e1c286efbaf1bc8071b614d72bdc0ec85,c334932f165f74fb96e7b120522584337460bbc3..dd5d8c241bd48efd50e2ca1ca6c1b1f8e836316d
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ FIREBALL,
 -/* function  */ W_Fireball,
 -/* ammotype  */ ammo_none,
 -/* impulse   */ 9,
 -/* flags     */ WEP_FLAG_SUPERWEAPON | WEP_TYPE_SPLASH,
 -/* rating    */ BOT_PICKUP_RATING_MID,
 -/* color     */ '1 0.5 0',
 -/* modelname */ "fireball",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairfireball",
 -/* wepimg    */ "weaponfireball",
 -/* refname   */ "fireball",
 -/* wepname   */ _("Fireball")
 -);
 +CLASS(Fireball, Weapon)
 +/* ammotype  */ //ATTRIB(Fireball, ammo_field, .int, ammo_none)
 +/* impulse   */ ATTRIB(Fireball, impulse, int, 9)
 +/* flags     */ ATTRIB(Fireball, spawnflags, int, WEP_FLAG_SUPERWEAPON | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(Fireball, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 +/* color     */ ATTRIB(Fireball, wpcolor, vector, '1 0.5 0');
 +/* modelname */ ATTRIB(Fireball, mdl, string, "fireball");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Fireball, m_model, Model, MDL_FIREBALL_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Fireball, w_crosshair, string, "gfx/crosshairfireball");
 +/* crosshair */ //ATTRIB(Fireball, w_crosshair_size, float, 0.65);
 +/* wepimg    */ ATTRIB(Fireball, model2, string, "weaponfireball");
 +/* refname   */ ATTRIB(Fireball, netname, string, "fireball");
 +/* wepname   */ ATTRIB(Fireball, message, string, _("Fireball"));
 +ENDCLASS(Fireball)
 +REGISTER_WEAPON(FIREBALL, NEW(Fireball));
  
  #define FIREBALL_SETTINGS(w_cvar,w_prop) FIREBALL_SETTINGS_LIST(w_cvar, w_prop, FIREBALL, fireball)
  #define FIREBALL_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -57,7 -55,7 +57,7 @@@ FIREBALL_SETTINGS(WEP_ADD_CVAR, WEP_ADD
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_fireball(void) { weapon_defaultspawnfunc(WEP_FIREBALL.m_id); }
spawnfunc(weapon_fireball) { weapon_defaultspawnfunc(WEP_FIREBALL.m_id); }
  
  void W_Fireball_Explode(void)
  {SELFPARAM();
@@@ -226,35 -224,35 +226,35 @@@ void W_Fireball_AttackEffect(float i, v
        Send_Effect(EFFECT_FIREBALL_PRE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
  }
  
 -void W_Fireball_Attack1_Frame4(void)
 +void W_Fireball_Attack1_Frame4(Weapon thiswep, entity actor, bool fire1, bool fire2)
  {
        W_Fireball_Attack1();
 -      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), w_ready);
 +      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), w_ready);
  }
  
 -void W_Fireball_Attack1_Frame3(void)
 +void W_Fireball_Attack1_Frame3(Weapon thiswep, entity actor, bool fire1, bool fire2)
  {
        W_Fireball_AttackEffect(0, '+1.25 +3.75 0');
 -      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame4);
 +      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame4);
  }
  
 -void W_Fireball_Attack1_Frame2(void)
 +void W_Fireball_Attack1_Frame2(Weapon thiswep, entity actor, bool fire1, bool fire2)
  {
        W_Fireball_AttackEffect(0, '-1.25 +3.75 0');
 -      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame3);
 +      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame3);
  }
  
 -void W_Fireball_Attack1_Frame1(void)
 +void W_Fireball_Attack1_Frame1(Weapon thiswep, entity actor, bool fire1, bool fire2)
  {
        W_Fireball_AttackEffect(1, '+1.25 -3.75 0');
 -      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame2);
 +      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame2);
  }
  
 -void W_Fireball_Attack1_Frame0(void)
 +void W_Fireball_Attack1_Frame0(Weapon thiswep, entity actor, bool fire1, bool fire2)
  {SELFPARAM();
        W_Fireball_AttackEffect(0, '-1.25 -3.75 0');
        sound(self, CH_WEAPON_SINGLE, SND_FIREBALL_PREFIRE2, VOL_BASE, ATTEN_NORM);
 -      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1);
 +      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1);
  }
  
  void W_Fireball_Firemine_Think(void)
@@@ -350,7 -348,11 +350,7 @@@ void W_Fireball_Attack2(void
        MUTATOR_CALLHOOK(EditProjectile, self, proj);
  }
  
 -bool W_Fireball(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Fireball, wr_aim, void(entity thiswep))
                {
                        self.BUTTON_ATCK = false;
                        self.BUTTON_ATCK2 = false;
                                        if(random() < 0.01) self.bot_primary_fireballmooth = 1;
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Fireball, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(self.BUTTON_ATCK)
 +                      if(fire1)
                        {
 -                              if(time >= self.fireball_primarytime)
 -                              if(weapon_prepareattack(0, WEP_CVAR_PRI(fireball, refire)))
 +                              if(time >= actor.fireball_primarytime)
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(fireball, refire)))
                                {
 -                                      W_Fireball_Attack1_Frame0();
 -                                      self.fireball_primarytime = time + WEP_CVAR_PRI(fireball, refire2) * W_WeaponRateFactor();
 +                                      W_Fireball_Attack1_Frame0(thiswep, actor, fire1, fire2);
 +                                      actor.fireball_primarytime = time + WEP_CVAR_PRI(fireball, refire2) * W_WeaponRateFactor();
                                }
                        }
 -                      else if(self.BUTTON_ATCK2)
 +                      else if(fire2)
                        {
 -                              if(weapon_prepareattack(1, WEP_CVAR_SEC(fireball, refire)))
 +                              if(weapon_prepareattack(actor, true, WEP_CVAR_SEC(fireball, refire)))
                                {
                                        W_Fireball_Attack2();
 -                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(fireball, animtime), w_ready);
 +                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(fireball, animtime), w_ready);
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Fireball, wr_init, void(entity thiswep))
                {
                        FIREBALL_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_SETUP:
 +              METHOD(Fireball, wr_setup, void(entity thiswep))
                {
                        self.ammo_field = ammo_none;
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 -              case WR_CHECKAMMO2:
 +              METHOD(Fireball, wr_checkammo1, bool(entity thiswep))
 +              {
 +                      return true; // infinite ammo
 +              }
 +              METHOD(Fireball, wr_checkammo2, bool(entity thiswep))
                {
                        return true; // fireball has infinite ammo
                }
 -              case WR_CONFIG:
 +              METHOD(Fireball, wr_config, void(entity thiswep))
                {
                        FIREBALL_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RESETPLAYER:
 +              METHOD(Fireball, wr_resetplayer, void(entity thiswep))
                {
                        self.fireball_primarytime = time;
 -                      return true;
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Fireball, wr_suicidemessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_FIREBALL_SUICIDE_FIREMINE;
                        else
                                return WEAPON_FIREBALL_SUICIDE_BLAST;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Fireball, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_FIREBALL_MURDER_FIREMINE;
                        else
                                return WEAPON_FIREBALL_MURDER_BLAST;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_Fireball(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Fireball, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        if(w_deathtype & HITTYPE_SECONDARY)
                                if(!w_issilent)
                                        sound(self, CH_SHOTS, SND_FIREBALL_IMPACT2, VOL_BASE, ATTEN_NORM * 0.25); // long range boom
                        }
 -
 -                      return true;
 -              }
 -              case WR_INIT:
 -              {
 -                      return true;
                }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index af62d534d9cb4cb5197a4e0358a4d15cb30c3b93,21755eac69a07005a2a8a24d58efab0d3f2b7c72..c7712bf2f608d97ea5d776a6fbbff67312373799
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ HAGAR,
 -/* function  */ W_Hagar,
 -/* ammotype  */ ammo_rockets,
 -/* impulse   */ 8,
 -/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
 -/* rating    */ BOT_PICKUP_RATING_MID,
 -/* color     */ '1 1 0.5',
 -/* modelname */ "hagar",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairhagar 0.8",
 -/* wepimg    */ "weaponhagar",
 -/* refname   */ "hagar",
 -/* wepname   */ _("Hagar")
 -);
 +CLASS(Hagar, Weapon)
 +/* ammotype  */ ATTRIB(Hagar, ammo_field, .int, ammo_rockets)
 +/* impulse   */ ATTRIB(Hagar, impulse, int, 8)
 +/* flags     */ ATTRIB(Hagar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(Hagar, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 +/* color     */ ATTRIB(Hagar, wpcolor, vector, '1 1 0.5');
 +/* modelname */ ATTRIB(Hagar, mdl, string, "hagar");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Hagar, m_model, Model, MDL_HAGAR_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Hagar, w_crosshair, string, "gfx/crosshairhagar");
 +/* crosshair */ ATTRIB(Hagar, w_crosshair_size, float, 0.8);
 +/* wepimg    */ ATTRIB(Hagar, model2, string, "weaponhagar");
 +/* refname   */ ATTRIB(Hagar, netname, string, "hagar");
 +/* wepname   */ ATTRIB(Hagar, message, string, _("Hagar"));
 +ENDCLASS(Hagar)
 +REGISTER_WEAPON(HAGAR, NEW(Hagar));
  
  #define HAGAR_SETTINGS(w_cvar,w_prop) HAGAR_SETTINGS_LIST(w_cvar, w_prop, HAGAR, hagar)
  #define HAGAR_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -58,7 -56,7 +58,7 @@@ HAGAR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PR
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_hagar(void) { weapon_defaultspawnfunc(WEP_HAGAR.m_id); }
spawnfunc(weapon_hagar) { weapon_defaultspawnfunc(WEP_HAGAR.m_id); }
  
  // NO bounce protection, as bounces are limited!
  
@@@ -123,11 -121,11 +123,11 @@@ void W_Hagar_Touch2(void
        }
  }
  
 -void W_Hagar_Attack(void)
 +void W_Hagar_Attack(Weapon thiswep)
  {SELFPARAM();
        entity missile;
  
 -      W_DecreaseAmmo(WEP_CVAR_PRI(hagar, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_PRI(hagar, ammo));
  
        W_SetupShot(self, false, 2, SND(HAGAR_FIRE), CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage));
  
        MUTATOR_CALLHOOK(EditProjectile, self, missile);
  }
  
 -void W_Hagar_Attack2(void)
 +void W_Hagar_Attack2(Weapon thiswep)
  {SELFPARAM();
        entity missile;
  
 -      W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo));
  
        W_SetupShot(self, false, 2, SND(HAGAR_FIRE), CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
  
@@@ -223,7 -221,7 +223,7 @@@ void W_Hagar_Attack2_Load_Release(void
        if(!self.hagar_load)
                return;
  
 -      weapon_prepareattack_do(1, WEP_CVAR_SEC(hagar, refire));
 +      weapon_prepareattack_do(self, true, WEP_CVAR_SEC(hagar, refire));
  
        W_SetupShot(self, false, 2, SND(HAGAR_FIRE), CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
        Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
                MUTATOR_CALLHOOK(EditProjectile, self, missile);
        }
  
 -      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hagar, load_animtime), w_ready);
 +      weapon_thinkf(self, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, load_animtime), w_ready);
        self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, refire) * W_WeaponRateFactor();
        self.hagar_load = 0;
  }
  
 -void W_Hagar_Attack2_Load(void)
 +void W_Hagar_Attack2_Load(Weapon thiswep)
  {SELFPARAM();
        // loadable hagar secondary attack, must always run each frame
  
                        {
                                // if we pressed primary fire while loading, unload all rockets and abort
                                self.weaponentity.state = WS_READY;
 -                              W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo
 +                              W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo
                                self.hagar_load = 0;
                                sound(self, CH_WEAPON_A, SND_HAGAR_BEEP, VOL_BASE, ATTN_NORM);
  
                        {
                                if(!self.hagar_loadblock && self.hagar_loadstep < time)
                                {
 -                                      W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo));
 +                                      W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo));
                                        self.weaponentity.state = WS_INUSE;
                                        self.hagar_load += 1;
                                        sound(self, CH_WEAPON_B, SND_HAGAR_LOAD, VOL_BASE * 0.8, ATTN_NORM); // sound is too loud according to most
                self.hagar_warning = false;
  
                // we aren't checking ammo during an attack, so we must do it here
 -              if(!(WEP_ACTION(self.weapon, WR_CHECKAMMO1) + WEP_ACTION(self.weapon, WR_CHECKAMMO2)))
 +              Weapon w = get_weaponinfo(self.weapon);
 +              if(!(w.wr_checkammo1(w) + w.wr_checkammo2(w)))
                if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
                {
                        // note: this doesn't force the switch
        }
  }
  
 -bool W_Hagar(int req)
 -{SELFPARAM();
 -      float ammo_amount;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Hagar, wr_aim, void(entity thiswep))
                {
                        if(random()>0.15)
                                self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), false);
                        else // not using secondary_speed since these are only 15% and should cause some ricochets without re-aiming
                                self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), false);
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Hagar, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
                        float loadable_secondary;
                        loadable_secondary = (WEP_CVAR_SEC(hagar, load) && WEP_CVAR(hagar, secondary));
  
                        if(loadable_secondary)
 -                              W_Hagar_Attack2_Load(); // must always run each frame
 -                      if(autocvar_g_balance_hagar_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo))) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      else if(self.BUTTON_ATCK && !self.hagar_load && !self.hagar_loadblock) // not while secondary is loaded or awaiting reset
 +                              W_Hagar_Attack2_Load(thiswep); // must always run each frame
 +                      if(autocvar_g_balance_hagar_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo))) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else if(fire1 && !actor.hagar_load && !actor.hagar_loadblock) // not while secondary is loaded or awaiting reset
                        {
 -                              if(weapon_prepareattack(0, WEP_CVAR_PRI(hagar, refire)))
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(hagar, refire)))
                                {
 -                                      W_Hagar_Attack();
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hagar, refire), w_ready);
 +                                      W_Hagar_Attack(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hagar, refire), w_ready);
                                }
                        }
 -                      else if(self.BUTTON_ATCK2 && !loadable_secondary && WEP_CVAR(hagar, secondary))
 +                      else if(fire2 && !loadable_secondary && WEP_CVAR(hagar, secondary))
                        {
 -                              if(weapon_prepareattack(1, WEP_CVAR_SEC(hagar, refire)))
 +                              if(weapon_prepareattack(actor, true, WEP_CVAR_SEC(hagar, refire)))
                                {
 -                                      W_Hagar_Attack2();
 -                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready);
 +                                      W_Hagar_Attack2(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready);
                                }
                        }
 -                      return true;
                }
 -              case WR_GONETHINK:
 +              METHOD(Hagar, wr_gonethink, void(entity thiswep))
                {
                        // we lost the weapon and want to prepare switching away
                        if(self.hagar_load)
                                self.weaponentity.state = WS_READY;
                                W_Hagar_Attack2_Load_Release();
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Hagar, wr_init, void(entity thiswep))
                {
                        HAGAR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_SETUP:
 +              METHOD(Hagar, wr_setup, void(entity thiswep))
                {
                        self.hagar_loadblock = false;
  
                        if(self.hagar_load)
                        {
 -                              W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo if necessary
 +                              W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo if necessary
                                self.hagar_load = 0;
                        }
 -
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Hagar, wr_checkammo1, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(HAGAR) >= WEP_CVAR_PRI(hagar, ammo);
 +                      float ammo_amount = self.WEP_AMMO(HAGAR) >= WEP_CVAR_PRI(hagar, ammo);
                        ammo_amount += self.(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_PRI(hagar, ammo);
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Hagar, wr_checkammo2, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(HAGAR) >= WEP_CVAR_SEC(hagar, ammo);
 +                      float ammo_amount = self.WEP_AMMO(HAGAR) >= WEP_CVAR_SEC(hagar, ammo);
                        ammo_amount += self.(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_SEC(hagar, ammo);
                        return ammo_amount;
                }
 -              case WR_CONFIG:
 +              METHOD(Hagar, wr_config, void(entity thiswep))
                {
                        HAGAR_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RESETPLAYER:
 +              METHOD(Hagar, wr_resetplayer, void(entity thiswep))
                {
                        self.hagar_load = 0;
 -                      return true;
                }
 -              case WR_PLAYERDEATH:
 +              METHOD(Hagar, wr_playerdeath, void(entity thiswep))
                {
                        // if we have any rockets loaded when we die, release them
                        if(self.hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath))
                                W_Hagar_Attack2_Load_Release();
 -
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Hagar, wr_reload, void(entity thiswep))
                {
                        if(!self.hagar_load) // require releasing loaded rockets first
 -                              W_Reload(min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), SND(RELOAD));
 -
 -                      return true;
 +                              W_Reload(self, min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Hagar, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_HAGAR_SUICIDE;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Hagar, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_HAGAR_MURDER_BURST;
                        else
                                return WEAPON_HAGAR_MURDER_SPRAY;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_Hagar(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Hagar, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 6;
                                else
                                        sound(self, CH_SHOTS, SND_HAGEXP3, VOL_BASE, ATTN_NORM);
                        }
 -
 -                      return true;
 -              }
 -              case WR_INIT:
 -              {
 -                      return true;
                }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index 5b61dddf9d47fb4dc85d8533c948911783ddbe56,ac300fe540b416b632cfd2f89702dacf30f80669..fb1d9fcdbe1cc2749464d35b1f9b78075db318df
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ HLAC,
 -/* function  */ W_HLAC,
 -/* ammotype  */ ammo_cells,
 -/* impulse   */ 6,
 -/* flags     */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
 -/* rating    */ BOT_PICKUP_RATING_MID,
 -/* color     */ '0 1 0',
 -/* modelname */ "hlac",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairhlac 0.6",
 -/* wepimg    */ "weaponhlac",
 -/* refname   */ "hlac",
 -/* wepname   */ _("Heavy Laser Assault Cannon")
 -);
 +CLASS(HLAC, Weapon)
 +/* ammotype  */ ATTRIB(HLAC, ammo_field, .int, ammo_cells)
 +/* impulse   */ ATTRIB(HLAC, impulse, int, 6)
 +/* flags     */ ATTRIB(HLAC, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(HLAC, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 +/* color     */ ATTRIB(HLAC, wpcolor, vector, '0 1 0');
 +/* modelname */ ATTRIB(HLAC, mdl, string, "hlac");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(HLAC, m_model, Model, MDL_HLAC_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(HLAC, w_crosshair, string, "gfx/crosshairhlac");
 +/* crosshair */ ATTRIB(HLAC, w_crosshair_size, float, 0.6);
 +/* wepimg    */ ATTRIB(HLAC, model2, string, "weaponhlac");
 +/* refname   */ ATTRIB(HLAC, netname, string, "hlac");
 +/* wepname   */ ATTRIB(HLAC, message, string, _("Heavy Laser Assault Cannon"));
 +ENDCLASS(HLAC)
 +REGISTER_WEAPON(HLAC, NEW(HLAC));
  
  #define HLAC_SETTINGS(w_cvar,w_prop) HLAC_SETTINGS_LIST(w_cvar, w_prop, HLAC, hlac)
  #define HLAC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -50,7 -48,7 +50,7 @@@ HLAC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PRO
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_hlac(void) { weapon_defaultspawnfunc(WEP_HLAC.m_id); }
spawnfunc(weapon_hlac) { weapon_defaultspawnfunc(WEP_HLAC.m_id); }
  
  void W_HLAC_Touch(void)
  {SELFPARAM();
        remove(self);
  }
  
 -void W_HLAC_Attack(void)
 +void W_HLAC_Attack(Weapon thiswep)
  {SELFPARAM();
        entity missile;
      float spread;
  
 -      W_DecreaseAmmo(WEP_CVAR_PRI(hlac, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_PRI(hlac, ammo));
  
      spread = WEP_CVAR_PRI(hlac, spread_min) + (WEP_CVAR_PRI(hlac, spread_add) * self.misc_bulletcounter);
      spread = min(spread,WEP_CVAR_PRI(hlac, spread_max));
@@@ -161,41 -159,40 +161,41 @@@ void W_HLAC_Attack2(void
  }
  
  // weapon frames
 -void W_HLAC_Attack_Frame(void)
 -{SELFPARAM();
 -      if(self.weapon != self.switchweapon) // abort immediately if switching
 +void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, bool fire1, bool fire2)
 +{
 +      if(actor.weapon != actor.switchweapon) // abort immediately if switching
        {
 -              w_ready();
 +              w_ready(thiswep, actor, fire1, fire2);
                return;
        }
  
 -      if(self.BUTTON_ATCK)
 +      if(actor.BUTTON_ATCK)
        {
 -              if(!WEP_ACTION(self.weapon, WR_CHECKAMMO1))
 -              if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +              Weapon w = get_weaponinfo(actor.weapon);
 +              if(!w.wr_checkammo1(w))
 +              if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
                {
 -                      W_SwitchWeapon_Force(self, w_getbestweapon(self));
 -                      w_ready();
 +                      W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
 +                      w_ready(thiswep, actor, fire1, fire2);
                        return;
                }
  
 -              ATTACK_FINISHED(self) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor();
 -              W_HLAC_Attack();
 -              self.misc_bulletcounter = self.misc_bulletcounter + 1;
 -        weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame);
 +              ATTACK_FINISHED(actor) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor();
 +              W_HLAC_Attack(WEP_HLAC);
 +              actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
 +        weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame);
        }
        else
        {
 -              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hlac, animtime), w_ready);
 +              weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, animtime), w_ready);
        }
  }
  
 -void W_HLAC_Attack2_Frame(void)
 +void W_HLAC_Attack2_Frame(Weapon thiswep)
  {SELFPARAM();
      float i;
  
 -      W_DecreaseAmmo(WEP_CVAR_SEC(hlac, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hlac, ammo));
  
      for(i=WEP_CVAR_SEC(hlac, shots);i>0;--i)
          W_HLAC_Attack2();
        }
  }
  
 -bool W_HLAC(int req)
 -{SELFPARAM();
 -      float ammo_amount;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(HLAC, wr_aim, void(entity thiswep))
                {
                        self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(hlac, speed), 0, WEP_CVAR_PRI(hlac, lifetime), false);
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(HLAC, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(autocvar_g_balance_hlac_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo))) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      else if(self.BUTTON_ATCK)
 +                      if(autocvar_g_balance_hlac_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo))) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else if(fire1)
                        {
 -                              if(weapon_prepareattack(0, WEP_CVAR_PRI(hlac, refire)))
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(hlac, refire)))
                                {
 -                                      self.misc_bulletcounter = 0;
 -                                      W_HLAC_Attack();
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame);
 +                                      actor.misc_bulletcounter = 0;
 +                                      W_HLAC_Attack(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame);
                                }
                        }
  
 -                      else if(self.BUTTON_ATCK2 && WEP_CVAR(hlac, secondary))
 +                      else if(fire2 && WEP_CVAR(hlac, secondary))
                        {
 -                              if(weapon_prepareattack(1, WEP_CVAR_SEC(hlac, refire)))
 +                              if(weapon_prepareattack(actor, true, WEP_CVAR_SEC(hlac, refire)))
                                {
 -                                      W_HLAC_Attack2_Frame();
 -                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready);
 +                                      W_HLAC_Attack2_Frame(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready);
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(HLAC, wr_init, void(entity thiswep))
                {
                        HLAC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(HLAC, wr_checkammo1, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(HLAC) >= WEP_CVAR_PRI(hlac, ammo);
 +                      float ammo_amount = self.WEP_AMMO(HLAC) >= WEP_CVAR_PRI(hlac, ammo);
                        ammo_amount += self.(weapon_load[WEP_HLAC.m_id]) >= WEP_CVAR_PRI(hlac, ammo);
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(HLAC, wr_checkammo2, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(HLAC) >= WEP_CVAR_SEC(hlac, ammo);
 +                      float ammo_amount = self.WEP_AMMO(HLAC) >= WEP_CVAR_SEC(hlac, ammo);
                        ammo_amount += self.(weapon_load[WEP_HLAC.m_id]) >= WEP_CVAR_SEC(hlac, ammo);
                        return ammo_amount;
                }
 -              case WR_CONFIG:
 +              METHOD(HLAC, wr_config, void(entity thiswep))
                {
                        HLAC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(HLAC, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo)), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo)), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(HLAC, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_HLAC_SUICIDE;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(HLAC, wr_killmessage, int(entity thiswep))
                {
                        return WEAPON_HLAC_MURDER;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_HLAC(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(HLAC, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 6;
                        pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1);
                        if(!w_issilent)
                                sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM);
 -
 -                      return true;
                }
 -              case WR_INIT:
 -              {
 -                      return true;
 -              }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index e9ddfb4f705be83942054f99c52fb96461de959a,f4c6a3c752a075b1a743c372b69b127150aa8457..bcbefbf82efa10882b7f41a856dae9d58404ea36
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id    */ HMG,
 -/* function    */ W_HeavyMachineGun,
 -/* ammotype    */ ammo_nails,
 -/* impulse     */ 3,
 -/* flags       */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON,
 -/* rating      */ BOT_PICKUP_RATING_HIGH,
 -/* color     */ '0.5 0.5 0',
 -/* modelname */ "ok_hmg",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairuzi 0.6",
 -/* wepimg    */ "weaponhmg",
 -/* refname   */ "hmg",
 -/* wepname   */ _("Heavy Machine Gun")
 -);
 +CLASS(HeavyMachineGun, Weapon)
 +/* ammotype  */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails)
 +/* impulse   */ ATTRIB(HeavyMachineGun, impulse, int, 3)
 +/* flags     */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON);
 +/* rating    */ ATTRIB(HeavyMachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 +/* color     */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0');
 +/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(HeavyMachineGun, m_model, Model, MDL_HMG_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi");
 +/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair_size, float, 0.6);
 +/* wepimg    */ ATTRIB(HeavyMachineGun, model2, string, "weaponhmg");
 +/* refname   */ ATTRIB(HeavyMachineGun, netname, string, "hmg");
 +/* wepname   */ ATTRIB(HeavyMachineGun, message, string, _("Heavy Machine Gun"));
 +ENDCLASS(HeavyMachineGun)
 +REGISTER_WEAPON(HMG, NEW(HeavyMachineGun));
  
  #define HMG_SETTINGS(w_cvar,w_prop) HMG_SETTINGS_LIST(w_cvar, w_prop, HMG, hmg)
  #define HMG_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -43,120 -41,135 +43,120 @@@ HMG_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP
  #ifdef IMPLEMENTATION
  #ifdef SVQC
  
void spawnfunc_weapon_hmg() { weapon_defaultspawnfunc(WEP_HMG.m_id); }
spawnfunc(weapon_hmg) { weapon_defaultspawnfunc(WEP_HMG.m_id); }
  
 -void W_HeavyMachineGun_Attack_Auto()
 -{SELFPARAM();
 -      if (!self.BUTTON_ATCK)
 +void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, bool fire1, bool fire2)
 +{
 +      if (!actor.BUTTON_ATCK)
        {
 -              w_ready();
 +              w_ready(thiswep, actor, fire1, fire2);
                return;
        }
  
 -      if(!WEP_ACTION(self.weapon, WR_CHECKAMMO1))
 -      if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +      Weapon w = get_weaponinfo(actor.weapon);
 +      if(!w.wr_checkammo1(w))
 +      if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
        {
 -              W_SwitchWeapon_Force(self, w_getbestweapon(self));
 -              w_ready();
 +              W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
 +              w_ready(thiswep, actor, fire1, fire2);
                return;
        }
  
 -      W_DecreaseAmmo(WEP_CVAR(hmg, ammo));
 +      W_DecreaseAmmo(WEP_HMG, self, WEP_CVAR(hmg, ammo));
  
 -      W_SetupShot (self, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(hmg, damage));
 +      W_SetupShot (actor, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(hmg, damage));
  
        if(!autocvar_g_norecoil)
        {
 -              self.punchangle_x = random () - 0.5;
 -              self.punchangle_y = random () - 0.5;
 +              actor.punchangle_x = random () - 0.5;
 +              actor.punchangle_y = random () - 0.5;
        }
  
 -      float hmg_spread = bound(WEP_CVAR(hmg, spread_min), WEP_CVAR(hmg, spread_min) + (WEP_CVAR(hmg, spread_add) * self.misc_bulletcounter), WEP_CVAR(hmg, spread_max));
 +      float hmg_spread = bound(WEP_CVAR(hmg, spread_min), WEP_CVAR(hmg, spread_min) + (WEP_CVAR(hmg, spread_add) * actor.misc_bulletcounter), WEP_CVAR(hmg, spread_max));
        fireBullet(w_shotorg, w_shotdir, hmg_spread, WEP_CVAR(hmg, solidpenetration), WEP_CVAR(hmg, damage), WEP_CVAR(hmg, force), WEP_HMG.m_id, 0);
  
 -      self.misc_bulletcounter = self.misc_bulletcounter + 1;
 +      actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
  
        Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
  
        W_MachineGun_MuzzleFlash();
 -      W_AttachToShotorg(self.muzzle_flash, '5 0 0');
 +      W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
  
        if (autocvar_g_casings >= 2) // casing code
 -              SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
 +              SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
  
 -      ATTACK_FINISHED(self) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor();
 -      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(hmg, refire), W_HeavyMachineGun_Attack_Auto);
 +      ATTACK_FINISHED(actor) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor();
 +      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(hmg, refire), W_HeavyMachineGun_Attack_Auto);
  }
  
 -bool W_HeavyMachineGun(int req)
 -{SELFPARAM();
 -      float ammo_amount;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(HeavyMachineGun, wr_aim, void(entity thiswep))
                {
                        if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200)
                                self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false);
                        else
                                self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false);
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(HeavyMachineGun, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(WEP_CVAR(hmg, reload_ammo) && self.clip_load < WEP_CVAR(hmg, ammo)) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      else
 +                      if(WEP_CVAR(hmg, reload_ammo) && actor.clip_load < WEP_CVAR(hmg, ammo)) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else
                        {
 -                              if (self.BUTTON_ATCK)
 -                              if (weapon_prepareattack(0, 0))
 +                              if (fire1)
 +                              if (weapon_prepareattack(actor, false, 0))
                                {
 -                                      self.misc_bulletcounter = 0;
 -                                      W_HeavyMachineGun_Attack_Auto();
 +                                      actor.misc_bulletcounter = 0;
 +                                      W_HeavyMachineGun_Attack_Auto(thiswep, actor, fire1, fire2);
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(HeavyMachineGun, wr_init, void(entity thiswep))
                {
                        HMG_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(HeavyMachineGun, wr_checkammo1, bool(entity thiswep))
                {
 -                      ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo);
 +                      float ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo);
  
                        if(autocvar_g_balance_hmg_reload_ammo)
                                ammo_amount += self.(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo);
  
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(HeavyMachineGun, wr_checkammo2, bool(entity thiswep))
                {
 -                      ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo);
 +                      float ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo);
  
                        if(autocvar_g_balance_hmg_reload_ammo)
                                ammo_amount += self.(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo);
  
                        return ammo_amount;
                }
 -              case WR_CONFIG:
 +              METHOD(HeavyMachineGun, wr_config, void(entity thiswep))
                {
                        HMG_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(HeavyMachineGun, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(WEP_CVAR(hmg, ammo), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, WEP_CVAR(hmg, ammo), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(HeavyMachineGun, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_THINKING_WITH_PORTALS;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(HeavyMachineGun, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_HMG_MURDER_SNIPE;
                        else
                                return WEAPON_HMG_MURDER_SPRAY;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_HeavyMachineGun(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(HeavyMachineGun, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 2;
                                        sound(self, CH_SHOTS, SND_RIC2, VOL_BASE, ATTEN_NORM);
                                else if(w_random < 0.2)
                                        sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTEN_NORM);
 -
 -                      return true;
                }
 -              case WR_INIT:
 -              {
 -                      return true;
 -              }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index ffee55dd2f1f43726dcbd82781490de37c936748,6c1f82fdf8561a6ed83387731cbbbb89bbe64343..9d5eb29ec844ce85ff71fd2a8de16a6681da2c5d
@@@ -1,30 -1,19 +1,30 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ HOOK,
 -/* function  */ W_Hook,
 -/* ammotype  */ ammo_fuel,
 -/* impulse   */ 0,
 -/* flags     */ WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
 -/* rating    */ 0,
 -/* color     */ '0 0.5 0',
 -/* modelname */ "hookgun",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairhook 0.5",
 -/* wepimg    */ "weaponhook",
 -/* refname   */ "hook",
 -/* wepname   */ _("Grappling Hook")
 -);
 +CLASS(Hook, Weapon)
 +/* ammotype  */ ATTRIB(Hook, ammo_field, .int, ammo_fuel)
 +/* impulse   */ ATTRIB(Hook, impulse, int, 0)
 +/* flags     */ ATTRIB(Hook, spawnflags, int, WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(Hook, bot_pickupbasevalue, float, 0);
 +/* color     */ ATTRIB(Hook, wpcolor, vector, '0 0.5 0');
 +/* modelname */ ATTRIB(Hook, mdl, string, "hookgun");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Hook, m_model, Model, MDL_HOOK_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Hook, w_crosshair, string, "gfx/crosshairhook");
 +/* crosshair */ ATTRIB(Hook, w_crosshair_size, float, 0.5);
 +/* wepimg    */ ATTRIB(Hook, model2, string, "weaponhook");
 +/* refname   */ ATTRIB(Hook, netname, string, "hook");
 +/* wepname   */ ATTRIB(Hook, message, string, _("Grappling Hook"));
 +ENDCLASS(Hook)
 +REGISTER_WEAPON(HOOK, NEW(Hook));
 +
 +CLASS(OffhandHook, OffhandWeapon)
 +    METHOD(OffhandHook, offhand_think, void(OffhandHook this, entity player, bool key_pressed))
 +    {
 +      Weapon wep = WEP_HOOK;
 +      WITH(entity, self, player, wep.wr_think(wep, self, key_pressed, false));
 +    }
 +ENDCLASS(OffhandHook)
 +OffhandHook OFFHAND_HOOK; STATIC_INIT(OFFHAND_HOOK) { OFFHAND_HOOK = NEW(OffhandHook); }
  
  #define HOOK_SETTINGS(w_cvar,w_prop) HOOK_SETTINGS_LIST(w_cvar, w_prop, HOOK, hook)
  #define HOOK_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -70,7 -59,16 +70,7 @@@ HOOK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PRO
  #ifdef IMPLEMENTATION
  #ifdef SVQC
  
- void spawnfunc_weapon_hook() { weapon_defaultspawnfunc(WEP_HOOK.m_id); }
 -spawnfunc(weapon_hook)
 -{
 -      if(g_grappling_hook) // offhand hook
 -      {
 -              startitem_failed = true;
 -              remove(self);
 -              return;
 -      }
 -      weapon_defaultspawnfunc(WEP_HOOK.m_id);
 -}
++spawnfunc(weapon_hook) { weapon_defaultspawnfunc(WEP_HOOK.m_id); }
  
  void W_Hook_ExplodeThink(void)
  {SELFPARAM();
@@@ -131,11 -129,11 +131,11 @@@ void W_Hook_Touch2(void
        self.use();
  }
  
 -void W_Hook_Attack2(void)
 +void W_Hook_Attack2(Weapon thiswep)
  {SELFPARAM();
        entity gren;
  
 -      //W_DecreaseAmmo(WEP_CVAR_SEC(hook, ammo)); // WEAPONTODO: Figure out how to handle ammo with hook secondary (gravitybomb)
 +      //W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hook, ammo)); // WEAPONTODO: Figure out how to handle ammo with hook secondary (gravitybomb)
        W_SetupShot(self, false, 4, SND(HOOKBOMB_FIRE), CH_WEAPON_A, WEP_CVAR_SEC(hook, damage));
  
        gren = spawn();
        MUTATOR_CALLHOOK(EditProjectile, self, gren);
  }
  
 -bool W_Hook(int req)
 -{SELFPARAM();
 -      float hooked_time_max, hooked_fuel;
 -
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Hook, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      // no bot AI for hook (yet?)
 -                      return true;
 -              }
 -              case WR_THINK:
 -              {
 -                      if(self.BUTTON_ATCK || self.BUTTON_HOOK)
 +                      if(fire1 || actor.BUTTON_HOOK)
                        {
 -                              if(!self.hook)
 -                              if(!(self.hook_state & HOOK_WAITING_FOR_RELEASE))
 -                              if(!(self.hook_state & HOOK_FIRING))
 -                              if(time > self.hook_refire)
 -                              if(weapon_prepareattack(0, -1))
 +                              if(!actor.hook)
 +                              if(!(actor.hook_state & HOOK_WAITING_FOR_RELEASE))
 +                              if(!(actor.hook_state & HOOK_FIRING))
 +                              if(time > actor.hook_refire)
 +                              if(weapon_prepareattack(actor, false, -1))
                                {
 -                                      W_DecreaseAmmo(WEP_CVAR_PRI(hook, ammo));
 -                                      self.hook_state |= HOOK_FIRING;
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready);
 +                                      W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(hook, ammo));
 +                                      actor.hook_state |= HOOK_FIRING;
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready);
                                }
                        }
  
 -                      if(self.BUTTON_ATCK2)
 +                      if(fire2)
                        {
 -                              if(weapon_prepareattack(1, WEP_CVAR_SEC(hook, refire)))
 +                              if(weapon_prepareattack(actor, true, WEP_CVAR_SEC(hook, refire)))
                                {
 -                                      W_Hook_Attack2();
 -                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready);
 +                                      W_Hook_Attack2(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready);
                                }
                        }
  
 -                      if(self.hook)
 +                      if(actor.hook)
                        {
                                // if hooked, no bombs, and increase the timer
 -                              self.hook_refire = max(self.hook_refire, time + WEP_CVAR_PRI(hook, refire) * W_WeaponRateFactor());
 +                              actor.hook_refire = max(actor.hook_refire, time + WEP_CVAR_PRI(hook, refire) * W_WeaponRateFactor());
  
                                // hook also inhibits health regeneration, but only for 1 second
 -                              if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 -                                      self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
 +                              if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
 +                                      actor.pauseregen_finished = max(actor.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
                        }
  
 -                      if(self.hook && self.hook.state == 1)
 +                      if(actor.hook && actor.hook.state == 1)
                        {
 -                              hooked_time_max = WEP_CVAR_PRI(hook, hooked_time_max);
 +                              float hooked_time_max = WEP_CVAR_PRI(hook, hooked_time_max);
                                if(hooked_time_max > 0)
                                {
 -                                      if( time > self.hook_time_hooked + hooked_time_max )
 -                                              self.hook_state |= HOOK_REMOVING;
 +                                      if( time > actor.hook_time_hooked + hooked_time_max )
 +                                              actor.hook_state |= HOOK_REMOVING;
                                }
  
 -                              hooked_fuel = WEP_CVAR_PRI(hook, hooked_ammo);
 +                              float hooked_fuel = WEP_CVAR_PRI(hook, hooked_ammo);
                                if(hooked_fuel > 0)
                                {
 -                                      if( time > self.hook_time_fueldecrease )
 +                                      if( time > actor.hook_time_fueldecrease )
                                        {
 -                                              if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +                                              if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
                                                {
 -                                                      if( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel )
 +                                                      if( actor.ammo_fuel >= (time - actor.hook_time_fueldecrease) * hooked_fuel )
                                                        {
 -                                                              W_DecreaseAmmo((time - self.hook_time_fueldecrease) * hooked_fuel);
 -                                                              self.hook_time_fueldecrease = time;
 +                                                              W_DecreaseAmmo(thiswep, actor, (time - actor.hook_time_fueldecrease) * hooked_fuel);
 +                                                              actor.hook_time_fueldecrease = time;
                                                                // decrease next frame again
                                                        }
                                                        else
                                                        {
 -                                                              self.ammo_fuel = 0;
 -                                                              self.hook_state |= HOOK_REMOVING;
 -                                                              W_SwitchWeapon_Force(self, w_getbestweapon(self));
 +                                                              actor.ammo_fuel = 0;
 +                                                              actor.hook_state |= HOOK_REMOVING;
 +                                                              W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
                                                        }
                                                }
                                        }
                        }
                        else
                        {
 -                              self.hook_time_hooked = time;
 -                              self.hook_time_fueldecrease = time + WEP_CVAR_PRI(hook, hooked_time_free);
 +                              actor.hook_time_hooked = time;
 +                              actor.hook_time_fueldecrease = time + WEP_CVAR_PRI(hook, hooked_time_free);
                        }
  
 -                      if(self.BUTTON_CROUCH)
 +                      if(actor.BUTTON_CROUCH)
                        {
 -                              self.hook_state &= ~HOOK_PULLING;
 -                              if(self.BUTTON_ATCK || self.BUTTON_HOOK)
 -                                      self.hook_state &= ~HOOK_RELEASING;
 +                              actor.hook_state &= ~HOOK_PULLING;
 +                              if(fire1 || actor.BUTTON_HOOK)
 +                                      actor.hook_state &= ~HOOK_RELEASING;
                                else
 -                                      self.hook_state |= HOOK_RELEASING;
 +                                      actor.hook_state |= HOOK_RELEASING;
                        }
                        else
                        {
 -                              self.hook_state |= HOOK_PULLING;
 -                              self.hook_state &= ~HOOK_RELEASING;
 +                              actor.hook_state |= HOOK_PULLING;
 +                              actor.hook_state &= ~HOOK_RELEASING;
  
 -                              if(self.BUTTON_ATCK || self.BUTTON_HOOK)
 +                              if(fire1 || actor.BUTTON_HOOK)
                                {
                                        // already fired
 -                                      if(self.hook)
 -                                              self.hook_state |= HOOK_WAITING_FOR_RELEASE;
 +                                      if(actor.hook)
 +                                              actor.hook_state |= HOOK_WAITING_FOR_RELEASE;
                                }
                                else
                                {
 -                                      self.hook_state |= HOOK_REMOVING;
 -                                      self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
 +                                      actor.hook_state |= HOOK_REMOVING;
 +                                      actor.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
                                }
                        }
  
 -                      return true;
 +                      _GrapplingHookFrame();
                }
 -              case WR_INIT:
 +              METHOD(Hook, wr_init, void(entity thiswep))
                {
                        HOOK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_SETUP:
 +              METHOD(Hook, wr_setup, void(entity thiswep))
                {
                        self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Hook, wr_checkammo1, bool(entity thiswep))
                {
                        if(self.hook)
                                return self.ammo_fuel > 0;
                        else
                                return self.ammo_fuel >= WEP_CVAR_PRI(hook, ammo);
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Hook, wr_checkammo2, bool(entity thiswep))
                {
                        // infinite ammo for now
                        return true; // self.ammo_cells >= WEP_CVAR_SEC(hook, ammo); // WEAPONTODO: see above
                }
 -              case WR_CONFIG:
 +              METHOD(Hook, wr_config, void(entity thiswep))
                {
                        HOOK_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RESETPLAYER:
 +              METHOD(Hook, wr_resetplayer, void(entity thiswep))
                {
                        self.hook_refire = time;
 -                      return true;
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Hook, wr_suicidemessage, int(entity thiswep))
                {
                        return false;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Hook, wr_killmessage, int(entity thiswep))
                {
                        return WEAPON_HOOK_MURDER;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_Hook(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Hook, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 2;
                        pointparticles(particleeffectnum(EFFECT_HOOK_EXPLODE), org2, '0 0 0', 1);
                        if(!w_issilent)
                                sound(self, CH_SHOTS, SND_HOOKBOMB_IMPACT, VOL_BASE, ATTN_NORM);
 -
 -                      return true;
 -              }
 -              case WR_INIT:
 -              {
 -                      return true;
                }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index 9c22591965dc4bf0018301eef82d6c3e755b68dd,e9a50ab4393d34c6eb976ea1dbae2c77bb211f8c..688600cd74f2e7b4cde3c98384507c963c952cf6
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ MACHINEGUN,
 -/* function  */ W_MachineGun,
 -/* ammotype  */ ammo_nails,
 -/* impulse   */ 3,
 -/* flags     */ WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
 -/* rating    */ BOT_PICKUP_RATING_MID,
 -/* color     */ '1 1 0',
 -/* modelname */ "uzi",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairuzi 0.6",
 -/* wepimg    */ "weaponuzi",
 -/* refname   */ "machinegun",
 -/* wepname   */ _("Machine Gun")
 -);
 +CLASS(MachineGun, Weapon)
 +/* ammotype  */ ATTRIB(MachineGun, ammo_field, .int, ammo_nails)
 +/* impulse   */ ATTRIB(MachineGun, impulse, int, 3)
 +/* flags     */ ATTRIB(MachineGun, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
 +/* rating    */ ATTRIB(MachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 +/* color     */ ATTRIB(MachineGun, wpcolor, vector, '1 1 0');
 +/* modelname */ ATTRIB(MachineGun, mdl, string, "uzi");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(MachineGun, m_model, Model, MDL_MACHINEGUN_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(MachineGun, w_crosshair, string, "gfx/crosshairuzi");
 +/* crosshair */ ATTRIB(MachineGun, w_crosshair_size, float, 0.6);
 +/* wepimg    */ ATTRIB(MachineGun, model2, string, "weaponuzi");
 +/* refname   */ ATTRIB(MachineGun, netname, string, "machinegun");
 +/* wepname   */ ATTRIB(MachineGun, message, string, _("MachineGun"));
 +ENDCLASS(MachineGun)
 +REGISTER_WEAPON(MACHINEGUN, NEW(MachineGun));
  
  #define MACHINEGUN_SETTINGS(w_cvar,w_prop) MACHINEGUN_SETTINGS_LIST(w_cvar, w_prop, MACHINEGUN, machinegun)
  #define MACHINEGUN_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -57,8 -55,8 +57,8 @@@ MACHINEGUN_SETTINGS(WEP_ADD_CVAR, WEP_A
  #ifdef IMPLEMENTATION
  #ifdef SVQC
  
void spawnfunc_weapon_machinegun(void)
- {SELFPARAM();
spawnfunc(weapon_machinegun)
+ {
        if(autocvar_sv_q3acompat_machineshotgunswap)
        if(self.classname != "droppedweapon")
        {
@@@ -67,7 -65,7 +67,7 @@@
        }
        weapon_defaultspawnfunc(WEP_MACHINEGUN.m_id);
  }
void spawnfunc_weapon_uzi(void) { spawnfunc_weapon_machinegun(); }
spawnfunc(weapon_uzi) { spawnfunc_weapon_machinegun(this); }
  
  void W_MachineGun_MuzzleFlash_Think(void)
  {SELFPARAM();
@@@ -104,7 -102,7 +104,7 @@@ void W_MachineGun_MuzzleFlash(void
        self.muzzle_flash.owner = self.muzzle_flash.realowner = self;
  }
  
 -void W_MachineGun_Attack(int deathtype)
 +void W_MachineGun_Attack(Weapon thiswep, int deathtype)
  {SELFPARAM();
        W_SetupShot(self, true, 0, SND(UZI_FIRE), CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage)));
        if(!autocvar_g_norecoil)
        Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
  
        W_MachineGun_MuzzleFlash();
 -      W_AttachToShotorg(self.muzzle_flash, '5 0 0');
 +      W_AttachToShotorg(self, self.muzzle_flash, '5 0 0');
  
        // casing code
        if(autocvar_g_casings >= 2)
                SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
  
        if(self.misc_bulletcounter == 1)
 -              W_DecreaseAmmo(WEP_CVAR(machinegun, first_ammo));
 +              W_DecreaseAmmo(thiswep, self, WEP_CVAR(machinegun, first_ammo));
        else
 -              W_DecreaseAmmo(WEP_CVAR(machinegun, sustained_ammo));
 +              W_DecreaseAmmo(thiswep, self, WEP_CVAR(machinegun, sustained_ammo));
  }
  
  // weapon frames
 -void W_MachineGun_Attack_Frame(void)
 -{SELFPARAM();
 -      if(self.weapon != self.switchweapon) // abort immediately if switching
 +void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, bool fire1, bool fire2)
 +{
 +      if(actor.weapon != actor.switchweapon) // abort immediately if switching
        {
 -              w_ready();
 +              w_ready(thiswep, actor, fire1, fire2);
                return;
        }
 -      if(self.BUTTON_ATCK)
 +      if(actor.BUTTON_ATCK)
        {
 -              if(!WEP_ACTION(self.weapon, WR_CHECKAMMO2))
 -              if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +              Weapon w = get_weaponinfo(actor.weapon);
 +              if(!w.wr_checkammo2(w))
 +              if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
                {
 -                      W_SwitchWeapon_Force(self, w_getbestweapon(self));
 -                      w_ready();
 +                      W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
 +                      w_ready(thiswep, actor, fire1, fire2);
                        return;
                }
 -              self.misc_bulletcounter = self.misc_bulletcounter + 1;
 -              W_MachineGun_Attack(WEP_MACHINEGUN.m_id);
 -              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
 +              actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
 +              W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id);
 +              weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
        }
        else
 -              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready);
 +              weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready);
  }
  
  
 -void W_MachineGun_Attack_Auto(void)
 -{SELFPARAM();
 +void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, bool fire1, bool fire2)
 +{
        float machinegun_spread;
  
 -      if(!self.BUTTON_ATCK)
 +      if(!fire1)
        {
 -              w_ready();
 +              w_ready(thiswep, actor, fire1, fire2);
                return;
        }
  
 -      if(!WEP_ACTION(self.weapon, WR_CHECKAMMO1))
 -      if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +      Weapon w = get_weaponinfo(actor.weapon);
 +      if(!w.wr_checkammo1(w))
 +      if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
        {
 -              W_SwitchWeapon_Force(self, w_getbestweapon(self));
 -              w_ready();
 +              W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
 +              w_ready(thiswep, actor, fire1, fire2);
                return;
        }
  
 -      W_DecreaseAmmo(WEP_CVAR(machinegun, sustained_ammo));
 +      W_DecreaseAmmo(WEP_MACHINEGUN, actor, WEP_CVAR(machinegun, sustained_ammo));
  
 -      W_SetupShot(self, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
 +      W_SetupShot(actor, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
        if(!autocvar_g_norecoil)
        {
 -              self.punchangle_x = random() - 0.5;
 -              self.punchangle_y = random() - 0.5;
 +              actor.punchangle_x = random() - 0.5;
 +              actor.punchangle_y = random() - 0.5;
        }
  
 -      machinegun_spread = bound(WEP_CVAR(machinegun, spread_min), WEP_CVAR(machinegun, spread_min) + (WEP_CVAR(machinegun, spread_add) * self.misc_bulletcounter), WEP_CVAR(machinegun, spread_max));
 +      machinegun_spread = bound(WEP_CVAR(machinegun, spread_min), WEP_CVAR(machinegun, spread_min) + (WEP_CVAR(machinegun, spread_add) * actor.misc_bulletcounter), WEP_CVAR(machinegun, spread_max));
        fireBullet(w_shotorg, w_shotdir, machinegun_spread, WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), WEP_MACHINEGUN.m_id, 0);
  
 -      self.misc_bulletcounter = self.misc_bulletcounter + 1;
 +      actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
  
        Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
  
        W_MachineGun_MuzzleFlash();
 -      W_AttachToShotorg(self.muzzle_flash, '5 0 0');
 +      W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
  
        if(autocvar_g_casings >= 2) // casing code
 -              SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
 +              SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
  
 -      ATTACK_FINISHED(self) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor();
 -      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto);
 +      ATTACK_FINISHED(actor) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor();
 +      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto);
  }
  
 -void W_MachineGun_Attack_Burst(void)
 -{SELFPARAM();
 -      W_SetupShot(self, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
 +void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, bool fire1, bool fire2)
 +{
 +      W_SetupShot(actor, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
        if(!autocvar_g_norecoil)
        {
 -              self.punchangle_x = random() - 0.5;
 -              self.punchangle_y = random() - 0.5;
 +              actor.punchangle_x = random() - 0.5;
 +              actor.punchangle_y = random() - 0.5;
        }
  
        fireBullet(w_shotorg, w_shotdir, WEP_CVAR(machinegun, burst_speed), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), WEP_MACHINEGUN.m_id, 0);
        Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
  
        W_MachineGun_MuzzleFlash();
 -      W_AttachToShotorg(self.muzzle_flash, '5 0 0');
 +      W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
  
        if(autocvar_g_casings >= 2) // casing code
 -              SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
 +              SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
  
 -      self.misc_bulletcounter = self.misc_bulletcounter + 1;
 -      if(self.misc_bulletcounter == 0)
 +      actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
 +      if(actor.misc_bulletcounter == 0)
        {
 -              ATTACK_FINISHED(self) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor();
 -              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready);
 +              ATTACK_FINISHED(actor) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor();
 +              weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready);
        }
        else
        {
 -              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst);
 +              weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst);
        }
  
  }
  
 -bool W_MachineGun(int req)
 -{SELFPARAM();
 -      float ammo_amount;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(MachineGun, wr_aim, void(entity thiswep))
                {
                        if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200)
                                self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false);
                        else
                                self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false);
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(WEP_CVAR(machinegun, reload_ammo) && self.clip_load < min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo))) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      else if(WEP_CVAR(machinegun, mode) == 1)
 +                      if(WEP_CVAR(machinegun, reload_ammo) && actor.clip_load < min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo))) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else
 +                      if(WEP_CVAR(machinegun, mode) == 1)
                        {
 -                              if(self.BUTTON_ATCK)
 -                              if(weapon_prepareattack(0, 0))
 +                              if(fire1)
 +                              if(weapon_prepareattack(actor, false, 0))
                                {
 -                                      self.misc_bulletcounter = 0;
 -                                      W_MachineGun_Attack_Auto();
 +                                      actor.misc_bulletcounter = 0;
 +                                      W_MachineGun_Attack_Auto(thiswep, actor, fire1, fire2);
                                }
  
 -                              if(self.BUTTON_ATCK2)
 -                              if(weapon_prepareattack(1, 0))
 +                              if(fire2)
 +                              if(weapon_prepareattack(actor, true, 0))
                                {
 -                                      if(!WEP_ACTION(self.weapon, WR_CHECKAMMO2))
 -                                      if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +                                      Weapon w = get_weaponinfo(actor.weapon);
 +                                      if(!w.wr_checkammo2(w))
 +                                      if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
                                        {
 -                                              W_SwitchWeapon_Force(self, w_getbestweapon(self));
 -                                              w_ready();
 -                                              return false;
 +                                              W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
 +                                              w_ready(thiswep, actor, fire1, fire2);
 +                                              return;
                                        }
  
 -                                      W_DecreaseAmmo(WEP_CVAR(machinegun, burst_ammo));
 +                                      W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, burst_ammo));
  
 -                                      self.misc_bulletcounter = WEP_CVAR(machinegun, burst) * -1;
 -                                      W_MachineGun_Attack_Burst();
 +                                      actor.misc_bulletcounter = WEP_CVAR(machinegun, burst) * -1;
 +                                      W_MachineGun_Attack_Burst(thiswep, actor, fire1, fire2);
                                }
                        }
                        else
                        {
  
 -                              if(self.BUTTON_ATCK)
 -                              if(weapon_prepareattack(0, 0))
 +                              if(fire1)
 +                              if(weapon_prepareattack(actor, false, 0))
                                {
 -                                      self.misc_bulletcounter = 1;
 -                                      W_MachineGun_Attack(WEP_MACHINEGUN.m_id); // sets attack_finished
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
 +                                      actor.misc_bulletcounter = 1;
 +                                      W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id); // sets attack_finished
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
                                }
  
 -                              if(self.BUTTON_ATCK2 && WEP_CVAR(machinegun, first))
 -                              if(weapon_prepareattack(1, 0))
 +                              if(fire2 && WEP_CVAR(machinegun, first))
 +                              if(weapon_prepareattack(actor, true, 0))
                                {
 -                                      self.misc_bulletcounter = 1;
 -                                      W_MachineGun_Attack(WEP_MACHINEGUN.m_id | HITTYPE_SECONDARY); // sets attack_finished
 -                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready);
 +                                      actor.misc_bulletcounter = 1;
 +                                      W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id | HITTYPE_SECONDARY); // sets attack_finished
 +                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready);
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(MachineGun, wr_init, void(entity thiswep))
                {
                        MACHINEGUN_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(MachineGun, wr_checkammo1, bool(entity thiswep))
                {
 +                      float ammo_amount;
                        if(WEP_CVAR(machinegun, mode) == 1)
                                ammo_amount = self.WEP_AMMO(MACHINEGUN) >= WEP_CVAR(machinegun, sustained_ammo);
                        else
                        }
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(MachineGun, wr_checkammo2, bool(entity thiswep))
                {
 +                      float ammo_amount;
                        if(WEP_CVAR(machinegun, mode) == 1)
                                ammo_amount = self.WEP_AMMO(MACHINEGUN) >= WEP_CVAR(machinegun, burst_ammo);
                        else
                        }
                        return ammo_amount;
                }
 -              case WR_CONFIG:
 +              METHOD(MachineGun, wr_config, void(entity thiswep))
                {
                        MACHINEGUN_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(MachineGun, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(MachineGun, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_THINKING_WITH_PORTALS;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(MachineGun, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_MACHINEGUN_MURDER_SNIPE;
                        else
                                return WEAPON_MACHINEGUN_MURDER_SPRAY;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_MachineGun(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(MachineGun, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 2;
                                        sound(self, CH_SHOTS, SND_RIC2, VOL_BASE, ATTN_NORM);
                                else if(w_random < 0.2)
                                        sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTN_NORM);
 -
 -                      return true;
 -              }
 -              case WR_INIT:
 -              {
 -                      return true;
 -              }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index be07f516f8786397feff0acd7b1bd2100a19621f,40866970fb3438d3fd2fcd83cdc0bfeaccc05b76..75cc7e4d706a2ef5b5e85ec647549f07d6f630fc
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ MINE_LAYER,
 -/* function  */ W_MineLayer,
 -/* ammotype  */ ammo_rockets,
 -/* impulse   */ 4,
 -/* flags     */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
 -/* rating    */ BOT_PICKUP_RATING_HIGH,
 -/* color     */ '0.75 1 0',
 -/* modelname */ "minelayer",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairminelayer 0.9",
 -/* wepimg    */ "weaponminelayer",
 -/* refname   */ "minelayer",
 -/* wepname   */ _("Mine Layer")
 -);
 +CLASS(MineLayer, Weapon)
 +/* ammotype  */ ATTRIB(MineLayer, ammo_field, .int, ammo_rockets)
 +/* impulse   */ ATTRIB(MineLayer, impulse, int, 4)
 +/* flags     */ ATTRIB(MineLayer, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(MineLayer, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 +/* color     */ ATTRIB(MineLayer, wpcolor, vector, '0.75 1 0');
 +/* modelname */ ATTRIB(MineLayer, mdl, string, "minelayer");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(MineLayer, m_model, Model, MDL_MINELAYER_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(MineLayer, w_crosshair, string, "gfx/crosshairminelayer");
 +/* crosshair */ ATTRIB(MineLayer, w_crosshair_size, float, 0.9);
 +/* wepimg    */ ATTRIB(MineLayer, model2, string, "weaponminelayer");
 +/* refname   */ ATTRIB(MineLayer, netname, string, "minelayer");
 +/* wepname   */ ATTRIB(MineLayer, message, string, _("Mine Layer"));
 +ENDCLASS(MineLayer)
 +REGISTER_WEAPON(MINE_LAYER, NEW(MineLayer));
  
  #define MINELAYER_SETTINGS(w_cvar,w_prop) MINELAYER_SETTINGS_LIST(w_cvar, w_prop, MINE_LAYER, minelayer)
  #define MINELAYER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -59,7 -57,7 +59,7 @@@ void W_MineLayer_Think(void)
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_minelayer(void) { weapon_defaultspawnfunc(WEP_MINE_LAYER.m_id); }
spawnfunc(weapon_minelayer) { weapon_defaultspawnfunc(WEP_MINE_LAYER.m_id); }
  
  void W_MineLayer_Stick(entity to)
  {SELFPARAM();
@@@ -125,8 -123,7 +125,8 @@@ void W_MineLayer_Explode(void
        if(self.realowner.weapon == WEP_MINE_LAYER.m_id)
        {
                setself(self.realowner);
 -              if(!WEP_ACTION(WEP_MINE_LAYER.m_id, WR_CHECKAMMO1))
 +              Weapon w = WEP_MINE_LAYER;
 +              if(!w.wr_checkammo1(w))
                {
                        self.cnt = WEP_MINE_LAYER.m_id;
                        ATTACK_FINISHED(self) = time;
@@@ -151,8 -148,7 +151,8 @@@ void W_MineLayer_DoRemoteExplode(void
        if(self.realowner.weapon == WEP_MINE_LAYER.m_id)
        {
                setself(self.realowner);
 -              if(!WEP_ACTION(WEP_MINE_LAYER.m_id, WR_CHECKAMMO1))
 +              Weapon w = WEP_MINE_LAYER;
 +              if(!w.wr_checkammo1(w))
                {
                        self.cnt = WEP_MINE_LAYER.m_id;
                        ATTACK_FINISHED(self) = time;
@@@ -308,7 -304,7 +308,7 @@@ void W_MineLayer_Damage(entity inflicto
                W_PrepareExplosionByDamage(attacker, W_MineLayer_Explode);
  }
  
 -void W_MineLayer_Attack(void)
 +void W_MineLayer_Attack(Weapon thiswep)
  {SELFPARAM();
        entity mine;
        entity flash;
                }
        }
  
 -      W_DecreaseAmmo(WEP_CVAR(minelayer, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR(minelayer, ammo));
  
        W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', false, 5, SND(MINE_FIRE), CH_WEAPON_A, WEP_CVAR(minelayer, damage));
        Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
        setmodel(flash, MDL_MINELAYER_MUZZLEFLASH); // precision set below
        SUB_SetFade(flash, time, 0.1);
        flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
 -      W_AttachToShotorg(flash, '5 0 0');
 +      W_AttachToShotorg(self, flash, '5 0 0');
  
        // common properties
  
@@@ -401,7 -397,13 +401,7 @@@ float W_MineLayer_PlacedMines(float det
        return minfound;
  }
  
 -bool W_MineLayer(int req)
 -{SELFPARAM();
 -      entity mine;
 -      float ammo_amount;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(MineLayer, wr_aim, void(entity thiswep))
                {
                        // aim and decide to fire if appropriate
                        if(self.minelayer_mines >= WEP_CVAR(minelayer, limit))
                                teamdamage = 0;
                                enemydamage = 0;
                                targetlist = findchainfloat(bot_attack, true);
 -                              mine = find(world, classname, "mine");
 +                              entity mine = find(world, classname, "mine");
                                while(mine)
                                {
                                        if(mine.realowner != self)
                                //      dprint(ftos(desirabledamage),"\n");
                                if(self.BUTTON_ATCK2 == true) self.BUTTON_ATCK = false;
                        }
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(MineLayer, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(autocvar_g_balance_minelayer_reload_ammo && self.clip_load < WEP_CVAR(minelayer, ammo)) // forced reload
 +                      if(autocvar_g_balance_minelayer_reload_ammo && actor.clip_load < WEP_CVAR(minelayer, ammo)) // forced reload
                        {
                                // not if we're holding the minelayer without enough ammo, but can detonate existing mines
 -                              if(!(W_MineLayer_PlacedMines(false) && self.WEP_AMMO(MINE_LAYER) < WEP_CVAR(minelayer, ammo)))
 -                                      WEP_ACTION(self.weapon, WR_RELOAD);
 +                              if(!(W_MineLayer_PlacedMines(false) && actor.WEP_AMMO(MINE_LAYER) < WEP_CVAR(minelayer, ammo))) {
 +                                      Weapon w = get_weaponinfo(actor.weapon);
 +                                      w.wr_reload(w);
 +                              }
                        }
 -                      else if(self.BUTTON_ATCK)
 +                      else if(fire1)
                        {
 -                              if(weapon_prepareattack(0, WEP_CVAR(minelayer, refire)))
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR(minelayer, refire)))
                                {
 -                                      W_MineLayer_Attack();
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready);
 +                                      W_MineLayer_Attack(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready);
                                }
                        }
  
 -                      if(self.BUTTON_ATCK2)
 +                      if(fire2)
                        {
                                if(W_MineLayer_PlacedMines(true))
 -                                      sound(self, CH_WEAPON_B, SND_MINE_DET, VOL_BASE, ATTN_NORM);
 +                                      sound(actor, CH_WEAPON_B, SND_MINE_DET, VOL_BASE, ATTN_NORM);
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(MineLayer, wr_init, void(entity thiswep))
                {
                        MINELAYER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(MineLayer, wr_checkammo1, bool(entity thiswep))
                {
                        // don't switch while placing a mine
                        if(ATTACK_FINISHED(self) <= time || self.weapon != WEP_MINE_LAYER.m_id)
                        {
 -                              ammo_amount = self.WEP_AMMO(MINE_LAYER) >= WEP_CVAR(minelayer, ammo);
 +                              float ammo_amount = self.WEP_AMMO(MINE_LAYER) >= WEP_CVAR(minelayer, ammo);
                                ammo_amount += self.(weapon_load[WEP_MINE_LAYER.m_id]) >= WEP_CVAR(minelayer, ammo);
                                return ammo_amount;
                        }
                        return true;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(MineLayer, wr_checkammo2, bool(entity thiswep))
                {
                        if(W_MineLayer_PlacedMines(false))
                                return true;
                        else
                                return false;
                }
 -              case WR_CONFIG:
 +              METHOD(MineLayer, wr_config, void(entity thiswep))
                {
                        MINELAYER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RESETPLAYER:
 +              METHOD(MineLayer, wr_resetplayers, void(entity thiswep))
                {
                        self.minelayer_mines = 0;
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(MineLayer, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(WEP_CVAR(minelayer, ammo), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, WEP_CVAR(minelayer, ammo), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(MineLayer, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_MINELAYER_SUICIDE;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(MineLayer, wr_killmessage, int(entity thiswep))
                {
                        return WEAPON_MINELAYER_MURDER;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_MineLayer(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(MineLayer, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 12;
                        pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), org2, '0 0 0', 1);
                        if(!w_issilent)
                                sound(self, CH_SHOTS, SND_MINE_EXP, VOL_BASE, ATTN_NORM);
 -
 -                      return true;
                }
 -              case WR_INIT:
 -              {
 -                      return true;
 -              }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index 5a7bf93a08f85a2fe41f7d088ca2ebeeca7685bc,1276cca5d9347bbcd45d4142014a2793aa9c703f..34d64f004eadb5ef53ed16942a90aaea6f54604d
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ MORTAR,
 -/* function  */ W_Mortar,
 -/* ammotype  */ ammo_rockets,
 -/* impulse   */ 4,
 -/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
 -/* rating    */ BOT_PICKUP_RATING_MID,
 -/* color     */ '1 0 0',
 -/* modelname */ "gl",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairgrenadelauncher 0.7",
 -/* wepimg    */ "weapongrenadelauncher",
 -/* refname   */ "mortar",
 -/* wepname   */ _("Mortar")
 -);
 +CLASS(Mortar, Weapon)
 +/* ammotype  */ ATTRIB(Mortar, ammo_field, .int, ammo_rockets)
 +/* impulse   */ ATTRIB(Mortar, impulse, int, 4)
 +/* flags     */ ATTRIB(Mortar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(Mortar, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 +/* color     */ ATTRIB(Mortar, wpcolor, vector, '1 0 0');
 +/* modelname */ ATTRIB(Mortar, mdl, string, "gl");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Mortar, m_model, Model, MDL_MORTAR_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Mortar, w_crosshair, string, "gfx/crosshairgrenadelauncher");
 +/* crosshair */ ATTRIB(Mortar, w_crosshair_size, float, 0.7);
 +/* wepimg    */ ATTRIB(Mortar, model2, string, "weapongrenadelauncher");
 +/* refname   */ ATTRIB(Mortar, netname, string, "mortar");
 +/* wepname   */ ATTRIB(Mortar, message, string, _("Mortar"));
 +ENDCLASS(Mortar)
 +REGISTER_WEAPON(MORTAR, NEW(Mortar));
  
  #define MORTAR_SETTINGS(w_cvar,w_prop) MORTAR_SETTINGS_LIST(w_cvar, w_prop, MORTAR, mortar)
  #define MORTAR_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -58,8 -56,8 +58,8 @@@ MORTAR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_P
  #ifdef IMPLEMENTATION
  #ifdef SVQC
  
void spawnfunc_weapon_mortar(void) { weapon_defaultspawnfunc(WEP_MORTAR.m_id); }
void spawnfunc_weapon_grenadelauncher(void) { spawnfunc_weapon_mortar(); }
spawnfunc(weapon_mortar) { weapon_defaultspawnfunc(WEP_MORTAR.m_id); }
spawnfunc(weapon_grenadelauncher) { spawnfunc_weapon_mortar(this); }
  
  void W_Mortar_Grenade_Explode(void)
  {SELFPARAM();
@@@ -198,11 -196,11 +198,11 @@@ void W_Mortar_Grenade_Touch2(void
        }
  }
  
 -void W_Mortar_Attack(void)
 +void W_Mortar_Attack(Weapon thiswep)
  {SELFPARAM();
        entity gren;
  
 -      W_DecreaseAmmo(WEP_CVAR_PRI(mortar, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_PRI(mortar, ammo));
  
        W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', false, 4, SND(GRENADE_FIRE), CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage));
        w_shotdir = v_forward; // no TrueAim for grenades please
        MUTATOR_CALLHOOK(EditProjectile, self, gren);
  }
  
 -void W_Mortar_Attack2(void)
 +void W_Mortar_Attack2(Weapon thiswep)
  {SELFPARAM();
        entity gren;
  
 -      W_DecreaseAmmo(WEP_CVAR_SEC(mortar, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(mortar, ammo));
  
        W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', false, 4, SND(GRENADE_FIRE), CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage));
        w_shotdir = v_forward; // no TrueAim for grenades please
  }
  
  .float bot_secondary_grenademooth;
 -bool W_Mortar(int req)
 -{SELFPARAM();
 -      entity nade;
 -      float nadefound;
 -      float ammo_amount;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +
 +              METHOD(Mortar, wr_aim, void(entity thiswep))
                {
                        self.BUTTON_ATCK = false;
                        self.BUTTON_ATCK2 = false;
                                        if(random() < 0.02) self.bot_secondary_grenademooth = 0;
                                }
                        }
 -
 -                      return true;
                }
                /*case WR_CALCINFO:
                {
                        wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime))));
                        wepinfo_ter_dps = 0;
                        */
 -              case WR_THINK:
 +              METHOD(Mortar, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(autocvar_g_balance_mortar_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo))) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      else if(self.BUTTON_ATCK)
 +                      if(autocvar_g_balance_mortar_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo))) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else if(fire1)
                        {
 -                              if(weapon_prepareattack(0, WEP_CVAR_PRI(mortar, refire)))
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(mortar, refire)))
                                {
 -                                      W_Mortar_Attack();
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready);
 +                                      W_Mortar_Attack(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready);
                                }
                        }
 -                      else if(self.BUTTON_ATCK2)
 +                      else if(fire2)
                        {
                                if(WEP_CVAR_SEC(mortar, remote_detonateprimary))
                                {
 -                                      nadefound = 0;
 -                                      for(nade = world; (nade = find(nade, classname, "grenade")); ) if(nade.realowner == self)
 +                                      bool nadefound = false;
 +                                      entity nade;
 +                                      for(nade = world; (nade = find(nade, classname, "grenade")); ) if(nade.realowner == actor)
                                        {
                                                if(!nade.gl_detonate_later)
                                                {
                                                        nade.gl_detonate_later = true;
 -                                                      nadefound = 1;
 +                                                      nadefound = true;
                                                }
                                        }
                                        if(nadefound)
 -                                              sound(self, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM);
 +                                              sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM);
                                }
 -                              else if(weapon_prepareattack(1, WEP_CVAR_SEC(mortar, refire)))
 +                              else if(weapon_prepareattack(actor, true, WEP_CVAR_SEC(mortar, refire)))
                                {
 -                                      W_Mortar_Attack2();
 -                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready);
 +                                      W_Mortar_Attack2(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready);
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Mortar, wr_init, void(entity thiswep))
                {
                        MORTAR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Mortar, wr_checkammo1, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(MORTAR) >= WEP_CVAR_PRI(mortar, ammo);
 +                      float ammo_amount = self.WEP_AMMO(MORTAR) >= WEP_CVAR_PRI(mortar, ammo);
                        ammo_amount += self.(weapon_load[WEP_MORTAR.m_id]) >= WEP_CVAR_PRI(mortar, ammo);
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Mortar, wr_checkammo2, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(MORTAR) >= WEP_CVAR_SEC(mortar, ammo);
 +                      float ammo_amount = self.WEP_AMMO(MORTAR) >= WEP_CVAR_SEC(mortar, ammo);
                        ammo_amount += self.(weapon_load[WEP_MORTAR.m_id]) >= WEP_CVAR_SEC(mortar, ammo);
                        return ammo_amount;
                }
 -              case WR_CONFIG:
 +              METHOD(Mortar, wr_config, void(entity thiswep))
                {
                        MORTAR_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Mortar, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), SND(RELOAD)); // WEAPONTODO
 -                      return true;
 +                      W_Reload(self, min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), SND(RELOAD)); // WEAPONTODO
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Mortar, wr_suicidemessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_MORTAR_SUICIDE_BOUNCE;
                        else
                                return WEAPON_MORTAR_SUICIDE_EXPLODE;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Mortar, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_MORTAR_MURDER_BOUNCE;
                        else
                                return WEAPON_MORTAR_MURDER_EXPLODE;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_Mortar(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Mortar, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 12;
                        pointparticles(particleeffectnum(EFFECT_GRENADE_EXPLODE), org2, '0 0 0', 1);
                        if(!w_issilent)
                                sound(self, CH_SHOTS, SND_GRENADE_IMPACT, VOL_BASE, ATTN_NORM);
 -
 -                      return true;
 -              }
 -              case WR_INIT:
 -              {
 -                      return true;
                }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index 50039d75fe645076f5f936db6f6fafbf9842a2be,66cdb21228b099c4d240a9b14ed12a0c5393cbf8..cf802ac7f42deb61d9e3fc169e1887095cfc1c70
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ PORTO,
 -/* function  */ W_Porto,
 -/* ammotype  */ ammo_none,
 -/* impulse   */ 0,
 -/* flags     */ WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON,
 -/* rating    */ 0,
 -/* color     */ '0.5 0.5 0.5',
 -/* modelname */ "porto",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairporto 0.6",
 -/* wepimg    */ "weaponporto",
 -/* refname   */ "porto",
 -/* wepname   */ _("Port-O-Launch")
 -);
 +CLASS(PortoLaunch, Weapon)
 +/* ammotype  */ ATTRIB(PortoLaunch, ammo_field, .int, ammo_none)
 +/* impulse   */ ATTRIB(PortoLaunch, impulse, int, 0)
 +/* flags     */ ATTRIB(PortoLaunch, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON);
 +/* rating    */ ATTRIB(PortoLaunch, bot_pickupbasevalue, float, 0);
 +/* color     */ ATTRIB(PortoLaunch, wpcolor, vector, '0.5 0.5 0.5');
 +/* modelname */ ATTRIB(PortoLaunch, mdl, string, "porto");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(PortoLaunch, m_model, Model, MDL_PORTO_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(PortoLaunch, w_crosshair, string, "gfx/crosshairporto");
 +/* crosshair */ ATTRIB(PortoLaunch, w_crosshair_size, float, 0.6);
 +/* wepimg    */ ATTRIB(PortoLaunch, model2, string, "weaponporto");
 +/* refname   */ ATTRIB(PortoLaunch, netname, string, "porto");
 +/* wepname   */ ATTRIB(PortoLaunch, message, string, _("Port-O-Launch"));
 +ENDCLASS(PortoLaunch)
 +REGISTER_WEAPON(PORTO, NEW(PortoLaunch));
  
  #define PORTO_SETTINGS(w_cvar,w_prop) PORTO_SETTINGS_LIST(w_cvar, w_prop, PORTO, porto)
  #define PORTO_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -43,7 -41,7 +43,7 @@@ PORTO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PR
  #ifdef SVQC
  #include "../../triggers/trigger/jumppads.qh"
  
void spawnfunc_weapon_porto(void) { weapon_defaultspawnfunc(WEP_PORTO.m_id); }
spawnfunc(weapon_porto) { weapon_defaultspawnfunc(WEP_PORTO.m_id); }
  
  void W_Porto_Success(void)
  {SELFPARAM();
@@@ -74,7 -72,7 +74,7 @@@ void W_Porto_Fail(float failhard
  
        self.realowner.porto_current = world;
  
 -      if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !(self.realowner.weapons & WEPSET_PORTO))
 +      if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !(self.realowner.weapons & WEPSET(PORTO)))
        {
                setsize(self, '-16 -16 0', '16 16 32');
                setorigin(self, self.origin + trace_plane_normal);
@@@ -285,103 -283,135 +285,103 @@@ void W_Porto_Attack(float type
        MUTATOR_CALLHOOK(EditProjectile, self, gren);
  }
  
 -bool w_nexball_weapon(int req); // WEAPONTODO
 -bool W_Porto(int req)
 -{SELFPARAM();
 -      //vector v_angle_save;
 -
 -      if(g_nexball) { return w_nexball_weapon(req); }
 -
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(PortoLaunch, wr_aim, void(entity thiswep))
                {
 +                      SELFPARAM();
                        self.BUTTON_ATCK = false;
                        self.BUTTON_ATCK2 = false;
                        if(!WEP_CVAR(porto, secondary))
                                if(bot_aim(WEP_CVAR_PRI(porto, speed), 0, WEP_CVAR_PRI(porto, lifetime), false))
                                        self.BUTTON_ATCK = true;
 -
 -                      return true;
                }
 -              case WR_CONFIG:
 +              METHOD(PortoLaunch, wr_config, void(entity this))
                {
                        PORTO_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
                        if(WEP_CVAR(porto, secondary))
                        {
 -                              if(self.BUTTON_ATCK)
 -                              if(!self.porto_current)
 -                              if(!self.porto_forbidden)
 -                              if(weapon_prepareattack(0, WEP_CVAR_PRI(porto, refire)))
 +                              if(fire1)
 +                              if(!actor.porto_current)
 +                              if(!actor.porto_forbidden)
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(porto, refire)))
                                {
                                        W_Porto_Attack(0);
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
                                }
  
 -                              if(self.BUTTON_ATCK2)
 -                              if(!self.porto_current)
 -                              if(!self.porto_forbidden)
 -                              if(weapon_prepareattack(1, WEP_CVAR_SEC(porto, refire)))
 +                              if(fire2)
 +                              if(!actor.porto_current)
 +                              if(!actor.porto_forbidden)
 +                              if(weapon_prepareattack(actor, true, WEP_CVAR_SEC(porto, refire)))
                                {
                                        W_Porto_Attack(1);
 -                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready);
 +                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready);
                                }
                        }
                        else
                        {
 -                              if(self.porto_v_angle_held)
 +                              if(actor.porto_v_angle_held)
                                {
 -                                      if(!self.BUTTON_ATCK2)
 +                                      if(!fire2)
                                        {
 -                                              self.porto_v_angle_held = 0;
 +                                              actor.porto_v_angle_held = 0;
  
 -                                              ClientData_Touch(self);
 +                                              ClientData_Touch(actor);
                                        }
                                }
                                else
                                {
 -                                      if(self.BUTTON_ATCK2)
 +                                      if(fire2)
                                        {
 -                                              self.porto_v_angle = self.v_angle;
 -                                              self.porto_v_angle_held = 1;
 +                                              actor.porto_v_angle = actor.v_angle;
 +                                              actor.porto_v_angle_held = 1;
  
 -                                              ClientData_Touch(self);
 +                                              ClientData_Touch(actor);
                                        }
                                }
 -                              if(self.porto_v_angle_held)
 -                                      makevectors(self.porto_v_angle); // override the previously set angles
 +                              if(actor.porto_v_angle_held)
 +                                      makevectors(actor.porto_v_angle); // override the previously set angles
  
 -                              if(self.BUTTON_ATCK)
 -                              if(!self.porto_current)
 -                              if(!self.porto_forbidden)
 -                              if(weapon_prepareattack(0, WEP_CVAR_PRI(porto, refire)))
 +                              if(fire1)
 +                              if(!actor.porto_current)
 +                              if(!actor.porto_forbidden)
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(porto, refire)))
                                {
                                        W_Porto_Attack(-1);
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
                                }
                        }
 -
 +              }
 +              METHOD(PortoLaunch, wr_checkammo1, bool(entity this))
 +              {
 +                      // always allow infinite ammo
                        return true;
                }
 -              case WR_CHECKAMMO1:
 -              case WR_CHECKAMMO2:
 +              METHOD(PortoLaunch, wr_checkammo2, bool(entity this))
                {
                        // always allow infinite ammo
                        return true;
                }
 -              case WR_INIT:
 +              METHOD(PortoLaunch, wr_init, void(entity this))
                {
                        PORTO_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_SETUP:
 +              METHOD(PortoLaunch, wr_setup, void(entity thiswep))
                {
 +                      SELFPARAM();
                        self.ammo_field = ammo_none;
 -                      return true;
                }
 -              case WR_RESETPLAYER:
 +              METHOD(PortoLaunch, wr_resetplayer, void(entity thiswep))
                {
 +                      SELFPARAM();
                        self.porto_current = world;
 -                      return true;
                }
 -      }
 -      return false;
 -}
  #endif
  #ifdef CSQC
 -bool W_Porto(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 -              {
 -                      LOG_INFO("Since when does Porto send DamageInfo?\n");
 -                      return true;
 +              METHOD(PortoLaunch, wr_impacteffect, void(entity this)) {
 +                      LOG_WARNING("Since when does Porto send DamageInfo?\n");
                }
 -              case WR_INIT:
 -              {
 -                      // nothing to do
 -                      return true;
 -              }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
  #endif
  #endif
index d994f01512427060016417c64534546673b21652,c156d84b46e9285841f055fffc78282f0a19e859..73590a5d565dc56a31291d1c992858cb665faa9a
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ RIFLE,
 -/* function  */ W_Rifle,
 -/* ammotype  */ ammo_nails,
 -/* impulse   */ 7,
 -/* flags     */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
 -/* rating    */ BOT_PICKUP_RATING_MID,
 -/* color     */ '0.5 1 0',
 -/* modelname */ "campingrifle",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairrifle 0.6",
 -/* wepimg    */ "weaponrifle",
 -/* refname   */ "rifle",
 -/* wepname   */ _("Rifle")
 -);
 +CLASS(Rifle, Weapon)
 +/* ammotype  */ ATTRIB(Rifle, ammo_field, .int, ammo_nails)
 +/* impulse   */ ATTRIB(Rifle, impulse, int, 7)
 +/* flags     */ ATTRIB(Rifle, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
 +/* rating    */ ATTRIB(Rifle, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 +/* color     */ ATTRIB(Rifle, wpcolor, vector, '0.5 1 0');
 +/* modelname */ ATTRIB(Rifle, mdl, string, "campingrifle");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Rifle, m_model, Model, MDL_RIFLE_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Rifle, w_crosshair, string, "gfx/crosshairrifle");
 +/* crosshair */ ATTRIB(Rifle, w_crosshair_size, float, 0.6);
 +/* wepimg    */ ATTRIB(Rifle, model2, string, "weaponrifle");
 +/* refname   */ ATTRIB(Rifle, netname, string, "rifle");
 +/* wepname   */ ATTRIB(Rifle, message, string, _("Rifle"));
 +ENDCLASS(Rifle)
 +REGISTER_WEAPON(RIFLE, NEW(Rifle));
  
  #define RIFLE_SETTINGS(w_cvar,w_prop) RIFLE_SETTINGS_LIST(w_cvar, w_prop, RIFLE, rifle)
  #define RIFLE_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -49,15 -47,15 +49,15 @@@ RIFLE_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PR
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_rifle(void) { weapon_defaultspawnfunc(WEP_RIFLE.m_id); }
void spawnfunc_weapon_campingrifle(void) { spawnfunc_weapon_rifle(); }
void spawnfunc_weapon_sniperrifle(void) { spawnfunc_weapon_rifle(); }
spawnfunc(weapon_rifle) { weapon_defaultspawnfunc(WEP_RIFLE.m_id); }
spawnfunc(weapon_campingrifle) { spawnfunc_weapon_rifle(this); }
spawnfunc(weapon_sniperrifle) { spawnfunc_weapon_rifle(this); }
  
 -void W_Rifle_FireBullet(float pSpread, float pDamage, float pForce, float pSolidPenetration, float pAmmo, int deathtype, float pTracer, float pShots, string pSound)
 +void W_Rifle_FireBullet(Weapon thiswep, float pSpread, float pDamage, float pForce, float pSolidPenetration, float pAmmo, int deathtype, float pTracer, float pShots, string pSound)
  {SELFPARAM();
        float i;
  
 -      W_DecreaseAmmo(pAmmo);
 +      W_DecreaseAmmo(thiswep, self, pAmmo);
  
        W_SetupShot(self, true, 2, pSound, CH_WEAPON_A, pDamage * pShots);
  
  
  void W_Rifle_Attack(void)
  {
 -      W_Rifle_FireBullet(WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE.m_id, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), SND(CAMPINGRIFLE_FIRE));
 +      W_Rifle_FireBullet(WEP_RIFLE, WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE.m_id, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), SND(CAMPINGRIFLE_FIRE));
  }
  
  void W_Rifle_Attack2(void)
  {
 -      W_Rifle_FireBullet(WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), SND(CAMPINGRIFLE_FIRE2));
 +      W_Rifle_FireBullet(WEP_RIFLE, WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), SND(CAMPINGRIFLE_FIRE2));
  }
  
  .void(void) rifle_bullethail_attackfunc;
  .float rifle_bullethail_frame;
  .float rifle_bullethail_animtime;
  .float rifle_bullethail_refire;
 -void W_Rifle_BulletHail_Continue(void)
 -{SELFPARAM();
 +void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, bool fire1, bool fire2)
 +{
        float r, sw, af;
  
 -      sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
 -      af = ATTACK_FINISHED(self);
 -      self.switchweapon = self.weapon;
 -      ATTACK_FINISHED(self) = time;
 -      LOG_INFO(ftos(self.WEP_AMMO(RIFLE)), "\n");
 -      r = weapon_prepareattack(self.rifle_bullethail_frame == WFRAME_FIRE2, self.rifle_bullethail_refire);
 -      if(self.switchweapon == self.weapon)
 -              self.switchweapon = sw;
 +      sw = actor.switchweapon; // make it not detect weapon changes as reason to abort firing
 +      af = ATTACK_FINISHED(actor);
 +      actor.switchweapon = actor.weapon;
 +      ATTACK_FINISHED(actor) = time;
 +      LOG_INFO(ftos(actor.WEP_AMMO(RIFLE)), "\n");
 +      r = weapon_prepareattack(actor, actor.rifle_bullethail_frame == WFRAME_FIRE2, actor.rifle_bullethail_refire);
 +      if(actor.switchweapon == actor.weapon)
 +              actor.switchweapon = sw;
        if(r)
        {
 -              self.rifle_bullethail_attackfunc();
 -              weapon_thinkf(self.rifle_bullethail_frame, self.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue);
 +              actor.rifle_bullethail_attackfunc();
 +              weapon_thinkf(actor, actor.rifle_bullethail_frame, actor.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue);
                LOG_INFO("thinkf set\n");
        }
        else
        {
 -              ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
 -              LOG_INFO("out of ammo... ", ftos(self.weaponentity.state), "\n");
 +              ATTACK_FINISHED(actor) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
 +              LOG_INFO("out of ammo... ", ftos(actor.weaponentity.state), "\n");
        }
  }
  
@@@ -126,18 -124,23 +126,18 @@@ void W_Rifle_BulletHail(float mode, voi
                self.rifle_bullethail_frame = fr;
                self.rifle_bullethail_animtime = animtime;
                self.rifle_bullethail_refire = refire;
 -              weapon_thinkf(fr, animtime, W_Rifle_BulletHail_Continue);
 +              weapon_thinkf(self, fr, animtime, W_Rifle_BulletHail_Continue);
        }
        else
        {
                // just one shot
 -              weapon_thinkf(fr, animtime, w_ready);
 +              weapon_thinkf(self, fr, animtime, w_ready);
        }
  }
  
  .float bot_secondary_riflemooth;
 -bool W_Rifle(int req)
 -{SELFPARAM();
 -      float ammo_amount;
  
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Rifle, wr_aim, void(entity thiswep))
                {
                        self.BUTTON_ATCK=false;
                        self.BUTTON_ATCK2=false;
                                        if(random() < 0.03) self.bot_secondary_riflemooth = 0;
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Rifle, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(autocvar_g_balance_rifle_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo))) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      else
 +                      if(autocvar_g_balance_rifle_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo))) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else
                        {
 -                              self.rifle_accumulator = bound(time - WEP_CVAR(rifle, bursttime), self.rifle_accumulator, time);
 -                              if(self.BUTTON_ATCK)
 -                              if(weapon_prepareattack_check(0, WEP_CVAR_PRI(rifle, refire)))
 -                              if(time >= self.rifle_accumulator + WEP_CVAR_PRI(rifle, burstcost))
 +                              actor.rifle_accumulator = bound(time - WEP_CVAR(rifle, bursttime), actor.rifle_accumulator, time);
 +                              if(fire1)
 +                              if(weapon_prepareattack_check(actor, false, WEP_CVAR_PRI(rifle, refire)))
 +                              if(time >= actor.rifle_accumulator + WEP_CVAR_PRI(rifle, burstcost))
                                {
 -                                      weapon_prepareattack_do(0, WEP_CVAR_PRI(rifle, refire));
 +                                      weapon_prepareattack_do(actor, false, WEP_CVAR_PRI(rifle, refire));
                                        W_Rifle_BulletHail(WEP_CVAR_PRI(rifle, bullethail), W_Rifle_Attack, WFRAME_FIRE1, WEP_CVAR_PRI(rifle, animtime), WEP_CVAR_PRI(rifle, refire));
 -                                      self.rifle_accumulator += WEP_CVAR_PRI(rifle, burstcost);
 +                                      actor.rifle_accumulator += WEP_CVAR_PRI(rifle, burstcost);
                                }
 -                              if(self.BUTTON_ATCK2)
 +                              if(fire2)
                                {
                                        if(WEP_CVAR(rifle, secondary))
                                        {
 -                                              if(WEP_CVAR_SEC(rifle, reload))
 -                                                      WEP_ACTION(self.weapon, WR_RELOAD);
 -                                              else
 +                                              if(WEP_CVAR_SEC(rifle, reload)) {
 +                                                      Weapon w = get_weaponinfo(actor.weapon);
 +                                                      w.wr_reload(w);
 +                                              } else
                                                {
 -                                                      if(weapon_prepareattack_check(1, WEP_CVAR_SEC(rifle, refire)))
 -                                                      if(time >= self.rifle_accumulator + WEP_CVAR_SEC(rifle, burstcost))
 +                                                      if(weapon_prepareattack_check(actor, true, WEP_CVAR_SEC(rifle, refire)))
 +                                                      if(time >= actor.rifle_accumulator + WEP_CVAR_SEC(rifle, burstcost))
                                                        {
 -                                                              weapon_prepareattack_do(1, WEP_CVAR_SEC(rifle, refire));
 +                                                              weapon_prepareattack_do(actor, true, WEP_CVAR_SEC(rifle, refire));
                                                                W_Rifle_BulletHail(WEP_CVAR_SEC(rifle, bullethail), W_Rifle_Attack2, WFRAME_FIRE2, WEP_CVAR_SEC(rifle, animtime), WEP_CVAR_PRI(rifle, refire));
 -                                                              self.rifle_accumulator += WEP_CVAR_SEC(rifle, burstcost);
 +                                                              actor.rifle_accumulator += WEP_CVAR_SEC(rifle, burstcost);
                                                        }
                                                }
                                        }
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Rifle, wr_init, void(entity thiswep))
                {
                        RIFLE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Rifle, wr_checkammo1, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(RIFLE) >= WEP_CVAR_PRI(rifle, ammo);
 +                      float ammo_amount = self.WEP_AMMO(RIFLE) >= WEP_CVAR_PRI(rifle, ammo);
                        ammo_amount += self.(weapon_load[WEP_RIFLE.m_id]) >= WEP_CVAR_PRI(rifle, ammo);
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Rifle, wr_checkammo2, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(RIFLE) >= WEP_CVAR_SEC(rifle, ammo);
 +                      float ammo_amount = self.WEP_AMMO(RIFLE) >= WEP_CVAR_SEC(rifle, ammo);
                        ammo_amount += self.(weapon_load[WEP_RIFLE.m_id]) >= WEP_CVAR_SEC(rifle, ammo);
                        return ammo_amount;
                }
 -              case WR_CONFIG:
 +              METHOD(Rifle, wr_config, void(entity thiswep))
                {
                        RIFLE_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RESETPLAYER:
 +              METHOD(Rifle, wr_resetplayer, void(entity thiswep))
                {
                        self.rifle_accumulator = time - WEP_CVAR(rifle, bursttime);
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Rifle, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo)), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo)), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Rifle, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_THINKING_WITH_PORTALS;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Rifle, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                        {
                                        return WEAPON_RIFLE_MURDER;
                        }
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_Rifle(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Rifle, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 2;
                                else if(w_random < 0.5)
                                        sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTN_NORM);
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Rifle, wr_init, void(entity thiswep))
                {
                        if(autocvar_cl_reticle && autocvar_cl_reticle_weapon)
                        {
                                precache_pic("gfx/reticle_nex");
                        }
 -                      return true;
                }
 -              case WR_ZOOMRETICLE:
 +              METHOD(Rifle, wr_zoomreticle, bool(entity thiswep))
                {
                        if(button_zoom || zoomscript_caught)
                        {
                                return false;
                        }
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index 0c75cceb3caa6bf479b7e29b3139bb59e3b39d38,4286ce433746a366bc78cebda4f4ffc43d7c2113..700e66be259c1a4b64059f7ec1003afd37ee25ff
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id    */ RPC,
 -/* function    */ W_RocketPropelledChainsaw,
 -/* ammotype    */ ammo_rockets,
 -/* impulse     */ 7,
 -/* flags       */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON,
 -/* rating      */ BOT_PICKUP_RATING_HIGH,
 -/* color     */ '0.5 0.5 0',
 -/* modelname */ "ok_rl",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairrocketlauncher 0.7",
 -/* wepimg    */ "weaponrpc",
 -/* refname   */ "rpc",
 -/* wepname     */ _("Rocket Propelled Chainsaw")
 -);
 +CLASS(RocketPropelledChainsaw, Weapon)
 +/* ammotype  */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets)
 +/* impulse   */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7)
 +/* flags     */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON);
 +/* rating    */ ATTRIB(RocketPropelledChainsaw, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 +/* color     */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0');
 +/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(RocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher");
 +/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair_size, float, 0.6);
 +/* wepimg    */ ATTRIB(RocketPropelledChainsaw, model2, string, "weaponrpc");
 +/* refname   */ ATTRIB(RocketPropelledChainsaw, netname, string, "rpc");
 +/* wepname   */ ATTRIB(RocketPropelledChainsaw, message, string, _("Rocket Propelled Chainsaw"));
 +ENDCLASS(RocketPropelledChainsaw)
 +REGISTER_WEAPON(RPC, NEW(RocketPropelledChainsaw));
  
  #define RPC_SETTINGS(w_cvar,w_prop) RPC_SETTINGS_LIST(w_cvar, w_prop, RPC, rpc)
  #define RPC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -47,7 -45,7 +47,7 @@@ RPC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_rpc() { weapon_defaultspawnfunc(WEP_RPC.m_id); }
spawnfunc(weapon_rpc) { weapon_defaultspawnfunc(WEP_RPC.m_id); }
  
  void W_RocketPropelledChainsaw_Explode()
  {SELFPARAM();
@@@ -104,12 -102,12 +104,12 @@@ void W_RocketPropelledChainsaw_Think(
        self.nextthink = time;
  }
  
 -void W_RocketPropelledChainsaw_Attack (void)
 +void W_RocketPropelledChainsaw_Attack (Weapon thiswep)
  {SELFPARAM();
        entity missile = spawn(); //WarpZone_RefSys_SpawnSameRefSys(self);
        entity flash = spawn ();
  
 -      W_DecreaseAmmo(WEP_CVAR(rpc, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR(rpc, ammo));
        W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', false, 5, SND(ROCKET_FIRE), CH_WEAPON_A, WEP_CVAR(rpc, damage));
        Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
        PROJECTILE_MAKETRIGGER(missile);
        setmodel(flash, MDL_RPC_MUZZLEFLASH); // precision set below
        SUB_SetFade (flash, time, 0.1);
        flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
 -      W_AttachToShotorg(flash, '5 0 0');
 +      W_AttachToShotorg(self, flash, '5 0 0');
        missile.pos1 = missile.velocity;
  
        MUTATOR_CALLHOOK(EditProjectile, self, missile);
  }
  
 -bool W_RocketPropelledChainsaw(int req)
 -{SELFPARAM();
 -      float ammo_amount = false;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(RocketPropelledChainsaw, wr_aim, void(entity thiswep))
                {
                        self.BUTTON_ATCK = bot_aim(WEP_CVAR(rpc, speed), 0, WEP_CVAR(rpc, lifetime), false);
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(RocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(WEP_CVAR(rpc, reload_ammo) && self.clip_load < WEP_CVAR(rpc, ammo))
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      else
 +                      if(WEP_CVAR(rpc, reload_ammo) && actor.clip_load < WEP_CVAR(rpc, ammo)) {
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else
                        {
 -                              if (self.BUTTON_ATCK)
 +                              if (fire1)
                                {
 -                                      if(weapon_prepareattack(0, WEP_CVAR(rpc, refire)))
 +                                      if(weapon_prepareattack(actor, false, WEP_CVAR(rpc, refire)))
                                        {
 -                                              W_RocketPropelledChainsaw_Attack();
 -                                              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready);
 +                                              W_RocketPropelledChainsaw_Attack(thiswep);
 +                                              weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready);
                                        }
                                }
  
 -                              if (self.BUTTON_ATCK2)
 +                              if (fire2)
                                {
                                        // to-do
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(RocketPropelledChainsaw, wr_init, void(entity thiswep))
                {
                        RPC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(RocketPropelledChainsaw, wr_checkammo1, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(RPC) >= WEP_CVAR(rpc, ammo);
 +                      float ammo_amount = self.WEP_AMMO(RPC) >= WEP_CVAR(rpc, ammo);
                        ammo_amount += self.(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR(rpc, ammo);
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(RocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep))
                {
                        return false;
                }
 -              case WR_CONFIG:
 +              METHOD(RocketPropelledChainsaw, wr_config, void(entity thiswep))
                {
                        RPC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(RocketPropelledChainsaw, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(WEP_CVAR(rpc, ammo), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, WEP_CVAR(rpc, ammo), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(RocketPropelledChainsaw, wr_suicidemessage, int(entity thiswep))
                {
                        if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
                                return WEAPON_RPC_SUICIDE_SPLASH;
                        else
                                return WEAPON_RPC_SUICIDE_DIRECT;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(RocketPropelledChainsaw, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_BLASTER_MURDER;
                        else
                                return WEAPON_RPC_MURDER_DIRECT;
                }
 -      }
  
 -      return false;
 -}
  #endif
  
  #ifdef CSQC
 -bool W_RocketPropelledChainsaw(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(RocketPropelledChainsaw, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 12;
                        pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), org2, '0 0 0', 1);
                        if(!w_issilent)
                                sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
 -
 -                      return true;
                }
 -              case WR_INIT:
 -              {
 -                      return true;
 -              }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
  
 -      return false;
 -}
  #endif
  #endif
index 64cb9ee17f9cd56ef7dd5286598c96736940f95c,1686ff5290fec13b21f13b7c773a598ca75a15d5..365061ec26c3b320de02bb2b285189e070f7afc1
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ SEEKER,
 -/* function  */ W_Seeker,
 -/* ammotype  */ ammo_rockets,
 -/* impulse   */ 8,
 -/* flags     */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
 -/* rating    */ BOT_PICKUP_RATING_MID,
 -/* color     */ '0.5 1 0',
 -/* modelname */ "seeker",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairseeker 0.8",
 -/* wepimg    */ "weaponseeker",
 -/* refname   */ "seeker",
 -/* wepname   */ _("T.A.G. Seeker")
 -);
 +CLASS(Seeker, Weapon)
 +/* ammotype  */ ATTRIB(Seeker, ammo_field, .int, ammo_rockets)
 +/* impulse   */ ATTRIB(Seeker, impulse, int, 8)
 +/* flags     */ ATTRIB(Seeker, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(Seeker, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 +/* color     */ ATTRIB(Seeker, wpcolor, vector, '0.5 1 0');
 +/* modelname */ ATTRIB(Seeker, mdl, string, "seeker");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Seeker, m_model, Model, MDL_SEEKER_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Seeker, w_crosshair, string, "gfx/crosshairseeker");
 +/* crosshair */ ATTRIB(Seeker, w_crosshair_size, float, 0.8);
 +/* wepimg    */ ATTRIB(Seeker, model2, string, "weaponseeker");
 +/* refname   */ ATTRIB(Seeker, netname, string, "seeker");
 +/* wepname   */ ATTRIB(Seeker, message, string, _("T.A.G. Seeker"));
 +ENDCLASS(Seeker)
 +REGISTER_WEAPON(SEEKER, NEW(Seeker));
  
  #define SEEKER_SETTINGS(w_cvar,w_prop) SEEKER_SETTINGS_LIST(w_cvar, w_prop, SEEKER, seeker)
  #define SEEKER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -86,7 -84,7 +86,7 @@@ SEEKER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_P
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_seeker(void) { weapon_defaultspawnfunc(WEP_SEEKER.m_id); }
spawnfunc(weapon_seeker) { weapon_defaultspawnfunc(WEP_SEEKER.m_id); }
  
  // ============================
  // Begin: Missile functions, these are general functions to be manipulated by other code
@@@ -245,11 -243,11 +245,11 @@@ void W_Seeker_Missile_Animate(void
  }
  */
  
 -void W_Seeker_Fire_Missile(vector f_diff, entity m_target)
 +void W_Seeker_Fire_Missile(Weapon thiswep, vector f_diff, entity m_target)
  {SELFPARAM();
        entity missile;
  
 -      W_DecreaseAmmo(WEP_CVAR(seeker, missile_ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR(seeker, missile_ammo));
  
        makevectors(self.v_angle);
        W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', false, 2, SND(SEEKER_FIRE), CH_WEAPON_A, 0);
@@@ -318,13 -316,13 +318,13 @@@ void W_Seeker_Flac_Touch(void
        W_Seeker_Flac_Explode();
  }
  
 -void W_Seeker_Fire_Flac(void)
 +void W_Seeker_Fire_Flac(Weapon thiswep)
  {SELFPARAM();
        entity missile;
        vector f_diff;
        float c;
  
 -      W_DecreaseAmmo(WEP_CVAR(seeker, flac_ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR(seeker, flac_ammo));
  
        c = self.bulletcounter % 4;
        switch(c)
@@@ -410,7 -408,7 +410,7 @@@ void W_Seeker_Attack(void
        if((!closest_target) || ((trace_fraction < 1) && (trace_ent != closest_target)))
                closest_target = world;
  
 -      W_Seeker_Fire_Missile('0 0 0', closest_target);
 +      W_Seeker_Fire_Missile(WEP_SEEKER, '0 0 0', closest_target);
  }
  
  void W_Seeker_Vollycontroller_Think(void) // TODO: Merge this with W_Seeker_Attack
        switch(c)
        {
                case 0:
 -                      W_Seeker_Fire_Missile('-1.25 -3.75 0', self.enemy);
 +                      W_Seeker_Fire_Missile(WEP_SEEKER, '-1.25 -3.75 0', self.enemy);
                        break;
                case 1:
 -                      W_Seeker_Fire_Missile('+1.25 -3.75 0', self.enemy);
 +                      W_Seeker_Fire_Missile(WEP_SEEKER, '+1.25 -3.75 0', self.enemy);
                        break;
                case 2:
 -                      W_Seeker_Fire_Missile('-1.25 +3.75 0', self.enemy);
 +                      W_Seeker_Fire_Missile(WEP_SEEKER, '-1.25 +3.75 0', self.enemy);
                        break;
                case 3:
                default:
 -                      W_Seeker_Fire_Missile('+1.25 +3.75 0', self.enemy);
 +                      W_Seeker_Fire_Missile(WEP_SEEKER, '+1.25 +3.75 0', self.enemy);
                        break;
        }
  
@@@ -556,10 -554,10 +556,10 @@@ void W_Seeker_Tag_Touch(void
        return;
  }
  
 -void W_Seeker_Fire_Tag(void)
 +void W_Seeker_Fire_Tag(Weapon thiswep)
  {SELFPARAM();
        entity missile;
 -      W_DecreaseAmmo(WEP_CVAR(seeker, tag_ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR(seeker, tag_ammo));
  
        W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', false, 2, SND(TAG_FIRE), CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count));
  
  // Begin: Genereal weapon functions
  // ============================
  
 -bool W_Seeker(int req)
 -{SELFPARAM();
 -      float ammo_amount;
 -
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Seeker, wr_aim, void(entity thiswep))
                {
                        if(WEP_CVAR(seeker, type) == 1)
                                if(W_Seeker_Tagged_Info(self, self.enemy) != world)
                                        self.BUTTON_ATCK2 = bot_aim(WEP_CVAR(seeker, tag_speed), 0, WEP_CVAR(seeker, tag_lifetime), false);
                        else
                                self.BUTTON_ATCK = bot_aim(WEP_CVAR(seeker, tag_speed), 0, WEP_CVAR(seeker, tag_lifetime), false);
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Seeker, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(autocvar_g_balance_seeker_reload_ammo && self.clip_load < min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo))) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -
 -                      else if(self.BUTTON_ATCK)
 +                      if(autocvar_g_balance_seeker_reload_ammo && actor.clip_load < min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo))) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else if(fire1)
                        {
                                if(WEP_CVAR(seeker, type) == 1)
                                {
 -                                      if(weapon_prepareattack(0, WEP_CVAR(seeker, missile_refire)))
 +                                      if(weapon_prepareattack(actor, false, WEP_CVAR(seeker, missile_refire)))
                                        {
                                                W_Seeker_Attack();
 -                                              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready);
 +                                              weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready);
                                        }
                                }
                                else
                                {
 -                                      if(weapon_prepareattack(0, WEP_CVAR(seeker, tag_refire)))
 +                                      if(weapon_prepareattack(actor, false, WEP_CVAR(seeker, tag_refire)))
                                        {
 -                                              W_Seeker_Fire_Tag();
 -                                              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
 +                                              W_Seeker_Fire_Tag(thiswep);
 +                                              weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
                                        }
                                }
                        }
  
 -                      else if(self.BUTTON_ATCK2)
 +                      else if(fire2)
                        {
                                if(WEP_CVAR(seeker, type) == 1)
                                {
 -                                      if(weapon_prepareattack(0, WEP_CVAR(seeker, tag_refire)))
 +                                      if(weapon_prepareattack(actor, false, WEP_CVAR(seeker, tag_refire)))
                                        {
 -                                              W_Seeker_Fire_Tag();
 -                                              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
 +                                              W_Seeker_Fire_Tag(thiswep);
 +                                              weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
                                        }
                                }
                                else
                                {
 -                                      if(weapon_prepareattack(0, WEP_CVAR(seeker, flac_refire)))
 +                                      if(weapon_prepareattack(actor, false, WEP_CVAR(seeker, flac_refire)))
                                        {
 -                                              W_Seeker_Fire_Flac();
 -                                              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready);
 +                                              W_Seeker_Fire_Flac(thiswep);
 +                                              weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready);
                                        }
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Seeker, wr_init, void(entity thiswep))
                {
                        SEEKER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Seeker, wr_checkammo1, bool(entity thiswep))
                {
 +                      float ammo_amount;
                        if(WEP_CVAR(seeker, type) == 1)
                        {
                                ammo_amount = self.WEP_AMMO(SEEKER) >= WEP_CVAR(seeker, missile_ammo);
                        }
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Seeker, wr_checkammo2, bool(entity thiswep))
                {
 +                      float ammo_amount;
                        if(WEP_CVAR(seeker, type) == 1)
                        {
                                ammo_amount = self.WEP_AMMO(SEEKER) >= WEP_CVAR(seeker, tag_ammo);
                        }
                        return ammo_amount;
                }
 -              case WR_CONFIG:
 +              METHOD(Seeker, wr_config, void(entity thiswep))
                {
                        SEEKER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Seeker, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Seeker, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_SEEKER_SUICIDE;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Seeker, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_SEEKER_MURDER_TAG;
                        else
                                return WEAPON_SEEKER_MURDER_SPRAY;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -bool W_Seeker(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Seeker, wr_impacteffect, void(entity thiswep))
                {
                        vector org2;
                        org2 = w_org + w_backoff * 6;
                                                sound(self, CH_SHOTS, SND_SEEKEREXP3, 1, ATTEN_NORM);
                                }
                        }
 -                      return true;
                }
 -              case WR_INIT:
 -              {
 -                      return true;
 -              }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index a9c758212c577c0acb61d54c1567bd2f4a6c7353,a067110b7285af22ea359248ab43981093786834..a99c5192fdf35b74b225053e148e431cb46fd2ea
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ SHOCKWAVE,
 -/* function  */ W_Shockwave,
 -/* ammotype  */ ammo_none,
 -/* impulse   */ 2,
 -/* flags     */ WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_FLAG_MUTATORBLOCKED,
 -/* rating    */ BOT_PICKUP_RATING_LOW,
 -/* color     */ '0.5 0.25 0',
 -/* modelname */ "shotgun",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairshotgun 0.7",
 -/* wepimg    */ "weaponshotgun",
 -/* refname   */ "shockwave",
 -/* wepname   */ _("Shockwave")
 -);
 +CLASS(Shockwave, Weapon)
 +/* ammotype  */ //ATTRIB(Shockwave, ammo_field, .int, ammo_none)
 +/* impulse   */ ATTRIB(Shockwave, impulse, int, 2)
 +/* flags     */ ATTRIB(Shockwave, spawnflags, int, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_FLAG_MUTATORBLOCKED);
 +/* rating    */ ATTRIB(Shockwave, bot_pickupbasevalue, float, BOT_PICKUP_RATING_LOW);
 +/* color     */ ATTRIB(Shockwave, wpcolor, vector, '0.5 0.25 0');
 +/* modelname */ ATTRIB(Shockwave, mdl, string, "shotgun");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Shockwave, m_model, Model, MDL_SHOCKWAVE_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Shockwave, w_crosshair, string, "gfx/crosshairshotgun");
 +/* crosshair */ ATTRIB(Shockwave, w_crosshair_size, float, 0.7);
 +/* wepimg    */ ATTRIB(Shockwave, model2, string, "weaponshotgun");
 +/* refname   */ ATTRIB(Shockwave, netname, string, "shockwave");
 +/* wepname   */ ATTRIB(Shockwave, message, string, _("Shockwave"));
 +ENDCLASS(Shockwave)
 +REGISTER_WEAPON(SHOCKWAVE, NEW(Shockwave));
  
  #define SHOCKWAVE_SETTINGS(w_cvar,w_prop) SHOCKWAVE_SETTINGS_LIST(w_cvar, w_prop, SHOCKWAVE, shockwave)
  #define SHOCKWAVE_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -84,8 -82,8 +84,8 @@@ void Net_ReadShockwaveParticle(void)
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_shockwave(void)
- {SELFPARAM();
spawnfunc(weapon_shockwave)
+ {
        //if(autocvar_sv_q3acompat_machineshockwaveswap) // WEAPONTODO
        if(autocvar_sv_q3acompat_machineshotgunswap)
        if(self.classname != "droppedweapon")
@@@ -229,17 -227,17 +229,17 @@@ void W_Shockwave_Melee_Think(void
        }
  }
  
 -void W_Shockwave_Melee(void)
 -{SELFPARAM();
 -      sound(self, CH_WEAPON_A, SND_SHOTGUN_MELEE, VOL_BASE, ATTN_NORM);
 -      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(shockwave, melee_animtime), w_ready);
 +void W_Shockwave_Melee(Weapon thiswep, entity actor, bool fire1, bool fire2)
 +{
 +      sound(actor, CH_WEAPON_A, SND_SHOTGUN_MELEE, VOL_BASE, ATTN_NORM);
 +      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(shockwave, melee_animtime), w_ready);
  
        entity meleetemp;
        meleetemp = spawn();
 -      meleetemp.owner = meleetemp.realowner = self;
 +      meleetemp.owner = meleetemp.realowner = actor;
        meleetemp.think = W_Shockwave_Melee_Think;
        meleetemp.nextthink = time + WEP_CVAR(shockwave, melee_delay) * W_WeaponRateFactor();
 -      W_SetupShot_Range(self, true, 0, "", 0, WEP_CVAR(shockwave, melee_damage), WEP_CVAR(shockwave, melee_range));
 +      W_SetupShot_Range(actor, true, 0, "", 0, WEP_CVAR(shockwave, melee_damage), WEP_CVAR(shockwave, melee_range));
  }
  
  // SHOCKWAVE ATTACK MODE
@@@ -668,67 -666,76 +668,67 @@@ void W_Shockwave_Attack(void
        }
  }
  
 -bool W_Shockwave(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Shockwave, wr_aim, void(entity thiswep))
                {
                        if(vlen(self.origin - self.enemy.origin) <= WEP_CVAR(shockwave, melee_range))
                                { self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false); }
                        else
                                { self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false); }
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Shockwave, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(self.BUTTON_ATCK)
 +                      if(fire1)
                        {
 -                              if(time >= self.shockwave_blasttime) // handle refire separately so the secondary can be fired straight after a primary
 +                              if(time >= actor.shockwave_blasttime) // handle refire separately so the secondary can be fired straight after a primary
                                {
 -                                      if(weapon_prepareattack(0, WEP_CVAR(shockwave, blast_animtime)))
 +                                      if(weapon_prepareattack(actor, false, WEP_CVAR(shockwave, blast_animtime)))
                                        {
                                                W_Shockwave_Attack();
 -                                              self.shockwave_blasttime = time + WEP_CVAR(shockwave, blast_refire) * W_WeaponRateFactor();
 -                                              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(shockwave, blast_animtime), w_ready);
 +                                              actor.shockwave_blasttime = time + WEP_CVAR(shockwave, blast_refire) * W_WeaponRateFactor();
 +                                              weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(shockwave, blast_animtime), w_ready);
                                        }
                                }
                        }
 -                      else if(self.BUTTON_ATCK2)
 +                      else if(fire2)
                        {
 -                              //if(self.clip_load >= 0) // we are not currently reloading
 -                              if(!self.crouch) // no crouchmelee please
 -                              if(weapon_prepareattack(1, WEP_CVAR(shockwave, melee_refire)))
 +                              //if(actor.clip_load >= 0) // we are not currently reloading
 +                              if(!actor.crouch) // no crouchmelee please
 +                              if(weapon_prepareattack(actor, true, WEP_CVAR(shockwave, melee_refire)))
                                {
                                        // attempt forcing playback of the anim by switching to another anim (that we never play) here...
 -                                      weapon_thinkf(WFRAME_FIRE1, 0, W_Shockwave_Melee);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, 0, W_Shockwave_Melee);
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Shockwave, wr_init, void(entity thiswep))
                {
                        SHOCKWAVE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 -              case WR_CHECKAMMO2:
 +              METHOD(Shockwave, wr_checkammo1, bool(entity thiswep))
 +              {
 +                      return true; // infinite ammo
 +              }
 +              METHOD(Shockwave, wr_checkammo2, bool(entity thiswep))
                {
                        // shockwave has infinite ammo
                        return true;
                }
 -              case WR_CONFIG:
 +              METHOD(Shockwave, wr_config, void(entity thiswep))
                {
                        SHOCKWAVE_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Shockwave, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_THINKING_WITH_PORTALS;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Shockwave, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_SHOCKWAVE_MURDER_SLAP;
                        else
                                return WEAPON_SHOCKWAVE_MURDER;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
  // WEAPONTODO: add client side settings for these
@@@ -851,13 -858,29 +851,13 @@@ void Net_ReadShockwaveParticle(void
        shockwave.sw_time = time;
  }
  
 -bool W_Shockwave(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +              METHOD(Shockwave, wr_impacteffect, void(entity thiswep))
                {
                        // handled by Net_ReadShockwaveParticle
                        //vector org2;
                        //org2 = w_org + w_backoff * 2;
                        //pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1);
 -                      return false;
 -              }
 -              case WR_INIT:
 -              {
 -                      return false;
                }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index 09bdf2107cd3584eaeecf8d95e2e876494f647e4,05f4cf6ab0bec6d3cdf88b381714882cfbc382b8..813fce32807b0e123838c2bf2ced2c68b2a3452f
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ SHOTGUN,
 -/* function  */ W_Shotgun,
 -/* ammotype  */ ammo_shells,
 -/* impulse   */ 2,
 -/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
 -/* rating    */ BOT_PICKUP_RATING_LOW,
 -/* color     */ '0.5 0.25 0',
 -/* modelname */ "shotgun",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairshotgun 0.65",
 -/* wepimg    */ "weaponshotgun",
 -/* refname   */ "shotgun",
 -/* wepname   */ _("Shotgun")
 -);
 +CLASS(Shotgun, Weapon)
 +/* ammotype  */ ATTRIB(Shotgun, ammo_field, .int, ammo_shells)
 +/* impulse   */ ATTRIB(Shotgun, impulse, int, 2)
 +/* flags     */ ATTRIB(Shotgun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
 +/* rating    */ ATTRIB(Shotgun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_LOW);
 +/* color     */ ATTRIB(Shotgun, wpcolor, vector, '0.5 0.25 0');
 +/* modelname */ ATTRIB(Shotgun, mdl, string, "shotgun");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Shotgun, m_model, Model, MDL_SHOTGUN_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Shotgun, w_crosshair, string, "gfx/crosshairshotgun");
 +/* crosshair */ ATTRIB(Shotgun, w_crosshair_size, float, 0.65);
 +/* wepimg    */ ATTRIB(Shotgun, model2, string, "weaponshotgun");
 +/* refname   */ ATTRIB(Shotgun, netname, string, "shotgun");
 +/* wepname   */ ATTRIB(Shotgun, message, string, _("Shotgun"));
 +ENDCLASS(Shotgun)
 +REGISTER_WEAPON(SHOTGUN, NEW(Shotgun));
  
  #define SHOTGUN_SETTINGS(w_cvar,w_prop) SHOTGUN_SETTINGS_LIST(w_cvar, w_prop, SHOTGUN, shotgun)
  #define SHOTGUN_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -54,14 -52,14 +54,14 @@@ SHOTGUN_SETTINGS(WEP_ADD_CVAR, WEP_ADD_
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_shotgun(void) { weapon_defaultspawnfunc(WEP_SHOTGUN.m_id); }
spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(WEP_SHOTGUN.m_id); }
  
 -void W_Shotgun_Attack(float isprimary)
 +void W_Shotgun_Attack(Weapon thiswep, float isprimary)
  {SELFPARAM();
        float   sc;
        entity flash;
  
 -      W_DecreaseAmmo(WEP_CVAR_PRI(shotgun, ammo));
 +      W_DecreaseAmmo(thiswep, self, WEP_CVAR_PRI(shotgun, ammo));
  
        W_SetupShot(self, true, 5, SND(SHOTGUN_FIRE), ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets));
        for(sc = 0;sc < WEP_CVAR_PRI(shotgun, bullets);sc = sc + 1)
@@@ -80,7 -78,7 +80,7 @@@
        flash.think = SUB_Remove;
        flash.nextthink = time + 0.06;
        flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
 -      W_AttachToShotorg(flash, '5 0 0');
 +      W_AttachToShotorg(self, flash, '5 0 0');
  }
  
  .float swing_prev;
@@@ -181,121 -179,128 +181,121 @@@ void W_Shotgun_Melee_Think(void
        }
  }
  
 -void W_Shotgun_Attack2(void)
 -{SELFPARAM();
 -      sound(self, CH_WEAPON_A, SND_SHOTGUN_MELEE, VOL_BASE, ATTEN_NORM);
 -      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(shotgun, animtime), w_ready);
 +void W_Shotgun_Attack2(Weapon thiswep, entity actor, bool fire1, bool fire2)
 +{
 +      sound(actor, CH_WEAPON_A, SND_SHOTGUN_MELEE, VOL_BASE, ATTEN_NORM);
 +      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(shotgun, animtime), w_ready);
  
        entity meleetemp;
        meleetemp = spawn();
 -      meleetemp.realowner = self;
 +      meleetemp.realowner = actor;
        meleetemp.think = W_Shotgun_Melee_Think;
        meleetemp.nextthink = time + WEP_CVAR_SEC(shotgun, melee_delay) * W_WeaponRateFactor();
 -      W_SetupShot_Range(self, true, 0, "", 0, WEP_CVAR_SEC(shotgun, damage), WEP_CVAR_SEC(shotgun, melee_range));
 +      W_SetupShot_Range(actor, true, 0, "", 0, WEP_CVAR_SEC(shotgun, damage), WEP_CVAR_SEC(shotgun, melee_range));
  }
  
  // alternate secondary weapon frames
 -void W_Shotgun_Attack3_Frame2()
 -{SELFPARAM();
 -      if (!WEP_ACTION(self.weapon, WR_CHECKAMMO2))
 -      if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +void W_Shotgun_Attack3_Frame2(Weapon thiswep, entity actor, bool fire1, bool fire2)
 +{
 +      Weapon w = get_weaponinfo(actor.weapon);
 +      if (!w.wr_checkammo2(w))
 +      if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
        {
 -              W_SwitchWeapon_Force(self, w_getbestweapon(self));
 -              w_ready();
 +              W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
 +              w_ready(thiswep, actor, fire1, fire2);
                return;
        }
  
 -      sound(self, CH_WEAPON_SINGLE, SND_Null, VOL_BASE, ATTN_NORM); // kill previous sound
 -      W_Shotgun_Attack(true); // actually is secondary, but we trick the last shot into playing full reload sound
 -      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready);
 +      sound(actor, CH_WEAPON_SINGLE, SND_Null, VOL_BASE, ATTN_NORM); // kill previous sound
 +      W_Shotgun_Attack(WEP_SHOTGUN, true); // actually is secondary, but we trick the last shot into playing full reload sound
 +      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready);
  }
 -void W_Shotgun_Attack3_Frame1()
 -{SELFPARAM();
 -      if (!WEP_ACTION(self.weapon, WR_CHECKAMMO2))
 -      if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, bool fire1, bool fire2)
 +{
 +      Weapon w = get_weaponinfo(actor.weapon);
 +      if (!w.wr_checkammo2(w))
 +      if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
        {
 -              W_SwitchWeapon_Force(self, w_getbestweapon(self));
 -              w_ready();
 +              W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
 +              w_ready(thiswep, actor, fire1, fire2);
                return;
        }
  
 -      W_Shotgun_Attack(false);
 -      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2);
 +      W_Shotgun_Attack(WEP_SHOTGUN, false);
 +      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2);
  }
  
  .float shotgun_primarytime;
  
 -float W_Shotgun(float req)
 -{SELFPARAM();
 -      float ammo_amount;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Shotgun, wr_aim, void(entity thiswep))
                {
                        if(vlen(self.origin-self.enemy.origin) <= WEP_CVAR_SEC(shotgun, melee_range))
                                self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false);
                        else
                                self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false);
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(WEP_CVAR(shotgun, reload_ammo) && self.clip_load < WEP_CVAR_PRI(shotgun, ammo)) // forced reload
 +                      if(WEP_CVAR(shotgun, reload_ammo) && actor.clip_load < WEP_CVAR_PRI(shotgun, ammo)) // forced reload
                        {
                                // don't force reload an empty shotgun if its melee attack is active
 -                              if(WEP_CVAR(shotgun, secondary) < 2)
 -                                      WEP_ACTION(self.weapon, WR_RELOAD);
 +                              if(WEP_CVAR(shotgun, secondary) < 2) {
 +                                      Weapon w = get_weaponinfo(actor.weapon);
 +                                      w.wr_reload(w);
 +                              }
                        }
                        else
                        {
 -                              if(self.BUTTON_ATCK)
 +                              if(fire1)
                                {
 -                                      if(time >= self.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary
 +                                      if(time >= actor.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary
                                        {
 -                                              if(weapon_prepareattack(0, WEP_CVAR_PRI(shotgun, animtime)))
 +                                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(shotgun, animtime)))
                                                {
 -                                                      W_Shotgun_Attack(true);
 -                                                      self.shotgun_primarytime = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor();
 -                                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready);
 +                                                      W_Shotgun_Attack(thiswep, true);
 +                                                      actor.shotgun_primarytime = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor();
 +                                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready);
                                                }
                                        }
                                }
 -                              else if(self.BUTTON_ATCK2 && WEP_CVAR(shotgun, secondary) == 2)
 +                              else if(fire2 && WEP_CVAR(shotgun, secondary) == 2)
                                {
 -                                      if(time >= self.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary
 +                                      if(time >= actor.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary
                                        {
 -                                              if(weapon_prepareattack(0, WEP_CVAR_SEC(shotgun, alt_animtime)))
 +                                              if(weapon_prepareattack(actor, false, WEP_CVAR_SEC(shotgun, alt_animtime)))
                                                {
 -                                                      W_Shotgun_Attack(false);
 -                                                      self.shotgun_primarytime = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor();
 -                                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1);
 +                                                      W_Shotgun_Attack(thiswep, false);
 +                                                      actor.shotgun_primarytime = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor();
 +                                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1);
                                                }
                                        }
                                }
                        }
 -                      if(self.clip_load >= 0) // we are not currently reloading
 -                      if(!self.crouch) // no crouchmelee please
 +                      if(actor.clip_load >= 0) // we are not currently reloading
 +                      if(!actor.crouch) // no crouchmelee please
                        if(WEP_CVAR(shotgun, secondary) == 1)
 -                      if((self.BUTTON_ATCK && self.WEP_AMMO(SHOTGUN) <= 0 && !(self.items & IT_UNLIMITED_WEAPON_AMMO)) || self.BUTTON_ATCK2)
 -                      if(weapon_prepareattack(1, WEP_CVAR_SEC(shotgun, refire)))
 +                      if((fire1 && actor.WEP_AMMO(SHOTGUN) <= 0 && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || fire2)
 +                      if(weapon_prepareattack(actor, true, WEP_CVAR_SEC(shotgun, refire)))
                        {
                                // attempt forcing playback of the anim by switching to another anim (that we never play) here...
 -                              weapon_thinkf(WFRAME_FIRE1, 0, W_Shotgun_Attack2);
 +                              weapon_thinkf(actor, WFRAME_FIRE1, 0, W_Shotgun_Attack2);
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Shotgun, wr_init, void(entity thiswep))
                {
                        SHOTGUN_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_SETUP:
 +              METHOD(Shotgun, wr_setup, void(entity thiswep))
                {
                        self.ammo_field = ammo_none;
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Shotgun, wr_checkammo1, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(SHOTGUN) >= WEP_CVAR_PRI(shotgun, ammo);
 +                      float ammo_amount = self.WEP_AMMO(SHOTGUN) >= WEP_CVAR_PRI(shotgun, ammo);
                        ammo_amount += self.(weapon_load[WEP_SHOTGUN.m_id]) >= WEP_CVAR_PRI(shotgun, ammo);
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Shotgun, wr_checkammo2, bool(entity thiswep))
                {
                        if(IS_BOT_CLIENT(self))
                        if(vlen(self.origin-self.enemy.origin) > WEP_CVAR_SEC(shotgun, melee_range))
                                case 1: return true; // melee does not use ammo
                                case 2: // secondary triple shot
                                {
 -                                      ammo_amount = self.WEP_AMMO(SHOTGUN) >= WEP_CVAR_PRI(shotgun, ammo);
 +                                      float ammo_amount = self.WEP_AMMO(SHOTGUN) >= WEP_CVAR_PRI(shotgun, ammo);
                                        ammo_amount += self.(weapon_load[WEP_SHOTGUN.m_id]) >= WEP_CVAR_PRI(shotgun, ammo);
                                        return ammo_amount;
                                }
                                default: return false; // secondary unavailable
                        }
                }
 -              case WR_CONFIG:
 +              METHOD(Shotgun, wr_config, void(entity thiswep))
                {
                        SHOTGUN_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Shotgun, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(WEP_CVAR_PRI(shotgun, ammo), SND(RELOAD)); // WEAPONTODO
 -                      return true;
 +                      W_Reload(self, WEP_CVAR_PRI(shotgun, ammo), SND(RELOAD)); // WEAPONTODO
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Shotgun, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_THINKING_WITH_PORTALS;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Shotgun, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_SECONDARY)
                                return WEAPON_SHOTGUN_MURDER_SLAP;
                        else
                                return WEAPON_SHOTGUN_MURDER;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
  .float prevric;
 -float W_Shotgun(float req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Shotgun, wr_impacteffect, void(entity thiswep))
                {
 -                      vector org2;
 -                      org2 = w_org + w_backoff * 2;
 +                      vector org2 = w_org + w_backoff * 2;
                        pointparticles(particleeffectnum(EFFECT_SHOTGUN_IMPACT), org2, w_backoff * 1000, 1);
                        if(!w_issilent && time - self.prevric > 0.25)
                        {
                                        sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTEN_NORM);
                                self.prevric = time;
                        }
 -
 -                      return true;
 -              }
 -              case WR_INIT:
 -              {
 -                      return true;
                }
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index 20958c793afa4a4ececae7e8c3943de4b70d858f,6d1cf3a6f043562c888e3cb9da1213b5257ff787..e6bd46cffeffae3a5068e14e7f981847b3744c2f
@@@ -1,22 -1,20 +1,22 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ TUBA,
 -/* function  */ W_Tuba,
 -/* ammotype  */ ammo_none,
 -/* impulse   */ 1,
 -/* flags     */ WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH,
 -/* rating    */ BOT_PICKUP_RATING_MID,
 -/* color     */ '0 1 0',
 -/* modelname */ "tuba",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairtuba",
 -/* wepimg    */ "weapontuba",
 -/* refname   */ "tuba",
 +CLASS(Tuba, Weapon)
 +/* ammotype  */ //ATTRIB(Tuba, ammo_field, .int, ammo_none)
 +/* impulse   */ ATTRIB(Tuba, impulse, int, 1)
 +/* flags     */ ATTRIB(Tuba, spawnflags, int, WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH);
 +/* rating    */ ATTRIB(Tuba, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
 +/* color     */ ATTRIB(Tuba, wpcolor, vector, '0 1 0');
 +/* modelname */ ATTRIB(Tuba, mdl, string, "tuba");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Tuba, m_model, Model, MDL_TUBA_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Tuba, w_crosshair, string, "gfx/crosshairtuba");
 +/* crosshair */ //ATTRIB(Tuba, w_crosshair_size, float, 0.65);
 +/* wepimg    */ ATTRIB(Tuba, model2, string, "weapontuba");
 +/* refname   */ ATTRIB(Tuba, netname, string, "tuba");
  /* xgettext:no-c-format */
 -/* wepname   */ _("@!#%'n Tuba")
 -);
 +/* wepname   */ ATTRIB(Tuba, message, string, _("@!#%'n Tuba"));
 +ENDCLASS(Tuba)
 +REGISTER_WEAPON(TUBA, NEW(Tuba));
  
  #define TUBA_SETTINGS(w_cvar,w_prop) TUBA_SETTINGS_LIST(w_cvar, w_prop, TUBA, tuba)
  #define TUBA_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -56,7 -54,7 +56,7 @@@ float W_Tuba_MarkClientOnlyFieldsAsUsed
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_tuba(void) { weapon_defaultspawnfunc(WEP_TUBA.m_id); }
spawnfunc(weapon_tuba) { weapon_defaultspawnfunc(WEP_TUBA.m_id); }
  
  bool W_Tuba_HasPlayed(entity pl, string melody, int instrument, bool ignorepitch, float mintempo, float maxtempo)
  {
@@@ -367,7 -365,11 +367,7 @@@ void W_Tuba_NoteOn(float hittype
        }
  }
  
 -bool W_Tuba(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Tuba, wr_aim, void(entity thiswep))
                {
                        // bots cannot play the Tuba well yet
                        // I think they should start with the recorder first
                                else
                                        self.BUTTON_ATCK2 = 1;
                        }
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Tuba, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(self.BUTTON_ATCK)
 -                      if(weapon_prepareattack(0, WEP_CVAR(tuba, refire)))
 +                      if(fire1)
 +                      if(weapon_prepareattack(actor, false, WEP_CVAR(tuba, refire)))
                        {
                                W_Tuba_NoteOn(0);
 -                              //weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready);
 -                              weapon_thinkf(WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
 +                              //weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready);
 +                              weapon_thinkf(actor, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
                        }
 -                      if(self.BUTTON_ATCK2)
 -                      if(weapon_prepareattack(1, WEP_CVAR(tuba, refire)))
 +                      if(fire2)
 +                      if(weapon_prepareattack(actor, true, WEP_CVAR(tuba, refire)))
                        {
                                W_Tuba_NoteOn(HITTYPE_SECONDARY);
 -                              //weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready);
 -                              weapon_thinkf(WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
 +                              //weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready);
 +                              weapon_thinkf(actor, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
                        }
 -                      if(self.tuba_note)
 +                      if(actor.tuba_note)
                        {
 -                              if(!self.BUTTON_ATCK && !self.BUTTON_ATCK2)
 +                              if(!fire1 && !fire2)
                                {
 -                                      WITH(entity, self, self.tuba_note, W_Tuba_NoteOff());
 +                                      WITH(entity, self, actor.tuba_note, W_Tuba_NoteOff());
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Tuba, wr_init, void(entity thiswep))
                {
                        TUBA_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_SETUP:
 +              METHOD(Tuba, wr_setup, void(entity thiswep))
                {
                        self.ammo_field = ammo_none;
                        self.tuba_instrument = 0;
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Tuba, wr_reload, void(entity thiswep))
                {
                        // switch to alternate instruments :)
                        if(self.weaponentity.state == WS_READY)
                                W_SetupShot(self, false, 0, "", 0, 0);
                                Send_Effect(EFFECT_TELEPORT, w_shotorg, '0 0 0', 1);
                                self.weaponentity.state = WS_INUSE;
 -                              weapon_thinkf(WFRAME_RELOAD, 0.5, w_ready);
 +                              weapon_thinkf(self, WFRAME_RELOAD, 0.5, w_ready);
                        }
 -
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 -              case WR_CHECKAMMO2:
 +              METHOD(Tuba, wr_checkammo1, bool(entity thiswep))
 +              {
 +                      return true; // infinite ammo
 +              }
 +              METHOD(Tuba, wr_checkammo2, bool(entity thiswep))
                {
                        return true; // tuba has infinite ammo
                }
 -              case WR_CONFIG:
 +              METHOD(Tuba, wr_config, void(entity thiswep))
                {
                        TUBA_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Tuba, wr_suicidemessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_BOUNCE)
                                return WEAPON_KLEINBOTTLE_SUICIDE;
                        else
                                return WEAPON_TUBA_SUICIDE;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Tuba, wr_killmessage, int(entity thiswep))
                {
                        if(w_deathtype & HITTYPE_BOUNCE)
                                return WEAPON_KLEINBOTTLE_MURDER;
                        else
                                return WEAPON_TUBA_MURDER;
                }
 -      }
 -      return false;
 -}
 -#endif
 -#ifdef CSQC
 -bool W_Tuba(int req)
 -{SELFPARAM();
 -      // nothing to do here; particles of tuba are handled differently
 -      // WEAPONTODO
  
 -      switch(req)
 -      {
 -              case WR_ZOOMRETICLE:
 -              {
 -                      // no weapon specific image for this weapon
 -                      return false;
 -              }
 -      }
 -
 -      return false;
 -}
  #endif
  #endif
index 5061af05442632128591eb59ad4f3c03866a55d3,8cdeb61accad228365716bf78c1f03baafaa1111..012bd852d1dbe21407f6096a39cf9d9d91e66855
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ VAPORIZER,
 -/* function  */ W_Vaporizer,
 -/* ammotype  */ ammo_cells,
 -/* impulse   */ 7,
 -/* flags     */ WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN,
 -/* rating    */ BOT_PICKUP_RATING_HIGH,
 -/* color     */ '0.5 1 1',
 -/* modelname */ "minstanex",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairminstanex 0.6",
 -/* wepimg    */ "weaponminstanex",
 -/* refname   */ "vaporizer",
 -/* wepname   */ _("Vaporizer")
 -);
 +CLASS(Vaporizer, Weapon)
 +/* ammotype  */ ATTRIB(Vaporizer, ammo_field, .int, ammo_cells)
 +/* impulse   */ ATTRIB(Vaporizer, impulse, int, 7)
 +/* flags     */ ATTRIB(Vaporizer, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN);
 +/* rating    */ ATTRIB(Vaporizer, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 +/* color     */ ATTRIB(Vaporizer, wpcolor, vector, '0.5 1 1');
 +/* modelname */ ATTRIB(Vaporizer, mdl, string, "minstanex");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Vaporizer, m_model, Model, MDL_VAPORIZER_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Vaporizer, w_crosshair, string, "gfx/crosshairminstanex");
 +/* crosshair */ ATTRIB(Vaporizer, w_crosshair_size, float, 0.6);
 +/* wepimg    */ ATTRIB(Vaporizer, model2, string, "weaponminstanex");
 +/* refname   */ ATTRIB(Vaporizer, netname, string, "vaporizer");
 +/* wepname   */ ATTRIB(Vaporizer, message, string, _("Vaporizer"));
 +ENDCLASS(Vaporizer)
 +REGISTER_WEAPON(VAPORIZER, NEW(Vaporizer));
  
  #define VAPORIZER_SETTINGS(w_cvar,w_prop) VAPORIZER_SETTINGS_LIST(w_cvar, w_prop, VAPORIZER, vaporizer)
  #define VAPORIZER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -57,8 -55,8 +57,8 @@@ VAPORIZER_SETTINGS(WEP_ADD_CVAR, WEP_AD
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_vaporizer(void) { weapon_defaultspawnfunc(WEP_VAPORIZER.m_id); }
void spawnfunc_weapon_minstanex(void) { spawnfunc_weapon_vaporizer(); }
spawnfunc(weapon_vaporizer) { weapon_defaultspawnfunc(WEP_VAPORIZER.m_id); }
spawnfunc(weapon_minstanex) { spawnfunc_weapon_vaporizer(this); }
  
  void W_RocketMinsta_Explosion(vector loc)
  {SELFPARAM();
@@@ -71,7 -69,7 +71,7 @@@
        remove(dmgent);
  }
  
 -void W_Vaporizer_Attack(void)
 +void W_Vaporizer_Attack(Weapon thiswep)
  {SELFPARAM();
        bool flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
        float vaporizer_damage = ((WEP_CVAR_PRI(vaporizer, damage) > 0) ? WEP_CVAR_PRI(vaporizer, damage) : 10000);
        if(!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
                W_RocketMinsta_Explosion(trace_endpos);
  
 -      W_DecreaseAmmo(((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)));
 +      W_DecreaseAmmo(thiswep, self, ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)));
  }
  
  void W_RocketMinsta_Laser_Explode (void)
@@@ -237,69 -235,76 +237,69 @@@ void W_RocketMinsta_Attack3 (void
      }
  }
  
 -float W_Vaporizer(float req)
 -{SELFPARAM();
 -      float ammo_amount;
 -      float vaporizer_ammo;
 -      float rapid = autocvar_g_rm_laser_rapid;
 -
 -      // now multiple WR_s use this
 -      vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
 -
 -      switch(req)
 -      {
 -              case WR_AIM:
 +              METHOD(Vaporizer, wr_aim, void(entity thiswep))
                {
                        if(self.WEP_AMMO(VAPORIZER) > 0)
                                self.BUTTON_ATCK = bot_aim(1000000, 0, 1, false);
                        else
                                self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(vaporizer, speed), 0, WEP_CVAR_SEC(vaporizer, lifetime), false); // WEAPONTODO: replace with proper vaporizer cvars
 -
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 +                      float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
                        // if the laser uses load, we also consider its ammo for reloading
 -                      if(WEP_CVAR(vaporizer, reload_ammo) && WEP_CVAR_SEC(vaporizer, ammo) && self.clip_load < min(vaporizer_ammo, WEP_CVAR_SEC(vaporizer, ammo))) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      else if(WEP_CVAR(vaporizer, reload_ammo) && self.clip_load < vaporizer_ammo) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      if(self.BUTTON_ATCK && (self.ammo_cells || !autocvar_g_rm) && !forbidWeaponUse(self))
 +                      if(WEP_CVAR(vaporizer, reload_ammo) && WEP_CVAR_SEC(vaporizer, ammo) && actor.clip_load < min(vaporizer_ammo, WEP_CVAR_SEC(vaporizer, ammo))) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else if(WEP_CVAR(vaporizer, reload_ammo) && actor.clip_load < vaporizer_ammo) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      }
 +                      if(fire1 && (actor.ammo_cells || !autocvar_g_rm) && !forbidWeaponUse(actor))
                        {
 -                              if(weapon_prepareattack(0, WEP_CVAR_PRI(vaporizer, refire)))
 +                              if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(vaporizer, refire)))
                                {
 -                                      W_Vaporizer_Attack();
 -                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready);
 +                                      W_Vaporizer_Attack(thiswep);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready);
                                }
                        }
 -                      if(self.BUTTON_ATCK2 || (self.BUTTON_ATCK && !self.ammo_cells && autocvar_g_rm))
 +                      if(fire2 || (fire1 && !actor.ammo_cells && autocvar_g_rm))
                        {
                                if((autocvar_g_rm && autocvar_g_rm_laser) || autocvar_g_rm_laser == 2)
                                {
 -                                      if(self.jump_interval <= time && !self.held_down)
 +                                      bool rapid = autocvar_g_rm_laser_rapid;
 +                                      if(actor.jump_interval <= time && !actor.held_down)
                                        {
                                                if(rapid)
 -                                                      self.held_down = true;
 -                                              self.jump_interval = time + autocvar_g_rm_laser_refire;
 -                                              self.jump_interval2 = time + autocvar_g_rm_laser_rapid_delay;
 +                                                      actor.held_down = true;
 +                                              actor.jump_interval = time + autocvar_g_rm_laser_refire;
 +                                              actor.jump_interval2 = time + autocvar_g_rm_laser_rapid_delay;
                                                damage_goodhits = 0;
                                                W_RocketMinsta_Attack2();
                                        }
 -                                      else if(rapid && self.jump_interval2 <= time && self.held_down)
 +                                      else if(rapid && actor.jump_interval2 <= time && actor.held_down)
                                        {
 -                                              self.jump_interval2 = time + autocvar_g_rm_laser_rapid_refire;
 +                                              actor.jump_interval2 = time + autocvar_g_rm_laser_rapid_refire;
                                                damage_goodhits = 0;
                                                W_RocketMinsta_Attack3();
 -                                              //weapon_thinkf(WFRAME_FIRE2, autocvar_g_rm_laser_rapid_animtime, w_ready);
 +                                              //weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_rm_laser_rapid_animtime, w_ready);
                                        }
                                }
 -                              else if (self.jump_interval <= time)
 +                              else if (actor.jump_interval <= time)
                                {
                                        // handle refire manually, so that primary and secondary can be fired without conflictions (important for instagib)
 -                                      self.jump_interval = time + WEP_CVAR_SEC(vaporizer, refire) * W_WeaponRateFactor();
 +                                      actor.jump_interval = time + WEP_CVAR_SEC(vaporizer, refire) * W_WeaponRateFactor();
  
                                        // decrease ammo for the laser?
                                        if(WEP_CVAR_SEC(vaporizer, ammo))
 -                                              W_DecreaseAmmo(WEP_CVAR_SEC(vaporizer, ammo));
 +                                              W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(vaporizer, ammo));
  
                                        // ugly instagib hack to reuse the fire mode of the laser
 -                                      makevectors(self.v_angle);
 -                                      int oldwep = self.weapon; // we can't avoid this hack
 -                                      self.weapon = WEP_BLASTER.m_id;
 +                                      makevectors(actor.v_angle);
 +                                      int oldwep = actor.weapon; // we can't avoid this hack
 +                                      actor.weapon = WEP_BLASTER.m_id;
                                        W_Blaster_Attack(
 +                                              actor,
                                                WEP_BLASTER.m_id | HITTYPE_SECONDARY,
                                                WEP_CVAR_SEC(vaporizer, shotangle),
                                                WEP_CVAR_SEC(vaporizer, damage),
                                                WEP_CVAR_SEC(vaporizer, delay),
                                                WEP_CVAR_SEC(vaporizer, lifetime)
                                        );
 -                                      self.weapon = oldwep;
 +                                      actor.weapon = oldwep;
  
                                        // now do normal refire
 -                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready);
 +                                      weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready);
                                }
                        }
                        else
 -                              self.held_down = false;
 -
 -                      return true;
 +                              actor.held_down = false;
                }
 -              case WR_INIT:
 +              METHOD(Vaporizer, wr_init, void(entity thiswep))
                {
                        //W_Blaster(WR_INIT); // Samual: Is this really the proper thing to do? Didn't we already run this previously?
                        VAPORIZER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_SETUP:
 +              METHOD(Vaporizer, wr_setup, void(entity thiswep))
                {
                        self.ammo_field = WEP_AMMO(VAPORIZER);
                        self.vaporizer_lasthit = 0;
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Vaporizer, wr_checkammo1, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(VAPORIZER) >= vaporizer_ammo;
 +                      float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
 +                      float ammo_amount = self.WEP_AMMO(VAPORIZER) >= vaporizer_ammo;
                        ammo_amount += self.(weapon_load[WEP_VAPORIZER.m_id]) >= vaporizer_ammo;
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Vaporizer, wr_checkammo2, bool(entity thiswep))
                {
                        if(!WEP_CVAR_SEC(vaporizer, ammo))
                                return true;
 -                      ammo_amount = self.WEP_AMMO(VAPORIZER) >= WEP_CVAR_SEC(vaporizer, ammo);
 +                      float ammo_amount = self.WEP_AMMO(VAPORIZER) >= WEP_CVAR_SEC(vaporizer, ammo);
                        ammo_amount += self.(weapon_load[WEP_VAPORIZER.m_id]) >= WEP_CVAR_SEC(vaporizer, ammo);
                        return ammo_amount;
                }
 -              case WR_CONFIG:
 +              METHOD(Vaporizer, wr_config, void(entity thiswep))
                {
                        VAPORIZER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RESETPLAYER:
 +              METHOD(Vaporizer, wr_resetplayer, void(entity thiswep))
                {
                        self.vaporizer_lasthit = 0;
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Vaporizer, wr_reload, void(entity thiswep))
                {
 +                      float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
                        float used_ammo;
                        if(WEP_CVAR_SEC(vaporizer, ammo))
                                used_ammo = min(vaporizer_ammo, WEP_CVAR_SEC(vaporizer, ammo));
                        else
                                used_ammo = vaporizer_ammo;
  
 -                      W_Reload(used_ammo, SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, used_ammo, SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Vaporizer, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_THINKING_WITH_PORTALS;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Vaporizer, wr_killmessage, int(entity thiswep))
                {
                        return WEAPON_VAPORIZER_MURDER;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
 -float W_Vaporizer(float req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Vaporizer, wr_impacteffect, void(entity thiswep))
                {
                        vector org2 = w_org + w_backoff * 6;
                        if(w_deathtype & HITTYPE_SECONDARY)
                                pointparticles(particleeffectnum(EFFECT_VORTEX_IMPACT), org2, '0 0 0', 1);
                                if(!w_issilent) { sound(self, CH_SHOTS, SND_NEXIMPACT, VOL_BASE, ATTN_NORM); }
                        }
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Vaporizer, wr_init, void(entity thiswep))
                {
                        if(autocvar_cl_reticle && autocvar_cl_reticle_weapon)
                        {
                                precache_pic("gfx/reticle_nex");
                        }
 -                      return true;
                }
 -              case WR_ZOOMRETICLE:
 +              METHOD(Vaporizer, wr_zoomreticle, bool(entity thiswep))
                {
                        if(button_zoom || zoomscript_caught)
                        {
                                return false;
                        }
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index 176a2585f022d4276650eb5feec8659ed5306ba3,8238ac22c83d87b9e7ab70242921185753bbe5bd..d09746724c8d9020bf7dfc882639ca9deba5952f
@@@ -1,21 -1,19 +1,21 @@@
  #ifndef IMPLEMENTATION
 -REGISTER_WEAPON(
 -/* WEP_##id  */ VORTEX,
 -/* function  */ W_Vortex,
 -/* ammotype  */ ammo_cells,
 -/* impulse   */ 7,
 -/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
 -/* rating    */ BOT_PICKUP_RATING_HIGH,
 -/* color     */ '0.5 1 1',
 -/* modelname */ "nex",
 -/* simplemdl */ "foobar",
 -/* crosshair */ "gfx/crosshairnex 0.65",
 -/* wepimg    */ "weaponnex",
 -/* refname   */ "vortex",
 -/* wepname   */ _("Vortex")
 -);
 +CLASS(Vortex, Weapon)
 +/* ammotype  */ ATTRIB(Vortex, ammo_field, .int, ammo_cells)
 +/* impulse   */ ATTRIB(Vortex, impulse, int, 7)
 +/* flags     */ ATTRIB(Vortex, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
 +/* rating    */ ATTRIB(Vortex, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
 +/* color     */ ATTRIB(Vortex, wpcolor, vector, '0.5 1 1');
 +/* modelname */ ATTRIB(Vortex, mdl, string, "nex");
 +#ifndef MENUQC
 +/* model     */ ATTRIB(Vortex, m_model, Model, MDL_VORTEX_ITEM);
 +#endif
 +/* crosshair */ ATTRIB(Vortex, w_crosshair, string, "gfx/crosshairnex");
 +/* crosshair */ ATTRIB(Vortex, w_crosshair_size, float, 0.65);
 +/* wepimg    */ ATTRIB(Vortex, model2, string, "weaponnex");
 +/* refname   */ ATTRIB(Vortex, netname, string, "vortex");
 +/* wepname   */ ATTRIB(Vortex, message, string, _("Vortex"));
 +ENDCLASS(Vortex)
 +REGISTER_WEAPON(VORTEX, NEW(Vortex));
  
  #define VORTEX_SETTINGS(w_cvar,w_prop) VORTEX_SETTINGS_LIST(w_cvar, w_prop, VORTEX, vortex)
  #define VORTEX_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
@@@ -61,8 -59,8 +61,8 @@@ VORTEX_SETTINGS(WEP_ADD_CVAR, WEP_ADD_P
  #endif
  #ifdef IMPLEMENTATION
  #ifdef SVQC
void spawnfunc_weapon_vortex(void) { weapon_defaultspawnfunc(WEP_VORTEX.m_id); }
void spawnfunc_weapon_nex(void) { spawnfunc_weapon_vortex(); }
spawnfunc(weapon_vortex) { weapon_defaultspawnfunc(WEP_VORTEX.m_id); }
spawnfunc(weapon_nex) { spawnfunc_weapon_vortex(this); }
  
  void SendCSQCVortexBeamParticle(float charge) {
        vector v;
@@@ -78,7 -76,7 +78,7 @@@
        WriteByte(MSG_BROADCAST, bound(0, 255 * charge, 255));
  }
  
 -void W_Vortex_Attack(float issecondary)
 +void W_Vortex_Attack(Weapon thiswep, float issecondary)
  {SELFPARAM();
        float mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, myammo, charge;
  
        //beam and muzzle flash done on client
        SendCSQCVortexBeamParticle(charge);
  
 -      W_DecreaseAmmo(myammo);
 +      W_DecreaseAmmo(thiswep, self, myammo);
  }
  
 -spawnfunc(weapon_vortex); // defined in t_items.qc
 -
  .float vortex_chargepool_pauseregen_finished;
 -bool W_Vortex(int req)
 -{SELFPARAM();
 -      float dt;
 -      float ammo_amount;
 -      switch(req)
 -      {
 -              case WR_AIM:
 +
 +              METHOD(Vortex, wr_aim, void(entity thiswep))
                {
                        if(bot_aim(1000000, 0, 1, false))
                                self.BUTTON_ATCK = true;
                                if(WEP_CVAR(vortex, charge))
                                        self.BUTTON_ATCK2 = true;
                        }
 -                      return true;
                }
 -              case WR_THINK:
 +              METHOD(Vortex, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
                {
 -                      if(WEP_CVAR(vortex, charge) && self.vortex_charge < WEP_CVAR(vortex, charge_limit))
 -                              self.vortex_charge = min(1, self.vortex_charge + WEP_CVAR(vortex, charge_rate) * frametime / W_TICSPERFRAME);
 +                      if(WEP_CVAR(vortex, charge) && actor.vortex_charge < WEP_CVAR(vortex, charge_limit))
 +                              actor.vortex_charge = min(1, actor.vortex_charge + WEP_CVAR(vortex, charge_rate) * frametime / W_TICSPERFRAME);
  
                        if(WEP_CVAR_SEC(vortex, chargepool))
 -                              if(self.vortex_chargepool_ammo < 1)
 +                              if(actor.vortex_chargepool_ammo < 1)
                                {
 -                                      if(self.vortex_chargepool_pauseregen_finished < time)
 -                                              self.vortex_chargepool_ammo = min(1, self.vortex_chargepool_ammo + WEP_CVAR_SEC(vortex, chargepool_regen) * frametime / W_TICSPERFRAME);
 -                                      self.pauseregen_finished = max(self.pauseregen_finished, time + WEP_CVAR_SEC(vortex, chargepool_pause_regen));
 +                                      if(actor.vortex_chargepool_pauseregen_finished < time)
 +                                              actor.vortex_chargepool_ammo = min(1, actor.vortex_chargepool_ammo + WEP_CVAR_SEC(vortex, chargepool_regen) * frametime / W_TICSPERFRAME);
 +                                      actor.pauseregen_finished = max(actor.pauseregen_finished, time + WEP_CVAR_SEC(vortex, chargepool_pause_regen));
                                }
  
 -                      if(autocvar_g_balance_vortex_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo))) // forced reload
 -                              WEP_ACTION(self.weapon, WR_RELOAD);
 -                      else
 +                      if(autocvar_g_balance_vortex_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo))) { // forced reload
 +                              Weapon w = get_weaponinfo(actor.weapon);
 +                              w.wr_reload(w);
 +                      } else
                        {
 -                              if(self.BUTTON_ATCK)
 +                              if(fire1)
                                {
 -                                      if(weapon_prepareattack(0, WEP_CVAR_PRI(vortex, refire)))
 +                                      if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(vortex, refire)))
                                        {
 -                                              W_Vortex_Attack(0);
 -                                              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(vortex, animtime), w_ready);
 +                                              W_Vortex_Attack(thiswep, 0);
 +                                              weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(vortex, animtime), w_ready);
                                        }
                                }
 -                              if((WEP_CVAR(vortex, charge) && !WEP_CVAR(vortex, secondary)) ? (self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) : self.BUTTON_ATCK2)
 +                              if((WEP_CVAR(vortex, charge) && !WEP_CVAR(vortex, secondary)) ? (actor.BUTTON_ZOOM | actor.BUTTON_ZOOMSCRIPT) : fire2)
                                {
                                        if(WEP_CVAR(vortex, charge))
                                        {
 -                                              self.vortex_charge_rottime = time + WEP_CVAR(vortex, charge_rot_pause);
 -                                              dt = frametime / W_TICSPERFRAME;
 +                                              actor.vortex_charge_rottime = time + WEP_CVAR(vortex, charge_rot_pause);
 +                                              float dt = frametime / W_TICSPERFRAME;
  
 -                                              if(self.vortex_charge < 1)
 +                                              if(actor.vortex_charge < 1)
                                                {
                                                        if(WEP_CVAR_SEC(vortex, chargepool))
                                                        {
                                                                if(WEP_CVAR_SEC(vortex, ammo))
                                                                {
                                                                        // always deplete if secondary is held
 -                                                                      self.vortex_chargepool_ammo = max(0, self.vortex_chargepool_ammo - WEP_CVAR_SEC(vortex, ammo) * dt);
 +                                                                      actor.vortex_chargepool_ammo = max(0, actor.vortex_chargepool_ammo - WEP_CVAR_SEC(vortex, ammo) * dt);
  
 -                                                                      dt = min(dt, (1 - self.vortex_charge) / WEP_CVAR(vortex, charge_rate));
 -                                                                      self.vortex_chargepool_pauseregen_finished = time + WEP_CVAR_SEC(vortex, chargepool_pause_regen);
 -                                                                      dt = min(dt, self.vortex_chargepool_ammo);
 +                                                                      dt = min(dt, (1 - actor.vortex_charge) / WEP_CVAR(vortex, charge_rate));
 +                                                                      actor.vortex_chargepool_pauseregen_finished = time + WEP_CVAR_SEC(vortex, chargepool_pause_regen);
 +                                                                      dt = min(dt, actor.vortex_chargepool_ammo);
                                                                        dt = max(0, dt);
  
 -                                                                      self.vortex_charge += dt * WEP_CVAR(vortex, charge_rate);
 +                                                                      actor.vortex_charge += dt * WEP_CVAR(vortex, charge_rate);
                                                                }
                                                        }
  
                                                        else if(WEP_CVAR_SEC(vortex, ammo))
                                                        {
 -                                                              if(self.BUTTON_ATCK2) // only eat ammo when the button is pressed
 +                                                              if(fire2) // only eat ammo when the button is pressed
                                                                {
 -                                                                      dt = min(dt, (1 - self.vortex_charge) / WEP_CVAR(vortex, charge_rate));
 -                                                                      if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +                                                                      dt = min(dt, (1 - actor.vortex_charge) / WEP_CVAR(vortex, charge_rate));
 +                                                                      if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
                                                                        {
                                                                                // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
                                                                                if(autocvar_g_balance_vortex_reload_ammo)
                                                                                {
 -                                                                                      dt = min(dt, (self.clip_load - WEP_CVAR_PRI(vortex, ammo)) / WEP_CVAR_SEC(vortex, ammo));
 +                                                                                      dt = min(dt, (actor.clip_load - WEP_CVAR_PRI(vortex, ammo)) / WEP_CVAR_SEC(vortex, ammo));
                                                                                        dt = max(0, dt);
                                                                                        if(dt > 0)
                                                                                        {
 -                                                                                              self.clip_load = max(WEP_CVAR_SEC(vortex, ammo), self.clip_load - WEP_CVAR_SEC(vortex, ammo) * dt);
 +                                                                                              actor.clip_load = max(WEP_CVAR_SEC(vortex, ammo), actor.clip_load - WEP_CVAR_SEC(vortex, ammo) * dt);
                                                                                        }
 -                                                                                      self.(weapon_load[WEP_VORTEX.m_id]) = self.clip_load;
 +                                                                                      actor.(weapon_load[WEP_VORTEX.m_id]) = actor.clip_load;
                                                                                }
                                                                                else
                                                                                {
 -                                                                                      dt = min(dt, (self.WEP_AMMO(VORTEX) - WEP_CVAR_PRI(vortex, ammo)) / WEP_CVAR_SEC(vortex, ammo));
 +                                                                                      dt = min(dt, (actor.WEP_AMMO(VORTEX) - WEP_CVAR_PRI(vortex, ammo)) / WEP_CVAR_SEC(vortex, ammo));
                                                                                        dt = max(0, dt);
                                                                                        if(dt > 0)
                                                                                        {
 -                                                                                              self.WEP_AMMO(VORTEX) = max(WEP_CVAR_SEC(vortex, ammo), self.WEP_AMMO(VORTEX) - WEP_CVAR_SEC(vortex, ammo) * dt);
 +                                                                                              actor.WEP_AMMO(VORTEX) = max(WEP_CVAR_SEC(vortex, ammo), actor.WEP_AMMO(VORTEX) - WEP_CVAR_SEC(vortex, ammo) * dt);
                                                                                        }
                                                                                }
                                                                        }
 -                                                                      self.vortex_charge += dt * WEP_CVAR(vortex, charge_rate);
 +                                                                      actor.vortex_charge += dt * WEP_CVAR(vortex, charge_rate);
                                                                }
                                                        }
  
                                                        else
                                                        {
 -                                                              dt = min(dt, (1 - self.vortex_charge) / WEP_CVAR(vortex, charge_rate));
 -                                                              self.vortex_charge += dt * WEP_CVAR(vortex, charge_rate);
 +                                                              dt = min(dt, (1 - actor.vortex_charge) / WEP_CVAR(vortex, charge_rate));
 +                                                              actor.vortex_charge += dt * WEP_CVAR(vortex, charge_rate);
                                                        }
                                                }
                                        }
                                        else if(WEP_CVAR(vortex, secondary))
                                        {
 -                                              if(weapon_prepareattack(0, WEP_CVAR_SEC(vortex, refire)))
 +                                              if(weapon_prepareattack(actor, false, WEP_CVAR_SEC(vortex, refire)))
                                                {
 -                                                      W_Vortex_Attack(1);
 -                                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_SEC(vortex, animtime), w_ready);
 +                                                      W_Vortex_Attack(thiswep, 1);
 +                                                      weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(vortex, animtime), w_ready);
                                                }
                                        }
                                }
                        }
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Vortex, wr_init, void(entity thiswep))
                {
                        VORTEX_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
 -                      return true;
                }
 -              case WR_SETUP:
 +              METHOD(Vortex, wr_setup, void(entity thiswep))
                {
                        self.vortex_lasthit = 0;
 -                      return true;
                }
 -              case WR_CHECKAMMO1:
 +              METHOD(Vortex, wr_checkammo1, bool(entity thiswep))
                {
 -                      ammo_amount = self.WEP_AMMO(VORTEX) >= WEP_CVAR_PRI(vortex, ammo);
 +                      float ammo_amount = self.WEP_AMMO(VORTEX) >= WEP_CVAR_PRI(vortex, ammo);
                        ammo_amount += (autocvar_g_balance_vortex_reload_ammo && self.(weapon_load[WEP_VORTEX.m_id]) >= WEP_CVAR_PRI(vortex, ammo));
                        return ammo_amount;
                }
 -              case WR_CHECKAMMO2:
 +              METHOD(Vortex, wr_checkammo2, bool(entity thiswep))
                {
                        if(WEP_CVAR(vortex, secondary))
                        {
                                // don't allow charging if we don't have enough ammo
 -                              ammo_amount = self.WEP_AMMO(VORTEX) >= WEP_CVAR_SEC(vortex, ammo);
 +                              float ammo_amount = self.WEP_AMMO(VORTEX) >= WEP_CVAR_SEC(vortex, ammo);
                                ammo_amount += self.(weapon_load[WEP_VORTEX.m_id]) >= WEP_CVAR_SEC(vortex, ammo);
                                return ammo_amount;
                        }
                                return false; // zoom is not a fire mode
                        }
                }
 -              case WR_CONFIG:
 +              METHOD(Vortex, wr_config, void(entity thiswep))
                {
                        VORTEX_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
 -                      return true;
                }
 -              case WR_RESETPLAYER:
 +              METHOD(Vortex, wr_resetplayer, void(entity thiswep))
                {
                        self.vortex_lasthit = 0;
 -                      return true;
                }
 -              case WR_RELOAD:
 +              METHOD(Vortex, wr_reload, void(entity thiswep))
                {
 -                      W_Reload(min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo)), SND(RELOAD));
 -                      return true;
 +                      W_Reload(self, min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo)), SND(RELOAD));
                }
 -              case WR_SUICIDEMESSAGE:
 +              METHOD(Vortex, wr_suicidemessage, int(entity thiswep))
                {
                        return WEAPON_THINKING_WITH_PORTALS;
                }
 -              case WR_KILLMESSAGE:
 +              METHOD(Vortex, wr_killmessage, int(entity thiswep))
                {
                        return WEAPON_VORTEX_MURDER;
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #ifdef CSQC
  float autocvar_g_balance_vortex_secondary = 0; // WEAPONTODO
 -bool W_Vortex(int req)
 -{SELFPARAM();
 -      switch(req)
 -      {
 -              case WR_IMPACTEFFECT:
 +
 +              METHOD(Vortex, wr_impacteffect, void(entity thiswep))
                {
 -                      vector org2;
 -                      org2 = w_org + w_backoff * 6;
 +                      vector org2 = w_org + w_backoff * 6;
                        pointparticles(particleeffectnum(EFFECT_VORTEX_IMPACT), org2, '0 0 0', 1);
                        if(!w_issilent)
                                sound(self, CH_SHOTS, SND_NEXIMPACT, VOL_BASE, ATTN_NORM);
 -
 -                      return true;
                }
 -              case WR_INIT:
 +              METHOD(Vortex, wr_init, void(entity thiswep))
                {
                        if(autocvar_cl_reticle && autocvar_cl_reticle_weapon)
                        {
                                precache_pic("gfx/reticle_nex");
                        }
 -                      return true;
                }
 -              case WR_ZOOMRETICLE:
 +              METHOD(Vortex, wr_zoomreticle, bool(entity thiswep))
                {
                        if(button_zoom || zoomscript_caught || (!WEP_CVAR(vortex, secondary) && button_attack2))
                        {
                                return false;
                        }
                }
 -      }
 -      return false;
 -}
 +
  #endif
  #endif
index bc8876304e409c43e748c670e0461a0f84f3c3ce,5358b662cf952e75673d8c1d5f083b34644e5662..454feedfbd7dc69ae2d1d60b488550f48a93c50a
@@@ -335,6 -335,8 +335,6 @@@ float autocvar_g_freezetag_warmup
  bool autocvar_g_full_getstatus_responses;
  bool autocvar_g_fullbrightitems;
  bool autocvar_g_fullbrightplayers;
 -#define autocvar_g_grappling_hook cvar("g_grappling_hook")
 -int autocvar_g_grappling_hook_tarzan;
  int autocvar_g_balance_grapplehook_pull_frozen;
  float autocvar_g_balance_grapplehook_nade_time;
  bool autocvar_g_balance_grapplehook_gravity;
@@@ -414,8 -416,22 +414,8 @@@ float autocvar_g_multijump_speed
  float autocvar_g_multijump_maxspeed;
  float autocvar_g_multijump_dodging = 1;
  string autocvar_g_mutatormsg;
 -float autocvar_g_nexball_basketball_bouncefactor;
 -float autocvar_g_nexball_basketball_bouncestop;
 -float autocvar_g_nexball_basketball_carrier_highspeed;
 -bool autocvar_g_nexball_basketball_meter;
 -float autocvar_g_nexball_basketball_meter_maxpower;
 -float autocvar_g_nexball_basketball_meter_minpower;
 -float autocvar_g_nexball_delay_collect;
 -float autocvar_g_nexball_delay_goal;
 -float autocvar_g_nexball_delay_start;
 -float autocvar_g_nexball_football_bouncefactor;
 -float autocvar_g_nexball_football_bouncestop;
  int autocvar_g_nexball_goalleadlimit;
  #define autocvar_g_nexball_goallimit cvar("g_nexball_goallimit")
 -bool autocvar_g_nexball_radar_showallplayers;
 -bool autocvar_g_nexball_sound_bounce;
 -int autocvar_g_nexball_trail_color;
  //float autocvar_g_nick_flood_penalty;
  int autocvar_g_nick_flood_penalty_red;
  int autocvar_g_nick_flood_penalty_yellow;
@@@ -686,6 -702,7 +686,7 @@@ int autocvar_g_monsters_max_perplayer
  float autocvar_g_monsters_damageforcescale = 0.8;
  float autocvar_g_monsters_target_range;
  bool autocvar_g_monsters_target_infront;
+ float autocvar_g_monsters_target_infront_range = 0.3;
  float autocvar_g_monsters_attack_range;
  int autocvar_g_monsters_score_kill;
  int autocvar_g_monsters_score_spawned;
index 4d7b409fbb67967cbb8c5379f7904a0e7c1234a7,629009761fbcece18ce7d57a4f2094d6ef842eef..06076689134f5c7afbf18eed37df77a68e6297fb
@@@ -102,8 -102,7 +102,8 @@@ void havocbot_ai(
  
                if(self.weapons)
                {
 -                      WEP_ACTION(self.weapon, WR_AIM);
 +                      Weapon w = get_weaponinfo(self.weapon);
 +                      w.wr_aim(w);
                        if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
                        {
                                self.BUTTON_ATCK = false;
@@@ -857,7 -856,7 +857,7 @@@ void havocbot_movetogoal(
  void havocbot_chooseenemy()
  {SELFPARAM();
        entity head, best, head2;
-       float rating, bestrating, i, hf;
+       float rating, bestrating, hf;
        vector eye, v;
        if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
        {
  
        self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
  
-       for(i = 0; ; ++i)
+       bool scan_transparent = false;
+       bool scan_secondary_targets = false;
+       bool have_secondary_targets = false;
+       while(true)
        {
-               while (head)
+               scan_secondary_targets = false;
+               :scan_targets
+               for( ; head; head = head.chain)
                {
+                       if(!scan_secondary_targets)
+                       {
+                               if(head.classname == "misc_breakablemodel")
+                               {
+                                       have_secondary_targets = true;
+                                       continue;
+                               }
+                       }
+                       else
+                       {
+                               if(head.classname != "misc_breakablemodel")
+                                       continue;
+                       }
                        v = (head.absmin + head.absmax) * 0.5;
                        rating = vlen(v - eye);
                        if (rating<autocvar_bot_ai_enemydetectionradius)
                                        bestrating = rating;
                                }
                        }
-                       head = head.chain;
+               }
+               if(!best && have_secondary_targets && !scan_secondary_targets)
+               {
+                       scan_secondary_targets = true;
+                       // restart the loop
+                       head = head2;
+                       bestrating = 100000000;
+                       goto scan_targets;
                }
  
                // I want to do a second scan if no enemy was found or I don't have weapons
                // TODO: Perform the scan when using the rifle (requires changes on the rifle code)
                if(best || self.weapons) // || self.weapon == WEP_RIFLE.m_id
                        break;
-               if(i)
+               if(scan_transparent)
                        break;
  
                // Set flags to see through transparent objects
                self.dphitcontentsmask |= DPCONTENTS_OPAQUE;
  
                head = head2;
+               scan_transparent = true;
        }
  
        // Restore hit flags
  
        self.enemy = best;
        self.havocbot_stickenemy = true;
+       if(best && best.classname == "misc_breakablemodel")
+               self.havocbot_stickenemy = false;
  }
  
  float havocbot_chooseweapon_checkreload(int new_weapon)
                float i, other_weapon_available = false;
                for(i = WEP_FIRST; i <= WEP_LAST; ++i)
                {
 +                      Weapon w = get_weaponinfo(i);
                        // if we are out of ammo for all other weapons, it's an emergency to switch to anything else
 -                      if (WEP_ACTION(i, WR_CHECKAMMO1) + WEP_ACTION(i, WR_CHECKAMMO2))
 +                      if (w.wr_checkammo1(w) + w.wr_checkammo2(w))
                                other_weapon_available = true;
                }
                if(other_weapon_available)
@@@ -980,7 -1008,7 +1010,7 @@@ void havocbot_chooseweapon(
        int i;
  
        // ;)
 -      if(g_weaponarena_weapons == WEPSET_TUBA)
 +      if(g_weaponarena_weapons == WEPSET(TUBA))
        {
                self.switchweapon = WEP_TUBA.m_id;
                return;
index d00e3aa8702336a7b78623e05af9a660ea86460d,da1a180d4c38dad14c8d1678b6edd6e2731ab8fc..7914804b9df976219bf2b6112365fd0abc0151f8
@@@ -205,6 -205,7 +205,7 @@@ void PutObserverInServer (void
        }
  
        self.frags = FRAGS_SPECTATOR;
+       self.bot_attack = false;
  
        MUTATOR_CALLHOOK(MakePlayerObserver);
  
@@@ -510,10 -511,10 +511,10 @@@ void PutClientInServer(
                if(g_weaponarena_random) // WEAPONTODO: more stuff that should be in a mutator. also: rename those cvars
                {
                        if(g_weaponarena_random_with_blaster)
 -                              self.weapons &= ~WEPSET_BLASTER;
 +                              self.weapons &= ~WEPSET(BLASTER);
                        W_RandomWeapons(self, g_weaponarena_random);
                        if(g_weaponarena_random_with_blaster)
 -                              self.weapons |= WEPSET_BLASTER;
 +                              self.weapons |= WEPSET(BLASTER);
                }
  
                self.items = start_items;
                // reset fields the weapons may use
                for (int j = WEP_FIRST; j <= WEP_LAST; ++j)
                {
 -                      WEP_ACTION(j, WR_RESETPLAYER);
 +                      Weapon w = get_weaponinfo(j);
 +                      w.wr_resetplayer(w);
  
                        // all weapons must be fully loaded when we spawn
                        entity e = get_weaponinfo(j);
@@@ -1236,7 -1236,7 +1237,7 @@@ void ClientConnect (void
                        Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MOTD, getwelcomemessage());
                }
  
 -              if(autocvar_g_bugrigs || (g_weaponarena_weapons == WEPSET_TUBA))
 +              if(autocvar_g_bugrigs || (g_weaponarena_weapons == WEPSET(TUBA)))
                        stuffcmd(self, "cl_cmd settemp chase_active 1\n");
        }
  
@@@ -2552,7 -2552,7 +2553,7 @@@ void PlayerPreThink (void
                {
                        self.items &= ~self.items_added;
  
 -                      W_WeaponFrame();
 +                      W_WeaponFrame(self);
  
                        self.items_added = 0;
                        if(self.items & ITEM_Jetpack.m_itemid)
diff --combined qcsrc/server/defs.qh
index 426a804cce958c1c8f13f77838d1e786328a4866,723b137a1366c54d7480eea6a8ee1523ced2ca1e..0ed30571f8eefc9ccbdc8a162198fbe773b9aa98
@@@ -5,8 -5,6 +5,6 @@@
  
  #define INDEPENDENT_ATTACK_FINISHED
  
- noref float require_spawnfunc_prefix; // if this float exists, only functions with spawnfunc_ name prefix qualify as spawn functions
  #define BUTTON_ATCK       button0
  #define BUTTON_JUMP       button2
  #define BUTTON_ATCK2      button3
@@@ -164,12 -162,13 +162,12 @@@ const float MAX_DAMAGEEXTRARADIUS = 16
  
  // WEAPONTODO
  .float autoswitch;
 -//float WEP_ACTION(float wpn, float wrequest);
  float client_hasweapon(entity cl, float wpn, float andammo, float complain);
 -void w_clear();
 -void w_ready();
 +void w_clear(Weapon thiswep, entity actor, bool fire1, bool fire2);
 +void w_ready(Weapon thiswep, entity actor, bool fire1, bool fire2);
  // VorteX: standalone think for weapons, so normal think on weaponentity can be reserved by weaponflashes (which needs update even player dies)
  .float weapon_nextthink;
 -.void() weapon_think;
 +.void(Weapon thiswep, entity actor, bool fire1, bool fire2) weapon_think;
  
  
  // weapon states (self.weaponentity.state)
diff --combined qcsrc/server/g_damage.qc
index 866586e7d93c97859836227e27bb1a70316c0c81,2cc773b73f526b53a46b927692be1ba1069d9970..11812f85c139652ed2e143ef74e3dda9e0bce682
@@@ -265,8 -265,7 +265,8 @@@ float Obituary_WeaponDeath
        if(death_weapon)
        {
                w_deathtype = deathtype;
 -              int death_message = WEP_ACTION(death_weapon, ((murder) ? WR_KILLMESSAGE : WR_SUICIDEMESSAGE));
 +              Weapon w = get_weaponinfo(death_weapon);
 +              int death_message = ((murder) ? w.wr_killmessage : w.wr_suicidemessage)(w);
                w_deathtype = false;
  
                if (death_message)
@@@ -561,6 -560,7 +561,7 @@@ void Freeze (entity targ, float freeze_
        targ.revive_progress = ((frozen_type == 3) ? 1 : 0);
        targ.health = ((frozen_type == 3) ? targ_maxhealth : 1);
        targ.revive_speed = freeze_time;
+       self.bot_attack = false;
  
        entity ice, head;
        ice = spawn();
  
  void Unfreeze (entity targ)
  {
+       if(!targ.frozen)
+               return;
        if(targ.frozen && targ.frozen != 3) // only reset health if target was frozen
                targ.health = ((IS_PLAYER(targ)) ? start_health : targ.max_health);
  
        targ.frozen = 0;
        targ.revive_progress = 0;
        targ.revival_time = time;
+       self.bot_attack = true;
  
        WaypointSprite_Kill(targ.waypointsprite_attached);
  
diff --combined qcsrc/server/g_world.qc
index 40d39a4cd67ae2d9ee3939167d0d85c08ed059fb,53990fb21392f0b22c64d91ab93e8cc49e39e526..0e550db1e46979abd1ac7cbb137beae0c0458f0e
@@@ -534,8 -534,8 +534,8 @@@ void RandomSeed_Spawn(
        WITH(entity, self, randomseed, randomseed.think()); // sets random seed and nextthink
  }
  
void spawnfunc___init_dedicated_server(void)
- {SELFPARAM();
spawnfunc(__init_dedicated_server)
+ {
        // handler for _init/_init map (only for dedicated server initialization)
  
        world_initialized = -1; // don't complain
  
        // needs to be done so early because of the constants they create
        static_init();
 -      CALL_ACCUMULATED_FUNCTION(RegisterTurrets);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
  
@@@ -571,8 -572,8 +571,8 @@@ void ClientInit_Spawn()
  void WeaponStats_Init();
  void WeaponStats_Shutdown();
  void Physics_AddStats();
void spawnfunc_worldspawn (void)
- {SELFPARAM();
spawnfunc(worldspawn)
+ {
        float fd, l, j, n;
        string s;
  
  
        // needs to be done so early because of the constants they create
        static_init();
 -      CALL_ACCUMULATED_FUNCTION(RegisterTurrets);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
  
                MUTATOR_CALLHOOK(BuildMutatorsString, s);
                s = ret_string;
  
 -              // simple, probably not good in the mutator system
 -              if(autocvar_g_grappling_hook)
 -                      s = strcat(s, ":grappling_hook");
 -
                // initialiation stuff, not good in the mutator system
                if(!autocvar_g_use_ammunition)
                        s = strcat(s, ":no_use_ammunition");
        world_initialized = 1;
  }
  
void spawnfunc_light (void)
- {SELFPARAM();
spawnfunc(light)
+ {
        //makestatic (self); // Who the f___ did that?
        remove(self);
  }
index d18960ec9f30adcaf64b9606e3fb390426e3b09f,a6164747dbdfa8fba56b601c5ea995496c4a35a1..910f33b7394ab615545044351f47c773dee59e86
@@@ -299,6 -299,8 +299,8 @@@ string formatmessage(string msg
                        case "x": replacement = ((cursor_ent.netname == "" || !cursor_ent) ? "nothing" : cursor_ent.netname); break;
                        case "s": replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1')); break;
                        case "S": replacement = ftos(vlen(self.velocity)); break;
+                       case "t": replacement = seconds_tostring(ceil(max(0, autocvar_timelimit * 60 + game_starttime - time))); break;
+                       case "T": replacement = seconds_tostring(floor(time - game_starttime)); break;
                        default:
                        {
                                MUTATOR_CALLHOOK(FormatMessage, escape, replacement, msg);
@@@ -706,7 -708,7 +708,7 @@@ void readplayerstartcvars(
  
        MUTATOR_CALLHOOK(SetStartItems);
  
 -      if ((start_items & ITEM_Jetpack.m_itemid) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
 +      if (start_items & ITEM_Jetpack.m_itemid)
        {
                start_items |= ITEM_JetpackRegen.m_itemid;
                start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
        for (i = WEP_FIRST; i <= WEP_LAST; ++i)
        {
                e = get_weaponinfo(i);
 -              if(precache_weapons & WepSet_FromWeapon(i))
 -                      WEP_ACTION(i, WR_INIT);
 +              if(precache_weapons & WepSet_FromWeapon(i)) {
 +                      Weapon w = get_weaponinfo(i);
 +                      w.wr_init(w);
 +              }
        }
  
        start_ammo_shells = max(0, start_ammo_shells);
@@@ -1434,7 -1434,7 +1436,7 @@@ string uid2name(string myuid) 
        return s;
  }
  
- float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
+ float MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
  {
      float m, i;
      vector start, org, delta, end, enddown, mstart;
      m = e.dphitcontentsmask;
      e.dphitcontentsmask = goodcontents | badcontents;
  
-     org = world.mins;
-     delta = world.maxs - world.mins;
+     org = boundmin;
+     delta = boundmax - boundmin;
  
      start = end = org;
  
          return false;
  }
  
+ float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
+ {
+       return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance);
+ }
  void write_recordmarker(entity pl, float tstart, float dt)
  {
      GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
index f338eb3f16f43637071905346927f61f1eb701b5,fc3583bb16bc6824dd5c0f4fce6638af8e631c13..b0be0422fa48636d968e2d1adec26ff091a13021
@@@ -106,6 -106,8 +106,8 @@@ float LostMovetypeFollow(entity ent)
  
  string uid2name(string myuid);
  
+ float MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance);
  float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance);
  
  string NearestLocation(vector p);
@@@ -336,6 -338,7 +338,6 @@@ void readlevelcvars(void
        sv_foginterval = cvar("sv_foginterval");
        g_cloaked = cvar("g_cloaked");
        g_footsteps = cvar("g_footsteps");
 -      g_grappling_hook = cvar("g_grappling_hook");
        g_jetpack = cvar("g_jetpack");
        sv_maxidle = cvar("sv_maxidle");
        sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
        if (!warmup_stage)
                game_starttime = time + cvar("g_start_delay");
  
 -      for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
 -              WEP_ACTION(i, WR_INIT);
 +      for(int i = WEP_FIRST; i <= WEP_LAST; ++i) {
 +              Weapon w = get_weaponinfo(i);
 +              w.wr_init(w);
 +      }
  
        readplayerstartcvars();
  }
index 6bf1b7790b97663a0089852a49deb4b49ac2a585,5dbe12d22d9c7701f042591b7d04e677509ec34f..5209b225f1084629f085a6cf65b9cc0b86ef7a22
@@@ -6,17 -6,15 +6,17 @@@
  #include "../../common/monsters/spawn.qh"
  #include "../../common/monsters/sv_monsters.qh"
  
void spawnfunc_invasion_spawnpoint()
- {SELFPARAM();
spawnfunc(invasion_spawnpoint)
+ {
        if(!g_invasion) { remove(self); return; }
  
        self.classname = "invasion_spawnpoint";
  
        if(autocvar_g_invasion_zombies_only) // precache only if it hasn't been already
 -      if(self.monsterid)
 -              MON_ACTION(self.monsterid, MR_PRECACHE);
 +      if(self.monsterid) {
 +              Monster mon = get_monsterinfo(self.monsterid);
 +              mon.mr_precache(mon);
 +      }
  }
  
  float invasion_PickMonster(float supermonster_count)
@@@ -432,10 -430,9 +432,10 @@@ void invasion_DelayedInit() // Do this 
  
  void invasion_Initialize()
  {
 -      if(autocvar_g_invasion_zombies_only)
 -              MON_ACTION(MON_ZOMBIE.monsterid, MR_PRECACHE);
 -      else
 +      if(autocvar_g_invasion_zombies_only) {
 +              Monster mon = MON_ZOMBIE;
 +              mon.mr_precache(mon);
 +      } else
        {
                float i;
                entity mon;
                        if((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM))
                                continue; // flying/swimming monsters not yet supported
  
 -                      MON_ACTION(i, MR_PRECACHE);
 +                      mon.mr_precache(mon);
                }
        }
  
index 1eb3c9fd0a1fa0c5dc05bf2c41f3e2235cb647b7,3675831acc915ff9961e7eba121a77230707acf9..8f0ce7e4466ddcd930f7f668c514b16894a4bc3d
@@@ -3,21 -3,6 +3,21 @@@
  
  #include "gamemode.qh"
  
 +float autocvar_g_nexball_basketball_bouncefactor;
 +float autocvar_g_nexball_basketball_bouncestop;
 +float autocvar_g_nexball_basketball_carrier_highspeed;
 +bool autocvar_g_nexball_basketball_meter;
 +float autocvar_g_nexball_basketball_meter_maxpower;
 +float autocvar_g_nexball_basketball_meter_minpower;
 +float autocvar_g_nexball_delay_collect;
 +float autocvar_g_nexball_delay_goal;
 +float autocvar_g_nexball_delay_start;
 +float autocvar_g_nexball_football_bouncefactor;
 +float autocvar_g_nexball_football_bouncestop;
 +bool autocvar_g_nexball_radar_showallplayers;
 +bool autocvar_g_nexball_sound_bounce;
 +int autocvar_g_nexball_trail_color;
 +
  float autocvar_g_nexball_safepass_turnrate;
  float autocvar_g_nexball_safepass_maxdist;
  float autocvar_g_nexball_safepass_holdtime;
@@@ -168,12 -153,11 +168,12 @@@ void GiveBall(entity plyr, entity ball
  
        plyr.weaponentity.weapons = plyr.weapons;
        plyr.weaponentity.switchweapon = plyr.weapon;
 -      plyr.weapons = WEPSET_PORTO;
 +      plyr.weapons = WEPSET(NEXBALL);
        setself(plyr);
 -      WEP_ACTION(WEP_PORTO.m_id, WR_RESETPLAYER);
 -      plyr.switchweapon = WEP_PORTO.m_id;
 -      W_SwitchWeapon(WEP_PORTO.m_id);
 +      Weapon w = WEP_NEXBALL;
 +      w.wr_resetplayer(w);
 +      plyr.switchweapon = WEP_NEXBALL.m_id;
 +      W_SwitchWeapon(WEP_NEXBALL.m_id);
        setself(this);
  }
  
@@@ -424,8 -408,8 +424,8 @@@ void GoalTouch(void
  //=======================//
  //       team ents       //
  //=======================//
void spawnfunc_nexball_team(void)
- {SELFPARAM();
spawnfunc(nexball_team)
+ {
        if(!g_nexball)
        {
                remove(self);
@@@ -550,8 -534,8 +550,8 @@@ void SpawnBall(void
        self.nextthink = game_starttime + autocvar_g_nexball_delay_start;
  }
  
void spawnfunc_nexball_basketball(void)
- {SELFPARAM();
spawnfunc(nexball_basketball)
+ {
        nexball_mode |= NBM_BASKETBALL;
        self.classname = "nexball_basketball";
        if (!(balls & BALL_BASKET))
        SpawnBall();
  }
  
void spawnfunc_nexball_football(void)
- {SELFPARAM();
spawnfunc(nexball_football)
+ {
        nexball_mode |= NBM_FOOTBALL;
        self.classname = "nexball_football";
        self.solid = SOLID_TRIGGER;
@@@ -614,37 -598,37 +614,37 @@@ void SpawnGoal(void
        self.touch = GoalTouch;
  }
  
void spawnfunc_nexball_redgoal(void)
- {SELFPARAM();
spawnfunc(nexball_redgoal)
+ {
        self.team = NUM_TEAM_1;
        SpawnGoal();
  }
void spawnfunc_nexball_bluegoal(void)
- {SELFPARAM();
spawnfunc(nexball_bluegoal)
+ {
        self.team = NUM_TEAM_2;
        SpawnGoal();
  }
void spawnfunc_nexball_yellowgoal(void)
- {SELFPARAM();
spawnfunc(nexball_yellowgoal)
+ {
        self.team = NUM_TEAM_3;
        SpawnGoal();
  }
void spawnfunc_nexball_pinkgoal(void)
- {SELFPARAM();
spawnfunc(nexball_pinkgoal)
+ {
        self.team = NUM_TEAM_4;
        SpawnGoal();
  }
  
void spawnfunc_nexball_fault(void)
- {SELFPARAM();
spawnfunc(nexball_fault)
+ {
        self.team = GOAL_FAULT;
        if(self.noise == "")
                self.noise = SND(TYPEHIT);
        SpawnGoal();
  }
  
void spawnfunc_nexball_out(void)
- {SELFPARAM();
spawnfunc(nexball_out)
+ {
        self.team = GOAL_OUT;
        if(self.noise == "")
                self.noise = SND(TYPEHIT);
  //Spawnfuncs preserved for compatibility
  //
  
void spawnfunc_ball(void)
spawnfunc(ball)
  {
-       spawnfunc_nexball_football();
+       spawnfunc_nexball_football(this);
  }
void spawnfunc_ball_football(void)
spawnfunc(ball_football)
  {
-       spawnfunc_nexball_football();
+       spawnfunc_nexball_football(this);
  }
void spawnfunc_ball_basketball(void)
spawnfunc(ball_basketball)
  {
-       spawnfunc_nexball_basketball();
+       spawnfunc_nexball_basketball(this);
  }
  // The "red goal" is defended by blue team. A ball in there counts as a point for red.
void spawnfunc_ball_redgoal(void)
spawnfunc(ball_redgoal)
  {
-       spawnfunc_nexball_bluegoal();   // I blame Revenant
+       spawnfunc_nexball_bluegoal(this);       // I blame Revenant
  }
void spawnfunc_ball_bluegoal(void)
spawnfunc(ball_bluegoal)
  {
-       spawnfunc_nexball_redgoal();    // but he didn't mean to cause trouble :p
+       spawnfunc_nexball_redgoal(this);        // but he didn't mean to cause trouble :p
  }
void spawnfunc_ball_fault(void)
spawnfunc(ball_fault)
  {
-       spawnfunc_nexball_fault();
+       spawnfunc_nexball_fault(this);
  }
void spawnfunc_ball_bound(void)
spawnfunc(ball_bound)
  {
-       spawnfunc_nexball_out();
+       spawnfunc_nexball_out(this);
  }
  
  //=======================//
@@@ -787,8 -771,11 +787,8 @@@ void W_Nexball_Attack2(void
        if(!autocvar_g_nexball_tackling)
                return;
  
 -      entity missile;
 -      if(!(balls & BALL_BASKET))
 -              return;
        W_SetupShot(self, false, 2, SND(NB_SHOOT2), CH_WEAPON_A, 0);
 -      missile = spawn();
 +      entity missile = spawn();
  
        missile.owner = self;
        missile.classname = "ballstealer";
@@@ -839,48 -826,48 +839,48 @@@ float ball_customize(
        return true;
  }
  
 -float w_nexball_weapon(float req)
 -{SELFPARAM();
 -      if(req == WR_THINK)
 +      METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, bool fire1, bool fire2))
        {
 -              if(self.BUTTON_ATCK)
 -                      if(weapon_prepareattack(0, autocvar_g_balance_nexball_primary_refire))
 +              if(fire1)
 +                      if(weapon_prepareattack(actor, false, autocvar_g_balance_nexball_primary_refire))
                                if(autocvar_g_nexball_basketball_meter)
                                {
                                        if(self.ballcarried && !self.metertime)
                                                self.metertime = time;
                                        else
 -                                              weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
 +                                              weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
                                }
                                else
                                {
                                        W_Nexball_Attack(-1);
 -                                      weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
 +                                      weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
                                }
 -              if(self.BUTTON_ATCK2)
 -                      if(weapon_prepareattack(1, autocvar_g_balance_nexball_secondary_refire))
 +              if(fire2)
 +                      if(weapon_prepareattack(actor, true, autocvar_g_balance_nexball_secondary_refire))
                        {
                                W_Nexball_Attack2();
 -                              weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready);
 +                              weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready);
                        }
  
 -              if(!self.BUTTON_ATCK && self.metertime && self.ballcarried)
 +              if(!fire1 && self.metertime && self.ballcarried)
                {
                        W_Nexball_Attack(time - self.metertime);
                        // DropBall or stealing will set metertime back to 0
 -                      weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
 +                      weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
                }
        }
 -      else if(req == WR_INIT)
 +      METHOD(BallStealer, wr_setup, void(BallStealer thiswep))
        {
 +              //weapon_setup(WEP_PORTO.m_id);
        }
 -      else if(req == WR_SETUP)
 +      METHOD(BallStealer, wr_checkammo1, bool(BallStealer thiswep))
        {
 -              //weapon_setup(WEP_PORTO.m_id);
 +              return true;
 +      }
 +      METHOD(BallStealer, wr_checkammo2, bool(BallStealer thiswep))
 +      {
 +              return true;
        }
 -      // No need to check WR_CHECKAMMO* or WR_AIM, it should always return true
 -      return true;
 -}
  
  MUTATOR_HOOKFUNCTION(nexball_BallDrop)
  {SELFPARAM();
@@@ -939,8 -926,7 +939,8 @@@ MUTATOR_HOOKFUNCTION(nexball_PlayerPreT
                        if(self.weaponentity.weapons)
                        {
                                self.weapons = self.weaponentity.weapons;
 -                              WEP_ACTION(WEP_PORTO.m_id, WR_RESETPLAYER);
 +                              Weapon w = WEP_NEXBALL;
 +                              w.wr_resetplayer(w);
                                self.switchweapon = self.weaponentity.switchweapon;
                                W_SwitchWeapon(self.switchweapon);
  
@@@ -960,7 -946,7 +960,7 @@@ MUTATOR_HOOKFUNCTION(nexball_PlayerSpaw
        self.weaponentity.weapons = '0 0 0';
  
        if(nexball_mode & NBM_BASKETBALL)
 -              self.weapons |= WEPSET_PORTO;
 +              self.weapons |= WEPSET(NEXBALL);
        else
                self.weapons = '0 0 0';
  
@@@ -977,9 -963,16 +977,9 @@@ MUTATOR_HOOKFUNCTION(nexball_PlayerPhys
        return false;
  }
  
 -MUTATOR_HOOKFUNCTION(nexball_SetStartItems)
 -{
 -      start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
 -
 -      return false;
 -}
 -
  MUTATOR_HOOKFUNCTION(nexball_ForbidThrowing)
  {SELFPARAM();
 -      if(self.weapon == WEP_MORTAR.m_id)
 +      if(self.weapon == WEP_NEXBALL.m_id)
                return true;
  
        return false;
  MUTATOR_HOOKFUNCTION(nexball_FilterItem)
  {SELFPARAM();
        if(self.classname == "droppedweapon")
 -      if(self.weapon == WEP_MORTAR.m_id)
 +      if(self.weapon == WEP_NEXBALL.m_id)
                return true;
  
        return false;
@@@ -1002,6 -995,7 +1002,6 @@@ MUTATOR_DEFINITION(gamemode_nexball
        MUTATOR_HOOK(PlayerSpawn, nexball_PlayerSpawn, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerPreThink, nexball_PlayerPreThink, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerPhysics, nexball_PlayerPhysics, CBC_ORDER_ANY);
 -      MUTATOR_HOOK(SetStartItems, nexball_SetStartItems, CBC_ORDER_ANY);
        MUTATOR_HOOK(ForbidThrowCurrentWeapon, nexball_ForbidThrowing, CBC_ORDER_ANY);
        MUTATOR_HOOK(FilterItem, nexball_FilterItem, CBC_ORDER_ANY);
  
index 6d79bf6f02b1342882b8a3374c2134e8e402ca63,cbe57ca2ac5f671c5c99325a5a2ca196e89a335c..5f057e582dd77d9df74a8baf79a2bdbaf4d54e87
@@@ -7,8 -7,8 +7,8 @@@
  
  #include "../../common/items/all.qc"
  
void spawnfunc_item_minst_cells()
- {SELFPARAM();
spawnfunc(item_minst_cells)
+ {
        if (!g_instagib) { remove(self); return; }
        if (!self.ammo_cells) self.ammo_cells = autocvar_g_instagib_ammo_drop;
        StartItemA(ITEM_VaporizerCells);
@@@ -333,7 -333,7 +333,7 @@@ MUTATOR_HOOKFUNCTION(instagib_SetStartI
        start_ammo_rockets = warmup_start_ammo_rockets = 0;
        start_ammo_fuel    = warmup_start_ammo_fuel    = 0;
  
 -      start_weapons = warmup_start_weapons = WEPSET_VAPORIZER;
 +      start_weapons = warmup_start_weapons = WEPSET(VAPORIZER);
        start_items |= IT_UNLIMITED_SUPERWEAPONS;
  
        return false;
@@@ -357,7 -357,7 +357,7 @@@ MUTATOR_HOOKFUNCTION(instagib_FilterIte
                e.noalign = self.noalign;
          e.cnt = self.cnt;
          e.team = self.team;
-               WITH(entity, self, e, spawnfunc_item_minst_cells());
+               WITH(entity, self, e, spawnfunc_item_minst_cells(e));
                return true;
        }
  
index 27eaed042b07a581163654bbf87112fd7bf6acf9,2114efc92d56cc0c1773e3b74a8c42a1e38d3875..d2086215f931dc94143c9c1e71958e8932d14a0b
@@@ -3,9 -3,9 +3,9 @@@
  
  #include "mutator.qh"
  
 -void W_Blaster_Attack(float, float, float, float, float, float, float, float, float, float);
 +void W_Blaster_Attack(entity, float, float, float, float, float, float, float, float, float, float);
void spawnfunc_weapon_hmg();
void spawnfunc_weapon_rpc();
spawnfunc(weapon_hmg);
spawnfunc(weapon_rpc);
  
  void ok_DecreaseCharge(entity ent, int wep)
  {
@@@ -84,7 -84,7 +84,7 @@@ MUTATOR_HOOKFUNCTION(ok_PlayerDies
        self.ok_item = true;
        self.noalign = true;
        self.pickup_anyway = true;
-       spawnfunc_item_armor_small();
+       spawnfunc_item_armor_small(this);
        self.movetype = MOVETYPE_TOSS;
        self.gravity = 1;
        self.reset = SUB_Remove;
@@@ -149,7 -149,6 +149,7 @@@ MUTATOR_HOOKFUNCTION(ok_PlayerPreThink
                int oldwep = self.weapon;
                self.weapon = WEP_BLASTER.m_id;
                W_Blaster_Attack(
 +                      self,
                        WEP_BLASTER.m_id | HITTYPE_SECONDARY,
                        WEP_CVAR_SEC(vaporizer, shotangle),
                        WEP_CVAR_SEC(vaporizer, damage),
                        self.ok_notice_time = time + 2;
                        play2(self, SND(DRYFIRE));
                }
 +              Weapon wpn = get_weaponinfo(self.weapon);
                if(self.weaponentity.state != WS_CLEAR)
 -                      w_ready();
 +                      w_ready(wpn, self, self.BUTTON_ATCK, self.BUTTON_ATCK2);
  
                self.weapon_blocked = true;
        }
@@@ -207,6 -205,9 +207,9 @@@ MUTATOR_HOOKFUNCTION(ok_PlayerSpawn
        return false;
  }
  
+ void _spawnfunc_weapon_hmg() { SELFPARAM(); spawnfunc_weapon_hmg(this); }
+ void _spawnfunc_weapon_rpc() { SELFPARAM(); spawnfunc_weapon_rpc(this); }
  MUTATOR_HOOKFUNCTION(ok_OnEntityPreSpawn)
  {SELFPARAM();
        if(autocvar_g_powerups)
                        wep.team = self.team;
                        wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
                        wep.pickup_anyway = true;
-                       wep.think = spawnfunc_weapon_hmg;
+                       wep.think = _spawnfunc_weapon_hmg;
                        wep.nextthink = time + 0.1;
                        return true;
                }
                        wep.team = self.team;
                        wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
                        wep.pickup_anyway = true;
-                       wep.think = spawnfunc_weapon_rpc;
+                       wep.think = _spawnfunc_weapon_rpc;
                        wep.nextthink = time + 0.1;
                        return true;
                }
@@@ -274,10 -275,10 +277,10 @@@ MUTATOR_HOOKFUNCTION(ok_SpectateCopy
  
  MUTATOR_HOOKFUNCTION(ok_StartItems)
  {
 -      WepSet ok_start_items = (WEPSET_MACHINEGUN | WEPSET_VORTEX | WEPSET_SHOTGUN);
 +      WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN));
  
 -      if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET_RPC; }
 -      if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET_HMG; }
 +      if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); }
 +      if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); }
  
        start_items |= IT_UNLIMITED_WEAPON_AMMO;
        start_weapons = warmup_start_weapons = ok_start_items;
diff --combined qcsrc/server/progs.inc
index 86ede635042427fc3e36a60d565794120fcf2bfd,5d7af0cd2b85bfe76f5c1b9be8f1e827dae15b99..f743790309fd9f019b3abcc063adedcf874e370d
@@@ -83,6 -83,7 +83,7 @@@
  #include "../common/campaign_file.qc"
  #include "../common/campaign_setup.qc"
  #include "../common/effects/effects.qc"
+ #include "../common/effects/effectinfo.qc"
  #include "../common/mapinfo.qc"
  #include "../common/monsters/spawn.qc"
  #include "../common/monsters/sv_monsters.qc"
  #include "../common/items/all.qc"
  #include "../common/monsters/all.qc"
  #include "../common/mutators/all.qc"
 +#include "../common/turrets/all.qc"
  #include "../common/vehicles/all.qc"
 -#include "../common/weapons/all.qc" // TODO
 +#include "../common/weapons/all.qc"
  
  #include "../common/turrets/sv_turrets.qc"
  #include "../common/turrets/config.qc"
  #include "../common/turrets/util.qc"
 -#include "../common/turrets/all.qc"
  #include "../common/turrets/checkpoint.qc"
  #include "../common/turrets/targettrigger.qc"
  #include "../common/weapons/config.qc"
diff --combined qcsrc/server/t_items.qc
index be4991c2a8e05b07bef76aa0ecc0294f1a82cad8,015d4aeb2cae9d50b22bd734a824a48e150b2e34..337c55ef8729450abf2b9fa7fd2d1a536db69637
@@@ -654,7 -654,7 +654,7 @@@ float Item_GiveTo(entity item, entity p
                        for(i = WEP_FIRST; i <= WEP_LAST; ++i)
                        if(it & WepSet_FromWeapon(i))
                        {
 -                              W_DropEvent(WR_PICKUP, player, i, item);
 +                              W_DropEvent(wr_pickup, player, i, item);
                                W_GiveWeapon(player, i);
                        }
                }
@@@ -1203,8 -1203,8 +1203,8 @@@ void StartItemA (entity a
      StartItem(strzone(a.m_model.model_str()), a.m_sound, a.m_respawntime(), a.m_respawntimejitter(), a.m_name, a.m_itemid, 0, a.m_itemflags, a.m_pickupevalfunc, a.m_botvalue);
  }
  
void spawnfunc_item_rockets()
- {SELFPARAM();
spawnfunc(item_rockets)
+ {
        if(!self.ammo_rockets)
                self.ammo_rockets = g_pickup_rockets;
        if(!self.pickup_anyway)
      StartItemA (ITEM_Rockets);
  }
  
void spawnfunc_item_bullets()
- {SELFPARAM();
spawnfunc(item_bullets)
+ {
        if(!weaponswapping)
        if(autocvar_sv_q3acompat_machineshotgunswap)
        if(self.classname != "droppedweapon")
        {
                weaponswapping = true;
-               spawnfunc_item_shells();
+               spawnfunc_item_shells(this);
                weaponswapping = false;
                return;
        }
      StartItemA (ITEM_Bullets);
  }
  
void spawnfunc_item_cells()
- {SELFPARAM();
spawnfunc(item_cells)
+ {
        if(!self.ammo_cells)
                self.ammo_cells = g_pickup_cells;
        if(!self.pickup_anyway)
        StartItemA (ITEM_Cells);
  }
  
void spawnfunc_item_plasma()
- {SELFPARAM();
spawnfunc(item_plasma)
+ {
        if(!self.ammo_plasma)
                self.ammo_plasma = g_pickup_plasma;
        if(!self.pickup_anyway)
        StartItemA (ITEM_Plasma);
  }
  
void spawnfunc_item_shells()
- {SELFPARAM();
spawnfunc(item_shells)
+ {
        if(!weaponswapping)
        if(autocvar_sv_q3acompat_machineshotgunswap)
        if(self.classname != "droppedweapon")
        {
                weaponswapping = true;
-               spawnfunc_item_bullets();
+               spawnfunc_item_bullets(this);
                weaponswapping = false;
                return;
        }
        StartItemA (ITEM_Shells);
  }
  
void spawnfunc_item_armor_small()
- {SELFPARAM();
spawnfunc(item_armor_small)
+ {
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armorsmall;
        if(!self.max_armorvalue)
        StartItemA (ITEM_ArmorSmall);
  }
  
void spawnfunc_item_armor_medium()
- {SELFPARAM();
spawnfunc(item_armor_medium)
+ {
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armormedium;
        if(!self.max_armorvalue)
        StartItemA (ITEM_ArmorMedium);
  }
  
void spawnfunc_item_armor_big()
- {SELFPARAM();
spawnfunc(item_armor_big)
+ {
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armorbig;
        if(!self.max_armorvalue)
        StartItemA (ITEM_ArmorLarge);
  }
  
void spawnfunc_item_armor_large()
- {SELFPARAM();
spawnfunc(item_armor_large)
+ {
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armorlarge;
        if(!self.max_armorvalue)
        StartItemA (ITEM_ArmorMega);
  }
  
void spawnfunc_item_health_small()
- {SELFPARAM();
spawnfunc(item_health_small)
+ {
        if(!self.max_health)
                self.max_health = g_pickup_healthsmall_max;
        if(!self.health)
        StartItemA (ITEM_HealthSmall);
  }
  
void spawnfunc_item_health_medium()
- {SELFPARAM();
spawnfunc(item_health_medium)
+ {
        if(!self.max_health)
                self.max_health = g_pickup_healthmedium_max;
        if(!self.health)
      StartItemA (ITEM_HealthMedium);
  }
  
void spawnfunc_item_health_large()
- {SELFPARAM();
spawnfunc(item_health_large)
+ {
        if(!self.max_health)
                self.max_health = g_pickup_healthlarge_max;
        if(!self.health)
        StartItemA (ITEM_HealthLarge);
  }
  
void spawnfunc_item_health_mega()
- {SELFPARAM();
spawnfunc(item_health_mega)
+ {
      if(!self.max_health)
          self.max_health = g_pickup_healthmega_max;
      if(!self.health)
  }
  
  // support old misnamed entities
void spawnfunc_item_armor1() { spawnfunc_item_armor_small(); }  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
void spawnfunc_item_armor25() { spawnfunc_item_armor_large(); }
void spawnfunc_item_health1() { spawnfunc_item_health_small(); }
void spawnfunc_item_health25() { spawnfunc_item_health_medium(); }
void spawnfunc_item_health100() { spawnfunc_item_health_mega(); }
spawnfunc(item_armor1) { spawnfunc_item_armor_small(this); }  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
spawnfunc(item_armor25) { spawnfunc_item_armor_large(this); }
spawnfunc(item_health1) { spawnfunc_item_health_small(this); }
spawnfunc(item_health25) { spawnfunc_item_health_medium(this); }
spawnfunc(item_health100) { spawnfunc_item_health_mega(this); }
  
void spawnfunc_item_strength()
- {SELFPARAM();
spawnfunc(item_strength)
+ {
                if(!self.strength_finished)
                        self.strength_finished = autocvar_g_balance_powerup_strength_time;
                StartItemA (ITEM_Strength);
  }
  
void spawnfunc_item_invincible()
- {SELFPARAM();
spawnfunc(item_invincible)
+ {
                if(!self.invincible_finished)
                        self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
                StartItemA (ITEM_Shield);
  }
  
  // compatibility:
void spawnfunc_item_quad() {SELFPARAM(); self.classname = "item_strength";spawnfunc_item_strength();}
spawnfunc(item_quad) { self.classname = "item_strength";spawnfunc_item_strength(this);}
  
  void target_items_use()
  {SELFPARAM();
                centerprint(activator, self.message);
  }
  
void spawnfunc_target_items (void)
- {SELFPARAM();
spawnfunc(target_items)
+ {
        float n, i, j;
        entity e;
        string s;
                                        if(s == e.netname)
                                        {
                                                self.weapons |= WepSet_FromWeapon(j);
 -                                              if(self.spawnflags == 0 || self.spawnflags == 2)
 -                                                      WEP_ACTION(e.weapon, WR_INIT);
 +                                              if(self.spawnflags == 0 || self.spawnflags == 2) {
 +                                                      Weapon w = get_weaponinfo(e.weapon);
 +                                                      w.wr_init(w);
 +                                              }
                                                break;
                                        }
                                }
                        e = get_weaponinfo(j);
                        if(argv(i) == e.netname)
                        {
 -                              WEP_ACTION(e.weapon, WR_INIT);
 +                              Weapon w = get_weaponinfo(e.weapon);
 +                              w.wr_init(w);
                                break;
                        }
                }
        }
  }
  
void spawnfunc_item_fuel(void)
- {SELFPARAM();
spawnfunc(item_fuel)
+ {
        if(!self.ammo_fuel)
                self.ammo_fuel = g_pickup_fuel;
        if(!self.pickup_anyway)
        StartItemA (ITEM_JetpackFuel);
  }
  
void spawnfunc_item_fuel_regen(void)
spawnfunc(item_fuel_regen)
  {
        if(start_items & ITEM_JetpackRegen.m_itemid)
        {
-               spawnfunc_item_fuel();
+               spawnfunc_item_fuel(this);
                return;
        }
        StartItemA (ITEM_JetpackRegen);
  }
  
void spawnfunc_item_jetpack(void)
- {SELFPARAM();
spawnfunc(item_jetpack)
+ {
        if(!self.ammo_fuel)
                self.ammo_fuel = g_pickup_fuel_jetpack;
        if(start_items & ITEM_Jetpack.m_itemid)
        {
-               spawnfunc_item_fuel();
+               spawnfunc_item_fuel(this);
                return;
        }
        StartItemA (ITEM_Jetpack);
@@@ -1831,10 -1828,8 +1831,10 @@@ float GiveItems(entity e, float beginar
                {
                        POSTGIVE_WEAPON(e, j, SND(WEAPONPICKUP), string_null);
                        if (!(save_weapons & WepSet_FromWeapon(j)))
 -                              if(e.weapons & WepSet_FromWeapon(j))
 -                                      WEP_ACTION(wi.weapon, WR_INIT);
 +                              if(e.weapons & WepSet_FromWeapon(j)) {
 +                                      Weapon w = get_weaponinfo(wi.weapon);
 +                                      w.wr_init(w);
 +                              }
                }
        }
        POSTGIVE_VALUE(e, strength_finished, 1, SND(POWERUP), SND(POWEROFF));