Fix vortex chargepol
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / mutator / nix / sv_nix.qc
1 #include "sv_nix.qh"
2
3 string autocvar_g_nix;
4 int autocvar_g_balance_nix_ammo_cells;
5 int autocvar_g_balance_nix_ammo_plasma;
6 int autocvar_g_balance_nix_ammo_fuel;
7 int autocvar_g_balance_nix_ammo_nails;
8 int autocvar_g_balance_nix_ammo_rockets;
9 int autocvar_g_balance_nix_ammo_shells;
10 int autocvar_g_balance_nix_ammoincr_cells;
11 int autocvar_g_balance_nix_ammoincr_plasma;
12 int autocvar_g_balance_nix_ammoincr_fuel;
13 int autocvar_g_balance_nix_ammoincr_nails;
14 int autocvar_g_balance_nix_ammoincr_rockets;
15 int autocvar_g_balance_nix_ammoincr_shells;
16 float autocvar_g_balance_nix_incrtime;
17 float autocvar_g_balance_nix_roundtime;
18 bool autocvar_g_nix_with_healtharmor;
19 bool autocvar_g_nix_with_blaster;
20 bool autocvar_g_nix_with_powerups;
21 int autocvar_g_pickup_cells_max;
22 int autocvar_g_pickup_plasma_max;
23 int autocvar_g_pickup_fuel_max;
24 int autocvar_g_pickup_nails_max;
25 int autocvar_g_pickup_rockets_max;
26 int autocvar_g_pickup_shells_max;
27
28 float g_nix_with_blaster;
29 // WEAPONTODO
30 int nix_weapon;
31 float nix_nextchange;
32 float nix_nextweapon;
33 .float nix_lastchange_id;
34 .float nix_lastinfotime;
35 .float nix_nextincr;
36
37 bool NIX_CanChooseWeapon(int wpn);
38
39 REGISTER_MUTATOR(nix, expr_evaluate(autocvar_g_nix) && !cvar("g_instagib") && !cvar("g_overkill"))
40 {
41         MUTATOR_ONADD
42         {
43                 g_nix_with_blaster = autocvar_g_nix_with_blaster;
44
45                 nix_nextchange = 0;
46                 nix_nextweapon = 0;
47
48                 FOREACH(Weapons, it != WEP_Null && NIX_CanChooseWeapon(it.m_id), { it.wr_init(it); });
49         }
50
51         MUTATOR_ONROLLBACK_OR_REMOVE
52         {
53                 // nothing to roll back
54         }
55
56         MUTATOR_ONREMOVE
57         {
58                 // as the PlayerSpawn hook will no longer run, NIX is turned off by this!
59                 FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), {
60                         SetResourceAmount(it, RESOURCE_SHELLS, start_ammo_shells);
61                         SetResourceAmount(it, RESOURCE_BULLETS, start_ammo_nails);
62                         SetResourceAmount(it, RESOURCE_ROCKETS, start_ammo_rockets);
63                         SetResourceAmount(it, RESOURCE_CELLS, start_ammo_cells);
64                         SetResourceAmount(it, RESOURCE_PLASMA, start_ammo_plasma);
65                         SetResourceAmount(it, RESOURCE_FUEL, start_ammo_fuel);
66                         it.weapons = start_weapons;
67                         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
68                         {
69                                 .entity weaponentity = weaponentities[slot];
70                                 if(it.(weaponentity).m_weapon == WEP_Null && slot != 0)
71                                         continue;
72                                 if(!client_hasweapon(it, it.(weaponentity).m_weapon, weaponentity, true, false))
73                                         it.(weaponentity).m_switchweapon = w_getbestweapon(it, weaponentity);
74                         }
75                 });
76         }
77
78         return false;
79 }
80
81 bool NIX_CanChooseWeapon(int wpn)
82 {
83         entity e = Weapons_from(wpn);
84         if (e == WEP_Null) return false; // skip dummies
85         if(g_weaponarena)
86         {
87                 if(!(g_weaponarena_weapons & e.m_wepset))
88                         return false;
89         }
90         else
91         {
92                 if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster)
93                         return false;
94                 if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
95                         return false;
96                 if (!(e.spawnflags & WEP_FLAG_NORMAL))
97                         return false;
98         }
99         return true;
100 }
101 void NIX_ChooseNextWeapon()
102 {
103         RandomSelection_Init();
104         FOREACH(Weapons, it != WEP_Null, {
105                 if(NIX_CanChooseWeapon(it.m_id))
106                         RandomSelection_AddFloat(it.m_id, 1, (it.m_id != nix_weapon));
107         });
108         nix_nextweapon = RandomSelection_chosen_float;
109 }
110
111 void NIX_GiveCurrentWeapon(entity this)
112 {
113         float dt;
114
115         if(!nix_nextweapon)
116                 NIX_ChooseNextWeapon();
117
118         dt = ceil(nix_nextchange - time);
119
120         if(dt <= 0)
121         {
122                 nix_weapon = nix_nextweapon;
123                 nix_nextweapon = 0;
124                 if (!nix_nextchange) // no round played yet?
125                         nix_nextchange = time; // start the first round now!
126                 else
127                         nix_nextchange = time + autocvar_g_balance_nix_roundtime;
128                 // Weapon w = Weapons_from(nix_weapon);
129                 // w.wr_init(w); // forget it, too slow
130         }
131
132         // get weapon info
133         entity e = Weapons_from(nix_weapon);
134
135         if(nix_nextchange != this.nix_lastchange_id) // this shall only be called once per round!
136         {
137                 SetResourceAmount(this, RESOURCE_SHELLS, 0);
138                 SetResourceAmount(this, RESOURCE_BULLETS, 0);
139                 SetResourceAmount(this, RESOURCE_ROCKETS, 0);
140                 SetResourceAmount(this, RESOURCE_CELLS, 0);
141                 SetResourceAmount(this, RESOURCE_PLASMA, 0);
142                 SetResourceAmount(this, RESOURCE_FUEL, 0);
143                 if(this.items & IT_UNLIMITED_WEAPON_AMMO)
144                 {
145                         switch (e.ammo_type)
146                         {
147                                 case RESOURCE_SHELLS:  SetResourceAmount(this, RESOURCE_SHELLS, autocvar_g_pickup_shells_max);  break;
148                                 case RESOURCE_BULLETS: SetResourceAmount(this, RESOURCE_BULLETS, autocvar_g_pickup_nails_max);   break;
149                                 case RESOURCE_ROCKETS: SetResourceAmount(this, RESOURCE_ROCKETS, autocvar_g_pickup_rockets_max); break;
150                                 case RESOURCE_CELLS:   SetResourceAmount(this, RESOURCE_CELLS, autocvar_g_pickup_cells_max);   break;
151                                 case RESOURCE_PLASMA:  SetResourceAmount(this, RESOURCE_PLASMA, autocvar_g_pickup_plasma_max);   break;
152                                 case RESOURCE_FUEL:    SetResourceAmount(this, RESOURCE_FUEL, autocvar_g_pickup_fuel_max);    break;
153                         }
154                 }
155                 else
156                 {
157                         switch (e.ammo_type)
158                         {
159                                 case RESOURCE_SHELLS:  SetResourceAmount(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammo_shells);  break;
160                                 case RESOURCE_BULLETS: SetResourceAmount(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammo_nails);   break;
161                                 case RESOURCE_ROCKETS: SetResourceAmount(this, RESOURCE_ROCKETS, autocvar_g_balance_nix_ammo_rockets); break;
162                                 case RESOURCE_CELLS:   SetResourceAmount(this, RESOURCE_CELLS, autocvar_g_balance_nix_ammo_cells);   break;
163                                 case RESOURCE_PLASMA:  SetResourceAmount(this, RESOURCE_PLASMA, autocvar_g_balance_nix_ammo_plasma);   break;
164                                 case RESOURCE_FUEL:    SetResourceAmount(this, RESOURCE_FUEL, autocvar_g_balance_nix_ammo_fuel);    break;
165                         }
166                 }
167
168                 this.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
169                 if(dt >= 1 && dt <= 5)
170                         this.nix_lastinfotime = -42;
171                 else
172                         Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon);
173
174                 e.wr_resetplayer(e, this);
175
176                 // all weapons must be fully loaded when we spawn
177                 if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
178                 {
179                         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
180                         {
181                                 .entity weaponentity = weaponentities[slot];
182                                 this.(weaponentity).(weapon_load[nix_weapon]) = e.reloading_ammo;
183                         }
184                 }
185
186                 // set last change info
187                 this.nix_lastchange_id = nix_nextchange;
188         }
189         if(this.nix_lastinfotime != dt)
190         {
191                 this.nix_lastinfotime = dt; // initial value 0 should count as "not seen"
192                 if(dt >= 1 && dt <= 5)
193                         Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt);
194         }
195
196         if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr)
197         {
198                 switch (e.ammo_type)
199                 {
200                         case RESOURCE_SHELLS:  GiveResource(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammoincr_shells);  break;
201                         case RESOURCE_BULLETS: GiveResource(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammoincr_nails);   break;
202                         case RESOURCE_ROCKETS: GiveResource(this, RESOURCE_ROCKETS, autocvar_g_balance_nix_ammoincr_rockets); break;
203                         case RESOURCE_CELLS:   GiveResource(this, RESOURCE_CELLS, autocvar_g_balance_nix_ammoincr_cells);   break;
204                         case RESOURCE_PLASMA:  GiveResource(this, RESOURCE_PLASMA, autocvar_g_balance_nix_ammoincr_plasma);   break;
205                         case RESOURCE_FUEL:    GiveResource(this, RESOURCE_FUEL, autocvar_g_balance_nix_ammoincr_fuel);    break;
206                 }
207
208                 this.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
209         }
210
211         this.weapons = '0 0 0';
212         if(g_nix_with_blaster)
213                 this.weapons |= WEPSET(BLASTER);
214         this.weapons |= e.m_wepset;
215
216     Weapon w = Weapons_from(nix_weapon);
217     for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
218     {
219         .entity weaponentity = weaponentities[slot];
220         if(this.(weaponentity).m_weapon == WEP_Null && slot != 0)
221                 continue;
222
223                 if(this.(weaponentity).m_switchweapon != w)
224                 if(!client_hasweapon(this, this.(weaponentity).m_switchweapon, weaponentity, true, false))
225                 {
226                         if(client_hasweapon(this, w, weaponentity, true, false))
227                                 W_SwitchWeapon(this, w, weaponentity);
228                 }
229         }
230 }
231
232 MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon)
233 {
234         return true; // no throwing in NIX
235 }
236
237 MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString)
238 {
239         M_ARGV(0, string) = strcat(M_ARGV(0, string), ":NIX");
240 }
241
242 MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString)
243 {
244         M_ARGV(0, string) = strcat(M_ARGV(0, string), ", NIX");
245 }
246
247 MUTATOR_HOOKFUNCTION(nix, FilterItem)
248 {
249         entity item = M_ARGV(0, entity);
250
251         if(item.itemdef.instanceOfHealth || item.itemdef.instanceOfArmor)
252         {
253                 return !autocvar_g_nix_with_healtharmor;
254         }
255         else if(item.itemdef.instanceOfPowerup)
256         {
257                 return !autocvar_g_nix_with_powerups;
258         }
259
260         return true; // delete all other items
261 }
262
263 MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn)
264 {
265         entity ent = M_ARGV(0, entity);
266
267         if(ent.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo)
268                 return true;
269 }
270
271 MUTATOR_HOOKFUNCTION(nix, PlayerPreThink)
272 {
273         entity player = M_ARGV(0, entity);
274
275         if(!game_stopped)
276         if(!IS_DEAD(player))
277         if(IS_PLAYER(player))
278                 NIX_GiveCurrentWeapon(player);
279 }
280
281 MUTATOR_HOOKFUNCTION(nix, ForbidRandomStartWeapons)
282 {
283         return true;
284 }
285
286 MUTATOR_HOOKFUNCTION(nix, PlayerSpawn)
287 {
288         entity player = M_ARGV(0, entity);
289
290         player.nix_lastchange_id = -1;
291         NIX_GiveCurrentWeapon(player); // overrides the weapons you got when spawning
292         player.items |= IT_UNLIMITED_SUPERWEAPONS;
293 }
294
295 MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST)
296 {
297         M_ARGV(0, string) = "NIX";
298 }