83886077c4683274635a0c5120387a6b29f5f139
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / mutator_minstagib.qc
1 void spawnfunc_item_minst_cells (void) 
2 {
3         if not(g_minstagib) { remove(self); return; }
4         if not(self.ammo_cells)
5                 self.ammo_cells = autocvar_g_minstagib_ammo_drop;
6                 
7         StartItem ("models/items/a_cells.md3",
8                            "misc/itempickup.wav", 45, 0,
9                            "MinstaNex Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100);
10 }
11
12 void minstagib_health_mega()
13 {
14         self.max_health = 1;
15         StartItem ("models/items/g_h100.md3",
16                            "misc/megahealth.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup,
17                            "Extralife", IT_NAILS, 0, FL_POWERUP, generic_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
18 }
19
20 .float minstagib_nextthink;
21 .float minstagib_needammo;
22 void minstagib_stop_countdown(entity e)
23 {
24         if (!e.minstagib_needammo)
25                 return;
26         Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_MINSTA_FINDAMMO);
27         e.minstagib_needammo = FALSE;
28 }
29 void minstagib_ammocheck(void)
30 {
31         if (time < self.minstagib_nextthink)
32                 return;
33
34         if (self.deadflag || gameover)
35                 minstagib_stop_countdown(self);
36         else if (self.ammo_cells > 0 || (self.items & IT_UNLIMITED_WEAPON_AMMO))
37                 minstagib_stop_countdown(self);
38         else
39         {
40                 self.minstagib_needammo = TRUE;
41                 if (self.health == 5)
42                 {
43                         Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0');
44                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_MINSTAGIB_TERMINATED);
45                 }
46                 else if (self.health == 10)
47                 {
48                         Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0');
49                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_1);
50                 }
51                 else if (self.health == 20)
52                 {
53                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
54                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_2);
55                 }
56                 else if (self.health == 30)
57                 {
58                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
59                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_3);
60                 }
61                 else if (self.health == 40)
62                 {
63                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
64                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_4);
65                 }
66                 else if (self.health == 50)
67                 {
68                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
69                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_5);
70                 }
71                 else if (self.health == 60)
72                 {
73                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
74                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_6);
75                 }
76                 else if (self.health == 70)
77                 {
78                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
79                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_7);
80                 }
81                 else if (self.health == 80)
82                 {
83                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
84                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_8);
85                 }
86                 else if (self.health == 90)
87                 {
88                         Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MINSTA_FINDAMMO);
89                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
90                         Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_9);
91                 }
92                 else if (self.health == 100)
93                 {
94                         Send_Notification(NOTIF_ONE_ONLY, self, MSG_MULTI, MULTI_MINSTA_FINDAMMO);
95                         Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0');
96                 }
97         }
98         self.minstagib_nextthink = time + 1;
99 }
100
101 MUTATOR_HOOKFUNCTION(minstagib_BotShouldAttack)
102 {
103         if(checkentity.items & IT_STRENGTH)
104                 return TRUE;
105                 
106         return FALSE;
107 }
108
109 MUTATOR_HOOKFUNCTION(minstagib_MakePlayerObserver)
110 {
111         minstagib_stop_countdown(self);
112         return FALSE;
113 }
114
115 MUTATOR_HOOKFUNCTION(minstagib_PlayerSpawn)
116 {
117         self.effects |= EF_FULLBRIGHT;
118         return FALSE;
119 }
120
121 MUTATOR_HOOKFUNCTION(minstagib_PlayerPreThink)
122 {
123         minstagib_ammocheck();
124         return FALSE;
125 }
126
127 MUTATOR_HOOKFUNCTION(minstagib_PlayerPowerups)
128 {
129         if not(self.effects & EF_FULLBRIGHT)
130                 self.effects |= EF_FULLBRIGHT;
131
132         if (self.items & IT_STRENGTH)
133         {
134                 play_countdown(self.strength_finished, "misc/poweroff.wav");
135                 if (time > self.strength_finished)
136                 {
137                         self.alpha = default_player_alpha;
138                         self.exteriorweaponentity.alpha = default_weapon_alpha;
139                         self.items &~= IT_STRENGTH;
140                         Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY);
141                 }
142         }
143         else
144         {
145                 if (time < self.strength_finished)
146                 {
147                         self.alpha = autocvar_g_minstagib_invis_alpha;
148                         self.exteriorweaponentity.alpha = autocvar_g_minstagib_invis_alpha;
149                         self.items |= IT_STRENGTH;
150                         Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_INVISIBILITY, self.netname);
151                         Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERUP_INVISIBILITY);
152                 }
153         }
154
155         if (self.items & IT_INVINCIBLE)
156         {
157                 play_countdown(self.invincible_finished, "misc/poweroff.wav");
158                 if (time > self.invincible_finished)
159                 {
160                         self.items &~= IT_INVINCIBLE;
161                         Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERDOWN_SPEED);
162                 }
163         }
164         else
165         {
166                 if (time < self.invincible_finished)
167                 {
168                         self.items |= IT_INVINCIBLE;
169                         Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_SPEED, self.netname);
170                         Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERUP_SPEED);
171                 }
172         }
173         return FALSE;
174 }
175
176 MUTATOR_HOOKFUNCTION(minstagib_PlayerPhysics)
177 {
178         if(self.items & IT_INVINCIBLE)
179                 self.stat_sv_maxspeed = self.stat_sv_maxspeed * autocvar_g_minstagib_speed_highspeed;
180                 
181         return FALSE;
182 }
183
184 MUTATOR_HOOKFUNCTION(minstagib_SplitHealthArmor)
185 {
186         damage_save = 0;
187         //damage_take = frag_damage; // frag_damage isn't even set here?!
188         
189         return FALSE;
190 }
191
192 MUTATOR_HOOKFUNCTION(minstagib_ForbidThrowing)
193 {
194         if (self.health < 1)
195                 return FALSE;
196                 
197         return TRUE;
198 }
199
200 MUTATOR_HOOKFUNCTION(minstagib_PlayerDamage)
201 {
202         if(autocvar_g_friendlyfire == 0 && !IsDifferentTeam(frag_target, frag_attacker) && IS_PLAYER(frag_target))
203                 frag_damage = 0;
204                 
205         if(IS_PLAYER(frag_target))
206         {
207                 if ((frag_deathtype == DEATH_FALL)  ||
208                         (frag_deathtype == DEATH_DROWN) ||
209                         (frag_deathtype == DEATH_SLIME) ||
210                         (frag_deathtype == DEATH_LAVA))
211                 {
212                         frag_damage = 0;
213                 }
214                 
215                 if (frag_target.armorvalue && (frag_deathtype == WEP_MINSTANEX) && frag_damage)
216                 {
217                         frag_target.armorvalue -= 1;
218                         Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_MINSTA_LIVES_REMAINING, frag_target.armorvalue);
219                         frag_damage = 0;
220                         frag_target.hitsound += 1;
221                         frag_attacker.hitsound += 1; // TODO change this to a future specific hitsound for armor hit
222                 }
223                 if (DEATH_ISWEAPON(frag_deathtype, WEP_LASER))
224                 {
225                         frag_damage = 0;
226                         frag_mirrordamage = 0;
227                         if (frag_target != frag_attacker)
228                         {
229                                 if ((frag_target.health >= 1) && IS_PLAYER(frag_target))
230                                         Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_MINSTA_SECONDARY);
231                                 frag_force = '0 0 0';
232                                 // keep mirrorfrag_force
233                                 frag_attacker = frag_target;
234                         }
235                 }
236         }
237         
238         if(frag_mirrordamage > 0)
239         {
240                 // just lose extra LIVES, don't kill the player for mirror damage
241                 if(frag_attacker.armorvalue > 0)
242                 {
243                         frag_attacker.armorvalue = frag_attacker.armorvalue - 1;
244                         Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_MINSTA_LIVES_REMAINING, frag_attacker.armorvalue);
245                         frag_attacker.hitsound += 1;
246                 }
247                 frag_mirrordamage = 0;
248         }
249         
250         if(frag_target.items & IT_STRENGTH)
251                 yoda = 1;
252                 
253         return FALSE;
254 }
255
256 MUTATOR_HOOKFUNCTION(minstagib_SetStartItems)
257 {
258         start_ammo_cells = cvar("g_minstagib_ammo_start");
259         
260         start_health = 100;
261         start_armorvalue = 0;
262         WEPSET_COPY_AW(start_weapons, WEP_MINSTANEX);
263         start_items |= IT_UNLIMITED_SUPERWEAPONS;
264                 
265         return FALSE;
266 }
267
268 MUTATOR_HOOKFUNCTION(minstagib_FilterItem)
269 {
270         if(self.classname == "item_cells")
271                 return TRUE; // no normal cells?
272                 
273         if(self.weapon == WEP_MINSTANEX && self.classname == "droppedweapon")
274         {
275                 self.ammo_cells = autocvar_g_minstagib_ammo_drop;
276                 return FALSE;
277         }
278         
279         if(self.weapon == WEP_ROCKET_LAUNCHER || self.weapon == WEP_NEX)
280         {
281                 entity e = spawn();
282                 setorigin(e, self.origin);
283                 entity oldself;
284                 oldself = self;
285                 self = e;
286                 spawnfunc_item_minst_cells();
287                 self = oldself;
288                 return TRUE;
289         }
290                 
291         if(self.flags & FL_POWERUP)
292                 return FALSE;
293                 
294         if(self.ammo_cells > autocvar_g_minstagib_ammo_drop && self.classname != "item_minst_cells")
295                 self.ammo_cells = autocvar_g_minstagib_ammo_drop;
296                 
297         if(self.ammo_cells && !self.weapon)
298                 return FALSE;
299                 
300         return TRUE;
301 }
302
303 MUTATOR_HOOKFUNCTION(minstagib_CustomizeWaypoint)
304 {
305         entity e = WaypointSprite_getviewentity(other);
306         
307         // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
308         // but only apply this to real players, not to spectators
309         if((self.owner.flags & FL_CLIENT) && (self.owner.items & IT_STRENGTH) && (e == other))
310         if(IsDifferentTeam(self.owner, e))
311                 return TRUE;
312         
313         return FALSE;
314 }
315
316 MUTATOR_HOOKFUNCTION(minstagib_ItemCountdown)
317 {
318         switch(self.items)
319         {
320                 case IT_STRENGTH:   item_name = "item-invis"; item_color = '0 0 1'; break;
321                 case IT_NAILS:      item_name = "item-extralife"; item_color = '1 0 0'; break;
322                 case IT_INVINCIBLE: item_name = "item-speed"; item_color = '1 0 1'; break;
323         }
324         return FALSE;
325 }
326
327 MUTATOR_HOOKFUNCTION(minstagib_GiveItem)
328 {
329         if(giveitem.ammo_cells)
330         {
331                 // play some cool sounds ;)
332                 if (IS_CLIENT(other))
333                 {
334                         if(other.health <= 5)
335                                 Send_Notification(NOTIF_ONE, other, MSG_ANNCE, ANNCE_MINSTAGIB_LASTSECOND);
336                         else if(other.health < 50)
337                                 Send_Notification(NOTIF_ONE, other, MSG_ANNCE, ANNCE_MINSTAGIB_NARROWLY);
338                 }
339
340                 if(other.health < 100)
341                         other.health = 100;
342                         
343                 player_pickedup = TRUE;
344         }
345         
346         if(giveitem.max_health)
347         {
348                 other.armorvalue = bound(other.armorvalue, 999, other.armorvalue + autocvar_g_minstagib_extralives);
349                 sprint(other, "^3You picked up some extra lives\n");
350                 player_pickedup = TRUE;
351         }
352                 
353         return TRUE;
354 }
355
356 MUTATOR_HOOKFUNCTION(minstagib_OnEntityPreSpawn)
357 {
358         if not(autocvar_g_powerups) { return FALSE; }
359         if not(self.classname == "item_strength" || self.classname == "item_invincible" || self.classname == "item_health_mega")
360                 return FALSE;
361         
362         entity e = spawn();
363         
364         if(random() < 0.3)
365                 e.think = spawnfunc_item_strength;
366         else if(random() < 0.6)
367                 e.think = minstagib_health_mega;
368         else
369                 e.think = spawnfunc_item_invincible;
370                 
371         e.nextthink = time + 0.1;
372         e.spawnflags = self.spawnflags;
373         e.noalign = self.noalign;
374         setorigin(e, self.origin);
375         
376         return TRUE;
377 }
378
379 MUTATOR_HOOKFUNCTION(minstagib_BuildMutatorsString)
380 {
381         ret_string = strcat(ret_string, ":MinstaGib");
382         return FALSE;
383 }
384
385 MUTATOR_HOOKFUNCTION(minstagib_BuildMutatorsPrettyString)
386 {
387         ret_string = strcat(ret_string, ", MinstaGib");
388         return FALSE;
389 }
390
391 MUTATOR_DEFINITION(mutator_minstagib)
392 {
393         MUTATOR_HOOK(BotShouldAttack, minstagib_BotShouldAttack, CBC_ORDER_ANY);
394         MUTATOR_HOOK(PlayerPhysics, minstagib_PlayerPhysics, CBC_ORDER_ANY);
395         MUTATOR_HOOK(PlayerSpawn, minstagib_PlayerSpawn, CBC_ORDER_ANY);
396         MUTATOR_HOOK(PlayerDamage_Calculate, minstagib_PlayerDamage, CBC_ORDER_ANY);
397         MUTATOR_HOOK(MakePlayerObserver, minstagib_MakePlayerObserver, CBC_ORDER_ANY);
398         MUTATOR_HOOK(SetStartItems, minstagib_SetStartItems, CBC_ORDER_ANY);
399         MUTATOR_HOOK(Item_GiveTo, minstagib_GiveItem, CBC_ORDER_ANY);
400         MUTATOR_HOOK(FilterItem, minstagib_FilterItem, CBC_ORDER_ANY);
401         MUTATOR_HOOK(CustomizeWaypoint, minstagib_CustomizeWaypoint, CBC_ORDER_ANY);
402         MUTATOR_HOOK(Item_RespawnCountdown, minstagib_ItemCountdown, CBC_ORDER_ANY);
403         MUTATOR_HOOK(PlayerDamage_SplitHealthArmor, minstagib_SplitHealthArmor, CBC_ORDER_ANY);
404         MUTATOR_HOOK(PlayerPowerups, minstagib_PlayerPowerups, CBC_ORDER_ANY);
405         MUTATOR_HOOK(ForbidThrowCurrentWeapon, minstagib_ForbidThrowing, CBC_ORDER_ANY);
406         MUTATOR_HOOK(PlayerPreThink, minstagib_PlayerPreThink, CBC_ORDER_ANY);
407         MUTATOR_HOOK(OnEntityPreSpawn, minstagib_OnEntityPreSpawn, CBC_ORDER_ANY);
408         MUTATOR_HOOK(BuildMutatorsString, minstagib_BuildMutatorsString, CBC_ORDER_ANY);
409         MUTATOR_HOOK(BuildMutatorsPrettyString, minstagib_BuildMutatorsPrettyString, CBC_ORDER_ANY);
410
411         return FALSE;
412 }