From: Mario <mario@smbclan.net>
Date: Sun, 8 May 2016 18:29:54 +0000 (+1000)
Subject: New nade type: Entrap (spawns a large orb for 10 seconds that slows down any enemies... 
X-Git-Tag: xonotic-v0.8.2~807^2~12
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=6b2d411ca49447dd972c6ae84b9b16818366930d;p=xonotic%2Fxonotic-data.pk3dir.git

New nade type: Entrap (spawns a large orb for 10 seconds that slows down any enemies that enter it)
---

diff --git a/gfx/hud/default/nade_entrap.tga b/gfx/hud/default/nade_entrap.tga
new file mode 100644
index 0000000000..358dc1a1f6
Binary files /dev/null and b/gfx/hud/default/nade_entrap.tga differ
diff --git a/gfx/hud/luma/nade_entrap.tga b/gfx/hud/luma/nade_entrap.tga
new file mode 100644
index 0000000000..27287ad3f2
Binary files /dev/null and b/gfx/hud/luma/nade_entrap.tga differ
diff --git a/models/ctf/orb.md3_0.skin b/models/ctf/orb.md3_0.skin
new file mode 100644
index 0000000000..89de3595c5
--- /dev/null
+++ b/models/ctf/orb.md3_0.skin
@@ -0,0 +1 @@
+ons_shield,nade_orb
diff --git a/models/nades/orb.md3 b/models/nades/orb.md3
new file mode 100644
index 0000000000..22d7bcc42d
Binary files /dev/null and b/models/nades/orb.md3 differ
diff --git a/models/nades/orb.md3_0.skin b/models/nades/orb.md3_0.skin
new file mode 100644
index 0000000000..bfeddf2f89
--- /dev/null
+++ b/models/nades/orb.md3_0.skin
@@ -0,0 +1 @@
+shield,nade_orb
diff --git a/mutators.cfg b/mutators.cfg
index 0f49665424..3078379327 100644
--- a/mutators.cfg
+++ b/mutators.cfg
@@ -267,6 +267,12 @@ set g_nades_heal_foe   -2 "Multiplier of health given to enemies"
 set g_nades_pokenade_monster_lifetime 150 "How long pokenade monster will survive"
 set g_nades_pokenade_monster_type "zombie" "Monster to spawn"
 
+// Entrap (8)
+set g_nades_entrap_strength 0.01 "Strength of the orb's movement slowing powers"
+set g_nades_entrap_speed 0.5 "Running speed while entrapped"
+set g_nades_entrap_time 10 "Life time of the orb"
+set g_nades_entrap_radius 500
+
 
 // ============
 //  camp check
diff --git a/qcsrc/common/models/all.inc b/qcsrc/common/models/all.inc
index 08aeaaeb7a..e2a278eb37 100644
--- a/qcsrc/common/models/all.inc
+++ b/qcsrc/common/models/all.inc
@@ -125,7 +125,7 @@ MODEL(PROJECTILE_ROCKETMINSTA_LASER,    "models/elaser.mdl");
 MODEL(PROJECTILE_NADE,                  W_Model("v_ok_grenade.md3"));
 MODEL(NADE_VIEW,                        W_Model("h_ok_grenade.iqm"));
 MODEL(NADE_TIMER,                       "models/ok_nade_counter/ok_nade_counter.md3");
-MODEL(NADE_HEAL,                        "models/ctf/shield.md3");
+MODEL(NADE_ORB,                         "models/nades/orb.md3");
 
 MODEL(GIB_CHUNK,                        "models/gibs/chunk.mdl");
 MODEL(GIB_LEG1,                         "models/gibs/leg1.md3");
diff --git a/qcsrc/common/mutators/mutator/nades/nades.inc b/qcsrc/common/mutators/mutator/nades/nades.inc
index d2f350c5f2..8a7337f610 100644
--- a/qcsrc/common/mutators/mutator/nades/nades.inc
+++ b/qcsrc/common/mutators/mutator/nades/nades.inc
@@ -60,3 +60,11 @@ REGISTER_NADE(MONSTER) {
     NADE_PROJECTILE(0, PROJECTILE_NADE_MONSTER, EFFECT_NADE_TRAIL_RED);
     NADE_PROJECTILE(1, PROJECTILE_NADE_MONSTER_BURN, EFFECT_NADE_TRAIL_BURN_RED);
 }
+
+REGISTER_NADE(ENTRAP) {
+    this.m_color = '0.15 0.85 0';
+    this.m_name = _("Entrap grenade");
+    this.m_icon = "nade_entrap";
+    NADE_PROJECTILE(0, PROJECTILE_NADE_ENTRAP, EFFECT_NADE_TRAIL_YELLOW);
+    NADE_PROJECTILE(1, PROJECTILE_NADE_ENTRAP_BURN, EFFECT_NADE_TRAIL_BURN_YELLOW);
+}
diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc
index cabfd31b81..ab4e2bc6d4 100644
--- a/qcsrc/common/mutators/mutator/nades/nades.qc
+++ b/qcsrc/common/mutators/mutator/nades/nades.qc
@@ -38,10 +38,19 @@ entity Nade_TrailEffect(int proj, int nade_team)
 REGISTER_MUTATOR(cl_nades, true);
 MUTATOR_HOOKFUNCTION(cl_nades, HUD_Draw_overlay)
 {
-	if (STAT(HEALING_ORB) <= time) return false;
-	MUTATOR_ARGV(0, vector) = NADE_TYPE_HEAL.m_color;
-	MUTATOR_ARGV(0, float) = STAT(HEALING_ORB_ALPHA);
-	return true;
+	if (STAT(HEALING_ORB) > time)
+	{
+		MUTATOR_ARGV(0, vector) = NADE_TYPE_HEAL.m_color;
+		MUTATOR_ARGV(0, float) = STAT(HEALING_ORB_ALPHA);
+		return true;
+	}
+	if (STAT(ENTRAP_ORB) > time)
+	{
+		MUTATOR_ARGV(0, vector) = NADE_TYPE_ENTRAP.m_color;
+		MUTATOR_ARGV(0, float) = STAT(ENTRAP_ORB_ALPHA);
+		return true;
+	}
+	return false;
 }
 MUTATOR_HOOKFUNCTION(cl_nades, Ent_Projectile)
 {
@@ -502,23 +511,91 @@ void nade_spawn_boom()
 	self.realowner.nade_spawnloc = spawnloc;
 }
 
-void nade_heal_think()
+void nades_orb_think()
 {SELFPARAM();
-	if(time >= self.ltime)
+	if(time >= this.ltime)
 	{
-		remove(self);
+		remove(this);
 		return;
 	}
 
-	self.nextthink = time;
+	this.nextthink = time;
 
-	if(time >= self.nade_special_time)
+	if(time >= this.nade_special_time)
 	{
-		self.nade_special_time = time+0.25;
-		self.nade_show_particles = 1;
+		this.nade_special_time = time+0.25;
+		this.nade_show_particles = 1;
 	}
 	else
-		self.nade_show_particles = 0;
+		this.nade_show_particles = 0;
+}
+
+entity nades_spawn_orb(entity own, entity realown, vector org, float orb_ltime, float orb_rad)
+{
+	// NOTE: this function merely places an orb
+	// you must add a custom touch function to the returned entity if desired
+	// also set .colormod if you wish to have it colorized
+	entity orb = spawn(); // Net_LinkEntity sets the classname (TODO)
+	orb.owner = own;
+	orb.realowner = realown;
+	setorigin(orb, org);
+
+	orb.orb_lifetime = orb_ltime; // required for timers
+	orb.ltime = time + orb.orb_lifetime;
+	orb.bot_dodge = false;
+	orb.team = realown.team;
+	orb.solid = SOLID_TRIGGER;
+
+	setmodel(orb, MDL_NADE_ORB);
+	orb.orb_radius = orb_rad; // required for fading
+	vector size = '1 1 1' * orb.orb_radius / 2;
+	setsize(orb, -size, size);
+
+	Net_LinkEntity(orb, true, 0, orb_send);
+	orb.SendFlags |= 1;
+
+	orb.think = nades_orb_think;
+	orb.nextthink = time;
+
+	return orb;
+}
+
+void nade_entrap_touch()
+{SELFPARAM();
+	if(DIFF_TEAM(other, self.realowner)) // TODO: what if realowner changes team or disconnects?
+	{
+		if (!isPushable(other))
+			return;
+
+		float pushdeltatime = time - other.lastpushtime;
+		if (pushdeltatime > 0.15) pushdeltatime = 0;
+		other.lastpushtime = time;
+		if(!pushdeltatime) return;
+
+		// div0: ticrate independent, 1 = identity (not 20)
+#ifdef SVQC
+		other.velocity = other.velocity * pow(autocvar_g_nades_entrap_strength, pushdeltatime);
+
+		UpdateCSQCProjectile(other);
+#elif defined(CSQC)
+		other.move_velocity = other.move_velocity * pow(autocvar_g_nades_entrap_strength, pushdeltatime);
+#endif
+
+		if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) )
+		{
+			entity show_tint = (IS_VEHICLE(other)) ? other.owner : other;
+			STAT(ENTRAP_ORB, show_tint) = time + 0.1;
+			STAT(ENTRAP_ORB_ALPHA, show_tint) = 0.75 * (self.ltime - time) / self.orb_lifetime;
+		}
+	}
+}
+
+void nade_entrap_boom(entity this)
+{
+	entity healer = nades_spawn_orb(this.owner, this.realowner, this.origin, autocvar_g_nades_entrap_time, autocvar_g_nades_entrap_radius);
+
+	healer.touch = nade_entrap_touch;
+	healer.colormod = NADE_TYPE_ENTRAP.m_color;
 }
 
 void nade_heal_touch()
@@ -559,34 +636,16 @@ void nade_heal_touch()
 	{
 		entity show_red = (IS_VEHICLE(other)) ? other.owner : other;
 		show_red.stat_healing_orb = time+0.1;
-		show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime;
+		show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.orb_lifetime;
 	}
 }
 
-void nade_heal_boom()
-{SELFPARAM();
-	entity healer;
-	healer = spawn();
-	healer.owner = self.owner;
-	healer.realowner = self.realowner;
-	setorigin(healer, self.origin);
-	healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar
-	healer.ltime = time + healer.healer_lifetime;
-	healer.team = self.realowner.team;
-	healer.bot_dodge = false;
-	healer.solid = SOLID_TRIGGER;
-	healer.touch = nade_heal_touch;
-
-	setmodel(healer, MDL_NADE_HEAL);
-	healer.healer_radius = autocvar_g_nades_nade_radius;
-	vector size = '1 1 1' * healer.healer_radius / 2;
-	setsize(healer,-size,size);
-
-	Net_LinkEntity(healer, true, 0, healer_send);
+void nade_heal_boom(entity this)
+{
+	entity healer = nades_spawn_orb(this.owner, this.realowner, this.origin, autocvar_g_nades_heal_time, autocvar_g_nades_nade_radius);
 
-	healer.think = nade_heal_think;
-	healer.nextthink = time;
-	healer.SendFlags |= 1;
+	healer.touch = nade_heal_touch;
+	healer.colormod = '1 0 0';
 }
 
 void nade_monster_boom()
@@ -633,6 +692,11 @@ void nade_boom()
 			expef = EFFECT_SPAWN_RED;
 			break;
 
+		case NADE_TYPE_ENTRAP:
+			nade_blast = false;
+			expef = EFFECT_SPAWN_YELLOW;
+			break;
+
 		default:
 		case NADE_TYPE_NORMAL:
 			expef = EFFECT_NADE_EXPLODE(self.realowner.team);
@@ -661,8 +725,9 @@ void nade_boom()
 		case NADE_TYPE_ICE: nade_ice_boom(); break;
 		case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(); break;
 		case NADE_TYPE_SPAWN: nade_spawn_boom(); break;
-		case NADE_TYPE_HEAL: nade_heal_boom(); break;
+		case NADE_TYPE_HEAL: nade_heal_boom(self); break;
 		case NADE_TYPE_MONSTER: nade_monster_boom(); break;
+		case NADE_TYPE_ENTRAP: nade_entrap_boom(self); break;
 	}
 
 	FOREACH_ENTITY_ENT(aiment, self,
@@ -1234,6 +1299,16 @@ MUTATOR_HOOKFUNCTION(nades, PlayerPreThink)
 	return false;
 }
 
+MUTATOR_HOOKFUNCTION(nades, PlayerPhysics)
+{SELFPARAM();
+	if (STAT(ENTRAP_ORB, this) > time)
+	{
+		this.stat_sv_maxspeed *= autocvar_g_nades_entrap_speed;
+		this.stat_sv_airspeedlimit_nonqw *= autocvar_g_nades_entrap_speed;
+	}
+	return false;
+}
+
 MUTATOR_HOOKFUNCTION(nades, PlayerSpawn)
 {SELFPARAM();
 	if(autocvar_g_nades_spawn)
@@ -1355,6 +1430,8 @@ MUTATOR_HOOKFUNCTION(nades, SpectateCopy)
 	self.bonus_nade_score = other.bonus_nade_score;
 	self.stat_healing_orb = other.stat_healing_orb;
 	self.stat_healing_orb_alpha = other.stat_healing_orb_alpha;
+	STAT(ENTRAP_ORB, this) = STAT(ENTRAP_ORB, other);
+	STAT(ENTRAP_ORB_ALPHA, this) = STAT(ENTRAP_ORB_ALPHA, other);
 	return false;
 }
 
diff --git a/qcsrc/common/mutators/mutator/nades/nades.qh b/qcsrc/common/mutators/mutator/nades/nades.qh
index da80327bb2..2df7113165 100644
--- a/qcsrc/common/mutators/mutator/nades/nades.qh
+++ b/qcsrc/common/mutators/mutator/nades/nades.qh
@@ -17,6 +17,8 @@ const int PROJECTILE_NADE_HEAL = 80;
 const int PROJECTILE_NADE_HEAL_BURN = 81;
 const int PROJECTILE_NADE_MONSTER = 82;
 const int PROJECTILE_NADE_MONSTER_BURN = 83;
+const int PROJECTILE_NADE_ENTRAP = 84;
+const int PROJECTILE_NADE_ENTRAP_BURN = 85;
 
 REGISTRY(Nades, BITS(4))
 #define Nades_from(i) _Nades_from(i, NADE_TYPE_Null)
@@ -56,8 +58,8 @@ Nade Nade_FromProjectile(int proj)
 
 #include "nades.inc"
 
-.float healer_lifetime;
-.float healer_radius;
+.float orb_lifetime;
+.float orb_radius;
 
 #ifdef SVQC
 
@@ -78,7 +80,7 @@ Nade Nade_FromProjectile(int proj)
 .float stat_healing_orb_alpha = _STAT(HEALING_ORB_ALPHA);
 .float nade_show_particles;
 
-bool healer_send(entity this, entity to, int sf);
+bool orb_send(entity this, entity to, int sf);
 
 // Remove nades that are being thrown
 void nades_Clear(entity player);
diff --git a/qcsrc/common/mutators/mutator/nades/net.qc b/qcsrc/common/mutators/mutator/nades/net.qc
index 70f2218e72..269574cda4 100644
--- a/qcsrc/common/mutators/mutator/nades/net.qc
+++ b/qcsrc/common/mutators/mutator/nades/net.qc
@@ -4,76 +4,82 @@
 
 #ifdef CSQC
 .float ltime;
-void healer_draw(entity this)
+void orb_draw(entity this)
 {
 	float dt = time - this.move_time;
 	this.move_time = time;
 	if(dt <= 0)
 		return;
 
-	this.alpha = (this.ltime - time) / this.healer_lifetime;
-	this.scale = min((1 - this.alpha)*this.healer_lifetime*4,1)*this.healer_radius;
+	this.alpha = (this.ltime - time) / this.orb_lifetime;
+	this.scale = min((1 - this.alpha)*this.orb_lifetime*4,1)*this.orb_radius;
 }
 
-void healer_setup(entity e)
+void orb_setup(entity e)
 {
-	setmodel(e, MDL_NADE_HEAL);
+	setmodel(e, MDL_NADE_ORB);
 
 	setorigin(e, e.origin);
 
 	float model_radius = e.maxs.x;
-	vector size = '1 1 1' * e.healer_radius / 2;
+	vector size = '1 1 1' * e.orb_radius / 2;
 	setsize(e,-size,size);
-	e.healer_radius = e.healer_radius/model_radius*0.6;
+	e.orb_radius = e.orb_radius/model_radius*0.6;
 
-	e.draw = healer_draw;
+	e.draw = orb_draw;
 	e.health = 255;
 	e.movetype = MOVETYPE_NONE;
 	e.solid = SOLID_NOT;
 	e.drawmask = MASK_NORMAL;
 	e.scale = 0.01;
 	e.avelocity = e.move_avelocity = '7 0 11';
-	e.colormod = '1 0 0';
 	e.renderflags |= RF_ADDITIVE;
 }
 #endif
 
-REGISTER_NET_LINKED(Nade_Heal)
+REGISTER_NET_LINKED(Nade_Orb)
 
 #ifdef CSQC
-NET_HANDLE(Nade_Heal, bool isNew)
+NET_HANDLE(Nade_Orb, bool isNew)
 {
-	Net_Accept(Nade_Heal);
+	Net_Accept(Nade_Orb);
 	int sf = ReadByte();
 	if (sf & 1) {
 		this.origin_x = ReadCoord();
 		this.origin_y = ReadCoord();
 		this.origin_z = ReadCoord();
 		setorigin(this, this.origin);
-		this.healer_lifetime = ReadByte();
-		this.healer_radius = ReadShort();
+		this.colormod_x = ReadCoord();
+		this.colormod_y = ReadCoord();
+		this.colormod_z = ReadCoord();
+		this.orb_lifetime = ReadByte();
+		this.orb_radius = ReadShort();
 		this.ltime = time + ReadByte()/10.0;
-		// this.ltime = time + this.healer_lifetime;
-		healer_setup(this);
+		// this.ltime = time + this.orb_lifetime;
+		orb_setup(this);
 	}
 	return true;
 }
 #endif
 
 #ifdef SVQC
-bool healer_send(entity this, entity to, int sf)
+bool orb_send(entity this, entity to, int sf)
 {
 	int channel = MSG_ENTITY;
-	WriteHeader(channel, Nade_Heal);
+	WriteHeader(channel, Nade_Orb);
 	WriteByte(channel, sf);
 	if (sf & 1) {
 		WriteCoord(channel, this.origin.x);
 		WriteCoord(channel, this.origin.y);
 		WriteCoord(channel, this.origin.z);
 
-		WriteByte(channel, this.healer_lifetime);
+		WriteCoord(channel, this.colormod.x);
+		WriteCoord(channel, this.colormod.y);
+		WriteCoord(channel, this.colormod.z);
+
+		WriteByte(channel, this.orb_lifetime);
 		//WriteByte(MSG_ENTITY, this.ltime - time + 1);
-		WriteShort(channel, this.healer_radius);
+		WriteShort(channel, this.orb_radius);
 		// round time delta to a 1/10th of a second
 		WriteByte(channel, (this.ltime - time)*10.0+0.5);
 	}
diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh
index e9828c69ee..54a76b3092 100644
--- a/qcsrc/common/stats.qh
+++ b/qcsrc/common/stats.qh
@@ -122,6 +122,8 @@ REGISTER_STAT(REVIVE_PROGRESS, float)
 REGISTER_STAT(ROUNDLOST, int)
 REGISTER_STAT(BUFF_TIME, float)
 REGISTER_STAT(CTF_FLAGSTATUS, int)
+REGISTER_STAT(ENTRAP_ORB, float)
+REGISTER_STAT(ENTRAP_ORB_ALPHA, float)
 
 #ifdef SVQC
 int autocvar_g_multijump;
diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh
index c565298e8f..d8345f64f8 100644
--- a/qcsrc/server/autocvars.qh
+++ b/qcsrc/server/autocvars.qh
@@ -517,6 +517,10 @@ float autocvar_g_nades_heal_time;
 float autocvar_g_nades_heal_rate;
 float autocvar_g_nades_heal_friend;
 float autocvar_g_nades_heal_foe;
+float autocvar_g_nades_entrap_strength;
+float autocvar_g_nades_entrap_speed;
+float autocvar_g_nades_entrap_radius;
+float autocvar_g_nades_entrap_time;
 string autocvar_g_nades_pokenade_monster_type;
 float autocvar_g_nades_pokenade_monster_lifetime;
 bool autocvar_g_jump_grunt;
diff --git a/textures/nade_orb.tga b/textures/nade_orb.tga
new file mode 100644
index 0000000000..45a97da4fd
Binary files /dev/null and b/textures/nade_orb.tga differ