]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Experimental new weapon: Flak Cannon
authorMario <zacjardine@y7mail.com>
Thu, 12 Feb 2015 19:44:30 +0000 (06:44 +1100)
committerMario <zacjardine@y7mail.com>
Thu, 12 Feb 2015 19:44:30 +0000 (06:44 +1100)
qcsrc/client/weapons/projectile.qc
qcsrc/common/constants.qh
qcsrc/common/effects.qh
qcsrc/common/notifications.qh
qcsrc/common/weapons/all.qh
qcsrc/common/weapons/w_flak.qc [new file with mode: 0644]
qcsrc/common/weapons/w_flak.qh [new file with mode: 0644]

index 54c606b39e196e274cd4448e6b3c7a82bf5dff0c..f4cab7ba009afe5519fddfca8b6b5a32507c7066 100644 (file)
@@ -1,4 +1,5 @@
 #include "projectile.qh"
+#include "../../common/weapons/w_flak.qh"
 
 void SUB_Stop()
 {
@@ -307,6 +308,9 @@ void Ent_Projectile()
                        case PROJECTILE_RPC: setmodel(self, "models/weapons/ok_rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); break;
                        case PROJECTILE_CANNONBALL: setmodel(self, "models/sphere/sphere.md3");self.traileffect = particleeffectnum("TR_ROCKET"); break;
 
+                       case PROJECTILE_FLAK: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_rifle_weak"); break;
+                       case PROJECTILE_FLAK_BOMB: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_KNIGHTSPIKE"); break;
+
                        default:
                                if(Nade_IDFromProjectile(self.cnt) != 0) { setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum(Nade_TrailEffect(self.cnt, self.team)); break; }
                                error("Received invalid CSQC projectile, can't work with this!");
@@ -350,6 +354,7 @@ void Ent_Projectile()
                                self.mins = '-32 -32 -32';
                                self.maxs = '32 32 32';
                                break;
+                       case PROJECTILE_FLAK_BOMB:
                        case PROJECTILE_GRENADE:
                                self.mins = '-3 -3 -3';
                                self.maxs = '3 3 3';
@@ -362,6 +367,12 @@ void Ent_Projectile()
                                self.move_bounce_factor = g_balance_mortar_bouncefactor;
                                self.move_bounce_stopspeed = g_balance_mortar_bouncestop;
                                break;
+                       case PROJECTILE_FLAK:
+                               self.mins = '0 0 -3';
+                               self.maxs = '0 0 -3';
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = Flak_Touch;
+                               break;
                        case PROJECTILE_SHAMBLER_LIGHTNING:
                                self.mins = '-8 -8 -8';
                                self.maxs = '8 8 8';
index e9deb2eec0eefefbcadc1b848f0d942295ea7373..9691d222b30793e9e5d254567136cef78e781c6c 100644 (file)
@@ -254,7 +254,9 @@ const int PROJECTILE_ROCKETMINSTA_LASER = 35;
 const int PROJECTILE_SUPERROCKET = 36;
 const int PROJECTILE_CANNONBALL = 38;
 
-const int PROJECTILE_RPC = 60;
+const int PROJECTILE_RPC = 39;
+const int PROJECTILE_FLAK = 40;
+const int PROJECTILE_FLAK_BOMB = 41;
 
 const int SPECIES_HUMAN = 0;
 const int SPECIES_ROBOT_SOLID = 1;
index 69662c41398acd74ea3cb113c949ac325d52b320..1378e5e1a50b4cc705217fc514f2dae4b7ac4b26 100644 (file)
@@ -32,6 +32,7 @@
     EFFECT(0, EFFECT_SHOTGUN_MUZZLEFLASH,       "shotgun_muzzleflash") \
     EFFECT(0, EFFECT_GRENADE_MUZZLEFLASH,       "grenadelauncher_muzzleflash") \
     EFFECT(0, EFFECT_GRENADE_EXPLODE,           "grenade_explode") \
+    EFFECT(0, EFFECT_FLAK_BOUNCE,               "flak_bounce") \
     EFFECT(0, EFFECT_CRYLINK_JOINEXPLODE,       "crylink_joinexplode") \
     EFFECT(0, EFFECT_CRYLINK_MUZZLEFLASH,       "crylink_muzzleflash") \
     EFFECT(0, EFFECT_VORTEX_MUZZLEFLASH,        "nex_muzzleflash") \
index e8f5c675a47026c9e2aa1564a86ddf8d1c459a2c..fdb48e287d64f3979513c02cea864865d4542885 100644 (file)
@@ -574,6 +574,9 @@ void Send_Notification_WOCOVA(
     MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_MURDER_FIREMINE,      3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponfireball",         _("^BG%s%s^K1 got burnt by ^BG%s^K1's firemine%s%s"), "") \
     MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_SUICIDE_BLAST,        2, 1, "s1 s2loc spree_lost", "s1",                 "weaponfireball",         _("^BG%s^K1 should have used a smaller gun%s%s"), "") \
     MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_SUICIDE_FIREMINE,     2, 1, "s1 s2loc spree_lost", "s1",                 "weaponfireball",         _("^BG%s^K1 forgot about their firemine%s%s"), "") \
+    MSG_INFO_NOTIF(1, INFO_WEAPON_FLAK_MURDER_SPLASH,            3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponflak",             _("^BG%s%s^K1 could not hide from ^BG%s^K1's Flak Cannon%s%s"), "") \
+    MSG_INFO_NOTIF(1, INFO_WEAPON_FLAK_MURDER_SPRAY,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponflak",             _("^BG%s%s^K1 was shredded by ^BG%s^K1's Flak Cannon%s%s"), "") \
+    MSG_INFO_NOTIF(1, INFO_WEAPON_FLAK_SUICIDE,                  2, 1, "s1 s2loc spree_lost", "s1",                 "weaponflak",             _("^BG%s^K1 was shredded by their own Flak Cannon%s%s"), "") \
     MSG_INFO_NOTIF(1, INFO_WEAPON_HAGAR_MURDER_BURST,            3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponhagar",            _("^BG%s%s^K1 was pummeled by a burst of ^BG%s^K1's Hagar rockets%s%s"), "") \
     MSG_INFO_NOTIF(1, INFO_WEAPON_HAGAR_MURDER_SPRAY,            3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponhagar",            _("^BG%s%s^K1 was pummeled by ^BG%s^K1's Hagar rockets%s%s"), "") \
     MSG_INFO_NOTIF(1, INFO_WEAPON_HAGAR_SUICIDE,                 2, 1, "s1 s2loc spree_lost", "s1",                 "weaponhagar",            _("^BG%s^K1 played with tiny Hagar rockets%s%s"), "") \
@@ -999,6 +1002,9 @@ void Send_Notification_WOCOVA(
     MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_MURDER_FIREMINE,      NO_MSG,        INFO_WEAPON_FIREBALL_MURDER_FIREMINE,      NO_MSG) \
     MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_SUICIDE_BLAST,        NO_MSG,        INFO_WEAPON_FIREBALL_SUICIDE_BLAST,        CENTER_DEATH_SELF_GENERIC) \
     MSG_MULTI_NOTIF(1, WEAPON_FIREBALL_SUICIDE_FIREMINE,     NO_MSG,        INFO_WEAPON_FIREBALL_SUICIDE_FIREMINE,     CENTER_DEATH_SELF_GENERIC) \
+    MSG_MULTI_NOTIF(1, WEAPON_FLAK_MURDER_SPLASH,            NO_MSG,        INFO_WEAPON_FLAK_MURDER_SPLASH,            NO_MSG) \
+    MSG_MULTI_NOTIF(1, WEAPON_FLAK_MURDER_SPRAY,             NO_MSG,        INFO_WEAPON_FLAK_MURDER_SPRAY,             NO_MSG) \
+    MSG_MULTI_NOTIF(1, WEAPON_FLAK_SUICIDE,                  NO_MSG,        INFO_WEAPON_FLAK_SUICIDE,                  NO_MSG) \
     MSG_MULTI_NOTIF(1, WEAPON_HAGAR_MURDER_BURST,            NO_MSG,        INFO_WEAPON_HAGAR_MURDER_BURST,            NO_MSG) \
     MSG_MULTI_NOTIF(1, WEAPON_HAGAR_MURDER_SPRAY,            NO_MSG,        INFO_WEAPON_HAGAR_MURDER_SPRAY,            NO_MSG) \
     MSG_MULTI_NOTIF(1, WEAPON_HAGAR_SUICIDE,                 NO_MSG,        INFO_WEAPON_HAGAR_SUICIDE,                 CENTER_DEATH_SELF_GENERIC) \
index 27f1666e7ae8c18388b7aa1310b9119e9f275ad8..9b9d175bc0bc7b57916367fe283bf8402191ad9e 100644 (file)
@@ -42,5 +42,6 @@
 // more other weapons
 #include "w_revolver.qc"
 #include "w_lightsabre.qc"
+#include "w_flak.qc"
 
 //#endif
diff --git a/qcsrc/common/weapons/w_flak.qc b/qcsrc/common/weapons/w_flak.qc
new file mode 100644 (file)
index 0000000..4344bf7
--- /dev/null
@@ -0,0 +1,444 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id  */ FLAK,
+/* function  */ W_Flak,
+/* ammotype  */ ammo_shells,
+/* impulse   */ 8,
+/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
+/* rating    */ BOT_PICKUP_RATING_MID,
+/* color     */ '1 1 0',
+/* modelname */ "flak",
+/* simplemdl */ "foobar",
+/* crosshair */ "gfx/crosshairflak 0.8",
+/* wepimg    */ "weaponflak",
+/* refname   */ "flak",
+/* wepname   */ _("Flak Cannon")
+);
+
+#define FLAK_SETTINGS(w_cvar,w_prop) FLAK_SETTINGS_LIST(w_cvar, w_prop, FLAK, flak)
+#define FLAK_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
+       w_cvar(id, sn, BOTH, ammo) \
+       w_cvar(id, sn, BOTH, animtime) \
+       w_cvar(id, sn, BOTH, bouncefactor) \
+       w_cvar(id, sn, BOTH, count) \
+       w_cvar(id, sn, BOTH, damage) \
+       w_cvar(id, sn, BOTH, damage_bouncefactor) \
+       w_cvar(id, sn, BOTH, force) \
+       w_cvar(id, sn, BOTH, gravity) \
+       w_cvar(id, sn, BOTH, refire) \
+       w_cvar(id, sn, BOTH, speed) \
+       w_cvar(id, sn, BOTH, speed_up) \
+       w_cvar(id, sn, BOTH, spread_side) \
+       w_cvar(id, sn, BOTH, spread_up) \
+       w_cvar(id, sn, BOTH, passthrough) \
+       w_cvar(id, sn, BOTH, lifetime) \
+       w_cvar(id, sn, NONE, bomb_ammo) \
+       w_cvar(id, sn, NONE, bomb) \
+       w_cvar(id, sn, NONE, bomb_count) \
+       w_cvar(id, sn, NONE, bomb_damage) \
+       w_cvar(id, sn, NONE, bomb_damageforcescale) \
+       w_cvar(id, sn, NONE, bomb_edgedamage) \
+       w_cvar(id, sn, NONE, bomb_force) \
+       w_cvar(id, sn, NONE, bomb_health) \
+       w_cvar(id, sn, NONE, bomb_lifetime) \
+       w_cvar(id, sn, NONE, bomb_radius) \
+       w_cvar(id, sn, NONE, bomb_speed) \
+       w_cvar(id, sn, NONE, bomb_speed_up) \
+       w_cvar(id, sn, NONE, bomb_spread_side) \
+       w_cvar(id, sn, NONE, bomb_spread_up) \
+       w_prop(id, sn, float,  reloading_ammo, reload_ammo) \
+       w_prop(id, sn, float,  reloading_time, reload_time) \
+       w_prop(id, sn, float,  switchdelay_raise, switchdelay_raise) \
+       w_prop(id, sn, float,  switchdelay_drop, switchdelay_drop) \
+       w_prop(id, sn, string, weaponreplace, weaponreplace) \
+       w_prop(id, sn, float,  weaponstart, weaponstart) \
+       w_prop(id, sn, float,  weaponstartoverride, weaponstartoverride) \
+       w_prop(id, sn, float,  weaponthrowable, weaponthrowable)
+
+#ifdef SVQC
+FLAK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
+#endif
+#else
+#ifdef SVQC
+#include "../effects.qh"
+
+void spawnfunc_weapon_flak(void) { weapon_defaultspawnfunc(WEP_FLAK); }
+
+void W_Flak_Projectile_Touch(void)
+{
+       PROJECTILE_TOUCH;
+
+       if(other.takedamage)
+       {
+               float damage, bnc, frc;
+               bool isprimary = !(self.projectiledeathtype & HITTYPE_SECONDARY);
+
+               if(other != self.enemy && (other != self.realowner || (self.projectiledeathtype & HITTYPE_BOUNCE)))
+               {
+                       bnc = pow(WEP_CVAR_BOTH(flak, isprimary, damage_bouncefactor), self.cnt);
+                       damage = WEP_CVAR_BOTH(flak, isprimary, damage) * bnc;
+                       frc = WEP_CVAR_BOTH(flak, isprimary, force) * bnc;
+
+                       vector force = normalize(NearestPointOnBox(other, self.origin) - self.origin + self.velocity) * frc;
+
+                       self.owner = self.realowner;
+                       Damage(other, self, self.realowner, damage, self.projectiledeathtype, self.origin, force);
+
+                       if(accuracy_isgooddamage(self.realowner, other))
+                               { accuracy_add(self.realowner, WEP_FLAK, 0, damage); }
+               }
+
+               //Send_Effect(EFFECT_FLAK_BOUNCE, self.origin, self.velocity, 1);
+
+               bool passThrough = WEP_CVAR_BOTH(flak, isprimary, passthrough);
+
+               if(passThrough <= 0)
+               {
+                       remove(self);
+                       return;
+               }
+
+               // semi-fix for "melee" hits
+               if(other == self.realowner && !(self.projectiledeathtype & HITTYPE_BOUNCE))
+                       self.velocity = self.velocity * -1;
+
+               // pass through - we don't want to bounce here, overwrite velocity later
+               self.oldvelocity = self.velocity * passThrough;
+
+               // we don't want to hit the same entity again right away
+               self.owner = other;
+               if(other.iscreature)
+                       self.enemy = other;
+       }
+       else if(other.solid == SOLID_BSP)
+       {   
+               spamsound(self, CH_SHOTS, W_Sound(strcat("casings", ftos(floor(random() * 3) + 1))), VOL_BASE * 0.7, ATTN_NORM);
+               //pointparticles(particleeffectnum("flak_bounce"), self.origin, self.velocity, 1);
+               self.owner = world;
+               self.projectiledeathtype |= HITTYPE_BOUNCE;
+               self.cnt += 1;
+       }
+}
+
+void W_Flak_Projectile_Think(void)
+{
+       if(time > self.spawnshieldtime)
+       {
+               remove(self);
+               return;
+       }
+
+       if(self.oldvelocity)
+       {
+               self.velocity = self.oldvelocity;
+               self.oldvelocity = '0 0 0';
+               UpdateCSQCProjectile(self);
+       }
+
+       self.nextthink = time;
+}
+
+entity W_Flak_Projectile(entity o, bool isprimary, float damage, float grav, float lt, float spd, float upspd, float bnc, float deathtype)
+{
+       entity e = spawn();
+       e.owner = e.realowner = o;
+       e.classname = "flak_proj";
+       e.bot_dodge = true;
+       e.bot_dodgerating = damage;
+       e.movetype = MOVETYPE_BOUNCE;
+       PROJECTILE_MAKETRIGGER(e);
+       e.projectiledeathtype = deathtype;
+       e.gravity = grav;
+       e.bouncefactor = bnc;
+       setorigin(e, w_shotorg);
+       setsize(e, '0 0 -3', '0 0 -3');
+
+       e.spawnshieldtime = time + lt;
+       e.nextthink = time;
+       e.think = W_Flak_Projectile_Think;
+       e.touch = W_Flak_Projectile_Touch;
+       W_SetupProjVelocity_Explicit(e, v_forward, v_up, spd, upspd, 0, 0, false);
+
+       e.angles = vectoangles(e.velocity);
+       e.flags = FL_PROJECTILE;
+       e.missile_flags = MIF_ARC;
+
+       CSQCProjectile(e, true, PROJECTILE_FLAK, true);   
+       return e;
+}
+
+void W_Flak_Attack1(bool isprimary)
+{
+       float pcount = WEP_CVAR_BOTH(flak, isprimary, count), i;
+       int dtype = (isprimary) ? WEP_FLAK : (WEP_FLAK | HITTYPE_SECONDARY | HITTYPE_BOUNCE);
+
+       W_DecreaseAmmo(WEP_CVAR_BOTH(flak, isprimary, ammo));
+
+       W_SetupShot_ProjectileSize(self, '0 0 -3', '0 0 -3', false, 4,
+                                                          W_Sound(((isprimary) ? "flak_fire2" : "flak_fire")), CH_WEAPON_A, WEP_CVAR_BOTH(flak, isprimary, damage) * pcount);
+       w_shotdir = v_forward;
+       vector a;
+
+       for(i = 0; i < pcount; ++i)
+       {
+               a = fixedvectoangles(w_shotdir);
+
+               fixedmakevectors(a + '1 0 0' *  random() * WEP_CVAR_BOTH(flak, isprimary, spread_up) + '0 1 0' * crandom() * WEP_CVAR_BOTH(flak, isprimary, spread_side));
+
+               W_Flak_Projectile(self, isprimary,
+                                                 WEP_CVAR_BOTH(flak, isprimary, damage),
+                                                 WEP_CVAR_BOTH(flak, isprimary, gravity),
+                                                 WEP_CVAR_BOTH(flak, isprimary, lifetime),
+                                                 WEP_CVAR_BOTH(flak, isprimary, speed),
+                                                 WEP_CVAR_BOTH(flak, isprimary, speed_up),
+                                                 WEP_CVAR_BOTH(flak, isprimary, bouncefactor),
+                                                 dtype);
+       }
+
+       entity flash = spawn();
+       setmodel(flash, "models/uziflash.md3");
+       flash.think = SUB_Remove;
+       flash.nextthink = time + 0.06;
+       flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+       W_AttachToShotorg(flash, '5 0 0');
+}
+
+void W_Flak_Bomb_Explode(void)
+{
+       vector dir = normalize(self.velocity);
+       dir = dir - 2 * (dir * trace_plane_normal) * trace_plane_normal; 
+
+       if(other.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(other))
+                       if(DIFF_TEAM(self.realowner, other))
+                               if(other.deadflag == DEAD_NO)
+                                       if(IsFlying(other))
+                                               Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
+
+       self.event_damage = func_null;
+       self.takedamage = DAMAGE_NO;
+       RadiusDamage(self, self.owner, WEP_CVAR(flak, bomb_damage), WEP_CVAR(flak, bomb_edgedamage), WEP_CVAR(flak, bomb_radius),
+                                world, world, WEP_CVAR(flak, bomb_force), self.projectiledeathtype, other);
+
+       float i, c = WEP_CVAR(flak, bomb_count);
+       w_shotorg = self.origin;
+
+       float spread_up, spread_side;
+
+       if(other.takedamage == DAMAGE_AIM && other.iscreature)
+       {
+               spread_side = 360;
+               spread_up = 360;
+       }
+       else
+       {
+               spread_side = WEP_CVAR(flak, bomb_spread_side);
+               spread_up = WEP_CVAR(flak, bomb_spread_up);
+       }
+
+       for(i = 0; i < c; ++i)
+       {
+               vector a = fixedvectoangles(dir);
+
+               fixedmakevectors(a + '1 0 0' * crandom() * spread_up
+                                                  + '0 1 0' * crandom() * spread_side);
+
+               entity p = W_Flak_Projectile(self.owner, false,
+                                                 WEP_CVAR_SEC(flak, damage),
+                                                 WEP_CVAR_SEC(flak, gravity),
+                                                 WEP_CVAR_SEC(flak, lifetime),
+                                                 WEP_CVAR_SEC(flak, speed),
+                                                 WEP_CVAR_SEC(flak, speed_up),
+                                                 WEP_CVAR_SEC(flak, bouncefactor),
+                                                 WEP_FLAK | HITTYPE_SECONDARY | HITTYPE_BOUNCE);
+
+               // do not hit the direct hit entity with shrapnel right away (causes very inconsistent behavior)
+               p.owner = other;
+       }
+
+       remove (self);
+}
+
+void W_Flak_Bomb_Touch(void)
+{
+       PROJECTILE_TOUCH;
+       self.use();
+}
+
+void W_Flak_Bomb_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       if(self.health <= 0)
+               return;
+
+       if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
+               return; // g_projectiles_damage says to halt
+
+       self.health = self.health - damage;
+
+       if(self.health <= 0)
+               W_PrepareExplosionByDamage(attacker, self.use);
+}
+
+void W_Flak_Attack3(void)
+{
+       if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
+               self.ammo_rockets -= WEP_CVAR(flak, bomb_ammo);
+
+       W_DecreaseAmmo(WEP_CVAR_SEC(flak, ammo));
+
+       W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', false, 4, W_Sound("flak_fire3"), CH_WEAPON_A, WEP_CVAR(flak, bomb_damage));
+       w_shotdir = v_forward;
+
+       entity e = spawn();
+       e.owner = e.realowner = self;
+       e.classname = "flak_bomb";
+       e.bot_dodge = true;
+       e.bot_dodgerating = WEP_CVAR(flak, bomb_damage);
+       e.movetype = MOVETYPE_BOUNCE;
+       PROJECTILE_MAKETRIGGER(e);
+       e.projectiledeathtype = WEP_FLAK | HITTYPE_SECONDARY;
+       setsize(e, '0 0 -3', '0 0 -3');
+       setorigin(e, w_shotorg);
+
+       e.nextthink = time + WEP_CVAR(flak, bomb_lifetime);
+       e.think = adaptor_think2use;
+       e.use = W_Flak_Bomb_Explode;
+       e.touch = W_Flak_Bomb_Touch;
+       e.takedamage = DAMAGE_YES;
+       e.health = WEP_CVAR(flak, bomb_health);
+       e.damageforcescale = WEP_CVAR(flak, bomb_damageforcescale);
+       e.event_damage = W_Flak_Bomb_Damage;
+       W_SetupProjVelocity_Explicit(e, w_shotdir, v_up, WEP_CVAR(flak, bomb_speed), WEP_CVAR(flak, bomb_speed_up), 0, 0, false);
+
+       e.angles = vectoangles(e.velocity);
+       e.flags = FL_PROJECTILE;
+       e.missile_flags = MIF_SPLASH | MIF_ARC | MIF_PROXY;
+
+       CSQCProjectile(e, true, PROJECTILE_FLAK_BOMB, true);
+}
+
+float W_Flak(float req)
+{
+       float ammo_amount;
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       if(random() < 0.35 && WEP_CVAR(flak, bomb) && self.ammo_rockets >= WEP_CVAR(flak, bomb_ammo))
+                               self.BUTTON_ATCK2 = bot_aim(WEP_CVAR(flak, bomb_speed), WEP_CVAR(flak, bomb_speed_up), WEP_CVAR(flak, bomb_lifetime), true);
+                       else
+                               self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(flak, speed), WEP_CVAR_PRI(flak, speed_up), WEP_CVAR_PRI(flak, lifetime), true);
+                       return true;
+               }
+               case WR_THINK:
+               {
+                       if(self.BUTTON_ATCK)
+                       if(weapon_prepareattack(0, WEP_CVAR_PRI(flak, refire)))
+                       {
+                               W_Flak_Attack1(true);
+                               weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(flak, animtime), w_ready);
+                       }
+
+                       if(self.BUTTON_ATCK2)
+                       if(weapon_prepareattack(1, WEP_CVAR_SEC(flak, refire)))
+                       {
+                               if(WEP_CVAR(flak, bomb))
+                                       W_Flak_Attack3();
+                               else
+                                       W_Flak_Attack1(false);
+                               weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(flak, animtime), w_ready);
+                       }
+
+                       return true;
+               }
+               case WR_INIT:
+               {
+                       precache_model(W_Model("g_flak.md3"));
+                       precache_model(W_Model("v_flak.md3"));
+                       precache_model(W_Model("h_flak.iqm"));
+                       precache_sound(W_Sound("flak_fire"));
+                       precache_sound(W_Sound("flak_fire2"));
+                       precache_sound(W_Sound("flak_fire3"));
+                       precache_sound(W_Sound("casings1"));
+                       precache_sound(W_Sound("casings2"));
+                       precache_sound(W_Sound("casings3"));
+                       FLAK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
+                       return true;
+               }
+               case WR_CONFIG:
+               {
+                       FLAK_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
+                       return true;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       ammo_amount = self.WEP_AMMO(FLAK) >= WEP_CVAR_PRI(flak, ammo);
+                       ammo_amount += self.(weapon_load[WEP_FLAK]) >= WEP_CVAR_PRI(flak, ammo);
+                       return ammo_amount;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       // can't use above logic here, as we have 2 ammo types - WEAPONTODO
+                       return self.ammo_shells >= WEP_CVAR_SEC(flak, ammo) && (!WEP_CVAR(flak, bomb) || self.ammo_rockets >= WEP_CVAR(flak, bomb_ammo));
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(min(WEP_CVAR_PRI(flak, ammo), WEP_CVAR_SEC(flak, ammo), WEP_CVAR(flak, bomb_ammo)), W_Sound("reload"));
+                       return true;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_FLAK_SUICIDE;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
+                               return WEAPON_FLAK_MURDER_SPLASH;
+                       else
+                               return WEAPON_FLAK_MURDER_SPRAY;
+               }
+       }
+
+       return true;
+}
+#endif
+#ifdef CSQC
+.float last_bounce;
+void Flak_Touch(void)
+{
+       if(time >= self.last_bounce)
+       {
+               self.last_bounce = time + 0.1; // spam
+       pointparticles(particleeffectnum("flak_bounce"), self.move_origin, self.move_velocity, 1);
+       }
+}
+
+float W_Flak(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 12;
+                       pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
+                       if(!w_issilent)
+                               sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+
+                       return true;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/rocket_impact.wav");
+                       return true;
+               }
+               case WR_ZOOMRETICLE:
+               {
+                       // no weapon specific image for this weapon
+                       return false;
+               }
+       }
+       return false;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_flak.qh b/qcsrc/common/weapons/w_flak.qh
new file mode 100644 (file)
index 0000000..d6613bf
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef W_FLAK_H
+#define W_FLAK_H
+
+#ifdef CSQC
+void Flak_Touch();
+#endif
+
+#endif
\ No newline at end of file