1 #include "sv_instagib.qh"
3 #include <server/client.qh>
4 #include <common/items/_mod.qh>
5 #include <common/mutators/mutator/powerups/_mod.qh>
6 #include <common/mutators/mutator/status_effects/_mod.qh>
7 #include "../random_items/sv_random_items.qh"
9 bool autocvar_g_instagib_damagedbycontents = true;
10 bool autocvar_g_instagib_blaster_keepdamage = false;
11 bool autocvar_g_instagib_blaster_keepforce = false;
12 bool autocvar_g_instagib_mirrordamage;
13 bool autocvar_g_instagib_friendlypush = true;
14 //int autocvar_g_instagib_ammo_drop;
15 bool autocvar_g_instagib_ammo_convert_cells;
16 bool autocvar_g_instagib_ammo_convert_rockets;
17 bool autocvar_g_instagib_ammo_convert_shells;
18 bool autocvar_g_instagib_ammo_convert_bullets;
20 /// \brief Returns a random classname of the instagib item.
21 /// \param[in] prefix Prefix of the cvars that hold probabilities.
22 /// \return Random classname of the instagib item.
23 string RandomItems_GetRandomInstagibItemClassName(string prefix)
25 RandomSelection_Init();
26 IL_EACH(g_instagib_items, Item_IsDefinitionAllowed(it),
28 string cvar_name = sprintf("g_%s_%s_probability", prefix,
29 it.m_canonical_spawnfunc);
30 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
32 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
35 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
37 return RandomSelection_chosen_string;
40 .float instagib_nextthink;
41 .float instagib_needammo;
42 void instagib_stop_countdown(entity e)
44 if (!e.instagib_needammo)
46 Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_INSTAGIB_FINDAMMO);
47 e.instagib_needammo = false;
50 void instagib_countdown(entity this)
52 float hp = GetResource(this, RES_HEALTH);
54 float dmg = (hp <= 10) ? 5 : 10;
55 Damage(this, this, this, dmg, DEATH_NOAMMO.m_id, DMG_NOWEP, this.origin, '0 0 0');
57 entity annce = (hp <= 5) ? ANNCE_INSTAGIB_TERMINATED : Announcer_PickNumber(CNT_NORMAL, ceil(hp / 10));
58 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, annce);
63 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO);
65 Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO);
69 void instagib_ammocheck(entity this)
71 if(time < this.instagib_nextthink)
74 return; // not a player
76 if(IS_DEAD(this) || game_stopped)
77 instagib_stop_countdown(this);
78 else if (GetResource(this, RES_CELLS) > 0 || (this.items & IT_UNLIMITED_AMMO) || (this.flags & FL_GODMODE))
79 instagib_stop_countdown(this);
80 else if(autocvar_g_rm && autocvar_g_rm_laser)
82 if(!this.instagib_needammo)
84 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE);
85 this.instagib_needammo = true;
90 this.instagib_needammo = true;
91 instagib_countdown(this);
93 this.instagib_nextthink = time + 1;
96 MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd)
98 FOREACH_CLIENT(IS_PLAYER(it), { instagib_stop_countdown(it); });
101 MUTATOR_HOOKFUNCTION(mutator_instagib, RandomItems_GetRandomItemClassName)
103 M_ARGV(1, string) = RandomItems_GetRandomInstagibItemClassName(
108 MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem)
110 entity item = M_ARGV(1, entity);
112 item.itemdef = ITEM_VaporizerCells;
115 MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn)
117 entity mon = M_ARGV(0, entity);
119 // always refill ammo
120 if(mon.monsterdef == MON_MAGE)
124 MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver)
126 entity player = M_ARGV(0, entity);
128 instagib_stop_countdown(player);
131 MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidRandomStartWeapons)
136 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerSpawn)
138 entity player = M_ARGV(0, entity);
140 player.effects |= EF_FULLBRIGHT;
143 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPreThink)
145 entity player = M_ARGV(0, entity);
147 instagib_ammocheck(player);
150 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen)
152 // no regeneration in instagib
156 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor)
158 M_ARGV(4, float) = M_ARGV(7, float); // take = damage
159 M_ARGV(5, float) = 0; // save
162 MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon)
164 // weapon dropping on death handled by FilterItem
168 MUTATOR_HOOKFUNCTION(mutator_instagib, Damage_Calculate)
170 entity frag_attacker = M_ARGV(1, entity);
171 entity frag_target = M_ARGV(2, entity);
172 float frag_deathtype = M_ARGV(3, float);
173 float frag_damage = M_ARGV(4, float);
174 float frag_mirrordamage = M_ARGV(5, float);
175 vector frag_force = M_ARGV(6, vector);
177 if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker))
180 if(IS_PLAYER(frag_target))
182 if(frag_deathtype == DEATH_FALL.m_id)
183 frag_damage = 0; // never count fall damage
185 if(!autocvar_g_instagib_damagedbycontents)
186 switch(DEATH_ENT(frag_deathtype))
195 if(IS_PLAYER(frag_attacker))
196 if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
198 if(!autocvar_g_instagib_friendlypush && SAME_TEAM(frag_target, frag_attacker))
199 frag_force = '0 0 0';
201 float armor = GetResource(frag_target, RES_ARMOR);
205 SetResource(frag_target, RES_ARMOR, armor);
207 frag_target.hitsound_damage_dealt += 1;
208 frag_attacker.hitsound_damage_dealt += 1;
209 Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, armor);
213 if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
215 if(frag_deathtype & HITTYPE_SECONDARY)
217 if(!autocvar_g_instagib_blaster_keepdamage || frag_attacker == frag_target)
220 if(!autocvar_g_instagib_mirrordamage)
221 frag_mirrordamage = 0; // never do mirror damage on enemies
224 if(frag_target != frag_attacker)
226 if(!autocvar_g_instagib_blaster_keepforce)
227 frag_force = '0 0 0';
233 if(!autocvar_g_instagib_mirrordamage) // only apply the taking lives hack if we don't want to support real damage mirroring
234 if(IS_PLAYER(frag_attacker))
235 if(frag_mirrordamage > 0)
237 // just lose extra LIVES, don't kill the player for mirror damage
238 float armor = GetResource(frag_attacker, RES_ARMOR);
242 SetResource(frag_attacker, RES_ARMOR, armor);
243 Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, armor);
244 frag_attacker.hitsound_damage_dealt += frag_mirrordamage;
246 frag_mirrordamage = 0;
249 if(frag_target.alpha && frag_target.alpha < 1)
250 if(IS_PLAYER(frag_target))
253 M_ARGV(4, float) = frag_damage;
254 M_ARGV(5, float) = frag_mirrordamage;
255 M_ARGV(6, vector) = frag_force;
258 MUTATOR_HOOKFUNCTION(mutator_instagib, SetStartItems, CBC_ORDER_LAST)
260 start_health = warmup_start_health = 100;
261 start_armorvalue = warmup_start_armorvalue = 0;
263 start_ammo_shells = warmup_start_ammo_shells = 0;
264 start_ammo_nails = warmup_start_ammo_nails = 0;
265 start_ammo_cells = warmup_start_ammo_cells = cvar("g_instagib_ammo_start");
266 start_ammo_plasma = warmup_start_ammo_plasma = 0;
267 start_ammo_rockets = warmup_start_ammo_rockets = 0;
268 //start_ammo_fuel = warmup_start_ammo_fuel = 0;
270 start_weapons = warmup_start_weapons = WEPSET(VAPORIZER);
271 start_items |= IT_UNLIMITED_SUPERWEAPONS;
274 MUTATOR_HOOKFUNCTION(mutator_instagib, SetWeaponArena)
276 // turn weapon arena off
277 M_ARGV(0, string) = "off";
280 void instagib_replace_item_with(entity this, GameItem def)
282 entity new_item = spawn();
285 case ITEM_Invisibility:
286 new_item.invisibility_finished = autocvar_g_instagib_invisibility_time;
289 new_item.speed_finished = autocvar_g_instagib_speed_time;
292 Item_CopyFields(this, new_item);
293 StartItem(new_item, def);
296 const int INSTAGIB_POWERUP_COUNT = 3;
297 GameItem instagib_remaining_powerups[INSTAGIB_POWERUP_COUNT];
299 // replaces item with a random powerup selected among the less spawned ones
300 void instagib_replace_item_with_random_powerup(entity item)
302 static int remaining_powerups_count = INSTAGIB_POWERUP_COUNT;
303 if (remaining_powerups_count == 0)
304 remaining_powerups_count = INSTAGIB_POWERUP_COUNT;
305 if (remaining_powerups_count == INSTAGIB_POWERUP_COUNT)
307 instagib_remaining_powerups[0] = ITEM_Invisibility;
308 instagib_remaining_powerups[1] = ITEM_ExtraLife;
309 instagib_remaining_powerups[2] = ITEM_Speed;
312 float r = floor(random() * remaining_powerups_count);
313 instagib_replace_item_with(item, instagib_remaining_powerups[r]);
314 for(int i = r; i < INSTAGIB_POWERUP_COUNT - 1; ++i)
315 instagib_remaining_powerups[i] = instagib_remaining_powerups[i + 1];
316 --remaining_powerups_count;
319 MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
321 entity item = M_ARGV(0, entity);
323 switch (item.itemdef)
325 case ITEM_Strength: case ITEM_Shield: case ITEM_HealthMega: case ITEM_ArmorMega:
326 if(autocvar_g_powerups)
327 instagib_replace_item_with_random_powerup(item);
330 if(autocvar_g_instagib_ammo_convert_cells)
331 instagib_replace_item_with(item, ITEM_VaporizerCells);
334 if(autocvar_g_instagib_ammo_convert_rockets)
335 instagib_replace_item_with(item, ITEM_VaporizerCells);
338 if(autocvar_g_instagib_ammo_convert_shells)
339 instagib_replace_item_with(item, ITEM_VaporizerCells);
342 if(autocvar_g_instagib_ammo_convert_bullets)
343 instagib_replace_item_with(item, ITEM_VaporizerCells);
349 case WEP_VAPORIZER.m_id:
350 if (ITEM_IS_LOOT(item))
352 SetResource(item, RES_CELLS, autocvar_g_instagib_ammo_drop);
356 case WEP_DEVASTATOR.m_id: case WEP_VORTEX.m_id:
357 instagib_replace_item_with(item, ITEM_VaporizerCells);
361 if(item.itemdef.instanceOfPowerup)
364 float cells = GetResource(item, RES_CELLS);
365 if(cells > autocvar_g_instagib_ammo_drop && item.classname != "item_vaporizer_cells")
366 SetResource(item, RES_CELLS, autocvar_g_instagib_ammo_drop);
368 if(cells && !item.weapon)
374 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies)
376 float frag_deathtype = M_ARGV(3, float);
378 if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
379 M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death
382 MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch)
384 if(MUTATOR_RETURNVALUE) return false;
386 entity item = M_ARGV(0, entity);
387 entity toucher = M_ARGV(1, entity);
389 if(GetResource(item, RES_CELLS))
391 // play some cool sounds ;)
392 float hp = GetResource(toucher, RES_HEALTH);
393 if (IS_CLIENT(toucher))
396 Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND);
398 Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY);
402 SetResource(toucher, RES_HEALTH, 100);
404 return MUT_ITEMTOUCH_CONTINUE;
407 if(item.itemdef == ITEM_ExtraLife)
409 GiveResource(toucher, RES_ARMOR, autocvar_g_instagib_extralives);
410 Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES, autocvar_g_instagib_extralives);
411 Inventory_pickupitem(item.itemdef, toucher);
412 return MUT_ITEMTOUCH_PICKUP;
415 return MUT_ITEMTOUCH_CONTINUE;
418 MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsString)
420 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":instagib");
423 MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsPrettyString)
425 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", InstaGib");
428 MUTATOR_HOOKFUNCTION(mutator_instagib, SetModname)
430 M_ARGV(0, string) = "InstaGib";