Merge branch 'master' into Mario/qcphysics_tweaks
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / mutator / overkill / sv_overkill.qc
1 #include "sv_overkill.qh"
2
3 #include "okshotgun.qh"
4 #include "okhmg.qh"
5 #include "okrpc.qh"
6
7 bool autocvar_g_overkill_powerups_replace;
8
9 bool autocvar_g_overkill_itemwaypoints = true;
10
11 .Weapon ok_lastwep[MAX_WEAPONSLOTS];
12
13 IntrusiveList g_overkill_items;
14 STATIC_INIT()
15 {
16         g_overkill_items = IL_NEW();
17         IL_PUSH(g_overkill_items, ITEM_HealthMega);
18         IL_PUSH(g_overkill_items, ITEM_ArmorSmall);
19         IL_PUSH(g_overkill_items, ITEM_ArmorMedium);
20         IL_PUSH(g_overkill_items, ITEM_ArmorBig);
21         IL_PUSH(g_overkill_items, ITEM_ArmorMega);
22 }
23
24 /// \brief Returns a random classname of the overkill item.
25 /// \param[in] prefix Prefix of the cvars that hold probabilities.
26 /// \return Random classname of the overkill item.
27 string RandomItems_GetRandomOverkillItemClassName(string prefix)
28 {
29         RandomSelection_Init();
30         IL_EACH(g_overkill_items, !(it.spawnflags & ITEM_FLAG_MUTATORBLOCKED) &&
31                 Item_IsDefinitionAllowed(it),
32         {
33                 string cvar_name = sprintf("g_%s_%s_probability", prefix,
34                         it.m_canonical_spawnfunc);
35                 if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
36                 {
37                         LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
38                         continue;
39                 }
40                 RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
41         });
42         string cvar_name = sprintf("g_%s_weapon_okhmg_probability", prefix);
43         if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
44         {
45                 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
46         }
47         else
48         {
49                 RandomSelection_AddString("weapon_okhmg", cvar(cvar_name), 1);
50         }
51         cvar_name = sprintf("g_%s_weapon_okrpc_probability", prefix);
52         if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
53         {
54                 LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
55         }
56         else
57         {
58                 RandomSelection_AddString("weapon_okrpc", cvar(cvar_name), 1);
59         }
60         return RandomSelection_chosen_string;
61 }
62
63
64 MUTATOR_HOOKFUNCTION(ok, RandomItems_GetRandomItemClassName)
65 {
66         M_ARGV(1, string) = RandomItems_GetRandomOverkillItemClassName(
67                 M_ARGV(0, string));
68         return true;
69 }
70
71 MUTATOR_HOOKFUNCTION(ok, Damage_Calculate, CBC_ORDER_LAST)
72 {
73         entity frag_attacker = M_ARGV(1, entity);
74         entity frag_target = M_ARGV(2, entity);
75         float frag_deathtype = M_ARGV(3, float);
76
77         if(IS_PLAYER(frag_attacker) && (IS_PLAYER(frag_target) || IS_VEHICLE(frag_target) || IS_TURRET(frag_target)))
78         if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
79         {
80                 if(frag_attacker != frag_target)
81                 if(!STAT(FROZEN, frag_target))
82                 if(!IS_DEAD(frag_target))
83                 {
84                         Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE);
85                         M_ARGV(6, vector) = '0 0 0'; // force
86                 }
87
88                 M_ARGV(4, float) = 0; // damage
89         }
90 }
91
92 void ok_DropItem(entity this, entity targ)
93 {
94         entity e = spawn();
95         e.ok_item = true;
96         Item_InitializeLoot(e, "item_armor_small", this.origin + '0 0 32',
97                 '0 0 200' + normalize(targ.origin - this.origin) * 500, 5);
98 }
99
100 MUTATOR_HOOKFUNCTION(ok, PlayerDies)
101 {
102         entity frag_attacker = M_ARGV(1, entity);
103         entity frag_target = M_ARGV(2, entity);
104
105         entity targ = ((IS_PLAYER(frag_attacker)) ? frag_attacker : frag_target);
106
107         ok_DropItem(frag_target, targ);
108
109         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
110         {
111                 .entity weaponentity = weaponentities[slot];
112
113                 frag_target.ok_lastwep[slot] = frag_target.(weaponentity).m_switchweapon;
114         }
115 }
116
117 MUTATOR_HOOKFUNCTION(ok, MonsterDropItem)
118 {
119         entity mon = M_ARGV(0, entity);
120         entity olditem = M_ARGV(1, entity);
121         entity frag_attacker = M_ARGV(2, entity);
122
123         delete(olditem);
124
125         M_ARGV(1, entity) = NULL;
126
127         ok_DropItem(mon, frag_attacker);
128 }
129
130 MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon)
131 {
132         return true;
133 }
134
135 MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
136 {
137         if (game_stopped)
138         {
139                 return;
140         }
141         entity player = M_ARGV(0, entity);
142         if (!IS_PLAYER(player) || IS_DEAD(player) || STAT(FROZEN, player))
143         {
144                 return;
145         }
146         if (!PHYS_INPUT_BUTTON_ATCK2(player) || forbidWeaponUse(player) ||
147                 !(round_handler_IsActive() && !round_handler_IsRoundStarted()))
148         {
149                 return;
150         }
151         // Allow secondary blaster during countdown.
152         for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
153         {
154                 .entity weaponentity = weaponentities[slot];
155                 Weapon weapon = player.(weaponentity).m_weapon;
156                 if (weapon == WEP_Null && slot != 0)
157                 {
158                         continue;
159                 }
160                 weapon.wr_think(weapon, player, weaponentity, 2);
161         }
162         PHYS_INPUT_BUTTON_ATCK2(player) = false;
163 }
164
165 MUTATOR_HOOKFUNCTION(ok, ForbidRandomStartWeapons)
166 {
167         return true;
168 }
169
170 MUTATOR_HOOKFUNCTION(ok, PlayerWeaponSelect)
171 {
172         entity player = M_ARGV(0, entity);
173
174         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
175         {
176                 .entity weaponentity = weaponentities[slot];
177                 entity thiswep = player.(weaponentity);
178
179                 if(player.ok_lastwep[slot] && player.ok_lastwep[slot] != WEP_Null)
180                 {
181                         Weapon newwep = player.ok_lastwep[slot];
182                         if(player.ok_lastwep[slot] == WEP_OVERKILL_HMG)
183                                 newwep = WEP_OVERKILL_MACHINEGUN;
184                         if(player.ok_lastwep[slot] == WEP_OVERKILL_RPC)
185                                 newwep = WEP_OVERKILL_NEX;
186                         thiswep.m_switchweapon = newwep;
187                         player.ok_lastwep[slot] = WEP_Null;
188                 }
189         }
190 }
191
192 bool ok_HandleItemWaypoints(entity e)
193 {
194         if(!autocvar_g_overkill_itemwaypoints)
195                 return false; // don't handle it
196
197         switch(e.itemdef)
198         {
199                 case ITEM_HealthMega: return true;
200                 case ITEM_ArmorMedium: return true;
201                 case ITEM_ArmorBig: return true;
202                 case ITEM_ArmorMega: return true;
203         }
204
205         return false;
206 }
207
208 MUTATOR_HOOKFUNCTION(ok, Item_RespawnCountdown)
209 {
210         entity item = M_ARGV(0, entity);
211         return ok_HandleItemWaypoints(item);
212 }
213
214 MUTATOR_HOOKFUNCTION(ok, Item_ScheduleRespawn)
215 {
216         entity item = M_ARGV(0, entity);
217         return ok_HandleItemWaypoints(item);
218 }
219
220 MUTATOR_HOOKFUNCTION(ok, FilterItem)
221 {
222         entity item = M_ARGV(0, entity);
223
224         if (item.ok_item)
225         {
226                 return false;
227         }
228         switch(item.itemdef)
229         {
230                 case ITEM_HealthMega: return autocvar_g_overkill_filter_healthmega;
231                 case ITEM_ArmorMedium: return autocvar_g_overkill_filter_armormedium;
232                 case ITEM_ArmorBig: return autocvar_g_overkill_filter_armorbig;
233                 case ITEM_ArmorMega: return autocvar_g_overkill_filter_armormega;
234         }
235         if (!autocvar_g_powerups || !autocvar_g_overkill_powerups_replace)
236         {
237                 return true;
238         }
239         if (item.classname == "item_strength")
240         {
241                 entity wep = new(weapon_okhmg);
242                 setorigin(wep, item.origin);
243                 wep.ok_item = true;
244                 wep.noalign = Item_ShouldKeepPosition(item);
245                 wep.cnt = item.cnt;
246                 wep.team = item.team;
247                 wep.respawntime = g_pickup_respawntime_superweapon;
248                 wep.pickup_anyway = true;
249                 wep.spawnfunc_checked = true;
250                 Item_Initialize(wep, "weapon_okhmg"); // doesn't actually use spawnfunc
251                 return true;
252         }
253         else if (item.classname == "item_shield")
254         {
255                 entity wep = new(weapon_okrpc);
256                 setorigin(wep, item.origin);
257                 wep.ok_item = true;
258                 wep.noalign = Item_ShouldKeepPosition(item);
259                 wep.cnt = item.cnt;
260                 wep.team = item.team;
261                 wep.respawntime = g_pickup_respawntime_superweapon;
262                 wep.pickup_anyway = true;
263                 wep.spawnfunc_checked = true;
264                 Item_Initialize(wep, "weapon_okrpc"); // doesn't actually use spawnfunc
265                 return true;
266         }
267         return true;
268 }
269
270 MUTATOR_HOOKFUNCTION(ok, SetStartItems, CBC_ORDER_LAST)
271 {
272         WepSet ok_start_items = (WEPSET(OVERKILL_MACHINEGUN) | WEPSET(OVERKILL_NEX) | WEPSET(OVERKILL_SHOTGUN));
273
274         if(WEP_OVERKILL_RPC.weaponstart > 0) { ok_start_items |= WEPSET(OVERKILL_RPC); }
275         if(WEP_OVERKILL_HMG.weaponstart > 0) { ok_start_items |= WEPSET(OVERKILL_HMG); }
276
277         start_items |= IT_UNLIMITED_WEAPON_AMMO;
278         start_weapons = warmup_start_weapons = ok_start_items;
279 }
280
281 MUTATOR_HOOKFUNCTION(ok, SetWeaponArena)
282 {
283         // turn weapon arena off
284         M_ARGV(0, string) = "off";
285 }
286
287 MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString)
288 {
289         M_ARGV(0, string) = strcat(M_ARGV(0, string), ":OK");
290 }
291
292 MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString)
293 {
294         M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Overkill");
295 }
296
297 MUTATOR_HOOKFUNCTION(ok, SetModname)
298 {
299         M_ARGV(0, string) = "Overkill";
300         return true;
301 }
302