]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/t_items.qc
testing:
[divverent/nexuiz.git] / data / qcsrc / server / t_items.qc
1 #define ITEM_RESPAWNTIME(i) ((i).respawntime + crandom() * (i).respawntimejitter)
2
3 floatfield Item_CounterField(float it)
4 {
5         switch(it)
6         {
7                 case IT_SHELLS:      return ammo_shells;
8                 case IT_NAILS:       return ammo_nails;
9                 case IT_ROCKETS:     return ammo_rockets;
10                 case IT_CELLS:       return ammo_cells;
11                 case IT_FUEL:        return ammo_fuel;
12                 case IT_5HP:         return health;
13                 case IT_25HP:        return health;
14                 case IT_HEALTH:      return health;
15                 case IT_ARMOR_SHARD: return armorvalue;
16                 case IT_ARMOR:       return armorvalue;
17                 // add more things here (health, armor)
18                 default:             error("requested item has no counter field");
19         }
20 }
21
22 string Item_CounterFieldName(float it)
23 {
24         switch(it)
25         {
26                 case IT_SHELLS:      return "shells";
27                 case IT_NAILS:       return "nails";
28                 case IT_ROCKETS:     return "rockets";
29                 case IT_CELLS:       return "cells";
30                 case IT_FUEL:        return "fuel";
31
32                 // add more things here (health, armor)
33                 default:             error("requested item has no counter field name");
34         }
35 }
36
37 .float max_armorvalue;
38
39 void Item_Respawn (void)
40 {
41         self.model = self.mdl;          // restore original model
42         self.solid = SOLID_TRIGGER;     // allow it to be touched again
43         sound (self, CHAN_TRIGGER, "misc/itemrespawn.wav", VOL_BASE, ATTN_NORM);        // play respawn sound
44         setorigin (self, self.origin);
45
46         //pointparticles(particleeffectnum("item_respawn"), self.origin + self.mins_z * '0 0 1' + '0 0 48', '0 0 0', 1);
47         pointparticles(particleeffectnum("item_respawn"), self.origin + 0.5 * (self.mins + self.maxs), '0 0 0', 1);
48 }
49
50 void Item_RespawnCountdown (void)
51 {
52         if(self.count >= 5)
53         {
54                 self.count = 0;
55                 if(self.waypointsprite_attached)
56                         WaypointSprite_Kill(self.waypointsprite_attached);
57                 Item_Respawn();
58         }
59         else
60         {
61                 self.nextthink = time + 1;
62                 self.count += 1;
63                 if(self.count == 1)
64                 {
65                         string name;
66                         vector rgb;
67                         name = string_null;
68                         if(g_minstagib)
69                         {
70                                 switch(self.items)
71                                 {
72                                         case IT_STRENGTH:   name = "item-invis"; rgb = '0 0 1'; break;
73                                         case IT_NAILS:      name = "item-extralife"; rgb = '1 0 0'; break;
74                                         case IT_INVINCIBLE: name = "item-speed"; rgb = '1 0 1'; break;
75                                 }
76                         }
77                         else
78                         {
79                                 switch(self.items)
80                                 {
81                                         case IT_STRENGTH:   name = "item-strength"; rgb = '0 0 1'; break;
82                                         case IT_INVINCIBLE: name = "item-shield"; rgb = '1 0 1'; break;
83                                 }
84                         }
85                         switch(self.items)
86                         {
87                                 case IT_FUEL_REGEN:     name = "item-fuelregen"; rgb = '1 0.5 0'; break;
88                                 case IT_JETPACK:        name = "item-jetpack"; rgb = '1 0.5 0'; break;
89                         }
90                         if(name)
91                         {
92                                 WaypointSprite_Attach(name, FALSE);
93                                 if(self.waypointsprite_attached)
94                                         WaypointSprite_UpdateTeamRadar(self.waypointsprite_attached, RADARICON_POWERUP, rgb);
95                         }
96                 }
97                 sound (self, CHAN_TRIGGER, "misc/itemrespawncountdown.wav", VOL_BASE, ATTN_NORM);       // play respawn sound
98                 if(self.waypointsprite_attached)
99                         WaypointSprite_Ping(self.waypointsprite_attached);
100         }
101 }
102
103 void Item_ScheduleRespawn(entity e)
104 {
105         if(e.flags & FL_POWERUP)
106         {
107                 e.think = Item_RespawnCountdown;
108                 e.nextthink = time + ITEM_RESPAWNTIME(e) - 5;
109         }
110         else
111         {
112                 e.think = Item_Respawn;
113                 e.nextthink = time + ITEM_RESPAWNTIME(e);
114         }
115 }
116
117 float Item_GiveTo(entity item, entity player)
118 {
119         float _switchweapon;
120         float pickedup;
121         float it;
122         float i;
123         entity e;
124
125         // if nothing happens to player, just return without taking the item
126         pickedup = FALSE;
127         _switchweapon = FALSE;
128
129         if (g_minstagib)
130         {
131                 _switchweapon = TRUE;
132                 if (item.ammo_cells)
133                 {
134                         pickedup = TRUE;
135                         // play some cool sounds ;)
136                         centerprint(player, "\n");
137                         if(player.health <= 5)
138                                 announce(player, "announcer/robotic/lastsecond.wav");
139                         else if(player.health < 50)
140                                 announce(player, "announcer/robotic/narrowly.wav");
141                         // sound not available
142                         // else if(item.items == IT_CELLS)
143                         //      play2(player, "announce/robotic/ammo.wav");
144
145                         if (item.weapons & WEPBIT_MINSTANEX)
146                                 W_GiveWeapon (player, WEP_MINSTANEX, "Nex");
147                         if (item.ammo_cells)
148                                 player.ammo_cells = min (player.ammo_cells + cvar("g_minstagib_ammo_drop"), 999);
149                         player.health = 100;
150                 }
151
152                 // extralife powerup
153                 if (item.max_health)
154                 {
155                         pickedup = TRUE;
156                         // sound not available
157                         // play2(player, "announce/robotic/extra.ogg\nplay2 announce/robotic/_lives.wav");
158                         player.armorvalue = player.armorvalue + cvar("g_minstagib_extralives");
159                         sprint(player, "^3You picked up some extra lives\n");
160                 }
161
162                 // invis powerup
163                 if (item.strength_finished)
164                 {
165                         pickedup = TRUE;
166                         // sound not available
167                         // play2(player, "announce/robotic/invisible.wav");
168                         player.strength_finished = max(player.strength_finished, time) + cvar("g_balance_powerup_strength_time");
169                 }
170
171                 // speed powerup
172                 if (item.invincible_finished)
173                 {
174                         pickedup = TRUE;
175                         // sound not available
176                         // play2(player, "announce/robotic/speed.wav");
177                         player.invincible_finished = max(player.invincible_finished, time) + cvar("g_balance_powerup_strength_time");
178                 }
179         }
180         else
181         {
182                 if (g_weapon_stay == 1)
183                 if not(item.flags & FL_NO_WEAPON_STAY)
184                 if (item.flags & FL_WEAPON)
185                 {
186                         if(item.classname == "droppedweapon")
187                         {
188                                 if (player.weapons & item.weapons)      // don't let players stack ammo by tossing weapons
189                                         goto skip;
190                         }
191                         else
192                         {
193                                 if (player.weapons & item.weapons)
194                                         goto skip;
195                         }
196                 }
197
198                 // in case the player has autoswitch enabled do the following:
199                 // if the player is using their best weapon before items are given, they
200                 // probably want to switch to an even better weapon after items are given
201                 if (player.autoswitch)
202                 if (player.switchweapon == w_getbestweapon(player))
203                         _switchweapon = TRUE;
204
205                 if not(player.weapons & W_WeaponBit(player.switchweapon))
206                         _switchweapon = TRUE;
207
208                 if (item.ammo_shells)
209                 if (player.ammo_shells < g_pickup_shells_max)
210                 {
211                         pickedup = TRUE;
212                         player.ammo_shells = min (player.ammo_shells + item.ammo_shells, g_pickup_shells_max);
213                 }
214                 if (item.ammo_nails)
215                 if (player.ammo_nails < g_pickup_nails_max)
216                 {
217                         pickedup = TRUE;
218                         player.ammo_nails = min (player.ammo_nails + item.ammo_nails, g_pickup_nails_max);
219                 }
220                 if (item.ammo_rockets)
221                 if (player.ammo_rockets < g_pickup_rockets_max)
222                 {
223                         pickedup = TRUE;
224                         player.ammo_rockets = min (player.ammo_rockets + item.ammo_rockets, g_pickup_rockets_max);
225                 }
226                 if (item.ammo_cells)
227                 if (player.ammo_cells < g_pickup_cells_max)
228                 {
229                         pickedup = TRUE;
230                         player.ammo_cells = min (player.ammo_cells + item.ammo_cells, g_pickup_cells_max);
231                 }
232                 if (item.ammo_fuel)
233                 if (player.ammo_fuel < g_pickup_fuel_max)
234                 {
235                         pickedup = TRUE;
236                         player.ammo_fuel = min(player.ammo_fuel + item.ammo_fuel, g_pickup_fuel_max);
237                         player.pauserotfuel_finished = max(player.pauserotfuel_finished, time + cvar("g_balance_pause_fuel_rot"));
238                 }
239
240                 if (item.flags & FL_WEAPON)
241                 if ((it = item.weapons - (item.weapons & player.weapons)))
242                 {
243                         pickedup = TRUE;
244                         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
245                         {
246                                 e = get_weaponinfo(i);
247                                 if(it & e.weapons)
248                                         W_GiveWeapon (player, e.weapon, item.netname);
249                         }
250                 }
251
252                 if((it = (item.items - (item.items & player.items)) & IT_PICKUPMASK))
253                 {
254                         pickedup = TRUE;
255                         player.items |= it;
256                 }
257
258                 if (item.strength_finished)
259                 {
260                         pickedup = TRUE;
261                         player.strength_finished = max(player.strength_finished, time) + cvar("g_balance_powerup_strength_time");
262                 }
263                 if (item.invincible_finished)
264                 {
265                         pickedup = TRUE;
266                         player.invincible_finished = max(player.invincible_finished, time) + cvar("g_balance_powerup_invincible_time");
267                 }
268                 //if (item.speed_finished)
269                 //{
270                 //      pickedup = TRUE;
271                 //      player.speed_finished = max(player.speed_finished, time) + cvar("g_balance_powerup_speed_time");
272                 //}
273                 //if (item.slowmo_finished)
274                 //{
275                 //      pickedup = TRUE;
276                 //      player.slowmo_finished = max(player.slowmo_finished, time) + (cvar("g_balance_powerup_slowmo_time") * cvar("g_balance_powerup_slowmo_speed"));
277                 //}
278
279                 if (item.health)
280                 if (player.health < item.max_health)
281                 {
282                         pickedup = TRUE;
283                         player.health = min(player.health + item.health, item.max_health);
284                         player.pauserothealth_finished = max(player.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
285                 }
286                 if (item.armorvalue)
287                 if (player.armorvalue < item.max_armorvalue)
288                 {
289                         pickedup = TRUE;
290                         player.armorvalue = min(player.armorvalue + item.armorvalue, item.max_armorvalue);
291                         player.pauserotarmor_finished = max(player.pauserotarmor_finished, time + cvar("g_balance_pause_armor_rot"));
292                 }
293         }
294
295 :skip
296         // always eat teamed entities
297         if(item.team)
298                 pickedup = TRUE;
299
300         if (!pickedup)
301                 return 0;
302
303         sound (player, CHAN_AUTO, item.item_pickupsound, VOL_BASE, ATTN_NORM);
304         if (_switchweapon)
305                 if (player.switchweapon != w_getbestweapon(player))
306                         W_SwitchWeapon_Force(player, w_getbestweapon(player));
307
308         return 1;
309 }
310
311 void Item_Touch (void)
312 {
313         entity e, head;
314
315         // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
316         if (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
317         {
318                 remove(self);
319                 return;
320         }
321         if (other.classname != "player")
322                 return;
323         if (other.deadflag)
324                 return;
325         if (self.solid != SOLID_TRIGGER)
326                 return;
327         if (self.owner == other)
328                 return;
329
330         if(!Item_GiveTo(self, other))
331                 return;
332
333         if (self.classname == "droppedweapon")
334                 remove (self);
335         else if((self.flags & FL_WEAPON) && !(self.flags & FL_NO_WEAPON_STAY) && g_weapon_stay)
336                 return;
337         else
338         {
339                 self.solid = SOLID_NOT;
340                 self.model = string_null;
341                 if(self.team)
342                 {
343                         RandomSelection_Init();
344                         for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
345                                 RandomSelection_Add(head, 0, head.cnt, 0);
346                         e = RandomSelection_chosen_ent;
347                 }
348                 else
349                         e = self;
350                 Item_ScheduleRespawn(e);
351         }
352 }
353
354 void Item_FindTeam()
355 {
356         entity head, e;
357
358         if(self.effects & EF_NODRAW)
359         {
360                 // marker for item team search
361                 dprint("Initializing item team ", ftos(self.team), "\n");
362                 RandomSelection_Init();
363                 for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
364                         RandomSelection_Add(head, 0, head.cnt, 0);
365                 e = RandomSelection_chosen_ent;
366                 e.state = 0;
367
368                 for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
369                 {
370                         if(head != e)
371                         {
372                                 // make it a non-spawned item
373                                 head.solid = SOLID_NOT;
374                                 head.model = string_null;
375                                 head.state = 1; // state 1 = initially hidden item
376                         }
377                         head.effects = head.effects - (head.effects & EF_NODRAW);
378                 }
379
380                 if(head.flags & FL_POWERUP) // do not spawn powerups initially!
381                 {
382                         head.solid = SOLID_NOT;
383                         head.model = string_null;
384                         Item_ScheduleRespawn(head);
385                 }
386         }
387 }
388
389 void Item_Reset()
390 {
391         if(self.state == 1)
392         {
393                 self.model = string_null;
394                 self.solid = SOLID_NOT;
395         }
396         else
397         {
398                 self.model = self.mdl;
399                 self.solid = SOLID_TRIGGER;
400         }
401         setorigin (self, self.origin);
402         self.think = SUB_Null;
403         self.nextthink = 0;
404
405         if(self.flags & FL_POWERUP) // do not spawn powerups initially!
406         {
407                 self.solid = SOLID_NOT;
408                 self.model = string_null;
409                 Item_ScheduleRespawn(self);
410         }
411 }
412
413 // Savage: used for item garbage-collection
414 // TODO: perhaps nice special effect?
415 void RemoveItem(void)
416 {
417         remove(self);
418 }
419
420 // pickup evaluation functions
421 // these functions decide how desirable an item is to the bots
422
423 float generic_pickupevalfunc(entity player, entity item) {return item.bot_pickupbasevalue;};
424
425 float weapon_pickupevalfunc(entity player, entity item)
426 {
427         // if we already have the weapon, rate it 1/5th normal value
428         if ((player.weapons & item.weapons) == item.weapons)
429                 return item.bot_pickupbasevalue * 0.2;
430         return item.bot_pickupbasevalue;
431 };
432
433 float commodity_pickupevalfunc(entity player, entity item)
434 {
435         float c;
436         c = 0;
437         // TODO: figure out if the player even has the weapon this ammo is for?
438         // may not affect strategy much though...
439         // find out how much more ammo/armor/health the player can hold
440         if (item.ammo_shells)
441         if (player.ammo_shells < g_pickup_shells_max)
442                 c = c + max(0, 1 - player.ammo_shells / g_pickup_shells_max);
443         if (item.ammo_nails)
444         if (player.ammo_nails < g_pickup_nails_max)
445                 c = c + max(0, 1 - player.ammo_nails / g_pickup_nails_max);
446         if (item.ammo_rockets)
447         if (player.ammo_rockets < g_pickup_rockets_max)
448                 c = c + max(0, 1 - player.ammo_rockets / g_pickup_rockets_max);
449         if (item.ammo_cells)
450         if (player.ammo_cells < g_pickup_cells_max)
451                 c = c + max(0, 1 - player.ammo_cells / g_pickup_cells_max);
452         if (item.armorvalue)
453         if (player.armorvalue < item.max_armorvalue)
454                 c = c + max(0, 1 - player.armorvalue / item.max_armorvalue);
455         if (item.health)
456         if (player.health < item.max_health)
457                 c = c + max(0, 1 - player.health / item.max_health);
458
459         return item.bot_pickupbasevalue * c;
460 };
461
462
463 .float is_item;
464 void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
465 {
466         startitem_failed = FALSE;
467
468         // is it a dropped weapon?
469         if (self.classname == "droppedweapon")
470         {
471                 self.reset = SUB_Remove;
472                 // it's a dropped weapon
473                 self.movetype = MOVETYPE_TOSS;
474                 self.solid = SOLID_TRIGGER;
475                 // Savage: remove thrown items after a certain period of time ("garbage collection")
476                 self.think = RemoveItem;
477                 self.nextthink = time + 60;
478                 // don't drop if in a NODROP zone (such as lava)
479                 traceline(self.origin, self.origin, MOVE_NORMAL, self);
480                 if (trace_dpstartcontents & DPCONTENTS_NODROP)
481                 {
482                         startitem_failed = TRUE;
483                         remove(self);
484                         return;
485                 }
486         }
487         else
488         {
489                 self.reset = Item_Reset;
490                 // it's a level item
491                 if(self.spawnflags & 1)
492                         self.noalign = 1;
493                 if (self.noalign)
494                         self.movetype = MOVETYPE_NONE;
495                 else
496                         self.movetype = MOVETYPE_TOSS;
497                 self.solid = SOLID_TRIGGER;
498                 // do item filtering according to game mode and other things
499                 if (!self.noalign)
500                 {
501                         // first nudge it off the floor a little bit to avoid math errors
502                         setorigin(self, self.origin + '0 0 1');
503                         // set item size before we spawn a spawnfunc_waypoint
504                         if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
505                                 setsize (self, '-16 -16 0', '16 16 48');
506                         else
507                                 setsize (self, '-16 -16 0', '16 16 32');
508                         // note droptofloor returns FALSE if stuck/or would fall too far
509                         droptofloor();
510                         waypoint_spawnforitem(self);
511                 }
512
513                 if(teams_matter)
514                 {
515                         if(self.notteam)
516                         {
517                                 print("removed non-teamplay ", self.classname, "\n");
518                                 startitem_failed = TRUE;
519                                 remove (self);
520                                 return;
521                         }
522                 }
523                 else
524                 {
525                         if(self.notfree)
526                         {
527                                 print("removed non-FFA ", self.classname, "\n");
528                                 startitem_failed = TRUE;
529                                 remove (self);
530                                 return;
531                         }
532                 }
533
534                 if(self.notq3a)
535                 {
536                         // We aren't TA or something like that, so we keep the Q3A entities
537                         print("removed non-Q3A ", self.classname, "\n");
538                         startitem_failed = TRUE;
539                         remove (self);
540                         return;
541                 }
542
543                 /*
544                  * can't do it that way, as it would break maps
545                  * TODO make a target_give like entity another way, that perhaps has
546                  * the weapon name in a key
547                 if(self.targetname)
548                 {
549                         // target_give not yet supported; maybe later
550                         print("removed targeted ", self.classname, "\n");
551                         startitem_failed = TRUE;
552                         remove (self);
553                         return;
554                 }
555                 */
556
557                 if(cvar("spawn_debug") >= 2)
558                 {
559                         entity otheritem;
560                         for(otheritem = findradius(self.origin, 3); otheritem; otheritem = otheritem.chain)
561                         {
562                                 if(otheritem.is_item)
563                                 {
564                                         dprint("XXX Found duplicated item: ", itemname, vtos(self.origin));
565                                         dprint(" vs ", otheritem.netname, vtos(otheritem.origin), "\n");
566                                         error("Mapper sucks.");
567                                 }
568                         }
569                         self.is_item = TRUE;
570                 }
571
572                 weaponsInMap |= weaponid;
573
574                 if(g_lms)
575                 {
576                         startitem_failed = TRUE;
577                         remove(self);
578                         return;
579                 }
580                 else if (g_weaponarena && ((weaponid & WEPBIT_ALL) || (itemid & IT_AMMO)))
581                 {
582                         startitem_failed = TRUE;
583                         remove(self);
584                         return;
585                 }
586                 else if (g_minstagib)
587                 {
588                         // don't remove dropped items and powerups
589                         if (self.classname != "minstagib")
590                         {
591                                 startitem_failed = TRUE;
592                                 remove (self);
593                                 return;
594                         }
595                 }
596                 else if ((!cvar("g_pickup_items") || g_nixnex) && itemid != IT_STRENGTH && itemid != IT_INVINCIBLE && itemid != IT_HEALTH)
597                 {
598                         startitem_failed = TRUE;
599                         remove (self);
600                         return;
601                 }
602
603                 precache_model (itemmodel);
604                 precache_sound (pickupsound);
605                 precache_sound ("misc/itemrespawn.wav");
606                 precache_sound ("misc/itemrespawncountdown.wav");
607
608                 if((itemid & (IT_STRENGTH | IT_INVINCIBLE | IT_HEALTH | IT_ARMOR | IT_KEY1 | IT_KEY2)) || (weaponid & WEPBIT_ALL))
609                         self.target = "###item###"; // for finding the nearest item using find()
610         }
611
612         self.bot_pickup = TRUE;
613         self.bot_pickupevalfunc = pickupevalfunc;
614         self.bot_pickupbasevalue = pickupbasevalue;
615         self.mdl = itemmodel;
616         self.item_pickupsound = pickupsound;
617         // let mappers override respawntime
618         if(!self.respawntime) // both set
619         {
620                 self.respawntime = defaultrespawntime;
621                 self.respawntimejitter = defaultrespawntimejitter;
622         }
623         self.netname = itemname;
624         self.items = itemid;
625         self.weapons = weaponid;
626         self.flags = FL_ITEM | itemflags;
627         self.touch = Item_Touch;
628         setmodel (self, self.mdl); // precision set below
629         self.effects |= EF_LOWPRECISION;
630         if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
631                 setsize (self, '-16 -16 0', '16 16 48');
632         else
633                 setsize (self, '-16 -16 0', '16 16 32');
634         if(itemflags & FL_WEAPON)
635                 self.modelflags |= MF_ROTATE;
636
637         if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely
638         if (itemflags & FL_WEAPON)
639         {
640                 // neutral team color for pickup weapons
641                 self.colormap = 1024; // color shirt=0 pants=0 grey
642         }
643
644         if (cvar("g_fullbrightitems"))
645                 self.effects = self.effects | EF_FULLBRIGHT;
646
647         self.state = 0;
648         if(self.team)
649         {
650                 if(!self.cnt)
651                         self.cnt = 1; // item probability weight
652                 self.effects = self.effects | EF_NODRAW; // marker for item team search
653                 InitializeEntity(self, Item_FindTeam, INITPRIO_FINDTARGET);
654         }
655         else if(self.flags & FL_POWERUP) // do not spawn powerups initially!
656         {
657                 self.solid = SOLID_NOT;
658                 self.model = string_null;
659                 Item_ScheduleRespawn(self);
660         }
661 }
662
663 /* replace items in minstagib
664  * IT_STRENGTH   = invisibility
665  * IT_NAILS      = extra lives
666  * IT_INVINCIBLE = speed
667  */
668 void minstagib_items (float itemid)
669 {
670         // we don't want to replace dropped weapons ;)
671         if (self.classname == "droppedweapon")
672         {
673                 self.ammo_cells = 25;
674                 StartItem ("models/weapons/g_nex.md3",
675                         "weapons/weaponpickup.wav", 15, 0,
676                         "MinstaNex", 0, WEPBIT_MINSTANEX, FL_WEAPON, generic_pickupevalfunc, 1000);
677                 return;
678         }
679
680         local float rnd;
681         self.classname = "minstagib";
682
683         // replace rocket launchers and nex guns with ammo cells
684         if (itemid == IT_CELLS)
685         {
686                 self.ammo_cells = 1;
687                 StartItem ("models/items/a_cells.md3",
688                         "misc/itempickup.wav", 45, 0,
689                         "Nex Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100);
690                 return;
691         }
692
693         // randomize
694         rnd = random() * 3;
695         if (rnd <= 1)
696                 itemid = IT_STRENGTH;
697         else if (rnd <= 2)
698                 itemid = IT_NAILS;
699         else
700                 itemid = IT_INVINCIBLE;
701
702         // replace with invis
703         if (itemid == IT_STRENGTH)
704         {
705                 self.effects = EF_ADDITIVE;
706                 self.strength_finished = 30;
707                 StartItem ("models/items/g_strength.md3",
708                         "misc/powerup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup,
709                         "Invisibility", IT_STRENGTH, 0, FL_POWERUP, generic_pickupevalfunc, BOT_PICKUP_RATING_MID);
710         }
711         // replace with extra lives
712         if (itemid == IT_NAILS)
713         {
714                 self.max_health = 1;
715                 StartItem ("models/items/g_h100.md3",
716                         "misc/megahealth.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup,
717                         "Extralife", IT_NAILS, 0, FL_POWERUP, generic_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
718
719         }
720         // replace with speed
721         if (itemid == IT_INVINCIBLE)
722         {
723                 self.effects = EF_ADDITIVE;
724                 self.invincible_finished = 30;
725                 StartItem ("models/items/g_invincible.md3",
726                         "misc/powerup_shield.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup,
727                         "Speed", IT_INVINCIBLE, 0, FL_POWERUP, generic_pickupevalfunc, BOT_PICKUP_RATING_MID);
728         }
729
730 }
731
732 float minst_no_auto_cells;
733 void minst_remove_item (void) {
734         if(minst_no_auto_cells)
735                 remove(self);
736 }
737
738 float weaponswapping;
739 float internalteam;
740
741 void weapon_defaultspawnfunc(float wpn)
742 {
743         entity e;
744         float t;
745         var .float ammofield;
746         string s;
747         entity oldself;
748         float i, j;
749
750         // set the respawntime in advance (so replaced weapons can copy it)
751
752         if(!self.respawntime)
753         {
754                 e = get_weaponinfo(wpn);
755                 if(e.items == IT_SUPERWEAPON)
756                 {
757                         self.respawntime = g_pickup_respawntime_powerup;
758                         self.respawntimejitter = g_pickup_respawntimejitter_powerup;
759                 }
760                 else
761                 {
762                         self.respawntime = g_pickup_respawntime_weapon;
763                         self.respawntimejitter = g_pickup_respawntimejitter_weapon;
764                 }
765         }
766
767         if(self.classname != "droppedweapon" && self.classname != "replacedweapon")
768         {
769                 e = get_weaponinfo(wpn);
770                 s = cvar_string(strcat("g_weaponreplace_", e.netname));
771                 if(s == "0")
772                 {
773                         remove(self);
774                         startitem_failed = TRUE;
775                         return;
776                 }
777                 t = tokenize_console(s);
778                 if(t >= 2)
779                 {
780                         self.team = --internalteam;
781                         oldself = self;
782                         for(i = 1; i < t; ++i)
783                         {
784                                 s = argv(i);
785                                 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
786                                 {
787                                         e = get_weaponinfo(j);
788                                         if(e.netname == s)
789                                         {
790                                                 self = spawn();
791                                                 copyentity(oldself, self);
792                                                 self.classname = "replacedweapon";
793                                                 weapon_defaultspawnfunc(j);
794                                                 break;
795                                         }
796                                 }
797                                 if(j > WEP_LAST)
798                                 {
799                                         print("The weapon replace list for ", oldself.classname, " contains an unknown weapon ", s, ". Skipped.\n");
800                                 }
801                         }
802                         self = oldself;
803                 }
804                 if(t >= 1)
805                 {
806                         s = argv(0);
807                         wpn = 0;
808                         for(j = WEP_FIRST; j <= WEP_LAST; ++j)
809                         {
810                                 e = get_weaponinfo(j);
811                                 if(e.netname == s)
812                                 {
813                                         wpn = j;
814                                         break;
815                                 }
816                         }
817                         if(j > WEP_LAST)
818                         {
819                                 print("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n");
820                         }
821                 }
822                 if(wpn == 0)
823                 {
824                         remove(self);
825                         startitem_failed = TRUE;
826                         return;
827                 }
828         }
829
830         e = get_weaponinfo(wpn);
831
832         if(e.items && e.items != IT_SUPERWEAPON)
833         {
834                 for(i = 0, j = 1; i < 24; ++i, j *= 2)
835                 {
836                         if(e.items & j)
837                         {
838                                 ammofield = Item_CounterField(j);
839                                 if(!self.ammofield)
840                                         self.ammofield = cvar(strcat("g_pickup_", Item_CounterFieldName(j)));
841                         }
842                 }
843         }
844         else
845         {
846                 self.flags |= FL_NO_WEAPON_STAY;
847         }
848
849         // weapon stay isn't supported for teamed weapons
850         if(self.team)
851                 self.flags |= FL_NO_WEAPON_STAY;
852
853         if(g_weapon_stay == 2 && self.classname != "droppedweapon")
854         {
855                 self.ammo_shells = 0;
856                 self.ammo_nails = 0;
857                 self.ammo_cells = 0;
858                 self.ammo_rockets = 0;
859                 // weapon stay 2: don't use ammo on weapon pickups; instead
860                 // initialize all ammo types to the pickup ammo unless set by g_start_ammo_*
861         }
862
863         StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapons, FL_WEAPON, weapon_pickupevalfunc, e.bot_pickupbasevalue);
864         if (self.modelindex) // don't precache if self was removed
865                 weapon_action(e.weapon, WR_PRECACHE);
866 }
867
868 void spawnfunc_weapon_shotgun (void);
869 void spawnfunc_weapon_uzi (void) {
870         if(q3acompat_machineshotgunswap)
871         if(self.classname != "droppedweapon")
872         {
873                 weapon_defaultspawnfunc(WEP_SHOTGUN);
874                 return;
875         }
876         weapon_defaultspawnfunc(WEP_UZI);
877 }
878
879 void spawnfunc_weapon_shotgun (void) {
880         if(q3acompat_machineshotgunswap)
881         if(self.classname != "droppedweapon")
882         {
883                 weapon_defaultspawnfunc(WEP_UZI);
884                 return;
885         }
886         weapon_defaultspawnfunc(WEP_SHOTGUN);
887 }
888
889 void spawnfunc_weapon_nex (void)
890 {
891         if (g_minstagib)
892         {
893                 minstagib_items(IT_CELLS);
894                 self.think = minst_remove_item;
895                 self.nextthink = time;
896                 return;
897         }
898         weapon_defaultspawnfunc(WEP_NEX);
899 }
900
901 void spawnfunc_weapon_minstanex (void)
902 {
903         if (g_minstagib)
904         {
905                 minstagib_items(IT_CELLS);
906                 self.think = minst_remove_item;
907                 self.nextthink = time;
908                 return;
909         }
910         weapon_defaultspawnfunc(WEP_MINSTANEX);
911 }
912
913 void spawnfunc_weapon_rocketlauncher (void)
914 {
915         if (g_minstagib)
916         {
917                 minstagib_items(IT_CELLS);
918                 self.think = minst_remove_item;
919                 self.nextthink = time;
920                 return;
921         }
922         weapon_defaultspawnfunc(WEP_ROCKET_LAUNCHER);
923 }
924
925 void spawnfunc_item_rockets (void) {
926         if(!self.ammo_rockets)
927                 self.ammo_rockets = g_pickup_rockets;
928         StartItem ("models/items/a_rockets.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "rockets", IT_ROCKETS, 0, 0, commodity_pickupevalfunc, 3000);
929 }
930
931 void spawnfunc_item_shells (void);
932 void spawnfunc_item_bullets (void) {
933         if(!weaponswapping)
934         if(q3acompat_machineshotgunswap)
935         if(self.classname != "droppedweapon")
936         {
937                 weaponswapping = TRUE;
938                 spawnfunc_item_shells();
939                 weaponswapping = FALSE;
940                 return;
941         }
942
943         if(!self.ammo_nails)
944                 self.ammo_nails = g_pickup_nails;
945         StartItem ("models/items/a_bullets.mdl", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "bullets", IT_NAILS, 0, 0, commodity_pickupevalfunc, 2000);
946 }
947
948 void spawnfunc_item_cells (void) {
949         if(!self.ammo_cells)
950                 self.ammo_cells = g_pickup_cells;
951         StartItem ("models/items/a_cells.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "cells", IT_CELLS, 0, 0, commodity_pickupevalfunc, 2000);
952 }
953
954 void spawnfunc_item_shells (void) {
955         if(!weaponswapping)
956         if(q3acompat_machineshotgunswap)
957         if(self.classname != "droppedweapon")
958         {
959                 weaponswapping = TRUE;
960                 spawnfunc_item_bullets();
961                 weaponswapping = FALSE;
962                 return;
963         }
964
965         if(!self.ammo_shells)
966                 self.ammo_shells = g_pickup_shells;
967         StartItem ("models/items/a_shells.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "shells", IT_SHELLS, 0, 0, commodity_pickupevalfunc, 500);
968 }
969
970 void spawnfunc_item_armor_small (void) {
971         if(!self.armorvalue)
972                 self.armorvalue = g_pickup_armorsmall;
973         if(!self.max_armorvalue)
974                 self.max_armorvalue = g_pickup_armorsmall_max;
975         StartItem ("models/items/g_a1.md3", "misc/armor1.wav", g_pickup_respawntime_short, g_pickup_respawntimejitter_short, "5 Armor", IT_ARMOR_SHARD, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
976 }
977
978 void spawnfunc_item_armor_medium (void) {
979         if(!self.armorvalue)
980                 self.armorvalue = g_pickup_armormedium;
981         if(!self.max_armorvalue)
982                 self.max_armorvalue = g_pickup_armormedium_max;
983         StartItem ("models/items/g_armormedium.md3", "misc/armor10.wav", g_pickup_respawntime_medium, g_pickup_respawntimejitter_medium, "25 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_MID);
984 }
985
986 void spawnfunc_item_armor_big (void) {
987         if(!self.armorvalue)
988                 self.armorvalue = g_pickup_armorbig;
989         if(!self.max_armorvalue)
990                 self.max_armorvalue = g_pickup_armorbig_max;
991         StartItem ("models/items/g_a50.md3", "misc/armor17_5.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "50 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, 20000);
992 }
993
994 void spawnfunc_item_armor_large (void) {
995         if(!self.armorvalue)
996                 self.armorvalue = g_pickup_armorlarge;
997         if(!self.max_armorvalue)
998                 self.max_armorvalue = g_pickup_armorlarge_max;
999         StartItem ("models/items/g_a25.md3", "misc/armor25.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "100 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
1000 }
1001
1002 void spawnfunc_item_health_small (void) {
1003         if(!self.max_health)
1004                 self.max_health = g_pickup_healthsmall_max;
1005         if(!self.health)
1006                 self.health = g_pickup_healthsmall;
1007         StartItem ("models/items/g_h1.md3", "misc/minihealth.wav", g_pickup_respawntime_short, g_pickup_respawntimejitter_short, "5 Health", IT_5HP, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
1008 }
1009
1010 void spawnfunc_item_health_medium (void) {
1011         if(!self.max_health)
1012                 self.max_health = g_pickup_healthmedium_max;
1013         if(!self.health)
1014                 self.health = g_pickup_healthmedium;
1015         StartItem ("models/items/g_h25.md3", "misc/mediumhealth.wav", g_pickup_respawntime_short, g_pickup_respawntimejitter_short, "25 Health", IT_25HP, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_MID);
1016 }
1017
1018 void spawnfunc_item_health_large (void) {
1019         if(!self.max_health)
1020                 self.max_health = g_pickup_healthlarge_max;
1021         if(!self.health)
1022                 self.health = g_pickup_healthlarge;
1023         StartItem ("models/items/g_h50.md3", "misc/mediumhealth.wav", g_pickup_respawntime_medium, g_pickup_respawntimejitter_medium, "50 Health", IT_25HP, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_MID);
1024 }
1025
1026 void spawnfunc_item_health_mega (void) {
1027         if(!cvar("g_powerup_superhealth"))
1028                 return;
1029
1030         if(g_arena && !cvar("g_arena_powerups"))
1031                 return;
1032
1033         if(g_minstagib) {
1034                 minstagib_items(IT_NAILS);
1035         } else {
1036                 if(!self.max_health)
1037                         self.max_health = g_pickup_healthmega_max;
1038                 if(!self.health)
1039                         self.health = g_pickup_healthmega;
1040                 StartItem ("models/items/g_h100.md3", "misc/megahealth.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "100 Health", IT_HEALTH, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
1041         }
1042 }
1043
1044 // support old misnamed entities
1045 void spawnfunc_item_armor1() { spawnfunc_item_armor_small(); }  // FIXME: in Quake this is green armor, in Nexuiz maps it is an armor shard
1046 void spawnfunc_item_armor25() { spawnfunc_item_armor_large(); }
1047 void spawnfunc_item_health1() { spawnfunc_item_health_small(); }
1048 void spawnfunc_item_health25() { spawnfunc_item_health_medium(); }
1049 void spawnfunc_item_health100() { spawnfunc_item_health_mega(); }
1050
1051 void spawnfunc_item_strength (void) {
1052         if(!cvar("g_powerup_strength"))
1053                 return;
1054
1055         if(g_arena && !cvar("g_arena_powerups"))
1056                 return;
1057
1058         if(g_minstagib) {
1059                 minstagib_items(IT_STRENGTH);
1060         } else {
1061                 precache_sound("weapons/strength_fire.wav");
1062                 self.strength_finished = 30;
1063                 self.effects = EF_ADDITIVE;
1064                 StartItem ("models/items/g_strength.md3", "misc/powerup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Strength Powerup", IT_STRENGTH, 0, FL_POWERUP, generic_pickupevalfunc, 100000);
1065         }
1066 }
1067
1068 void spawnfunc_item_invincible (void) {
1069         if(!cvar("g_powerup_shield"))
1070                 return;
1071
1072         if(g_arena && !cvar("g_arena_powerups"))
1073                 return;
1074
1075         if(g_minstagib) {
1076                 minstagib_items(IT_INVINCIBLE);
1077         } else {
1078                 self.invincible_finished = 30;
1079                 self.effects = EF_ADDITIVE;
1080                 StartItem ("models/items/g_invincible.md3", "misc/powerup_shield.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Shield", IT_INVINCIBLE, 0, FL_POWERUP, generic_pickupevalfunc, 100000);
1081         }
1082 }
1083
1084 void spawnfunc_item_minst_cells (void) {
1085         if (g_minstagib)
1086         {
1087                 minst_no_auto_cells = 1;
1088                 minstagib_items(IT_CELLS);
1089         }
1090         else
1091                 remove(self);
1092 }
1093
1094 // compatibility:
1095 void spawnfunc_item_quad (void) {self.classname = "item_strength";spawnfunc_item_strength();}
1096
1097 float target_item_func_set(float a, float b)
1098 {
1099         if(b == 0)
1100                 return a;
1101         else if(b < 0)
1102                 return 0;
1103         else
1104                 return b;
1105 }
1106
1107 float target_item_func_min(float a, float b)
1108 {
1109         if(b == 0)
1110                 return a;
1111         else if(b < 0)
1112                 return 0;
1113         else
1114                 return min(a, b);
1115 }
1116
1117 float target_item_func_max(float a, float b)
1118 {
1119         return max(a, b);
1120 }
1121
1122 float target_item_func_bitset(float a, float b)
1123 {
1124         return b;
1125 }
1126
1127 float target_item_func_and(float a, float b)
1128 {
1129         return a & b;
1130 }
1131
1132 float target_item_func_itembitset(float a, float b)
1133 {
1134         return (a - (a & (IT_PICKUPMASK | IT_STRENGTH | IT_INVINCIBLE))) | b;
1135 }
1136
1137 float target_item_func_itemand(float a, float b)
1138 {
1139         return (a - (a & (IT_PICKUPMASK | IT_STRENGTH | IT_INVINCIBLE))) | (a & b);
1140 }
1141
1142 float target_item_func_or(float a, float b)
1143 {
1144         return a | b;
1145 }
1146
1147 float target_item_func_andnot(float a, float b)
1148 {
1149         return a - (a & b);
1150 }
1151
1152 float target_item_changed;
1153 void target_item_change(float binary, .float field, float(float a, float b) func, string sound_increase, string sound_decrease)
1154 {
1155         float n, d;
1156         n = func(activator.field, self.field);
1157
1158         if(binary)
1159         {
1160                 d = n & activator.field;
1161                 if(d != n) // bits added?
1162                         d = +1;
1163                 else if(d != activator.field) // bits removed?
1164                         d = -1;
1165                 else
1166                         d = 0;
1167         }
1168         else
1169                 d = n - activator.field;
1170
1171         if(d < 0)
1172         {
1173                 if(sound_decrease != "")
1174                         sound (activator, CHAN_AUTO, sound_decrease, VOL_BASE, ATTN_NORM);
1175                 target_item_changed = 1;
1176         }
1177         else if(d > 0)
1178         {
1179                 if(sound_increase != "")
1180                         sound (activator, CHAN_AUTO, sound_increase, VOL_BASE, ATTN_NORM);
1181                 target_item_changed = 1;
1182         }
1183         activator.field = n;
1184 }
1185
1186 void target_items_use (void)
1187 {
1188         float h0, a0;
1189
1190         if(activator.classname == "droppedweapon")
1191         {
1192                 EXACTTRIGGER_TOUCH;
1193                 remove(activator);
1194                 return;
1195         }
1196
1197         if(activator.classname != "player")
1198                 return;
1199         if(activator.deadflag != DEAD_NO)
1200                 return;
1201         EXACTTRIGGER_TOUCH;
1202
1203         entity e;
1204         for(e = world; (e = find(e, classname, "droppedweapon")); )
1205                 if(e.enemy == activator)
1206                         remove(e);
1207
1208         float _switchweapon;
1209         _switchweapon = FALSE;
1210         if (activator.autoswitch)
1211                 if (activator.switchweapon == w_getbestweapon(activator))
1212                         _switchweapon = TRUE;
1213
1214         a0 = activator.armorvalue;
1215         h0 = activator.health;
1216         target_item_changed = 0;
1217
1218         if(self.spawnflags == 0) // SET
1219         {
1220                 target_item_change(0, ammo_shells, target_item_func_set, "misc/itempickup.wav", "");
1221                 target_item_change(0, ammo_nails, target_item_func_set, "misc/itempickup.wav", "");
1222                 target_item_change(0, ammo_rockets, target_item_func_set, "misc/itempickup.wav", "");
1223                 target_item_change(0, ammo_cells, target_item_func_set, "misc/itempickup.wav", "");
1224                 target_item_change(0, ammo_fuel, target_item_func_set, "misc/itempickup.wav", "");
1225                 target_item_change(0, health, target_item_func_set, "misc/megahealth.wav", "");
1226                 target_item_change(0, armorvalue, target_item_func_set, "misc/armor25.wav", "");
1227                 target_item_change(1, items, target_item_func_itembitset, "misc/powerup.wav", "misc/poweroff.wav");
1228                 target_item_change(1, weapons, target_item_func_bitset, "weapons/weaponpickup.wav", "");
1229
1230                 if((self.items & activator.items) & IT_STRENGTH)
1231                         activator.strength_finished = time + self.strength_finished;
1232                 if((self.items & activator.items) & IT_INVINCIBLE)
1233                         activator.invincible_finished = time + self.invincible_finished;
1234         }
1235         else if(self.spawnflags == 1) // AND/MIN
1236         {
1237                 target_item_change(0, ammo_shells, target_item_func_min, "misc/itempickup.wav", "");
1238                 target_item_change(0, ammo_nails, target_item_func_min, "misc/itempickup.wav", "");
1239                 target_item_change(0, ammo_rockets, target_item_func_min, "misc/itempickup.wav", "");
1240                 target_item_change(0, ammo_cells, target_item_func_min, "misc/itempickup.wav", "");
1241                 target_item_change(0, ammo_fuel, target_item_func_min, "misc/itempickup.wav", "");
1242                 target_item_change(0, health, target_item_func_min, "misc/megahealth.wav", "");
1243                 target_item_change(0, armorvalue, target_item_func_min, "misc/armor25.wav", "");
1244                 target_item_change(1, items, target_item_func_itemand, "misc/powerup.wav", "misc/poweroff.wav");
1245                 target_item_change(1, weapons, target_item_func_and, "weapons/weaponpickup.wav", "");
1246
1247                 if((self.items & activator.items) & IT_STRENGTH)
1248                         activator.strength_finished = min(activator.strength_finished, time + self.strength_finished);
1249                 if((self.items & activator.items) & IT_INVINCIBLE)
1250                         activator.invincible_finished = min(activator.invincible_finished, time + self.invincible_finished);
1251         }
1252         else if(self.spawnflags == 2) // OR/MAX
1253         {
1254                 target_item_change(0, ammo_shells, target_item_func_max, "misc/itempickup.wav", "");
1255                 target_item_change(0, ammo_nails, target_item_func_max, "misc/itempickup.wav", "");
1256                 target_item_change(0, ammo_rockets, target_item_func_max, "misc/itempickup.wav", "");
1257                 target_item_change(0, ammo_cells, target_item_func_max, "misc/itempickup.wav", "");
1258                 target_item_change(0, ammo_fuel, target_item_func_max, "misc/itempickup.wav", "");
1259                 target_item_change(0, health, target_item_func_max, "misc/megahealth.wav", "");
1260                 target_item_change(0, armorvalue, target_item_func_max, "misc/armor25.wav", "");
1261                 target_item_change(1, items, target_item_func_or, "misc/powerup.wav", "misc/poweroff.wav");
1262                 target_item_change(1, weapons, target_item_func_or, "weapons/weaponpickup.wav", "");
1263
1264                 if((self.items & activator.items) & IT_STRENGTH)
1265                         activator.strength_finished = max(activator.strength_finished, time + self.strength_finished);
1266                 if((self.items & activator.items) & IT_INVINCIBLE)
1267                         activator.invincible_finished = max(activator.invincible_finished, time + self.invincible_finished);
1268         }
1269         else if(self.spawnflags == 4) // ANDNOT/MIN
1270         {
1271                 target_item_change(0, ammo_shells, target_item_func_min, "misc/itempickup.wav", "");
1272                 target_item_change(0, ammo_nails, target_item_func_min, "misc/itempickup.wav", "");
1273                 target_item_change(0, ammo_rockets, target_item_func_min, "misc/itempickup.wav", "");
1274                 target_item_change(0, ammo_cells, target_item_func_min, "misc/itempickup.wav", "");
1275                 target_item_change(0, ammo_fuel, target_item_func_min, "misc/itempickup.wav", "");
1276                 target_item_change(0, health, target_item_func_min, "misc/megahealth.wav", "");
1277                 target_item_change(0, armorvalue, target_item_func_min, "misc/armor25.wav", "");
1278                 target_item_change(1, items, target_item_func_andnot, "misc/powerup.wav", "misc/poweroff.wav");
1279                 target_item_change(1, weapons, target_item_func_andnot, "weapons/weaponpickup.wav", "");
1280
1281                 if((self.items & activator.items) & IT_STRENGTH)
1282                         activator.strength_finished = min(activator.strength_finished, time + self.strength_finished);
1283                 if((self.items & activator.items) & IT_INVINCIBLE)
1284                         activator.invincible_finished = min(activator.invincible_finished, time + self.invincible_finished);
1285         }
1286
1287         if not(activator.items & IT_STRENGTH)
1288                 activator.strength_finished = 0;
1289         if not(activator.items & IT_INVINCIBLE)
1290                 activator.invincible_finished = 0;
1291
1292         if(activator.health > h0)
1293                 activator.pauserothealth_finished = max(activator.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
1294         else if(activator.health < h0)
1295                 activator.pauseregen_finished = max(activator.pauseregen_finished, time + cvar("g_balance_pause_health_regen"));
1296
1297         if(activator.armorvalue > a0)
1298                 activator.pauserotarmor_finished = max(activator.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
1299
1300         if not(activator.weapons & W_WeaponBit(activator.switchweapon))
1301                 _switchweapon = TRUE;
1302         if(_switchweapon)
1303                 W_SwitchWeapon_Force(activator, w_getbestweapon(activator));
1304
1305         if(target_item_changed)
1306                 centerprint(activator, self.message);
1307 }
1308
1309 void spawnfunc_target_items (void)
1310 {
1311         float n, i, j;
1312         entity e;
1313         self.use = target_items_use;
1314         if(!self.strength_finished)
1315                 self.strength_finished = cvar("g_balance_powerup_strength_time");
1316         if(!self.invincible_finished)
1317                 self.invincible_finished = cvar("g_balance_powerup_invincible_time");
1318
1319         precache_sound("misc/itempickup.wav");
1320         precache_sound("misc/itempickup.wav");
1321         precache_sound("misc/itempickup.wav");
1322         precache_sound("misc/itempickup.wav");
1323         precache_sound("misc/megahealth.wav");
1324         precache_sound("misc/armor25.wav");
1325         precache_sound("misc/powerup.wav");
1326         precache_sound("misc/poweroff.wav");
1327         precache_sound("weapons/weaponpickup.wav");
1328
1329         n = tokenize_console(self.netname);
1330         for(i = 0; i < n; ++i)
1331         {
1332                 if(argv(i) == "unlimited_ammo")         self.items |= IT_UNLIMITED_AMMO;
1333                 if(argv(i) == "unlimited_weapon_ammo")  self.items |= IT_UNLIMITED_WEAPON_AMMO;
1334                 if(argv(i) == "unlimited_superweapons") self.items |= IT_UNLIMITED_SUPERWEAPONS;
1335                 if(argv(i) == "strength")               self.items |= IT_STRENGTH;
1336                 if(argv(i) == "invincible")             self.items |= IT_INVINCIBLE;
1337                 if(argv(i) == "jetpack")                self.items |= IT_JETPACK;
1338                 if(argv(i) == "fuel_regen")             self.items |= IT_FUEL_REGEN;
1339                 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
1340                 {
1341                         e = get_weaponinfo(j);
1342                         if(argv(i) == e.netname)
1343                         {
1344                                 self.weapons |= e.weapons;
1345                                 if(self.spawnflags == 0 || self.spawnflags == 2)
1346                                         weapon_action(e.weapon, WR_PRECACHE);
1347                         }
1348                 }
1349         }
1350 }
1351
1352 void spawnfunc_item_fuel(void)
1353 {
1354         if(!self.ammo_fuel)
1355                 self.ammo_fuel = g_pickup_fuel;
1356         StartItem ("models/items/g_fuel.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "Fuel", IT_FUEL, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
1357 }
1358
1359 void spawnfunc_item_fuel_regen(void)
1360 {
1361         StartItem ("models/items/g_fuelregen.md3", "misc/itempickup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Fuel regenerator", IT_FUEL_REGEN, 0, FL_POWERUP, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
1362 }
1363
1364 void spawnfunc_item_jetpack(void)
1365 {
1366         if(g_grappling_hook)
1367                 return; // sorry, but these two can't coexist (same button)
1368         if(!self.ammo_fuel)
1369                 self.ammo_fuel = g_pickup_fuel_jetpack;
1370         StartItem ("models/items/g_jetpack.md3", "misc/itempickup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Jet pack", IT_JETPACK, 0, FL_POWERUP, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
1371 }