228a1f152318eab29af124c060d2c92e4c29abb4
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / mutator_overkill.qc
1 void ok_DecreaseCharge(entity ent, float wep)
2 {
3         if not(autocvar_g_overkill_ammo_charge) return;
4
5         entity wepent = get_weaponinfo(wep);
6
7         if(wepent.netname == "")
8                 return; // dummy
9
10         ent.ammo_charge[wep] -= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname));
11 }
12
13 void ok_IncreaseCharge(entity ent, float wep)
14 {
15         entity wepent = get_weaponinfo(wep);
16
17         if(wepent.netname == "")
18                 return; // dummy
19
20         if(autocvar_g_overkill_ammo_charge)
21         if not(ent.BUTTON_ATCK)
22         if(ent.ammo_charge[wep] <= autocvar_g_overkill_ammo_charge_limit)
23                 ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME);
24 }
25
26 float ok_CheckWeaponCharge(entity ent, float wep, float swep)
27 {
28         if not(autocvar_g_overkill_ammo_charge) return TRUE;
29         //if(wep != swep) return TRUE; // switching, don't even bother checking
30
31         entity wepent = get_weaponinfo(wep);
32
33         if(wepent.netname == "")
34                 return 0; // dummy
35
36         return !(ent.ammo_charge[wep] < cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
37 }
38
39 MUTATOR_HOOKFUNCTION(ok_PlayerDamage_Calculate)
40 {
41         if(DEATH_ISWEAPON(frag_deathtype, WEP_LASER) && frag_target == frag_attacker)
42                 frag_damage = 0;
43
44         if(!IS_PLAYER(frag_target) || frag_target == frag_attacker)
45                 return FALSE;
46
47         if not (DEATH_ISWEAPON(frag_deathtype, WEP_LASER))
48                 return FALSE;
49
50         frag_damage = 0;
51         if(frag_target.health >= 1 && !frag_target.freezetag_frozen)
52                 Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_MINSTA_SECONDARY);
53
54         frag_force = '0 0 0';
55
56         return FALSE;
57 }
58
59 MUTATOR_HOOKFUNCTION(ok_BuildMutatorsString)
60 {
61         ret_string = strcat(ret_string, ":OK");
62         return FALSE;
63 }
64
65 MUTATOR_HOOKFUNCTION(ok_BuildMutatorsPrettyString)
66 {
67         ret_string = strcat(ret_string, ", Overkill");
68         return FALSE;
69 }
70
71 void ok_Item_Touch()
72 {
73         if(IS_PLAYER(other) && other.deadflag == DEAD_NO && !other.freezetag_frozen)
74         {
75                 Item_Touch();
76                 remove(self);
77         }
78 }
79
80 MUTATOR_HOOKFUNCTION(ok_PlayerDies)
81 {
82         self.ok_lastwep = self.weapon;
83         self.ok_deathloc = self.origin;
84
85         entity oldself = self;
86         float gpi = autocvar_g_pickup_items;
87         
88         cvar_set("g_pickup_items", "1");
89         self = spawn();
90         self.ok_item = TRUE;
91         self.noalign = TRUE;
92         spawnfunc_item_armor_small();
93         self.movetype = MOVETYPE_TOSS;
94         self.gravity = 1;
95         setorigin(self, frag_target.origin + '0 0 32');
96         self.velocity = '0 0 200' + normalize(frag_attacker.origin - self.origin) * 500;
97         self.touch = ok_Item_Touch;
98         SUB_SetFade(self, time + 5, 1);
99         self = oldself;
100         cvar_set("g_pickup_items", ftos(gpi));
101
102         return FALSE;
103 }
104
105 MUTATOR_HOOKFUNCTION(ok_ForbidThrowCurrentWeapon)
106 {
107         nades_CheckThrow();
108
109         return TRUE;
110 }
111
112 MUTATOR_HOOKFUNCTION(ok_PlayerPreThink)
113 {
114         if(intermission_running || gameover)
115                 return FALSE;
116
117         if(self.deadflag != DEAD_NO || !IS_PLAYER(self))
118                 return FALSE;
119
120         if(self.freezetag_frozen)
121                 return FALSE;
122
123         ok_IncreaseCharge(self, self.weapon);
124
125         if(self.BUTTON_ATCK2)
126                 if(self.jump_interval <= time)
127                 {
128                         self.jump_interval = time + autocvar_g_balance_laser_primary_refire * W_WeaponRateFactor();
129                         makevectors(self.v_angle);
130                         float w = self.weapon;
131                         self.weapon = WEP_LASER;
132                         W_Laser_Attack(3);
133                         self.weapon = w;
134                 }
135
136         self.weapon_blocked = FALSE;
137
138         self.ok_ammo_charge = self.ammo_charge[self.weapon];
139
140         if(autocvar_g_overkill_ammo_charge)
141         if(!ok_CheckWeaponCharge(self, self.weapon, self.switchweapon))
142         {
143                 if(autocvar_g_overkill_ammo_charge_notice && time > self.ok_notice_time && self.BUTTON_ATCK && IS_REAL_CLIENT(self))
144                 {
145                         //Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERKILL_CHARGE);
146                         self.ok_notice_time = time + 2;
147                         play2(self, "weapons/dryfire.wav");
148                 }
149                 if(self.weaponentity.state != WS_CLEAR)
150                         w_ready();
151
152                 self.weapon_blocked = TRUE;
153         }
154
155         self.BUTTON_ATCK2 = 0;
156
157         return FALSE;
158 }
159
160 MUTATOR_HOOKFUNCTION(ok_PlayerSpawn)
161 {
162         if(autocvar_g_overkill_ammo_charge)
163         {
164                 float i;
165
166                 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
167                         self.ammo_charge[i] = autocvar_g_overkill_ammo_charge_limit;
168
169                 self.ok_use_ammocharge = 1;
170                 self.ok_notice_time = time;
171         }
172         else
173                 self.ok_use_ammocharge = 0;
174
175         if(autocvar_g_overkill_spawnsystem_delay_death)
176                 self.ok_spawnsys_timer = time + autocvar_g_overkill_spawnsystem_delay_death;
177
178         if(teamplay == 0 || autocvar_g_overkill_spawnsystem == 0)
179                 return FALSE;
180
181         entity team_mate, best_mate = world;
182         vector best_spot = '0 0 0';
183         float pc = 0., best_dist = 0., dist = 0.;
184         FOR_EACH_PLAYER(team_mate)
185         {
186                 if((autocvar_g_overkill_spawnsystem_check_health != 0 && team_mate.health >= 100) || autocvar_g_overkill_spawnsystem_check_health == 0)
187                         if(team_mate.deadflag == DEAD_NO)
188                                 if(team_mate.ok_spawnsys_timer < time)
189                                         if(team_mate.team == self.team && (g_ctf || g_tdm || g_keyhunt))
190                                                 if(team_mate != self)
191                                                 {
192                                                         tracebox(team_mate.origin, PL_MIN, PL_MAX, team_mate.origin - '0 0 100', MOVE_WORLDONLY, team_mate);
193                                                         if(trace_fraction != 1.0)
194                                                                 if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
195                                                                 {
196                                                                         pc = pointcontents(trace_endpos + '0 0 1');
197                                                                         if(pc == CONTENT_EMPTY)
198                                                                         {
199                                                                                 if(vlen(team_mate.velocity) > 5)
200                                                                                         fixedmakevectors(vectoangles(team_mate.velocity));
201                                                                                 else
202                                                                                         fixedmakevectors(team_mate.angles);
203
204                                                                                 for(pc = 0; pc != 5; ++pc) // test 5 diffrent spots close to mate
205                                                                                 {
206                                                                                         switch(pc)
207                                                                                         {
208                                                                                         case 0:
209                                                                                                 tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 128, MOVE_NORMAL, team_mate);
210                                                                                                 break;
211                                                                                         case 1:
212                                                                                                 tracebox(team_mate.origin , PL_MIN, PL_MAX,team_mate.origin - v_right * 128 , MOVE_NORMAL, team_mate);
213                                                                                                 break;
214                                                                                         case 2:
215                                                                                                 tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate);
216                                                                                                 break;
217                                                                                         case 3:
218                                                                                                 tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate);
219                                                                                                 break;
220                                                                                         case 4:
221                                                                                                 tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_forward * 128, MOVE_NORMAL, team_mate);
222                                                                                                 break;
223                                                                                         }
224
225                                                                                         if(trace_fraction == 1.0)
226                                                                                         {
227                                                                                                 traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NORMAL, team_mate);
228                                                                                                 if(trace_fraction != 1.0)
229                                                                                                 {
230                                                                                                         if(autocvar_g_overkill_spawnsystem_close2death)
231                                                                                                         {
232                                                                                                                 dist = vlen(trace_endpos - self.ok_deathloc);
233                                                                                                                 if(dist < best_dist || best_dist == 0)
234                                                                                                                 {
235                                                                                                                         best_dist = dist;
236                                                                                                                         best_spot = trace_endpos;
237                                                                                                                         best_mate = team_mate;
238                                                                                                                 }
239                                                                                                         }
240                                                                                                         else
241                                                                                                         {
242                                                                                                                 setorigin(self, trace_endpos);
243                                                                                                                 self.angles = team_mate.angles;
244                                                                                                                 team_mate.ok_spawnsys_timer = time + autocvar_g_overkill_spawnsystem_delay;
245                                                                                                                 return FALSE;
246                                                                                                         }
247                                                                                                 }
248                                                                                         }
249                                                                                 }
250                                                                         }
251                                                                 }
252                                                 }
253         }
254
255         if(autocvar_g_overkill_spawnsystem_close2death)
256                 if(best_dist)
257                 {
258                         setorigin(self, best_spot);
259                         self.angles = best_mate.angles;
260                         best_mate.ok_spawnsys_timer = time + autocvar_g_overkill_spawnsystem_delay;
261                 }
262
263         return 0;
264 }
265
266 void start_hmg()
267 {
268         float gpi = autocvar_g_pickup_items;
269         cvar_set("g_pickup_items", "1");
270         self.classname = "weapon_hmg";
271         self.ok_item = TRUE;
272         //self.weapons = WEP_HMG;
273         //self.flags |= FL_POWERUP;
274         self.respawntime = autocvar_g_overkill_superguns_respawn_time;
275         setmodel(self, "models/weapons/g_ok_hmg.md3");
276         spawnfunc_weapon_hmg();
277
278         cvar_set("g_pickup_items", ftos(gpi));
279 }
280
281 void start_rpc()
282 {
283         float gpi = autocvar_g_pickup_items;
284         cvar_set("g_pickup_items", "1");
285         self.classname = "weapon_rpc";
286         self.respawntime = autocvar_g_overkill_superguns_respawn_time;
287         self.ok_item = TRUE;
288         //self.weapons = WEP_RPC;
289         //self.flags |= FL_POWERUP;
290         setmodel(self, "models/weapons/g_ok_rl.md3");
291         spawnfunc_weapon_rpc();
292         cvar_set("g_pickup_items", ftos(gpi));
293 }
294
295 void start_mh_anyway()
296 {
297         cvar_set("g_pickup_items", "1");
298         self.classname = "item_health_mega";
299         self.ok_item = TRUE;
300         spawnfunc_item_health_mega();
301         cvar_set("g_pickup_items", "0");
302 }
303
304 void start_ma_anyway()
305 {
306         cvar_set("g_pickup_items", "1");
307         self.classname = "item_armor_large";
308         self.ok_item = TRUE;
309         spawnfunc_item_armor_large();
310         cvar_set("g_pickup_items", "0");
311 }
312
313 MUTATOR_HOOKFUNCTION(ok_OnEntityPreSpawn)
314 {
315         if(autocvar_g_powerups)
316         if(autocvar_g_overkill_powerups_replace)
317         if(self.classname == "item_strength")
318         {
319                 entity wep = spawn();
320                 setorigin(wep, self.origin);
321                 wep.ok_item = TRUE;
322                 wep.think = start_hmg;
323                 wep.nextthink = time + 0.1;
324                 return TRUE;
325         }
326
327         if(autocvar_g_powerups)
328         if(autocvar_g_overkill_powerups_replace)
329         if(self.classname == "item_invincible")
330         {
331                 entity wep = spawn();
332                 setorigin(wep, self.origin);
333                 wep.ok_item = TRUE;
334                 wep.think = start_rpc;
335                 wep.nextthink = time + 0.1;
336                 return TRUE;
337         }
338
339         return FALSE;
340 }
341
342 MUTATOR_HOOKFUNCTION(ok_ItemRemove)
343 {
344         if(self.ok_item)
345                 return FALSE;
346
347         switch(self.items)
348         {
349                 case IT_HEALTH: return !(autocvar_g_overkill_100h_anyway);
350                 case IT_ARMOR: return !(autocvar_g_overkill_100a_anyway);
351         }
352         
353         return TRUE;
354 }
355
356 MUTATOR_HOOKFUNCTION(ok_SpectateCopy)
357 {
358         self.ammo_charge[self.weapon] = other.ammo_charge[other.weapon];
359         self.ok_use_ammocharge = other.ok_use_ammocharge;
360
361         return FALSE;
362 }
363
364 MUTATOR_HOOKFUNCTION(ok_SetModname)
365 {
366         modname = "Overkill";
367         return TRUE;
368 }
369
370 MUTATOR_HOOKFUNCTION(ok_StartItems)
371 {
372         start_items |= IT_UNLIMITED_WEAPON_AMMO;
373         start_weapons = (WEPSET_UZI | WEPSET_NEX | WEPSET_SHOTGUN);
374
375         start_ammo_nails = start_ammo_cells = start_ammo_shells = start_ammo_rockets =
376         warmup_start_ammo_nails = warmup_start_ammo_cells = warmup_start_ammo_shells = warmup_start_ammo_rockets = autocvar_g_overkill_ammo_start;
377
378         return FALSE;
379 }
380
381 void ok_Initialize()
382 {
383         precache_all_playermodels("models/ok_player/*.dpm");
384
385         precache_model("models/weapons/h_ok_rl.iqm");
386         precache_model("models/weapons/v_ok_rl.md3");
387         precache_model("models/weapons/g_ok_rl.md3");
388         precache_model("models/weapons/ok_rocket.md3");
389
390         precache_model("models/weapons/h_ok_mg.iqm");
391         precache_model("models/weapons/v_ok_mg.md3");
392         precache_model("models/weapons/g_ok_mg.md3");
393
394         precache_model("models/weapons/h_ok_hmg.iqm");
395         precache_model("models/weapons/v_ok_hmg.md3");
396         precache_model("models/weapons/g_ok_hmg.md3");
397
398         precache_model("models/weapons/h_ok_shotgun.iqm");
399         precache_model("models/weapons/v_ok_shotgun.md3");
400         precache_model("models/weapons/g_ok_shotgun.md3");
401
402         precache_model("models/weapons/h_ok_sniper.iqm");
403         precache_model("models/weapons/v_ok_sniper.md3");
404         precache_model("models/weapons/g_ok_sniper.md3");
405
406         precache_sound("weapons/dryfire.wav");
407         
408         addstat(STAT_OK_AMMO_CHARGE, AS_FLOAT, ok_use_ammocharge);
409         addstat(STAT_OK_AMMO_CHARGEPOOl, AS_FLOAT, ok_ammo_charge);
410
411         w_uzi(WR_PRECACHE);
412         w_nex(WR_PRECACHE);
413         w_shotgun(WR_PRECACHE);
414         w_laser(WR_PRECACHE);
415
416         (get_weaponinfo(WEP_RPC)).spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
417         (get_weaponinfo(WEP_HMG)).spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
418
419         (get_weaponinfo(WEP_SHOTGUN)).mdl = "ok_shotgun";
420         (get_weaponinfo(WEP_UZI)).mdl = "ok_mg";
421         (get_weaponinfo(WEP_NEX)).mdl = "ok_sniper";
422
423         string s;
424         float fh = fopen("overkill.cfg", FILE_READ);
425         if(fh >= 0)
426         {
427                 while((s = fgets(fh)))
428                 {
429                         tokenize_console(s);
430                         if not(argv(0) == "" || argv(1) == "//" || argv(1) == "")
431                                 cvar_settemp(argv(0), argv(1));
432                 }
433                 fclose(fh);
434         }
435         else
436                 dprint("^1Mutator Overkill: WARNING! overkill.cfg NOT found, things will be strange!\n");
437 }
438
439 MUTATOR_DEFINITION(mutator_overkill)
440 {
441         MUTATOR_HOOK(ForbidThrowCurrentWeapon, ok_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
442         MUTATOR_HOOK(BuildMutatorsString, ok_BuildMutatorsString, CBC_ORDER_ANY);
443         MUTATOR_HOOK(BuildMutatorsPrettyString, ok_BuildMutatorsPrettyString, CBC_ORDER_ANY);
444         MUTATOR_HOOK(PlayerPreThink, ok_PlayerPreThink, CBC_ORDER_LAST);
445         MUTATOR_HOOK(PlayerSpawn, ok_PlayerSpawn, CBC_ORDER_LAST);
446         MUTATOR_HOOK(PlayerDamage_Calculate, ok_PlayerDamage_Calculate, CBC_ORDER_LAST);
447         MUTATOR_HOOK(PlayerDies, ok_PlayerDies, CBC_ORDER_ANY);
448         MUTATOR_HOOK(OnEntityPreSpawn, ok_OnEntityPreSpawn, CBC_ORDER_ANY);
449         MUTATOR_HOOK(SetModname, ok_SetModname, CBC_ORDER_ANY);
450         MUTATOR_HOOK(FilterItem, ok_ItemRemove, CBC_ORDER_ANY);
451         MUTATOR_HOOK(SpectateCopy, ok_SpectateCopy, CBC_ORDER_ANY);
452         MUTATOR_HOOK(SetStartItems, ok_StartItems, CBC_ORDER_ANY);
453
454         MUTATOR_ONADD
455         {
456                 ok_Initialize();
457         }
458
459         MUTATOR_ONREMOVE
460         {
461                 (get_weaponinfo(WEP_RPC)).spawnflags |= WEP_FLAG_MUTATORBLOCKED;
462                 (get_weaponinfo(WEP_HMG)).spawnflags |= WEP_FLAG_MUTATORBLOCKED;
463         }
464
465         return FALSE;
466 }