eb14a3159efd0f9f9e4e6fbdcc46cecb6718cb0e
[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                 // vortex too
187                 if(WEP_CVAR(vortex, charge))
188                 {
189                         if(WEP_CVAR_SEC(vortex, chargepool))
190                                 this.vortex_chargepool_ammo = 1;
191                         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
192                         {
193                                 .entity weaponentity = weaponentities[slot];
194                                 this.(weaponentity).vortex_charge = WEP_CVAR(vortex, charge_start);
195                         }
196                 }
197
198                 // set last change info
199                 this.nix_lastchange_id = nix_nextchange;
200         }
201         if(this.nix_lastinfotime != dt)
202         {
203                 this.nix_lastinfotime = dt; // initial value 0 should count as "not seen"
204                 if(dt >= 1 && dt <= 5)
205                         Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt);
206         }
207
208         if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr)
209         {
210                 switch (e.ammo_type)
211                 {
212                         case RESOURCE_SHELLS:  GiveResource(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammoincr_shells);  break;
213                         case RESOURCE_BULLETS: GiveResource(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammoincr_nails);   break;
214                         case RESOURCE_ROCKETS: GiveResource(this, RESOURCE_ROCKETS, autocvar_g_balance_nix_ammoincr_rockets); break;
215                         case RESOURCE_CELLS:   GiveResource(this, RESOURCE_CELLS, autocvar_g_balance_nix_ammoincr_cells);   break;
216                         case RESOURCE_PLASMA:  GiveResource(this, RESOURCE_PLASMA, autocvar_g_balance_nix_ammoincr_plasma);   break;
217                         case RESOURCE_FUEL:    GiveResource(this, RESOURCE_FUEL, autocvar_g_balance_nix_ammoincr_fuel);    break;
218                 }
219
220                 this.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
221         }
222
223         this.weapons = '0 0 0';
224         if(g_nix_with_blaster)
225                 this.weapons |= WEPSET(BLASTER);
226         this.weapons |= e.m_wepset;
227
228     Weapon w = Weapons_from(nix_weapon);
229     for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
230     {
231         .entity weaponentity = weaponentities[slot];
232         if(this.(weaponentity).m_weapon == WEP_Null && slot != 0)
233                 continue;
234
235                 if(this.(weaponentity).m_switchweapon != w)
236                 if(!client_hasweapon(this, this.(weaponentity).m_switchweapon, weaponentity, true, false))
237                 {
238                         if(client_hasweapon(this, w, weaponentity, true, false))
239                                 W_SwitchWeapon(this, w, weaponentity);
240                 }
241         }
242 }
243
244 MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon)
245 {
246         return true; // no throwing in NIX
247 }
248
249 MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString)
250 {
251         M_ARGV(0, string) = strcat(M_ARGV(0, string), ":NIX");
252 }
253
254 MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString)
255 {
256         M_ARGV(0, string) = strcat(M_ARGV(0, string), ", NIX");
257 }
258
259 MUTATOR_HOOKFUNCTION(nix, FilterItem)
260 {
261         entity item = M_ARGV(0, entity);
262
263         if(item.itemdef.instanceOfHealth || item.itemdef.instanceOfArmor)
264         {
265                 return !autocvar_g_nix_with_healtharmor;
266         }
267         else if(item.itemdef.instanceOfPowerup)
268         {
269                 return !autocvar_g_nix_with_powerups;
270         }
271
272         return true; // delete all other items
273 }
274
275 MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn)
276 {
277         entity ent = M_ARGV(0, entity);
278
279         if(ent.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo)
280                 return true;
281 }
282
283 MUTATOR_HOOKFUNCTION(nix, PlayerPreThink)
284 {
285         entity player = M_ARGV(0, entity);
286
287         if(!game_stopped)
288         if(!IS_DEAD(player))
289         if(IS_PLAYER(player))
290                 NIX_GiveCurrentWeapon(player);
291 }
292
293 MUTATOR_HOOKFUNCTION(nix, ForbidRandomStartWeapons)
294 {
295         return true;
296 }
297
298 MUTATOR_HOOKFUNCTION(nix, PlayerSpawn)
299 {
300         entity player = M_ARGV(0, entity);
301
302         player.nix_lastchange_id = -1;
303         NIX_GiveCurrentWeapon(player); // overrides the weapons you got when spawning
304         player.items |= IT_UNLIMITED_SUPERWEAPONS;
305 }
306
307 MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST)
308 {
309         M_ARGV(0, string) = "NIX";
310 }