1 #include "sv_instagib.qh"
3 int autocvar_g_instagib_ammo_drop;
4 int autocvar_g_instagib_extralives;
5 float autocvar_g_instagib_speed_highspeed;
7 #include <server/client.qh>
9 #include <common/items/_mod.qh>
11 REGISTER_MUTATOR(mutator_instagib, cvar("g_instagib") && !g_nexball);
13 spawnfunc(item_minst_cells)
15 if (!g_instagib) { delete(this); return; }
16 if (!this.ammo_cells) this.ammo_cells = autocvar_g_instagib_ammo_drop;
17 StartItem(this, ITEM_VaporizerCells);
20 void instagib_invisibility(entity this)
22 this.strength_finished = autocvar_g_balance_powerup_strength_time;
23 StartItem(this, ITEM_Invisibility);
26 void instagib_extralife(entity this)
28 StartItem(this, ITEM_ExtraLife);
31 void instagib_speed(entity this)
33 this.invincible_finished = autocvar_g_balance_powerup_invincible_time;
34 StartItem(this, ITEM_Speed);
37 .float instagib_nextthink;
38 .float instagib_needammo;
39 void instagib_stop_countdown(entity e)
41 if (!e.instagib_needammo)
43 Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_INSTAGIB_FINDAMMO);
44 e.instagib_needammo = false;
46 void instagib_ammocheck(entity this)
48 if(time < this.instagib_nextthink)
51 return; // not a player
53 if(IS_DEAD(this) || gameover)
54 instagib_stop_countdown(this);
55 else if (this.ammo_cells > 0 || (this.items & IT_UNLIMITED_WEAPON_AMMO) || (this.flags & FL_GODMODE))
56 instagib_stop_countdown(this);
57 else if(autocvar_g_rm && autocvar_g_rm_laser)
59 if(!this.instagib_needammo)
61 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE);
62 this.instagib_needammo = true;
67 this.instagib_needammo = true;
70 Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
71 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED);
73 else if (this.health <= 10)
75 Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
76 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_1);
78 else if (this.health <= 20)
80 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
81 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_2);
83 else if (this.health <= 30)
85 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
86 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_3);
88 else if (this.health <= 40)
90 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
91 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_4);
93 else if (this.health <= 50)
95 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
96 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_5);
98 else if (this.health <= 60)
100 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
101 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_6);
103 else if (this.health <= 70)
105 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
106 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_7);
108 else if (this.health <= 80)
110 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
111 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_8);
113 else if (this.health <= 90)
115 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO);
116 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
117 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_9);
121 Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO);
122 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0');
125 this.instagib_nextthink = time + 1;
128 MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd)
130 FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(instagib_stop_countdown(it)));
133 MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem)
135 entity item = M_ARGV(1, entity);
137 item.monster_loot = spawnfunc_item_minst_cells;
140 MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn)
142 entity mon = M_ARGV(0, entity);
144 // always refill ammo
145 if(mon.monsterid == MON_MAGE.monsterid)
149 MUTATOR_HOOKFUNCTION(mutator_instagib, BotShouldAttack)
151 entity targ = M_ARGV(1, entity);
153 if (targ.items & ITEM_Invisibility.m_itemid)
157 MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver)
159 entity player = M_ARGV(0, entity);
161 instagib_stop_countdown(player);
164 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerSpawn)
166 entity player = M_ARGV(0, entity);
168 player.effects |= EF_FULLBRIGHT;
171 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPreThink)
173 entity player = M_ARGV(0, entity);
175 instagib_ammocheck(player);
178 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen)
180 // no regeneration in instagib
184 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPowerups)
186 entity player = M_ARGV(0, entity);
188 if (!(player.effects & EF_FULLBRIGHT))
189 player.effects |= EF_FULLBRIGHT;
191 if (player.items & ITEM_Invisibility.m_itemid)
193 play_countdown(player, player.strength_finished, SND_POWEROFF);
194 if (time > player.strength_finished)
196 player.alpha = default_player_alpha;
197 player.exteriorweaponentity.alpha = default_weapon_alpha;
198 player.items &= ~ITEM_Invisibility.m_itemid;
199 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY);
204 if (time < player.strength_finished)
206 player.alpha = autocvar_g_instagib_invis_alpha;
207 player.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha;
208 player.items |= ITEM_Invisibility.m_itemid;
209 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_INVISIBILITY, player.netname);
210 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_INVISIBILITY);
214 if (player.items & ITEM_Speed.m_itemid)
216 play_countdown(player, player.invincible_finished, SND_POWEROFF);
217 if (time > player.invincible_finished)
219 player.items &= ~ITEM_Speed.m_itemid;
220 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_SPEED);
225 if (time < player.invincible_finished)
227 player.items |= ITEM_Speed.m_itemid;
228 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SPEED, player.netname);
229 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_SPEED);
234 .float stat_sv_maxspeed;
236 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPhysics)
238 entity player = M_ARGV(0, entity);
240 if(player.items & ITEM_Speed.m_itemid)
241 player.stat_sv_maxspeed = player.stat_sv_maxspeed * autocvar_g_instagib_speed_highspeed;
244 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor)
246 M_ARGV(4, float) = M_ARGV(7, float); // take = damage
247 M_ARGV(5, float) = 0; // save
250 MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon)
252 // weapon dropping on death handled by FilterItem
256 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_Calculate)
258 entity frag_attacker = M_ARGV(1, entity);
259 entity frag_target = M_ARGV(2, entity);
260 float frag_deathtype = M_ARGV(3, float);
261 float frag_damage = M_ARGV(4, float);
262 float frag_mirrordamage = M_ARGV(5, float);
263 vector frag_force = M_ARGV(6, vector);
265 if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker))
268 if(IS_PLAYER(frag_target))
270 if(frag_deathtype == DEATH_FALL.m_id)
271 frag_damage = 0; // never count fall damage
273 if(!autocvar_g_instagib_damagedbycontents)
274 switch(DEATH_ENT(frag_deathtype))
283 if(IS_PLAYER(frag_attacker))
284 if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
286 if(!autocvar_g_instagib_friendlypush && SAME_TEAM(frag_target, frag_attacker))
287 frag_force = '0 0 0';
289 if(frag_target.armorvalue)
291 frag_target.armorvalue -= 1;
293 frag_target.damage_dealt += 1;
294 frag_attacker.damage_dealt += 1;
295 Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue);
299 if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
301 if(frag_deathtype & HITTYPE_SECONDARY)
303 if(!autocvar_g_instagib_blaster_keepdamage || frag_attacker == frag_target)
306 if(!autocvar_g_instagib_mirrordamage)
307 frag_mirrordamage = 0; // never do mirror damage on enemies
310 if(frag_target != frag_attacker)
312 if(frag_damage <= 0 && frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); }
313 if(!autocvar_g_instagib_blaster_keepforce)
314 frag_force = '0 0 0';
320 if(!autocvar_g_instagib_mirrordamage) // only apply the taking lives hack if we don't want to support real damage mirroring
321 if(IS_PLAYER(frag_attacker))
322 if(frag_mirrordamage > 0)
324 // just lose extra LIVES, don't kill the player for mirror damage
325 if(frag_attacker.armorvalue > 0)
327 frag_attacker.armorvalue -= 1;
328 Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue);
329 frag_attacker.damage_dealt += frag_mirrordamage;
331 frag_mirrordamage = 0;
334 if(frag_target.alpha && frag_target.alpha < 1)
335 if(IS_PLAYER(frag_target))
338 M_ARGV(4, float) = frag_damage;
339 M_ARGV(5, float) = frag_mirrordamage;
340 M_ARGV(6, vector) = frag_force;
343 MUTATOR_HOOKFUNCTION(mutator_instagib, SetStartItems)
345 start_health = warmup_start_health = 100;
346 start_armorvalue = warmup_start_armorvalue = 0;
348 start_ammo_shells = warmup_start_ammo_shells = 0;
349 start_ammo_nails = warmup_start_ammo_nails = 0;
350 start_ammo_cells = warmup_start_ammo_cells = cvar("g_instagib_ammo_start");
351 start_ammo_plasma = warmup_start_ammo_plasma = 0;
352 start_ammo_rockets = warmup_start_ammo_rockets = 0;
353 start_ammo_fuel = warmup_start_ammo_fuel = 0;
355 start_weapons = warmup_start_weapons = WEPSET(VAPORIZER);
356 start_items |= IT_UNLIMITED_SUPERWEAPONS;
359 MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
361 entity item = M_ARGV(0, entity);
363 if(item.classname == "item_cells")
364 return true; // no normal cells?
366 if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon")
368 item.ammo_cells = autocvar_g_instagib_ammo_drop;
372 if(item.weapon == WEP_DEVASTATOR.m_id || item.weapon == WEP_VORTEX.m_id)
375 setorigin(e, item.origin);
376 e.noalign = item.noalign;
379 e.spawnfunc_checked = true;
380 spawnfunc_item_minst_cells(e);
384 if(item.flags & FL_POWERUP)
387 if(item.ammo_cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells")
388 item.ammo_cells = autocvar_g_instagib_ammo_drop;
390 if(item.ammo_cells && !item.weapon)
396 MUTATOR_HOOKFUNCTION(mutator_instagib, CustomizeWaypoint)
398 entity wp = M_ARGV(0, entity);
399 entity player = M_ARGV(1, entity);
401 entity e = WaypointSprite_getviewentity(player);
403 // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
404 // but only apply this to real players, not to spectators
405 if((wp.owner.flags & FL_CLIENT) && (wp.owner.items & ITEM_Invisibility.m_itemid) && (e == player))
406 if(DIFF_TEAM(wp.owner, e))
410 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies)
412 float frag_deathtype = M_ARGV(3, float);
414 if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
415 M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death
418 MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch)
420 entity item = M_ARGV(0, entity);
421 entity toucher = M_ARGV(1, entity);
425 // play some cool sounds ;)
426 if (IS_CLIENT(toucher))
428 if(toucher.health <= 5)
429 Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND);
430 else if(toucher.health < 50)
431 Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY);
434 if(toucher.health < 100)
435 toucher.health = 100;
437 return MUT_ITEMTOUCH_CONTINUE;
440 if(item.itemdef == ITEM_ExtraLife)
442 toucher.armorvalue = bound(toucher.armorvalue, 999, toucher.armorvalue + autocvar_g_instagib_extralives);
443 Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES);
444 return MUT_ITEMTOUCH_PICKUP;
447 return MUT_ITEMTOUCH_CONTINUE;
450 MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn)
452 if (!autocvar_g_powerups) { return; }
453 entity ent = M_ARGV(0, entity);
454 // Can't use .itemdef here
455 if (!(ent.classname == "item_strength" || ent.classname == "item_invincible" || ent.classname == "item_health_mega"))
462 setthink(e, instagib_invisibility);
464 setthink(e, instagib_extralife);
466 setthink(e, instagib_speed);
468 e.nextthink = time + 0.1;
469 e.spawnflags = ent.spawnflags;
470 e.noalign = ent.noalign;
471 setorigin(e, ent.origin);
476 MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsString)
478 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":instagib");
481 MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsPrettyString)
483 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", instagib");
486 MUTATOR_HOOKFUNCTION(mutator_instagib, SetModname)
488 M_ARGV(0, string) = "InstaGib";