Capture __FILE__ with AUTOCVAR
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / mutator_instagib.qc
1 #include "../_all.qh"
2
3 #include "mutator.qh"
4
5 #include "../cl_client.qh"
6 #include "../../common/buffs.qh"
7
8 #include "../../common/items/all.qc"
9
10 void spawnfunc_item_minst_cells (void)
11 {
12         if (!g_instagib) { remove(self); return; }
13         if (!self.ammo_cells)
14                 self.ammo_cells = autocvar_g_instagib_ammo_drop;
15
16         StartItemA (ITEM_VaporizerCells);
17 }
18
19 void instagib_health_mega()
20 {
21         self.max_health = 1;
22         StartItemA (ITEM_ExtraLife);
23 }
24
25 .float instagib_nextthink;
26 .float instagib_needammo;
27 void instagib_stop_countdown(entity e)
28 {
29         if (!e.instagib_needammo)
30                 return;
31         Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_INSTAGIB_FINDAMMO);
32         e.instagib_needammo = false;
33 }
34 void instagib_ammocheck()
35 {
36         if(time < self.instagib_nextthink)
37                 return;
38         if(!IS_PLAYER(self))
39                 return; // not a player
40
41         if(self.deadflag || gameover)
42                 instagib_stop_countdown(self);
43         else if (self.ammo_cells > 0 || (self.items & IT_UNLIMITED_WEAPON_AMMO) || (self.flags & FL_GODMODE))
44                 instagib_stop_countdown(self);
45         else if(autocvar_g_rm && autocvar_g_rm_laser)
46         {
47                 if(!self.instagib_needammo)
48                 {
49                         Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE);
50                         self.instagib_needammo = true;
51                 }
52         }
53         else
54         {
55                 self.instagib_needammo = true;
56                 if (self.health <= 5)
57                 {
58                         Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0');
59                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED);
60                 }
61                 else if (self.health <= 10)
62                 {
63                         Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0');
64                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_1);
65                 }
66                 else if (self.health <= 20)
67                 {
68                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
69                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_2);
70                 }
71                 else if (self.health <= 30)
72                 {
73                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
74                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_3);
75                 }
76                 else if (self.health <= 40)
77                 {
78                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
79                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_4);
80                 }
81                 else if (self.health <= 50)
82                 {
83                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
84                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_5);
85                 }
86                 else if (self.health <= 60)
87                 {
88                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
89                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_6);
90                 }
91                 else if (self.health <= 70)
92                 {
93                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
94                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_7);
95                 }
96                 else if (self.health <= 80)
97                 {
98                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
99                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_8);
100                 }
101                 else if (self.health <= 90)
102                 {
103                         Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO);
104                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
105                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_9);
106                 }
107                 else
108                 {
109                         Send_Notification(NOTIF_ONE_ONLY, self, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO);
110                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
111                 }
112         }
113         self.instagib_nextthink = time + 1;
114 }
115
116 MUTATOR_HOOKFUNCTION(instagib_MatchEnd)
117 {
118         entity head;
119         FOR_EACH_PLAYER(head)
120                 instagib_stop_countdown(head);
121
122         return false;
123 }
124
125 MUTATOR_HOOKFUNCTION(instagib_MonsterLoot)
126 {
127         other.monster_loot = spawnfunc_item_minst_cells;
128
129         return false;
130 }
131
132 MUTATOR_HOOKFUNCTION(instagib_MonsterSpawn)
133 {
134         // always refill ammo
135         if(self.monsterid == MON_MAGE.monsterid)
136                 self.skin = 1;
137
138         return false;
139 }
140
141 MUTATOR_HOOKFUNCTION(instagib_BotShouldAttack)
142 {
143         if(checkentity.items & ITEM_Strength.m_itemid)
144                 return true;
145
146         return false;
147 }
148
149 MUTATOR_HOOKFUNCTION(instagib_MakePlayerObserver)
150 {
151         instagib_stop_countdown(self);
152         return false;
153 }
154
155 MUTATOR_HOOKFUNCTION(instagib_PlayerSpawn)
156 {
157         self.effects |= EF_FULLBRIGHT;
158         return false;
159 }
160
161 MUTATOR_HOOKFUNCTION(instagib_PlayerPreThink)
162 {
163         instagib_ammocheck();
164         return false;
165 }
166
167 MUTATOR_HOOKFUNCTION(instagib_PlayerRegen)
168 {
169         // no regeneration in instagib
170         return true;
171 }
172
173 MUTATOR_HOOKFUNCTION(instagib_PlayerPowerups)
174 {
175         if (!(self.effects & EF_FULLBRIGHT))
176                 self.effects |= EF_FULLBRIGHT;
177
178         if (self.items & ITEM_Strength.m_itemid)
179         {
180                 play_countdown(self.strength_finished, "misc/poweroff.wav");
181                 if (time > self.strength_finished)
182                 {
183                         self.alpha = default_player_alpha;
184                         self.exteriorweaponentity.alpha = default_weapon_alpha;
185                         self.items &= ~ITEM_Strength.m_itemid;
186                         Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY);
187                 }
188         }
189         else
190         {
191                 if (time < self.strength_finished)
192                 {
193                         self.alpha = autocvar_g_instagib_invis_alpha;
194                         self.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha;
195                         self.items |= ITEM_Strength.m_itemid;
196                         Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_INVISIBILITY, self.netname);
197                         Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERUP_INVISIBILITY);
198                 }
199         }
200
201         if (self.items & ITEM_Shield.m_itemid)
202         {
203                 play_countdown(self.invincible_finished, "misc/poweroff.wav");
204                 if (time > self.invincible_finished)
205                 {
206                         self.items &= ~ITEM_Shield.m_itemid;
207                         Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERDOWN_SPEED);
208                 }
209         }
210         else
211         {
212                 if (time < self.invincible_finished)
213                 {
214                         self.items |= ITEM_Shield.m_itemid;
215                         Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_SPEED, self.netname);
216                         Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERUP_SPEED);
217                 }
218         }
219         return false;
220 }
221
222 MUTATOR_HOOKFUNCTION(instagib_PlayerPhysics)
223 {
224         if(self.items & ITEM_Shield.m_itemid)
225                 self.stat_sv_maxspeed = self.stat_sv_maxspeed * autocvar_g_instagib_speed_highspeed;
226
227         return false;
228 }
229
230 MUTATOR_HOOKFUNCTION(instagib_SplitHealthArmor)
231 {
232         damage_save = 0;
233         damage_take = frag_damage;
234
235         return false;
236 }
237
238 MUTATOR_HOOKFUNCTION(instagib_ForbidThrowing)
239 {
240         // weapon dropping on death handled by FilterItem
241
242         return true;
243 }
244
245 MUTATOR_HOOKFUNCTION(instagib_PlayerDamage)
246 {
247         if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker))
248                 frag_damage = 0;
249
250         if(IS_PLAYER(frag_target))
251         {
252                 if(frag_deathtype == DEATH_FALL)
253                         frag_damage = 0; // never count fall damage
254
255                 if(!autocvar_g_instagib_damagedbycontents)
256                 switch(frag_deathtype)
257                 {
258                         case DEATH_DROWN:
259                         case DEATH_SLIME:
260                         case DEATH_LAVA:
261                                 frag_damage = 0;
262                                 break;
263                 }
264
265                 if(IS_PLAYER(frag_attacker))
266                 if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER.m_id))
267                 {
268                         if(frag_target.armorvalue)
269                         {
270                                 frag_target.armorvalue -= 1;
271                                 frag_damage = 0;
272                                 frag_target.damage_dealt += 1;
273                                 frag_attacker.damage_dealt += 1; // TODO: change this to a specific hitsound for armor hit
274                                 Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue);
275                         }
276                 }
277
278                 if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER.m_id))
279                 {
280                         if(frag_deathtype & HITTYPE_SECONDARY)
281                         {
282                                 frag_damage = frag_mirrordamage = 0;
283
284                                 if(frag_target != frag_attacker)
285                                 {
286                                         if(frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); }
287                                         frag_force = '0 0 0';
288                                 }
289                         }
290                 }
291         }
292
293         if(IS_PLAYER(frag_attacker))
294         if(frag_mirrordamage > 0)
295         {
296                 // just lose extra LIVES, don't kill the player for mirror damage
297                 if(frag_attacker.armorvalue > 0)
298                 {
299                         frag_attacker.armorvalue -= 1;
300                         Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue);
301                         frag_attacker.damage_dealt += 1;
302                 }
303                 frag_mirrordamage = 0;
304         }
305
306         if((frag_target.buffs & BUFF_INVISIBLE.m_itemid) || (frag_target.items & ITEM_Strength.m_itemid))
307                 yoda = 1;
308
309         return false;
310 }
311
312 MUTATOR_HOOKFUNCTION(instagib_SetStartItems)
313 {
314         start_health       = warmup_start_health       = 100;
315         start_armorvalue   = warmup_start_armorvalue   = 0;
316
317         start_ammo_shells  = warmup_start_ammo_shells  = 0;
318         start_ammo_nails   = warmup_start_ammo_nails   = 0;
319         start_ammo_cells   = warmup_start_ammo_cells   = cvar("g_instagib_ammo_start");
320         start_ammo_plasma  = warmup_start_ammo_plasma  = 0;
321         start_ammo_rockets = warmup_start_ammo_rockets = 0;
322         start_ammo_fuel    = warmup_start_ammo_fuel    = 0;
323
324         start_weapons = warmup_start_weapons = WEPSET_VAPORIZER;
325         start_items |= IT_UNLIMITED_SUPERWEAPONS;
326
327         return false;
328 }
329
330 MUTATOR_HOOKFUNCTION(instagib_FilterItem)
331 {
332         if(self.classname == "item_cells")
333                 return true; // no normal cells?
334
335         if(self.weapon == WEP_VAPORIZER.m_id && self.classname == "droppedweapon")
336         {
337                 self.ammo_cells = autocvar_g_instagib_ammo_drop;
338                 return false;
339         }
340
341         if(self.weapon == WEP_DEVASTATOR.m_id || self.weapon == WEP_VORTEX.m_id)
342         {
343                 entity e = spawn(), oldself;
344                 setorigin(e, self.origin);
345                 oldself = self;
346                 self = e;
347                 self.noalign = oldself.noalign;
348                 self.cnt = oldself.cnt;
349                 self.team = oldself.team;
350                 spawnfunc_item_minst_cells();
351                 self = oldself;
352                 return true;
353         }
354
355         if(self.flags & FL_POWERUP)
356                 return false;
357
358         if(self.ammo_cells > autocvar_g_instagib_ammo_drop && self.classname != "item_minst_cells")
359                 self.ammo_cells = autocvar_g_instagib_ammo_drop;
360
361         if(self.ammo_cells && !self.weapon)
362                 return false;
363
364         return true;
365 }
366
367 MUTATOR_HOOKFUNCTION(instagib_CustomizeWaypoint)
368 {
369         entity e = WaypointSprite_getviewentity(other);
370
371         // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
372         // but only apply this to real players, not to spectators
373         if((self.owner.flags & FL_CLIENT) && (self.owner.items & ITEM_Strength.m_itemid) && (e == other))
374         if(DIFF_TEAM(self.owner, e))
375                 return true;
376
377         return false;
378 }
379
380 MUTATOR_HOOKFUNCTION(instagib_ItemCountdown)
381 {
382         switch (self.items)
383         {
384                 case ITEM_Strength.m_itemid:  item_name = "item-invis";     item_color = '0 0 1'; break;
385                 case ITEM_ExtraLife.m_itemid: item_name = "item-extralife"; item_color = '1 0 0'; break;
386                 case ITEM_Shield.m_itemid:    item_name = "item-speed";     item_color = '1 0 1'; break;
387         }
388         return false;
389 }
390
391 MUTATOR_HOOKFUNCTION(instagib_ItemTouch)
392 {
393         if(self.ammo_cells)
394         {
395                 // play some cool sounds ;)
396                 if (IS_CLIENT(other))
397                 {
398                         if(other.health <= 5)
399                                 Send_Notification(NOTIF_ONE, other, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND);
400                         else if(other.health < 50)
401                                 Send_Notification(NOTIF_ONE, other, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY);
402                 }
403
404                 if(other.health < 100)
405                         other.health = 100;
406
407                 return MUT_ITEMTOUCH_CONTINUE;
408         }
409
410         if(self.max_health)
411         {
412                 other.armorvalue = bound(other.armorvalue, 999, other.armorvalue + autocvar_g_instagib_extralives);
413                 Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_EXTRALIVES);
414                 return MUT_ITEMTOUCH_PICKUP;
415         }
416
417         return MUT_ITEMTOUCH_CONTINUE;
418 }
419
420 MUTATOR_HOOKFUNCTION(instagib_OnEntityPreSpawn)
421 {
422         if (!autocvar_g_powerups) { return false; }
423         if (!(self.classname == "item_strength" || self.classname == "item_invincible" || self.itemdef == ITEM_HealthMega))
424                 return false;
425
426         entity e = spawn();
427
428         if(random() < 0.3)
429                 e.think = spawnfunc_item_strength;
430         else if(random() < 0.6)
431                 e.think = instagib_health_mega;
432         else
433                 e.think = spawnfunc_item_invincible;
434
435         e.nextthink = time + 0.1;
436         e.spawnflags = self.spawnflags;
437         e.noalign = self.noalign;
438         setorigin(e, self.origin);
439
440         return true;
441 }
442
443 MUTATOR_HOOKFUNCTION(instagib_BuildMutatorsString)
444 {
445         ret_string = strcat(ret_string, ":instagib");
446         return false;
447 }
448
449 MUTATOR_HOOKFUNCTION(instagib_BuildMutatorsPrettyString)
450 {
451         ret_string = strcat(ret_string, ", instagib");
452         return false;
453 }
454
455 MUTATOR_HOOKFUNCTION(instagib_SetModname)
456 {
457         modname = "instagib";
458         return true;
459 }
460
461 MUTATOR_DEFINITION(mutator_instagib)
462 {
463         MUTATOR_HOOK(MatchEnd, instagib_MatchEnd, CBC_ORDER_ANY);
464         MUTATOR_HOOK(MonsterDropItem, instagib_MonsterLoot, CBC_ORDER_ANY);
465         MUTATOR_HOOK(MonsterSpawn, instagib_MonsterSpawn, CBC_ORDER_ANY);
466         MUTATOR_HOOK(BotShouldAttack, instagib_BotShouldAttack, CBC_ORDER_ANY);
467         MUTATOR_HOOK(PlayerPhysics, instagib_PlayerPhysics, CBC_ORDER_ANY);
468         MUTATOR_HOOK(PlayerSpawn, instagib_PlayerSpawn, CBC_ORDER_ANY);
469         MUTATOR_HOOK(PlayerDamage_Calculate, instagib_PlayerDamage, CBC_ORDER_ANY);
470         MUTATOR_HOOK(MakePlayerObserver, instagib_MakePlayerObserver, CBC_ORDER_ANY);
471         MUTATOR_HOOK(SetStartItems, instagib_SetStartItems, CBC_ORDER_ANY);
472         MUTATOR_HOOK(ItemTouch, instagib_ItemTouch, CBC_ORDER_ANY);
473         MUTATOR_HOOK(FilterItem, instagib_FilterItem, CBC_ORDER_ANY);
474         MUTATOR_HOOK(CustomizeWaypoint, instagib_CustomizeWaypoint, CBC_ORDER_ANY);
475         MUTATOR_HOOK(Item_RespawnCountdown, instagib_ItemCountdown, CBC_ORDER_ANY);
476         MUTATOR_HOOK(PlayerDamage_SplitHealthArmor, instagib_SplitHealthArmor, CBC_ORDER_ANY);
477         MUTATOR_HOOK(PlayerPowerups, instagib_PlayerPowerups, CBC_ORDER_ANY);
478         MUTATOR_HOOK(ForbidThrowCurrentWeapon, instagib_ForbidThrowing, CBC_ORDER_ANY);
479         MUTATOR_HOOK(PlayerPreThink, instagib_PlayerPreThink, CBC_ORDER_ANY);
480         MUTATOR_HOOK(PlayerRegen, instagib_PlayerRegen, CBC_ORDER_ANY);
481         MUTATOR_HOOK(OnEntityPreSpawn, instagib_OnEntityPreSpawn, CBC_ORDER_ANY);
482         MUTATOR_HOOK(BuildMutatorsString, instagib_BuildMutatorsString, CBC_ORDER_ANY);
483         MUTATOR_HOOK(BuildMutatorsPrettyString, instagib_BuildMutatorsPrettyString, CBC_ORDER_ANY);
484         MUTATOR_HOOK(SetModname, instagib_SetModname, CBC_ORDER_ANY);
485
486         return false;
487 }