]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into mirceakitsune/weapon_mine_layer
authorMircea Kitsune <sonichedgehog_hyperblast00@yahoo.com>
Sat, 2 Oct 2010 12:45:48 +0000 (15:45 +0300)
committerMircea Kitsune <sonichedgehog_hyperblast00@yahoo.com>
Sat, 2 Oct 2010 12:45:48 +0000 (15:45 +0300)
24 files changed:
balanceXonotic.cfg
gfx/hud/default/weaponminelayer.tga [new file with mode: 0644]
gfx/hud/luminos/weaponminelayer.tga [new file with mode: 0644]
gfx/hud/old/weaponminelayer.tga [new file with mode: 0644]
maps/minelayer_test.bsp [new file with mode: 0644]
maps/minelayer_test.map [new file with mode: 0644]
models/mine.md3 [new file with mode: 0644]
models/weapons/g_minelayer.md3 [new file with mode: 0644]
models/weapons/h_minelayer.iqm [new file with mode: 0644]
models/weapons/h_minelayer.iqm.framegroups [new file with mode: 0644]
models/weapons/v_minelayer.md3 [new file with mode: 0644]
qcsrc/client/hud.qc
qcsrc/client/projectile.qc
qcsrc/common/constants.qh
qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c
qcsrc/qc-server.cbp
qcsrc/server/defs.qh
qcsrc/server/miscfunctions.qc
qcsrc/server/w_all.qc
qcsrc/server/w_minelayer.qc [new file with mode: 0644]
sound/weapons/mine_det.ogg [new file with mode: 0644]
sound/weapons/mine_exp.ogg [new file with mode: 0644]
sound/weapons/mine_fire.ogg [new file with mode: 0644]
sound/weapons/mine_stick.wav [new file with mode: 0644]

index 076a4935b6404b5fe2a7a40dc4bec0a767754b71..a3d6a0e2a312a19d0d18850f78b8c8a38b0f1d24 100644 (file)
@@ -303,6 +303,27 @@ set g_balance_grenadelauncher_secondary_damageforcescale 0
 set g_balance_grenadelauncher_secondary_bouncefactor 0.7
 set g_balance_grenadelauncher_secondary_bouncestop 0.12
 // }}}
+// {{{ minelayer // TODO
+set g_balance_minelayer_damage 35
+set g_balance_minelayer_edgedamage 30
+set g_balance_minelayer_force 250
+set g_balance_minelayer_radius 175
+set g_balance_minelayer_detectionradius 150
+set g_balance_minelayer_speed 0
+set g_balance_minelayer_speedstart 750
+set g_balance_minelayer_lifetime 60
+set g_balance_minelayer_refire 1.5
+set g_balance_minelayer_animtime 0.4
+set g_balance_minelayer_ammo 5
+set g_balance_minelayer_health 10
+set g_balance_minelayer_limit 4 // 0 disables the limit
+set g_balance_minelayer_damageforcescale 0
+set g_balance_minelayer_detonatedelay -1 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
+set g_balance_minelayer_remote_damage 45
+set g_balance_minelayer_remote_edgedamage 40
+set g_balance_minelayer_remote_radius 200
+set g_balance_minelayer_remote_force 300
+// }}}
 // {{{ electro // TODO
 set g_balance_electro_lightning 1
 set g_balance_electro_primary_damage 90
diff --git a/gfx/hud/default/weaponminelayer.tga b/gfx/hud/default/weaponminelayer.tga
new file mode 100644 (file)
index 0000000..3fbccf9
Binary files /dev/null and b/gfx/hud/default/weaponminelayer.tga differ
diff --git a/gfx/hud/luminos/weaponminelayer.tga b/gfx/hud/luminos/weaponminelayer.tga
new file mode 100644 (file)
index 0000000..3fbccf9
Binary files /dev/null and b/gfx/hud/luminos/weaponminelayer.tga differ
diff --git a/gfx/hud/old/weaponminelayer.tga b/gfx/hud/old/weaponminelayer.tga
new file mode 100644 (file)
index 0000000..444247f
Binary files /dev/null and b/gfx/hud/old/weaponminelayer.tga differ
diff --git a/maps/minelayer_test.bsp b/maps/minelayer_test.bsp
new file mode 100644 (file)
index 0000000..8d778dd
Binary files /dev/null and b/maps/minelayer_test.bsp differ
diff --git a/maps/minelayer_test.map b/maps/minelayer_test.map
new file mode 100644 (file)
index 0000000..ee20966
--- /dev/null
@@ -0,0 +1,79 @@
+\r
+// entity 0\r
+{\r
+"classname" "worldspawn"\r
+// brush 0\r
+{\r
+( 1024 1024 1152 ) ( 1024 -1024 1152 ) ( -1024 1024 1152 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 1024 1152 ) ( -1024 1024 1152 ) ( 1024 1024 1024 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 1024 1152 ) ( 1024 1024 1024 ) ( 1024 -1024 1152 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 -1024 1024 ) ( 1024 -1024 1024 ) ( -1024 1024 1024 ) skies/exosystem 0 0 0 0.5 0.5 0 0 0\r
+( -1024 -1024 1024 ) ( -1024 -1024 1152 ) ( 1024 -1024 1024 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 -1024 1024 ) ( -1024 1024 1024 ) ( -1024 -1024 1152 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+}\r
+// brush 1\r
+{\r
+( -1024 1024 1024 ) ( -1024 -1024 1024 ) ( -1152 1024 1024 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 1024 1024 ) ( -1152 1024 1024 ) ( -1024 1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 1024 1024 ) ( -1024 1024 0 ) ( -1024 -1024 1024 ) eX/eXmetalrib01 0 0 0 1 1 0 0 0\r
+( -1152 -1024 0 ) ( -1024 -1024 0 ) ( -1152 1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1152 -1024 0 ) ( -1152 -1024 1024 ) ( -1024 -1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1152 -1024 0 ) ( -1152 1024 0 ) ( -1152 -1024 1024 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+}\r
+// brush 2\r
+{\r
+( 1152 1024 1024 ) ( 1152 -1024 1024 ) ( 1024 1024 1024 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1152 1024 1024 ) ( 1024 1024 1024 ) ( 1152 1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1152 1024 1024 ) ( 1152 1024 0 ) ( 1152 -1024 1024 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 -1024 0 ) ( 1152 -1024 0 ) ( 1024 1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 -1024 0 ) ( 1024 -1024 1024 ) ( 1152 -1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 -1024 0 ) ( 1024 1024 0 ) ( 1024 -1024 1024 ) eX/eXmetalrib01 0 0 0 1 1 0 0 0\r
+}\r
+// brush 3\r
+{\r
+( -1024 -1024 1024 ) ( 1024 -1024 1024 ) ( -1024 -1152 1024 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 -1024 1024 ) ( -1024 -1152 1024 ) ( -1024 -1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 -1024 1024 ) ( -1024 -1024 0 ) ( 1024 -1024 1024 ) eX/eXmetalrib01 0 0 0 1 1 0 0 0\r
+( 1024 -1152 0 ) ( 1024 -1024 0 ) ( -1024 -1152 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 -1152 0 ) ( 1024 -1152 1024 ) ( 1024 -1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 -1152 0 ) ( -1024 -1152 0 ) ( 1024 -1152 1024 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+}\r
+// brush 4\r
+{\r
+( -1024 1152 1024 ) ( 1024 1152 1024 ) ( -1024 1024 1024 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 1152 1024 ) ( -1024 1024 1024 ) ( -1024 1152 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 1152 1024 ) ( -1024 1152 0 ) ( 1024 1152 1024 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 1024 0 ) ( 1024 1152 0 ) ( -1024 1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 1024 0 ) ( 1024 1024 1024 ) ( 1024 1152 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 1024 0 ) ( -1024 1024 0 ) ( 1024 1024 1024 ) eX/eXmetalrib01 0 0 0 1 1 0 0 0\r
+}\r
+// brush 5\r
+{\r
+( 1024 1024 0 ) ( 1024 -1024 0 ) ( -1024 1024 0 ) eX/eXmetalFloor02 0 0 0 1 1 0 0 0\r
+( 1024 1024 0 ) ( -1024 1024 0 ) ( 1024 1024 -128 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( 1024 1024 0 ) ( 1024 1024 -128 ) ( 1024 -1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 -1024 -128 ) ( 1024 -1024 -128 ) ( -1024 1024 -128 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 -1024 -128 ) ( -1024 -1024 0 ) ( 1024 -1024 -128 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+( -1024 -1024 -128 ) ( -1024 1024 -128 ) ( -1024 -1024 0 ) common/caulk 0 0 0 0.25 0.25 0 0 0\r
+}\r
+}\r
+// entity 1\r
+{\r
+"classname" "info_player_deathmatch"\r
+"origin" "0.000000 0.000000 32.000000"\r
+}\r
+// entity 2\r
+{\r
+"classname" "item_rockets"\r
+"origin" "256.000000 128.000000 32.000000"\r
+}\r
+// entity 3\r
+{\r
+"classname" "item_rockets"\r
+"origin" "256.000000 -128.000000 32.000000"\r
+}\r
+// entity 4\r
+{\r
+"classname" "weapon_minelayer"\r
+"origin" "256.000000 0.000000 32.000000"\r
+}\r
diff --git a/models/mine.md3 b/models/mine.md3
new file mode 100644 (file)
index 0000000..7758e3e
Binary files /dev/null and b/models/mine.md3 differ
diff --git a/models/weapons/g_minelayer.md3 b/models/weapons/g_minelayer.md3
new file mode 100644 (file)
index 0000000..f0dce9d
Binary files /dev/null and b/models/weapons/g_minelayer.md3 differ
diff --git a/models/weapons/h_minelayer.iqm b/models/weapons/h_minelayer.iqm
new file mode 100644 (file)
index 0000000..0c6ed41
Binary files /dev/null and b/models/weapons/h_minelayer.iqm differ
diff --git a/models/weapons/h_minelayer.iqm.framegroups b/models/weapons/h_minelayer.iqm.framegroups
new file mode 100644 (file)
index 0000000..0a59625
--- /dev/null
@@ -0,0 +1,4 @@
+1 8 20 0 // fire
+9 5 20 0 // fire2
+15 200 20 1 // idle
+215 40 20 0 // reload
diff --git a/models/weapons/v_minelayer.md3 b/models/weapons/v_minelayer.md3
new file mode 100644 (file)
index 0000000..5d06b3c
Binary files /dev/null and b/models/weapons/v_minelayer.md3 differ
index c1dbede921a44231d5f157cf383d697b23dd248d..0f54a2857f578fe96fedd305bba7663a7aa903ee 100644 (file)
@@ -1478,6 +1478,7 @@ float GetAmmoTypeForWep(float i)
                case WEP_UZI: return 1;
                case WEP_CAMPINGRIFLE: return 1;
                case WEP_GRENADE_LAUNCHER: return 2;
+               case WEP_MINE_LAYER: return 2;
                case WEP_ELECTRO: return 3;
                case WEP_CRYLINK: return 3;
                case WEP_HLAC: return 3;
index 9a773379b5d6eed97a57ae1189f2651ffc6c67d8..39aabc44adf4a430f357c0e6e82d4a6d07e596ef 100644 (file)
@@ -279,6 +279,7 @@ void Ent_Projectile()
                        case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
                        case PROJECTILE_GRENADE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
                        case PROJECTILE_GRENADE_BOUNCING: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
+                       case PROJECTILE_MINE: setmodel(self, "models/mine.md3");self.traileffect = particleeffectnum(""); break;
                        case PROJECTILE_LASER: setmodel(self, "models/laser.mdl");self.traileffect = particleeffectnum(""); break;
                        case PROJECTILE_HLAC: setmodel(self, "models/hlac_bullet.md3");self.traileffect = particleeffectnum(""); break;
                        case PROJECTILE_PORTO_RED: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
@@ -330,6 +331,10 @@ void Ent_Projectile()
                                self.move_bounce_factor = g_balance_grenadelauncher_secondary_bouncefactor;
                                self.move_bounce_stopspeed = g_balance_grenadelauncher_secondary_bouncestop;
                                break;
+                       case PROJECTILE_MINE:
+                               self.mins = '-4 -4 -4';
+                               self.maxs = '4 4 4';
+                               break;
                        case PROJECTILE_PORTO_RED:
                                self.colormod = '2 1 1';
                                self.alphamod = 0.5;
@@ -407,6 +412,7 @@ void Projectile_Precache()
        precache_model("models/ebomb.mdl");
        precache_model("models/elaser.mdl");
        precache_model("models/grenademodel.md3");
+       precache_model("models/mine.md3");
        precache_model("models/hagarmissile.mdl");
        precache_model("models/hlac_bullet.md3");
        precache_model("models/laser.mdl");
index 5ebeb54659e4ba7e936c0666077f967fa5cd22b5..f03875bfc1c9e04493da812c0bbb9bc7c23ae932 100644 (file)
@@ -454,20 +454,21 @@ float PROJECTILE_CRYLINK = 5;
 float PROJECTILE_ELECTRO_BEAM = 6;
 float PROJECTILE_GRENADE = 7;
 float PROJECTILE_GRENADE_BOUNCING = 8;
-float PROJECTILE_LASER = 9;
-float PROJECTILE_HLAC = 10;
-float PROJECTILE_SEEKER = 11;
-float PROJECTILE_FLAC = 12;
-float PROJECTILE_PORTO_RED = 13;
-float PROJECTILE_PORTO_BLUE = 14;
-float PROJECTILE_HOOKBOMB = 15;
-float PROJECTILE_HAGAR = 16;
-float PROJECTILE_HAGAR_BOUNCING = 17;
-float PROJECTILE_BULLET_GLOWING = 18;
-float PROJECTILE_CRYLINK_BOUNCING = 19;
-float PROJECTILE_FIREBALL = 20;
-float PROJECTILE_FIREMINE = 21;
-float PROJECTILE_BULLET_GLOWING_TRACER = 22;
+float PROJECTILE_MINE = 9;
+float PROJECTILE_LASER = 10;
+float PROJECTILE_HLAC = 11;
+float PROJECTILE_SEEKER = 12;
+float PROJECTILE_FLAC = 13;
+float PROJECTILE_PORTO_RED = 14;
+float PROJECTILE_PORTO_BLUE = 15;
+float PROJECTILE_HOOKBOMB = 16;
+float PROJECTILE_HAGAR = 17;
+float PROJECTILE_HAGAR_BOUNCING = 18;
+float PROJECTILE_BULLET_GLOWING = 19;
+float PROJECTILE_CRYLINK_BOUNCING = 20;
+float PROJECTILE_FIREBALL = 21;
+float PROJECTILE_FIREMINE = 22;
+float PROJECTILE_BULLET_GLOWING_TRACER = 23;
 
 float SPECIES_HUMAN        =  0;
 float SPECIES_ROBOT_SOLID  =  1;
index 517f68bd311b7ee10176b8f8ab1e9b8a65127905..c0d16a959727cb2c7bf3de5b980153b7eba124ea 100644 (file)
@@ -268,7 +268,7 @@ void XonoticMutatorsDialog_fill(entity me)
                me.TDempty(me, 0.2);
                me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_start_weapon_laser", "0", "No start weapons"));
                        e.cvarOffValue = "-1";
-                       makeMulti(e, "g_start_weapon_shotgun g_start_weapon_uzi g_start_weapon_grenadelauncher g_start_weapon_electro g_start_weapon_crylink g_start_weapon_nex g_start_weapon_hagar g_start_weapon_rocketlauncher g_start_weapon_campingrifle g_start_weapon_hlac g_start_weapon_seeker g_start_weapon_minstanex g_start_weapon_hook g_start_weapon_porto g_start_weapon_tuba");
+                       makeMulti(e, "g_start_weapon_shotgun g_start_weapon_uzi g_start_weapon_grenadelauncher g_start_weapon_minelayer g_start_weapon_electro g_start_weapon_crylink g_start_weapon_nex g_start_weapon_hagar g_start_weapon_rocketlauncher g_start_weapon_campingrifle g_start_weapon_hlac g_start_weapon_seeker g_start_weapon_minstanex g_start_weapon_hook g_start_weapon_porto g_start_weapon_tuba");
 
        me.gotoRC(me, me.rows - 1, 0);
                me.TD(me, 1, me.columns, e = makeXonoticButton("OK", '0 0 0'));
index 00912bea3389eb6347f9f18bd68416a2ea975f99..eaa30d2d46ae4ca1d4936fe77220542541c1dad9 100644 (file)
                <Unit filename="w_electro.qc" />
                <Unit filename="w_fireball.qc" />
                <Unit filename="w_grenadelauncher.qc" />
+               <Unit filename="w_minelayer.qc" />
                <Unit filename="w_hagar.qc" />
                <Unit filename="w_hlac.qc" />
                <Unit filename="w_hook.qc" />
index f084e18d4b0b69f6a92b5009321e419e583145c5..47f514c32b07096e485bb994fc1be4b2090214f9 100644 (file)
@@ -294,6 +294,7 @@ string getTimeoutText(float addOneSecond);
 .entity flagcarried;
 
 .entity lastrocket;
+.entity lastmine;
 
 .float playerid;
 float playerid_last;
index 3db4e47be8d2e154732916bda9d1de44bee0cad8..d4232424072f84aa90d2c7fd220371e4a91aa923 100644 (file)
@@ -989,7 +989,7 @@ void readplayerstartcvars()
        if (g_weaponarena)
        {
                start_weapons = g_weaponarena;
-               if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
+               if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_MINE_LAYER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
                        start_ammo_rockets = 999;
                if (g_weaponarena & WEPBIT_SHOTGUN)
                        start_ammo_shells = 999;
index 9a9b3830244602e00861704efe6716a2229f6304..83fc8acb5de47e9f58406588fdebce20099ba899 100644 (file)
@@ -2,6 +2,7 @@
 #include "w_shotgun.qc"
 #include "w_uzi.qc"
 #include "w_grenadelauncher.qc"
+#include "w_minelayer.qc"
 #include "w_electro.qc"
 #include "w_crylink.qc"
 #include "w_nex.qc"
diff --git a/qcsrc/server/w_minelayer.qc b/qcsrc/server/w_minelayer.qc
new file mode 100644 (file)
index 0000000..8709bd1
--- /dev/null
@@ -0,0 +1,387 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(MINE_LAYER, w_minelayer, IT_ROCKETS, 9, WEP_FLAG_NORMAL | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_HIGH, "minelayer", "minelayer", "Mine Layer");
+#else
+#ifdef SVQC
+.float minelayer_detonate;
+.float mine_number;
+
+void spawnfunc_weapon_minelayer (void)
+{
+       weapon_defaultspawnfunc(WEP_MINE_LAYER);
+}
+
+void W_Mine_Unregister()
+{
+       if(self.owner && self.owner.lastmine == self)
+               self.owner.lastmine = world;
+}
+
+void W_Mine_Explode ()
+{
+       W_Mine_Unregister();
+
+       if(other.takedamage == DAMAGE_AIM)
+               if(other.classname == "player")
+                       if(IsDifferentTeam(self.owner, other))
+                               if(IsFlying(other))
+                                       AnnounceTo(self.owner, "airshot");
+
+       self.event_damage = SUB_Null;
+       self.takedamage = DAMAGE_NO;
+
+       RadiusDamage (self, self.owner, cvar("g_balance_minelayer_damage"), cvar("g_balance_minelayer_edgedamage"), cvar("g_balance_minelayer_radius"), world, cvar("g_balance_minelayer_force"), self.projectiledeathtype, other);
+
+       if (self.owner.weapon == WEP_MINE_LAYER)
+       {
+               if(self.owner.ammo_rockets < cvar("g_balance_minelayer_ammo"))
+               {
+                       self.owner.cnt = WEP_MINE_LAYER;
+                       ATTACK_FINISHED(self.owner) = time;
+                       self.owner.switchweapon = w_getbestweapon(self.owner);
+               }
+       }
+       remove (self);
+}
+
+void W_Mine_DoRemoteExplode ()
+{
+       W_Mine_Unregister();
+
+       self.event_damage = SUB_Null;
+       self.takedamage = DAMAGE_NO;
+
+       RadiusDamage (self, self.owner, cvar("g_balance_minelayer_remote_damage"), cvar("g_balance_minelayer_remote_edgedamage"), cvar("g_balance_minelayer_remote_radius"), world, cvar("g_balance_minelayer_remote_force"), self.projectiledeathtype | HITTYPE_BOUNCE, world);
+
+       if (self.owner.weapon == WEP_MINE_LAYER)
+       {
+               if(self.owner.ammo_rockets < cvar("g_balance_minelayer_ammo"))
+               {
+                       self.owner.cnt = WEP_MINE_LAYER;
+                       ATTACK_FINISHED(self.owner) = time;
+                       self.owner.switchweapon = w_getbestweapon(self.owner);
+               }
+       }
+       remove (self);
+}
+
+void W_Mine_RemoteExplode()
+{
+       if(self.owner.deadflag == DEAD_NO)
+       if(self.owner.lastmine)
+       {
+               if((self.spawnshieldtime >= 0)
+                       ? (time >= self.spawnshieldtime) // timer
+                       : (vlen(NearestPointOnBox(self.owner, self.origin) - self.origin) > cvar("g_balance_minelayer_radius")) // safety device
+               )
+               {
+                       W_Mine_DoRemoteExplode();
+               }
+       }
+}
+
+void W_Mine_Think (void)
+{
+       self.nextthink = time;
+       if (time > self.cnt)
+       {
+               other = world;
+               self.projectiledeathtype |= HITTYPE_BOUNCE;
+               W_Mine_Explode ();
+               return;
+       }
+
+       // detect players who are close the mine and explode if the player should detonate it
+       entity head;
+       head = findradius(self.origin, cvar("g_balance_minelayer_detectionradius"));
+
+       while(head)
+       {
+               if(head.classname == "player" && head.deadflag == DEAD_NO)
+               if(head != self.owner)
+               if(IsDifferentTeam(head, self.owner)) // don't detonate for team mates
+                       W_Mine_Explode();
+               head = head.chain;
+       }
+
+       // remote detonation
+       if (self.owner.weapon == WEP_MINE_LAYER)
+       if (self.owner.deadflag == DEAD_NO)
+       if (self.minelayer_detonate)
+               W_Mine_RemoteExplode();
+
+       if(self.csqcprojectile_clientanimate == 0)
+               UpdateCSQCProjectile(self);
+}
+
+void W_Mine_Touch (void)
+{
+       PROJECTILE_TOUCH;
+       spamsound (self, CHAN_PROJECTILE, "weapons/mine_stick.wav", VOL_BASE, ATTN_NORM);
+       self.movetype = MOVETYPE_NONE; // lock the mine in place
+       // TODO: make sure this doesn't cause the mine to get stuck in the air if it falls over a moving entity
+}
+
+void W_Mine_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       if (self.health <= 0)
+               return;
+       self.health = self.health - damage;
+       self.angles = vectoangles(self.velocity);
+       if (self.health <= 0)
+               W_PrepareExplosionByDamage(attacker, W_Mine_Explode);
+}
+
+void W_Mine_Attack (void)
+{
+       local entity mine;
+       local entity flash;
+
+       // scan how many mines we placed, and return if we reached our limit
+       if(cvar("g_balance_minelayer_limit"))
+       {
+               entity mine;
+               self.mine_number = 0;
+               for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
+                       self.mine_number += 1;
+
+               if(self.mine_number >= cvar("g_balance_minelayer_limit"))
+               {
+                       // the refire delay keeps this message from being spammed
+                       sprint(self, strcat("You cannot place more than ^2", cvar_string("g_balance_minelayer_limit"), " ^7mines at a time\n") );
+                       play2(self, "weapons/unavailable.wav");
+                       return;
+               }
+       }
+
+       if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
+               self.ammo_rockets = self.ammo_rockets - cvar("g_balance_minelayer_ammo");
+
+       W_SetupShot_ProjectileSize (self, '-4 -4 -4', '4 4 4', FALSE, 5, "weapons/mine_fire.wav", cvar("g_balance_minelayer_damage"));
+       pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       mine = WarpZone_RefSys_SpawnSameRefSys(self);
+       mine.owner = self;
+       self.lastmine = mine;
+       if(cvar("g_balance_minelayer_detonatedelay") >= 0)
+               mine.spawnshieldtime = time + cvar("g_balance_minelayer_detonatedelay");
+       else
+               mine.spawnshieldtime = -1;
+       mine.classname = "mine";
+       mine.bot_dodge = TRUE;
+       mine.bot_dodgerating = cvar("g_balance_minelayer_damage") * 2; // * 2 because it can detonate inflight which makes it even more dangerous
+
+       mine.takedamage = DAMAGE_YES;
+       mine.damageforcescale = cvar("g_balance_minelayer_damageforcescale");
+       mine.health = cvar("g_balance_minelayer_health");
+       mine.event_damage = W_Mine_Damage;
+
+       mine.movetype = MOVETYPE_TOSS;
+       PROJECTILE_MAKETRIGGER(mine);
+       mine.projectiledeathtype = WEP_MINE_LAYER;
+       setsize (mine, '-4 -4 -4', '4 4 4'); // give it some size so it can be shot
+
+       setorigin (mine, w_shotorg - v_forward * 4); // move it back so it hits the wall at the right point
+       W_SetupProjectileVelocity(mine, cvar("g_balance_minelayer_speedstart"), 0);
+       mine.angles = vectoangles (mine.velocity);
+
+       mine.touch = W_Mine_Touch;
+       mine.think = W_Mine_Think;
+       mine.nextthink = time;
+       mine.cnt = time + cvar("g_balance_minelayer_lifetime");
+       mine.flags = FL_PROJECTILE;
+
+       CSQCProjectile(mine, FALSE, PROJECTILE_MINE, TRUE);
+
+       // muzzle flash for 1st person view
+       flash = spawn ();
+       setmodel (flash, "models/flash.md3"); // precision set below
+       SUB_SetFade (flash, time, 0.1);
+       flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+       W_AttachToShotorg(flash, '5 0 0');
+
+       // common properties
+}
+
+void spawnfunc_weapon_minelayer (void); // defined in t_items.qc
+
+float w_minelayer(float req)
+{
+       entity mine;
+       float minfound;
+       if (req == WR_AIM)
+       {
+               // aim and decide to fire if appropriate
+               self.BUTTON_ATCK = bot_aim(cvar("g_balance_minelayer_speed"), 0, cvar("g_balance_minelayer_lifetime"), FALSE);
+               if(skill >= 2) // skill 0 and 1 bots won't detonate mines!
+               {
+                       // decide whether to detonate mines
+                       local entity mine, targetlist, targ;
+                       local float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
+                       local float selfdamage, teamdamage, enemydamage;
+                       edgedamage = cvar("g_balance_minelayer_edgedamage");
+                       coredamage = cvar("g_balance_minelayer_damage");
+                       edgeradius = cvar("g_balance_minelayer_radius");
+                       recipricoledgeradius = 1 / edgeradius;
+                       selfdamage = 0;
+                       teamdamage = 0;
+                       enemydamage = 0;
+                       targetlist = findchainfloat(bot_attack, TRUE);
+                       mine = find(world, classname, "mine");
+                       while (mine)
+                       {
+                               if (mine.owner != self)
+                               {
+                                       mine = find(mine, classname, "mine");
+                                       continue;
+                               }
+                               targ = targetlist;
+                               while (targ)
+                               {
+                                       d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - mine.origin);
+                                       d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
+                                       // count potential damage according to type of target
+                                       if (targ == self)
+                                               selfdamage = selfdamage + d;
+                                       else if (targ.team == self.team && teams_matter)
+                                               teamdamage = teamdamage + d;
+                                       else if (bot_shouldattack(targ))
+                                               enemydamage = enemydamage + d;
+                                       targ = targ.chain;
+                               }
+                               mine = find(mine, classname, "mine");
+                       }
+                       local float desirabledamage;
+                       desirabledamage = enemydamage;
+                       if (teamplay != 1 && time > self.invincible_finished && time > self.spawnshieldtime)
+                               desirabledamage = desirabledamage - selfdamage * cvar("g_balance_selfdamagepercent");
+                       if (self.team && teamplay != 1)
+                               desirabledamage = desirabledamage - teamdamage;
+
+                       mine = find(world, classname, "mine");
+                       while (mine)
+                       {
+                               if (mine.owner != self)
+                               {
+                                       mine = find(mine, classname, "mine");
+                                       continue;
+                               }
+                               makevectors(mine.v_angle);
+                               targ = targetlist;
+                               if (skill > 9) // normal players only do this for the target they are tracking
+                               {
+                                       targ = targetlist;
+                                       while (targ)
+                                       {
+                                               if (
+                                                       (v_forward * normalize(mine.origin - targ.origin)< 0.1)
+                                                       && desirabledamage > 0.1*coredamage
+                                               )self.BUTTON_ATCK2 = TRUE;
+                                               targ = targ.chain;
+                                       }
+                               }else{
+                                       local float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000);
+                                       //As the distance gets larger, a correct detonation gets near imposible
+                                       //Bots are assumed to use the mine spawnfunc_light to see if the mine gets near a player
+                                       if(v_forward * normalize(mine.origin - self.enemy.origin)< 0.1)
+                                               if(self.enemy.classname == "player")
+                                                       if(desirabledamage >= 0.1*coredamage)
+                                                               if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1))
+                                                                       self.BUTTON_ATCK2 = TRUE;
+                               //      dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
+                               }
+
+                               mine = find(mine, classname, "mine");
+                       }
+                       // if we would be doing at X percent of the core damage, detonate it
+                       // but don't fire a new shot at the same time!
+                       if (desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events
+                               self.BUTTON_ATCK2 = TRUE;
+                       if ((skill > 6.5) && (selfdamage > self.health))
+                               self.BUTTON_ATCK2 = FALSE;
+                       //if(self.BUTTON_ATCK2 == TRUE)
+                       //      dprint(ftos(desirabledamage),"\n");
+                       if (self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE;
+               }
+       }
+       else if (req == WR_THINK)
+       {
+               if (self.BUTTON_ATCK)
+               {
+                       if(weapon_prepareattack(0, cvar("g_balance_minelayer_refire")))
+                       {
+                               W_Mine_Attack();
+                               weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_minelayer_animtime"), w_ready);
+                       }
+               }
+
+               if (self.BUTTON_ATCK2)
+               {
+                       minfound = 0;
+                       for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
+                       {
+                               if(!mine.minelayer_detonate)
+                               {
+                                       mine.minelayer_detonate = TRUE;
+                                       minfound = 1;
+                               }
+                       }
+                       if(minfound)
+                               sound (self, CHAN_WEAPON2, "weapons/mine_det.wav", VOL_BASE, ATTN_NORM);
+               }
+       }
+       else if (req == WR_PRECACHE)
+       {
+               precache_model ("models/flash.md3");
+               precache_model ("models/weapons/g_minelayer.md3");
+               precache_model ("models/weapons/v_minelayer.md3");
+               precache_model ("models/weapons/h_minelayer.iqm");
+               precache_sound ("weapons/mine_det.wav");
+               precache_sound ("weapons/mine_fire.wav");
+               precache_sound ("weapons/mine_stick.wav");
+       }
+       else if (req == WR_SETUP)
+       {
+               weapon_setup(WEP_MINE_LAYER);
+       }
+       else if (req == WR_CHECKAMMO1)
+       {
+               // don't switch while placing a mine
+               if ((ATTACK_FINISHED(self) <= time || self.weapon != WEP_MINE_LAYER)
+                       && self.ammo_rockets < cvar("g_balance_minelayer_ammo"))
+                       return FALSE;
+       }
+       else if (req == WR_CHECKAMMO2)
+               return FALSE;
+       return TRUE;
+};
+#endif
+#ifdef CSQC
+float w_minelayer(float req)
+{
+       if(req == WR_IMPACTEFFECT)
+       {
+               vector org2;
+               org2 = w_org + w_backoff * 12;
+               pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
+               if(!w_issilent)
+                       sound(self, CHAN_PROJECTILE, "weapons/mine_exp.wav", VOL_BASE, ATTN_NORM);
+       }
+       else if(req == WR_PRECACHE)
+       {
+               precache_sound("weapons/mine_exp.wav");
+       }
+       else if (req == WR_SUICIDEMESSAGE)
+               w_deathtypestring = "%s exploded";
+       else if (req == WR_KILLMESSAGE)
+       {
+               if(w_deathtype & HITTYPE_BOUNCE) // (remote detonation)
+                       w_deathtypestring = "%s got too close to %s's mine";
+               else if(w_deathtype & HITTYPE_SPLASH)
+                       w_deathtypestring = "%s almost dodged %s's mine";
+               else
+                       w_deathtypestring = "%s stepped on %s's mine";
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/sound/weapons/mine_det.ogg b/sound/weapons/mine_det.ogg
new file mode 100644 (file)
index 0000000..3dd5e12
Binary files /dev/null and b/sound/weapons/mine_det.ogg differ
diff --git a/sound/weapons/mine_exp.ogg b/sound/weapons/mine_exp.ogg
new file mode 100644 (file)
index 0000000..664c444
Binary files /dev/null and b/sound/weapons/mine_exp.ogg differ
diff --git a/sound/weapons/mine_fire.ogg b/sound/weapons/mine_fire.ogg
new file mode 100644 (file)
index 0000000..a6408b3
Binary files /dev/null and b/sound/weapons/mine_fire.ogg differ
diff --git a/sound/weapons/mine_stick.wav b/sound/weapons/mine_stick.wav
new file mode 100644 (file)
index 0000000..ae2cb49
Binary files /dev/null and b/sound/weapons/mine_stick.wav differ