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