]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
Cleanup common mutators: remove IMPLEMENTATION macro
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / mutator / overkill / sv_overkill.qc
1 #include "sv_overkill.qh"
2
3 bool autocvar_g_overkill_powerups_replace;
4 float autocvar_g_overkill_superguns_respawn_time;
5 bool autocvar_g_overkill_100h_anyway;
6 bool autocvar_g_overkill_100a_anyway;
7 bool autocvar_g_overkill_ammo_charge;
8 float autocvar_g_overkill_ammo_charge_notice;
9 float autocvar_g_overkill_ammo_charge_limit;
10
11 .vector ok_deathloc;
12 .float ok_spawnsys_timer;
13 .float ok_lastwep;
14 .float ok_item;
15
16 .float ok_notice_time;
17 .float ammo_charge[Weapons_MAX];
18 .float ok_use_ammocharge = _STAT(OK_AMMO_CHARGE);
19 .float ok_ammo_charge = _STAT(OK_AMMO_CHARGEPOOL);
20
21 .float ok_pauseregen_finished;
22
23 void(entity ent, float wep) ok_DecreaseCharge;
24
25 void ok_Initialize();
26
27 REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
28 {
29         MUTATOR_ONADD
30         {
31                 ok_Initialize();
32         }
33
34         MUTATOR_ONREMOVE
35         {
36                 WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
37                 WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
38         }
39 }
40
41 MUTATOR_HOOKFUNCTION(ok, W_DecreaseAmmo)
42 {
43         entity actor = M_ARGV(0, entity);
44         if (actor.ok_use_ammocharge)
45         {
46                 ok_DecreaseCharge(actor, PS(actor).m_weapon.m_id);
47                 return true;
48         }
49 }
50
51 MUTATOR_HOOKFUNCTION(ok, W_Reload)
52 {
53         entity actor = M_ARGV(0, entity);
54         return actor.ok_use_ammocharge;
55 }
56
57 void W_Blaster_Attack(entity, .entity, float, float, float, float, float, float, float, float, float, float);
58 spawnfunc(weapon_hmg);
59 spawnfunc(weapon_rpc);
60
61 void ok_DecreaseCharge(entity ent, int wep)
62 {
63         if(!ent.ok_use_ammocharge) return;
64
65         entity wepent = Weapons_from(wep);
66
67         if (wepent == WEP_Null) return;  // dummy
68
69         ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
70 }
71
72 void ok_IncreaseCharge(entity ent, int wep)
73 {
74         entity wepent = Weapons_from(wep);
75
76         if (wepent == WEP_Null) return;  // dummy
77
78         if(ent.ok_use_ammocharge)
79         if(!PHYS_INPUT_BUTTON_ATCK(ent)) // not while attacking?
80                 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);
81 }
82
83 float ok_CheckWeaponCharge(entity ent, int wep)
84 {
85         if(!ent.ok_use_ammocharge) return true;
86
87         entity wepent = Weapons_from(wep);
88
89         if(wepent == WEP_Null) return false;  // dummy
90
91         return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
92 }
93
94 MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST)
95 {
96         entity frag_attacker = M_ARGV(1, entity);
97         entity frag_target = M_ARGV(2, entity);
98         float frag_deathtype = M_ARGV(3, float);
99
100         if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target))
101         if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
102         {
103                 if(frag_attacker != frag_target)
104                 if(frag_target.health > 0)
105                 if(STAT(FROZEN, frag_target) == 0)
106                 if(!IS_DEAD(frag_target))
107                 {
108                         Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE);
109                         M_ARGV(6, vector) = '0 0 0';
110                 }
111
112                 M_ARGV(4, float) = 0;
113         }
114 }
115
116 MUTATOR_HOOKFUNCTION(ok, PlayerDamage_SplitHealthArmor)
117 {
118         entity frag_target = M_ARGV(2, entity);
119         float damage_take = M_ARGV(4, float);
120
121         if(damage_take)
122                 frag_target.ok_pauseregen_finished = max(frag_target.ok_pauseregen_finished, time + 2);
123 }
124
125 void ok_DropItem(entity this, entity targ)
126 {
127         entity e = new(droppedweapon); // hax
128         e.ok_item = true;
129         e.noalign = true;
130         e.pickup_anyway = true;
131         e.spawnfunc_checked = true;
132         spawnfunc_item_armor_small(e);
133         if (!wasfreed(e)) { // might have been blocked by a mutator
134         set_movetype(e, MOVETYPE_TOSS);
135         e.gravity = 1;
136         e.reset = SUB_Remove;
137         setorigin(e, this.origin + '0 0 32');
138         e.velocity = '0 0 200' + normalize(targ.origin - this.origin) * 500;
139         SUB_SetFade(e, time + 5, 1);
140         }
141 }
142
143 MUTATOR_HOOKFUNCTION(ok, PlayerDies)
144 {
145         entity frag_attacker = M_ARGV(1, entity);
146         entity frag_target = M_ARGV(2, entity);
147
148         entity targ = ((frag_attacker) ? frag_attacker : frag_target);
149
150         ok_DropItem(frag_target, targ);
151
152         frag_target.ok_lastwep = PS(frag_target).m_switchweapon.m_id;
153 }
154
155 MUTATOR_HOOKFUNCTION(ok, MonsterDropItem)
156 {
157         entity mon = M_ARGV(0, entity);
158         entity olditem = M_ARGV(1, entity);
159         entity frag_attacker = M_ARGV(2, entity);
160
161         delete(olditem);
162
163         M_ARGV(1, entity) = NULL;
164
165         ok_DropItem(mon, frag_attacker);
166 }
167
168 MUTATOR_HOOKFUNCTION(ok, PlayerRegen)
169 {
170         entity player = M_ARGV(0, entity);
171
172         // overkill's values are different, so use custom regen
173         if(!STAT(FROZEN, player))
174         {
175                 player.armorvalue = CalcRotRegen(player.armorvalue, autocvar_g_balance_armor_regenstable, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear,
176                         1 * frametime * (time > player.ok_pauseregen_finished), 0, 0, 1, 1 * frametime * (time > player.pauserotarmor_finished), autocvar_g_balance_armor_limit);
177                 player.health = CalcRotRegen(player.health, autocvar_g_balance_health_regenstable, 0, 100, 1 * frametime * (time > player.ok_pauseregen_finished), 200, 0,
178                         autocvar_g_balance_health_rotlinear, 1 * frametime * (time > player.pauserothealth_finished), autocvar_g_balance_health_limit);
179
180                 float minf, maxf, limitf;
181
182                 maxf = autocvar_g_balance_fuel_rotstable;
183                 minf = autocvar_g_balance_fuel_regenstable;
184                 limitf = autocvar_g_balance_fuel_limit;
185
186                 player.ammo_fuel = CalcRotRegen(player.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear,
187                         frametime * (time > player.pauseregen_finished) * ((player.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > player.pauserotfuel_finished), limitf);
188         }
189         return true; // return true anyway, as frozen uses no regen
190 }
191
192 MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon)
193 {
194         return true;
195 }
196
197 MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
198 {
199         if(intermission_running || gameover)
200                 return;
201
202         entity player = M_ARGV(0, entity);
203
204         if(IS_DEAD(player) || !IS_PLAYER(player) || STAT(FROZEN, player))
205                 return;
206
207         if(player.ok_lastwep)
208         {
209                 Weapon newwep = Weapons_from(player.ok_lastwep);
210                 if(player.ok_lastwep == WEP_HMG.m_id)
211                         newwep = WEP_MACHINEGUN;
212                 if(player.ok_lastwep == WEP_RPC.m_id)
213                         newwep = WEP_VORTEX;
214                 PS(player).m_switchweapon = newwep;
215                 player.ok_lastwep = 0;
216         }
217
218         ok_IncreaseCharge(player, PS(player).m_weapon.m_id);
219
220         if(PHYS_INPUT_BUTTON_ATCK2(player))
221         if(!forbidWeaponUse(player) || player.weapon_blocked) // allow if weapon is blocked
222         if(time >= player.jump_interval)
223         {
224                 player.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(player);
225                 makevectors(player.v_angle);
226
227                 Weapon oldwep = PS(player).m_weapon;
228                 PS(player).m_weapon = WEP_BLASTER;
229                 W_Blaster_Attack(
230                         player,
231                         weaponentities[0], // TODO: unhardcode
232                         WEP_BLASTER.m_id | HITTYPE_SECONDARY,
233                         WEP_CVAR_SEC(vaporizer, shotangle),
234                         WEP_CVAR_SEC(vaporizer, damage),
235                         WEP_CVAR_SEC(vaporizer, edgedamage),
236                         WEP_CVAR_SEC(vaporizer, radius),
237                         WEP_CVAR_SEC(vaporizer, force),
238                         WEP_CVAR_SEC(vaporizer, speed),
239                         WEP_CVAR_SEC(vaporizer, spread),
240                         WEP_CVAR_SEC(vaporizer, delay),
241                         WEP_CVAR_SEC(vaporizer, lifetime)
242                 );
243                 PS(player).m_weapon = oldwep;
244         }
245
246         player.weapon_blocked = false;
247
248         player.ok_ammo_charge = player.ammo_charge[PS(player).m_weapon.m_id];
249
250         if(player.ok_use_ammocharge)
251         if(!ok_CheckWeaponCharge(player, PS(player).m_weapon.m_id))
252         {
253                 if(autocvar_g_overkill_ammo_charge_notice && time > player.ok_notice_time && PHYS_INPUT_BUTTON_ATCK(player) && IS_REAL_CLIENT(player) && PS(player).m_weapon == PS(player).m_switchweapon)
254                 {
255                         //Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERKILL_CHARGE);
256                         player.ok_notice_time = time + 2;
257                         play2(player, SND(DRYFIRE));
258                 }
259                 Weapon wpn = PS(player).m_weapon;
260                 .entity weaponentity = weaponentities[0]; // TODO: unhardcode
261                 if(player.(weaponentity).state != WS_CLEAR)
262                         w_ready(wpn, player, weaponentity, PHYS_INPUT_BUTTON_ATCK(player) | (PHYS_INPUT_BUTTON_ATCK2(player) << 1));
263
264                 player.weapon_blocked = true;
265         }
266
267         PHYS_INPUT_BUTTON_ATCK2(player) = false;
268 }
269
270 MUTATOR_HOOKFUNCTION(ok, PlayerSpawn)
271 {
272         entity player = M_ARGV(0, entity);
273
274         if(autocvar_g_overkill_ammo_charge)
275         {
276                 FOREACH(Weapons, it != WEP_Null, LAMBDA(player.ammo_charge[it.m_id] = autocvar_g_overkill_ammo_charge_limit));
277
278                 player.ok_use_ammocharge = 1;
279                 player.ok_notice_time = time;
280         }
281         else
282                 player.ok_use_ammocharge = 0;
283
284         // if player changed their weapon while dead, don't switch to their death weapon
285         if(player.impulse)
286                 player.ok_lastwep = 0;
287
288         player.ok_pauseregen_finished = time + 2;
289 }
290
291 void self_spawnfunc_weapon_hmg(entity this) { spawnfunc_weapon_hmg(this); }
292 void self_spawnfunc_weapon_rpc(entity this) { spawnfunc_weapon_rpc(this); }
293
294 MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn)
295 {
296         entity ent = M_ARGV(0, entity);
297
298         if(autocvar_g_powerups)
299         if(autocvar_g_overkill_powerups_replace)
300         {
301                 if(ent.classname == "item_strength")
302                 {
303                         entity wep = new(weapon_hmg);
304                         setorigin(wep, ent.origin);
305                         setmodel(wep, MDL_OK_HMG);
306                         wep.ok_item = true;
307                         wep.noalign = ent.noalign;
308                         wep.cnt = ent.cnt;
309                         wep.team = ent.team;
310                         wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
311                         wep.pickup_anyway = true;
312                         wep.spawnfunc_checked = true;
313                         setthink(wep, self_spawnfunc_weapon_hmg);
314                         wep.nextthink = time + 0.1;
315                         return true;
316                 }
317
318                 if(ent.classname == "item_invincible")
319                 {
320                         entity wep = new(weapon_rpc);
321                         setorigin(wep, ent.origin);
322                         setmodel(wep, MDL_OK_RPC);
323                         wep.ok_item = true;
324                         wep.noalign = ent.noalign;
325                         wep.cnt = ent.cnt;
326                         wep.team = ent.team;
327                         wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
328                         wep.pickup_anyway = true;
329                         wep.spawnfunc_checked = true;
330                         setthink(wep, self_spawnfunc_weapon_rpc);
331                         wep.nextthink = time + 0.1;
332                         return true;
333                 }
334         }
335 }
336
337 MUTATOR_HOOKFUNCTION(ok, FilterItem)
338 {
339         entity item = M_ARGV(0, entity);
340
341         if(item.ok_item)
342                 return;
343
344         switch(item.items)
345         {
346                 case ITEM_HealthMega.m_itemid: return !(autocvar_g_overkill_100h_anyway);
347                 case ITEM_ArmorMega.m_itemid: return !(autocvar_g_overkill_100a_anyway);
348         }
349
350         return true;
351 }
352
353 MUTATOR_HOOKFUNCTION(ok, SpectateCopy)
354 {
355         entity spectatee = M_ARGV(0, entity);
356         entity client = M_ARGV(1, entity);
357
358         client.ammo_charge[PS(client).m_weapon.m_id] = spectatee.ammo_charge[PS(spectatee).m_weapon.m_id];
359         client.ok_use_ammocharge = spectatee.ok_use_ammocharge;
360 }
361
362 MUTATOR_HOOKFUNCTION(ok, SetStartItems)
363 {
364         WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN));
365
366         if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); }
367         if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); }
368
369         start_items |= IT_UNLIMITED_WEAPON_AMMO;
370         start_weapons = warmup_start_weapons = ok_start_items;
371 }
372
373 MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString)
374 {
375         M_ARGV(0, string) = strcat(M_ARGV(0, string), ":OK");
376 }
377
378 MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString)
379 {
380         M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Overkill");
381 }
382
383 MUTATOR_HOOKFUNCTION(ok, SetModname)
384 {
385         M_ARGV(0, string) = "Overkill";
386         return true;
387 }
388
389 void ok_SetCvars()
390 {
391         // hack to force overkill playermodels
392         cvar_settemp("sv_defaultcharacter", "1");
393         cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
394         cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm");
395         cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
396 }
397
398 void ok_Initialize()
399 {
400         ok_SetCvars();
401
402         precache_all_playermodels("models/ok_player/*.dpm");
403
404         WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
405         WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
406
407         WEP_SHOTGUN.mdl = "ok_shotgun";
408         WEP_MACHINEGUN.mdl = "ok_mg";
409         WEP_VORTEX.mdl = "ok_sniper";
410 }