]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/t_items.qc
Make temporary cvar g_csqc_items set to 0 fully behave as old svqc items did
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / t_items.qc
1 #define ISF_LOCATION 2
2 #define ISF_MODEL    4
3 #define ISF_STATUS   8
4     #define ITS_ANIMATED  2
5     #define ITS_AVAILABLE 4
6     #define ITS_POWERUP   8
7     #define ITS_ALLOWFB   16
8
9 .float ItemStatus;
10
11 #ifdef CSQC
12 .vector colormod;
13 void ItemDraw()
14 {
15     if(self.ItemStatus & ITS_AVAILABLE)
16     {
17         self.alpha = 1;
18         self.colormod = '1 1 1';
19     }
20     else
21     {
22         self.alpha = 0.5;
23         self.colormod = '-1 -1 -1';
24     }
25     
26     if(self.ItemStatus & ITS_ANIMATED)
27     {
28         self.angles += '0 180 0' * frametime;
29         setorigin(self, '0 0 10' + self.oldorigin + '0 0 8' * sin(time * 2));        
30     }
31 }
32
33 void ItemDrawSimple()
34 {
35     
36 }
37
38 void ItemRead(float _IsNew)
39 {
40     float sf = ReadByte();
41
42     if(sf & ISF_LOCATION)
43     {
44         self.origin_x = ReadCoord();
45         self.origin_y = ReadCoord();
46         self.origin_z = ReadCoord();
47         setorigin(self, self.origin);
48         self.oldorigin = self.origin;
49     }
50     
51     if(sf & ISF_STATUS) // need to read/write status frist so model can handle simple, fb etc.
52         self.ItemStatus = ReadByte();    
53     
54     if(sf & ISF_MODEL) // handle simple items, fullbright and so on here
55     {
56         if(self.mdl)
57             strunzone(self.mdl);
58
59         self.draw      = ItemDraw;
60         self.drawmask  = MASK_NORMAL;
61         self.movetype  = MOVETYPE_NOCLIP;
62         self.mdl       = strzone(ReadString());
63         setmodel(self, self.mdl);
64     }
65 }
66 #endif
67
68 #ifdef SVQC
69 float ItemSend(entity to, float sf)
70 {
71         WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM);
72         
73         WriteByte(MSG_ENTITY, sf);
74
75         //WriteByte(MSG_ENTITY, self.cnt);
76     if(sf & ISF_LOCATION)
77     {
78         WriteCoord(MSG_ENTITY, self.origin_x);
79         WriteCoord(MSG_ENTITY, self.origin_y);
80         WriteCoord(MSG_ENTITY, self.origin_z);
81     }
82
83     if(sf & ISF_STATUS)
84         WriteByte(MSG_ENTITY, self.ItemStatus);
85
86     if(sf & ISF_MODEL)
87         WriteString(MSG_ENTITY, self.mdl);
88                     
89     return TRUE;
90 }
91
92
93 float have_pickup_item(void)
94 {
95         // minstagib: only allow filtered items
96         if(g_minstagib)
97                 if(self.classname != "minstagib")
98                         return FALSE;
99
100         if(self.flags & FL_POWERUP)
101         {
102                 if(autocvar_g_powerups > 0)
103                         return TRUE;
104                 if(autocvar_g_powerups == 0)
105                         return FALSE;
106                 if(g_lms)
107                         return FALSE;
108                 if(g_ca)
109                         return FALSE;
110                 if(g_arena)
111                         return FALSE;
112         }
113         else
114         {
115                 if(autocvar_g_pickup_items > 0)
116                         return TRUE;
117                 if(autocvar_g_pickup_items == 0)
118                         return FALSE;
119                 if(g_lms)
120                         return FALSE;
121                 if(g_ca)
122                         return FALSE;
123                 if(g_weaponarena)
124                         if((self.weapons & WEPBIT_ALL) || (self.items & IT_AMMO))
125                                 return FALSE;
126         }
127         return TRUE;
128 }
129
130 #define ITEM_RESPAWN_TICKS 10
131
132 #define ITEM_RESPAWNTIME(i)         ((i).respawntime + crandom() * (i).respawntimejitter)
133         // range: respawntime - respawntimejitter .. respawntime + respawntimejitter
134 #define ITEM_RESPAWNTIME_INITIAL(i) (ITEM_RESPAWN_TICKS + random() * ((i).respawntime + (i).respawntimejitter - ITEM_RESPAWN_TICKS))
135         // range: 10 .. respawntime + respawntimejitter
136
137 floatfield Item_CounterField(float it)
138 {
139         switch(it)
140         {
141                 case IT_SHELLS:      return ammo_shells;
142                 case IT_NAILS:       return ammo_nails;
143                 case IT_ROCKETS:     return ammo_rockets;
144                 case IT_CELLS:       return ammo_cells;
145                 case IT_FUEL:        return ammo_fuel;
146                 case IT_5HP:         return health;
147                 case IT_25HP:        return health;
148                 case IT_HEALTH:      return health;
149                 case IT_ARMOR_SHARD: return armorvalue;
150                 case IT_ARMOR:       return armorvalue;
151                 // add more things here (health, armor)
152                 default:             error("requested item has no counter field");
153         }
154 }
155
156 string Item_CounterFieldName(float it)
157 {
158         switch(it)
159         {
160                 case IT_SHELLS:      return "shells";
161                 case IT_NAILS:       return "nails";
162                 case IT_ROCKETS:     return "rockets";
163                 case IT_CELLS:       return "cells";
164                 case IT_FUEL:        return "fuel";
165
166                 // add more things here (health, armor)
167                 default:             error("requested item has no counter field name");
168         }
169 }
170
171 .float max_armorvalue;
172 .float pickup_anyway;
173
174 float Item_Customize()
175 {
176         if(self.spawnshieldtime)
177                 return TRUE;
178         if(self.weapons != (self.weapons & other.weapons))
179         {
180                 self.colormod = '0 0 0';
181                 self.glowmod = self.colormod;
182                 self.alpha = 0.5 + 0.5 * g_ghost_items; // halfway more alpha
183                 return TRUE;
184         }
185         else
186         {
187                 if(g_ghost_items)
188                 {
189                         self.colormod = stov(autocvar_g_ghost_items_color);
190                         self.glowmod = self.colormod;
191                         self.alpha = g_ghost_items;
192                         return TRUE;
193                 }
194                 else
195                         return FALSE;
196         }
197 }
198
199 void Item_Show (entity e, float mode)
200 {    
201         e.effects &~= EF_ADDITIVE | EF_STARDUST | EF_FULLBRIGHT | EF_NODEPTHTEST;
202         if (mode > 0)
203         {
204                 // make the item look normal, and be touchable
205                 e.model = e.mdl;
206                 e.solid = SOLID_TRIGGER;
207                 e.colormod = '0 0 0';
208                 self.glowmod = self.colormod;
209                 e.alpha = 0;
210                 if not (cvar("g_csqc_items"))
211             e.customizeentityforclient = func_null;
212
213                 e.spawnshieldtime = 1;
214                 self.ItemStatus |= ITS_AVAILABLE;
215         }
216         else if (mode < 0)
217         {
218                 // hide the item completely
219                 e.model = string_null;
220                 e.solid = SOLID_NOT;
221                 e.colormod = '0 0 0';
222                 self.glowmod = self.colormod;
223                 e.alpha = 0;
224                 if not (cvar("g_csqc_items"))
225             e.customizeentityforclient = func_null;
226
227                 e.spawnshieldtime = 1;
228                 self.ItemStatus &~= ITS_AVAILABLE;
229         }
230         else if((e.flags & FL_WEAPON) && !(e.flags & FL_NO_WEAPON_STAY) && g_weapon_stay)
231         {
232                 // make the item translucent and not touchable
233                 e.model = e.mdl;
234                 e.solid = SOLID_TRIGGER; // can STILL be picked up!
235                 e.colormod = '0 0 0';
236                 self.glowmod = self.colormod;
237                 e.effects |= EF_STARDUST;
238                 if not (cvar("g_csqc_items"))
239             e.customizeentityforclient = Item_Customize;
240
241                 e.spawnshieldtime = 0; // field indicates whether picking it up may give you anything other than the weapon
242                 self.ItemStatus |= ITS_AVAILABLE;
243         }
244         else if(g_ghost_items)
245         {
246                 // make the item translucent and not touchable
247                 e.model = e.mdl;
248                 e.solid = SOLID_NOT;
249                 e.colormod = stov(autocvar_g_ghost_items_color);
250                 e.glowmod = e.colormod;
251                 e.alpha = g_ghost_items;
252                 if not (cvar("g_csqc_items"))
253             e.customizeentityforclient = func_null;
254
255                 e.spawnshieldtime = 1;
256                 self.ItemStatus &~= ITS_AVAILABLE;
257         }
258         else
259         {
260                 // hide the item completely
261                 e.model = string_null;
262                 e.solid = SOLID_NOT;
263                 e.colormod = '0 0 0';
264                 e.glowmod = e.colormod;
265                 e.alpha = 0;
266         if not (cvar("g_csqc_items"))
267             e.customizeentityforclient = func_null;
268
269                 e.spawnshieldtime = 1;
270                 self.ItemStatus &~= ITS_AVAILABLE;
271         }
272
273         if (e.items & (IT_STRENGTH | IT_INVINCIBLE))
274         {
275                 e.effects |= EF_ADDITIVE | EF_FULLBRIGHT;
276                 self.ItemStatus |= ITS_POWERUP;
277         }
278         
279         if (autocvar_g_nodepthtestitems)
280                 e.effects |= EF_NODEPTHTEST;
281                 
282         if (autocvar_g_fullbrightitems)
283         {
284                 e.effects |= EF_FULLBRIGHT;
285                 self.ItemStatus |= ITS_ALLOWFB;
286         }
287
288         // relink entity (because solid may have changed)
289         setorigin(e, e.origin);
290     self.SendFlags |= ISF_STATUS;
291
292 }
293
294 void Item_Respawn (void)
295 {
296         Item_Show(self, 1);
297         if(!g_minstagib && self.items == IT_STRENGTH)
298                 sound (self, CH_TRIGGER, "misc/strength_respawn.wav", VOL_BASE, ATTN_NORM);     // play respawn sound
299         else if(!g_minstagib && self.items == IT_INVINCIBLE)
300                 sound (self, CH_TRIGGER, "misc/shield_respawn.wav", VOL_BASE, ATTN_NORM);       // play respawn sound
301         else
302                 sound (self, CH_TRIGGER, "misc/itemrespawn.wav", VOL_BASE, ATTN_NORM);  // play respawn sound
303         setorigin (self, self.origin);
304
305         //pointparticles(particleeffectnum("item_respawn"), self.origin + self.mins_z * '0 0 1' + '0 0 48', '0 0 0', 1);
306         pointparticles(particleeffectnum("item_respawn"), self.origin + 0.5 * (self.mins + self.maxs), '0 0 0', 1);
307 }
308
309 void Item_RespawnCountdown (void)
310 {
311         if(self.count >= ITEM_RESPAWN_TICKS)
312         {
313                 if(self.waypointsprite_attached)
314                         WaypointSprite_Kill(self.waypointsprite_attached);
315                 Item_Respawn();
316         }
317         else
318         {
319                 self.nextthink = time + 1;
320                 self.count += 1;
321                 if(self.count == 1)
322                 {
323                         string name;
324                         vector rgb;
325                         name = string_null;
326                         if(g_minstagib)
327                         {
328                                 switch(self.items)
329                                 {
330                                         case IT_STRENGTH:   name = "item-invis"; rgb = '0 0 1'; break;
331                                         case IT_NAILS:      name = "item-extralife"; rgb = '1 0 0'; break;
332                                         case IT_INVINCIBLE: name = "item-speed"; rgb = '1 0 1'; break;
333                                 }
334                         }
335                         else
336                         {
337                                 switch(self.items)
338                                 {
339                                         case IT_STRENGTH:   name = "item-strength"; rgb = '0 0 1'; break;
340                                         case IT_INVINCIBLE: name = "item-shield"; rgb = '1 0 1'; break;
341                                 }
342                         }
343                         switch(self.items)
344                         {
345                                 case IT_FUEL_REGEN:     name = "item-fuelregen"; rgb = '1 0.5 0'; break;
346                                 case IT_JETPACK:        name = "item-jetpack"; rgb = '0.5 0.5 0.5'; break;
347                         }
348                         if(self.flags & FL_WEAPON)
349                         {
350                                 entity wi = get_weaponinfo(self.weapon);
351                                 if(wi)
352                                 {
353                                         name = wi.model2;
354                                         rgb = '1 0 0';
355                                 }
356                         }
357                         if(!name)
358                         {
359                                 print("Unknown powerup-marked item is wanting to respawn\n");
360                                 localcmd(sprintf("prvm_edict server %d\n", num_for_edict(self)));
361                         }
362                         if(name)
363                         {
364                                 WaypointSprite_Spawn(name, 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, TRUE, RADARICON_POWERUP, rgb);
365                                 if(self.waypointsprite_attached)
366                                         WaypointSprite_UpdateBuildFinished(self.waypointsprite_attached, time + ITEM_RESPAWN_TICKS);
367                         }
368                 }
369                 sound (self, CH_TRIGGER, "misc/itemrespawncountdown.wav", VOL_BASE, ATTN_NORM); // play respawn sound
370                 if(self.waypointsprite_attached)
371                 {
372                         WaypointSprite_Ping(self.waypointsprite_attached);
373                         //WaypointSprite_UpdateHealth(self.waypointsprite_attached, self.count);
374                 }
375         }
376 }
377
378 void Item_ScheduleRespawnIn(entity e, float t)
379 {
380         if((e.flags & FL_POWERUP) || (e.weapons & WEPBIT_SUPERWEAPONS))
381         {
382                 e.think = Item_RespawnCountdown;
383                 e.nextthink = time + max(0, t - ITEM_RESPAWN_TICKS);
384                 e.count = 0;
385         }
386         else
387         {
388                 e.think = Item_Respawn;
389                 e.nextthink = time + t;
390         }
391 }
392
393 void Item_ScheduleRespawn(entity e)
394 {
395         if(e.respawntime > 0)
396         {
397                 Item_Show(e, 0);
398                 Item_ScheduleRespawnIn(e, ITEM_RESPAWNTIME(e));
399         }
400         else // if respawntime is -1, this item does not respawn
401                 Item_Show(e, -1);
402 }
403
404 void Item_ScheduleInitialRespawn(entity e)
405 {
406         Item_Show(e, 0);
407         Item_ScheduleRespawnIn(e, game_starttime - time + ITEM_RESPAWNTIME_INITIAL(e));
408 }
409
410 float ITEM_MODE_NONE = 0;
411 float ITEM_MODE_HEALTH = 1;
412 float ITEM_MODE_ARMOR = 2;
413 float ITEM_MODE_FUEL = 3;
414 float Item_GiveAmmoTo(entity item, entity player, .float ammofield, float ammomax, float mode)
415 {
416         if (!item.ammofield)
417                 return FALSE;
418
419         if (item.spawnshieldtime)
420         {
421                 if ((player.ammofield < ammomax) || item.pickup_anyway)
422                 {
423                         player.ammofield = bound(player.ammofield, ammomax, player.ammofield + item.ammofield);
424                         goto YEAH;
425                 }
426         }
427         else if(g_weapon_stay == 2)
428         {
429                 float mi = min(item.ammofield, ammomax);
430                 if (player.ammofield < mi)
431                 {
432                         player.ammofield = mi;
433                         goto YEAH;
434                 }
435         }
436
437         return FALSE;
438
439 :YEAH
440         switch(mode)
441         {
442                 case ITEM_MODE_FUEL:
443                         player.pauserotfuel_finished = max(player.pauserotfuel_finished, time + autocvar_g_balance_pause_fuel_rot);
444                         break;
445                 case ITEM_MODE_HEALTH:
446                         player.pauserothealth_finished = max(player.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
447                         break;
448                 case ITEM_MODE_ARMOR:
449                         player.pauserotarmor_finished = max(player.pauserotarmor_finished, time + autocvar_g_balance_pause_armor_rot);
450                         break;
451                 default:
452                         break;
453         }
454         return TRUE;
455 }
456
457 float Item_GiveTo(entity item, entity player)
458 {
459         float _switchweapon;
460         float pickedup;
461         float it;
462         float i;
463         entity e;
464
465         // if nothing happens to player, just return without taking the item
466         pickedup = FALSE;
467         _switchweapon = FALSE;
468
469         if (g_minstagib)
470         {
471                 float prevcells = player.ammo_cells;
472
473                 pickedup |= Item_GiveAmmoTo(item, player, ammo_fuel, g_pickup_fuel_max, ITEM_MODE_FUEL);
474                 pickedup |= Item_GiveAmmoTo(item, player, ammo_cells, 999, ITEM_MODE_NONE);
475
476                 if(player.ammo_cells > prevcells)
477                 {
478                         _switchweapon = TRUE;
479
480                         // play some cool sounds ;)
481                         if (clienttype(player) == CLIENTTYPE_REAL)
482                         {
483                                 if(player.health <= 5)
484                                         AnnounceTo(player, "lastsecond");
485                                 else if(player.health < 50)
486                                         AnnounceTo(player, "narrowly");
487                         }
488                         // sound not available
489                         // else if(item.items == IT_CELLS)
490                         //      AnnounceTo(player, "ammo");
491
492                         if (item.weapons & WEPBIT_MINSTANEX)
493                                 W_GiveWeapon (player, WEP_MINSTANEX, item.netname);
494                         player.health = 100;
495                 }
496
497                 if((it = (item.items - (item.items & player.items)) & IT_PICKUPMASK))
498                 {
499                         pickedup = TRUE;
500                         player.items |= it;
501                         sprint (player, strcat("You got the ^2", item.netname, "\n"));
502                 }
503
504                 // extralife powerup
505                 if (item.max_health)
506                 {
507                         pickedup = TRUE;
508                         // sound not available
509                         // AnnounceTo(player, "_lives");
510                         player.armorvalue = bound(player.armorvalue, 999, player.armorvalue + autocvar_g_minstagib_extralives);
511                         sprint(player, "^3You picked up some extra lives\n");
512                 }
513
514                 // invis powerup
515                 if (item.strength_finished)
516                 {
517                         pickedup = TRUE;
518                         // sound not available
519                         // AnnounceTo(player, "invisible");
520                         player.strength_finished = max(player.strength_finished, time) + autocvar_g_balance_powerup_strength_time;
521                 }
522
523                 // speed powerup
524                 if (item.invincible_finished)
525                 {
526                         pickedup = TRUE;
527                         // sound not available
528                         // AnnounceTo(player, "speed");
529                         player.invincible_finished = max(player.invincible_finished, time) + autocvar_g_balance_powerup_strength_time;
530                 }
531         }
532         else
533         {
534                 // in case the player has autoswitch enabled do the following:
535                 // if the player is using their best weapon before items are given, they
536                 // probably want to switch to an even better weapon after items are given
537                 if (player.autoswitch)
538                 if (player.switchweapon == w_getbestweapon(player))
539                         _switchweapon = TRUE;
540
541                 if not(player.weapons & W_WeaponBit(player.switchweapon))
542                         _switchweapon = TRUE;
543
544                 pickedup |= Item_GiveAmmoTo(item, player, ammo_fuel, g_pickup_fuel_max, ITEM_MODE_FUEL);
545                 pickedup |= Item_GiveAmmoTo(item, player, ammo_shells, g_pickup_shells_max, ITEM_MODE_NONE);
546                 pickedup |= Item_GiveAmmoTo(item, player, ammo_nails, g_pickup_nails_max, ITEM_MODE_NONE);
547                 pickedup |= Item_GiveAmmoTo(item, player, ammo_rockets, g_pickup_rockets_max, ITEM_MODE_NONE);
548                 pickedup |= Item_GiveAmmoTo(item, player, ammo_cells, g_pickup_cells_max, ITEM_MODE_NONE);
549                 pickedup |= Item_GiveAmmoTo(item, player, health, item.max_health, ITEM_MODE_HEALTH);
550                 pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue, ITEM_MODE_ARMOR);
551
552                 if (item.flags & FL_WEAPON)
553                 if ((it = item.weapons - (item.weapons & player.weapons)) || (item.spawnshieldtime && self.pickup_anyway))
554                 {
555                         pickedup = TRUE;
556                         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
557                         {
558                                 e = get_weaponinfo(i);
559                                 if(it & e.weapons)
560                                         W_GiveWeapon (player, e.weapon, item.netname);
561                         }
562                 }
563
564                 if((it = (item.items - (item.items & player.items)) & IT_PICKUPMASK))
565                 {
566                         pickedup = TRUE;
567                         player.items |= it;
568                         sprint (player, strcat("You got the ^2", item.netname, "\n"));
569                 }
570
571                 if (item.strength_finished)
572                 {
573                         pickedup = TRUE;
574                         player.strength_finished = max(player.strength_finished, time) + item.strength_finished;
575                 }
576                 if (item.invincible_finished)
577                 {
578                         pickedup = TRUE;
579                         player.invincible_finished = max(player.invincible_finished, time) + item.invincible_finished;
580                 }
581                 if (item.superweapons_finished)
582                 {
583                         pickedup = TRUE;
584                         player.superweapons_finished = max(player.superweapons_finished, time) + item.superweapons_finished;
585                 }
586         }
587
588 :skip
589         // always eat teamed entities
590         if(item.team)
591                 pickedup = TRUE;
592
593         if (!pickedup)
594                 return 0;
595
596         if (_switchweapon)
597                 if (player.switchweapon != w_getbestweapon(player))
598                         W_SwitchWeapon_Force(player, w_getbestweapon(player));
599
600         return 1;
601 }
602
603 void Item_Touch (void)
604 {
605         entity e, head;
606
607         // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
608         if (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
609         {
610                 remove(self);
611                 return;
612         }
613         if (other.classname != "player")
614                 return;
615         if (other.deadflag)
616                 return;
617         if (self.solid != SOLID_TRIGGER)
618                 return;
619         if (self.owner == other)
620                 return;
621
622         if (self.classname == "droppedweapon")
623         {
624                 self.strength_finished = max(0, self.strength_finished - time);
625                 self.invincible_finished = max(0, self.invincible_finished - time);
626                 self.superweapons_finished = max(0, self.superweapons_finished - time);
627         }
628
629         if(!Item_GiveTo(self, other))
630         {
631                 if (self.classname == "droppedweapon")
632                 {
633                         // undo what we did above
634                         self.strength_finished += time;
635                         self.invincible_finished += time;
636                         self.superweapons_finished += time;
637                 }
638                 return;
639         }
640
641         other.last_pickup = time;
642
643         pointparticles(particleeffectnum("item_pickup"), self.origin, '0 0 0', 1);
644         sound (other, CH_TRIGGER, self.item_pickupsound, VOL_BASE, ATTN_NORM);
645
646         if (self.classname == "droppedweapon")
647                 remove (self);
648         else if not(self.spawnshieldtime)
649                 return;
650         else
651         {
652                 if(self.team)
653                 {
654                         RandomSelection_Init();
655                         for(head = world; (head = findfloat(head, team, self.team)); )
656                         {
657                                 if(head.flags & FL_ITEM)
658                                 {
659                                         Item_Show(head, -1);
660                                         RandomSelection_Add(head, 0, string_null, head.cnt, 0);
661                                 }
662                         }
663                         e = RandomSelection_chosen_ent;
664
665                 }
666                 else
667                         e = self;
668                 Item_ScheduleRespawn(e);
669         }
670 }
671
672 void Item_Reset()
673 {
674         Item_Show(self, !self.state);
675         setorigin (self, self.origin);
676
677         if(self.classname != "droppedweapon")
678         {
679                 self.think = SUB_Null;
680                 self.nextthink = 0;
681
682                 if(self.waypointsprite_attached)
683                         WaypointSprite_Kill(self.waypointsprite_attached);
684
685                 if((self.flags & FL_POWERUP) | (self.weapons & WEPBIT_SUPERWEAPONS)) // do not spawn powerups initially!
686                         Item_ScheduleInitialRespawn(self);
687         }
688 }
689
690 void Item_FindTeam()
691 {
692         entity head, e;
693
694         if(self.effects & EF_NODRAW)
695         {
696                 // marker for item team search
697                 dprint("Initializing item team ", ftos(self.team), "\n");
698                 RandomSelection_Init();
699                 for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
700                         RandomSelection_Add(head, 0, string_null, head.cnt, 0);
701                 e = RandomSelection_chosen_ent;
702                 e.state = 0;
703                 Item_Show(e, 1);
704
705                 for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
706                 {
707                         if(head != e)
708                         {
709                                 // make it a non-spawned item
710                                 Item_Show(head, -1);
711                                 head.state = 1; // state 1 = initially hidden item
712                         }
713                         head.effects &~= EF_NODRAW;
714                 }
715
716                 Item_Reset();
717         }
718 }
719
720 // Savage: used for item garbage-collection
721 // TODO: perhaps nice special effect?
722 void RemoveItem(void)
723 {
724         remove(self);
725 }
726
727 // pickup evaluation functions
728 // these functions decide how desirable an item is to the bots
729
730 float generic_pickupevalfunc(entity player, entity item) {return item.bot_pickupbasevalue;}
731
732 float weapon_pickupevalfunc(entity player, entity item)
733 {
734         float c, i, j, position;
735
736         // See if I have it already
737         if(player.weapons & item.weapons == item.weapons)
738         {
739                 // If I can pick it up
740                 if(!item.spawnshieldtime)
741                         c = 0;
742                 else if(player.ammo_cells || player.ammo_shells || player.ammo_nails || player.ammo_rockets)
743                 {
744                         // Skilled bots will grab more
745                         c = bound(0, skill / 10, 1) * 0.5;
746                 }
747                 else
748                         c = 0;
749         }
750         else
751                 c = 1;
752
753         // If custom weapon priorities for bots is enabled rate most wanted weapons higher
754         if( bot_custom_weapon && c )
755         {
756                 for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
757                 {
758                         // Find weapon
759                         if( (get_weaponinfo(i)).weapons & item.weapons  != item.weapons )
760                                 continue;
761
762                         // Find the highest position on any range
763                         position = -1;
764                         for(j = 0; j < WEP_LAST ; ++j){
765                                 if(
766                                                 bot_weapons_far[j] == i ||
767                                                 bot_weapons_mid[j] == i ||
768                                                 bot_weapons_close[j] == i
769                                   )
770                                 {
771                                         position = j;
772                                         break;
773                                 }
774                         }
775
776                         // Rate it
777                         if (position >= 0 )
778                         {
779                                 position = WEP_LAST - position;
780                                 // item.bot_pickupbasevalue is overwritten here
781                                 return (BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ))) * c;
782                         }
783                 }
784         }
785
786         return item.bot_pickupbasevalue * c;
787 }
788
789 float commodity_pickupevalfunc(entity player, entity item)
790 {
791         float c, i, need_shells, need_nails, need_rockets, need_cells, need_fuel;
792         entity wi;
793         c = 0;
794
795         // Detect needed ammo
796         for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
797         {
798                 wi = get_weaponinfo(i);
799
800                 if not(wi.weapons & player.weapons)
801                         continue;
802
803                 if(wi.items & IT_SHELLS)
804                         need_shells = TRUE;
805                 else if(wi.items & IT_NAILS)
806                         need_nails = TRUE;
807                 else if(wi.items & IT_ROCKETS)
808                         need_rockets = TRUE;
809                 else if(wi.items & IT_CELLS)
810                         need_cells = TRUE;
811                 else if(wi.items & IT_FUEL)
812                         need_cells = TRUE;
813         }
814
815         // TODO: figure out if the player even has the weapon this ammo is for?
816         // may not affect strategy much though...
817         // find out how much more ammo/armor/health the player can hold
818         if (need_shells)
819         if (item.ammo_shells)
820         if (player.ammo_shells < g_pickup_shells_max)
821                 c = c + max(0, 1 - player.ammo_shells / g_pickup_shells_max);
822         if (need_nails)
823         if (item.ammo_nails)
824         if (player.ammo_nails < g_pickup_nails_max)
825                 c = c + max(0, 1 - player.ammo_nails / g_pickup_nails_max);
826         if (need_rockets)
827         if (item.ammo_rockets)
828         if (player.ammo_rockets < g_pickup_rockets_max)
829                 c = c + max(0, 1 - player.ammo_rockets / g_pickup_rockets_max);
830         if (need_cells)
831         if (item.ammo_cells)
832         if (player.ammo_cells < g_pickup_cells_max)
833                 c = c + max(0, 1 - player.ammo_cells / g_pickup_cells_max);
834         if (need_fuel)
835         if (item.ammo_fuel)
836         if (player.ammo_fuel < g_pickup_fuel_max)
837                 c = c + max(0, 1 - player.ammo_fuel / g_pickup_fuel_max);
838         if (item.armorvalue)
839         if (player.armorvalue < item.max_armorvalue)
840                 c = c + max(0, 1 - player.armorvalue / item.max_armorvalue);
841         if (item.health)
842         if (player.health < item.max_health)
843                 c = c + max(0, 1 - player.health / item.max_health);
844
845         return item.bot_pickupbasevalue * c;
846 }
847
848
849 .float is_item;
850 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)
851 {
852         startitem_failed = FALSE;
853
854         self.items = itemid;
855         self.weapons = weaponid;
856         self.flags = FL_ITEM | itemflags;
857
858         // is it a dropped weapon?
859         if (self.classname == "droppedweapon")
860         {
861                 self.reset = SUB_Remove;
862                 // it's a dropped weapon
863                 self.movetype = MOVETYPE_TOSS;
864
865                 // Savage: remove thrown items after a certain period of time ("garbage collection")
866                 self.think = RemoveItem;
867                 self.nextthink = time + 20;
868
869                 if(self.strength_finished || self.invincible_finished || self.superweapons_finished)
870                 /*
871                 if(self.items == 0)
872                 if(self.weapons == (self.weapons & WEPBIT_SUPERWEAPONS)) // only superweapons
873                 if(self.ammo_nails == 0)
874                 if(self.ammo_cells == 0)
875                 if(self.ammo_rockets == 0)
876                 if(self.ammo_shells == 0)
877                 if(self.ammo_fuel == 0)
878                 if(self.health == 0)
879                 if(self.armorvalue == 0)
880                 */
881                 {
882                         // if item is worthless after a timer, have it expire then
883                         self.nextthink = max(self.strength_finished, self.invincible_finished, self.superweapons_finished);
884                 }
885
886                 // don't drop if in a NODROP zone (such as lava)
887                 traceline(self.origin, self.origin, MOVE_NORMAL, self);
888                 if (trace_dpstartcontents & DPCONTENTS_NODROP)
889                 {
890                         startitem_failed = TRUE;
891                         remove(self);
892                         return;
893                 }
894         }
895         else
896         {
897                 if(MUTATOR_CALLHOOK(FilterItem)) // error means we do not want the item
898                 {
899                         startitem_failed = TRUE;
900                         remove(self);
901                         return;
902                 }
903
904                 if(!have_pickup_item())
905                 {
906                         startitem_failed = TRUE;
907                         remove (self);
908                         return;
909                 }
910
911                 if(self.model != "")
912                         itemmodel = self.model;
913                 if(self.item_pickupsound != "")
914                         pickupsound = self.item_pickupsound;
915
916                 self.reset = Item_Reset;
917                 // it's a level item
918                 if(self.spawnflags & 1)
919                         self.noalign = 1;
920                 if (self.noalign)
921                         self.movetype = MOVETYPE_NONE;
922                 else
923                         self.movetype = MOVETYPE_TOSS;
924                 // do item filtering according to game mode and other things
925                 if (!self.noalign)
926                 {
927                         // first nudge it off the floor a little bit to avoid math errors
928                         setorigin(self, self.origin + '0 0 1');
929                         // set item size before we spawn a spawnfunc_waypoint
930                         if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
931                                 setsize (self, '-16 -16 0', '16 16 48');
932                         else
933                                 setsize (self, '-16 -16 0', '16 16 32');
934                         // note droptofloor returns FALSE if stuck/or would fall too far
935                         droptofloor();
936                         waypoint_spawnforitem(self);
937                 }
938
939                 /*
940                  * can't do it that way, as it would break maps
941                  * TODO make a target_give like entity another way, that perhaps has
942                  * the weapon name in a key
943                 if(self.targetname)
944                 {
945                         // target_give not yet supported; maybe later
946                         print("removed targeted ", self.classname, "\n");
947                         startitem_failed = TRUE;
948                         remove (self);
949                         return;
950                 }
951                 */
952
953                 if(autocvar_spawn_debug >= 2)
954                 {
955                         entity otheritem;
956                         for(otheritem = findradius(self.origin, 3); otheritem; otheritem = otheritem.chain)
957                         {
958                                 if(otheritem.is_item)
959                                 {
960                                         dprint("XXX Found duplicated item: ", itemname, vtos(self.origin));
961                                         dprint(" vs ", otheritem.netname, vtos(otheritem.origin), "\n");
962                                         error("Mapper sucks.");
963                                 }
964                         }
965                         self.is_item = TRUE;
966                 }
967
968                 weaponsInMap |= weaponid;
969
970                 precache_model (itemmodel);
971                 precache_sound (pickupsound);
972
973                 precache_sound ("misc/itemrespawncountdown.wav");
974                 if(!g_minstagib && itemid == IT_STRENGTH)
975                         precache_sound ("misc/strength_respawn.wav");
976                 else if(!g_minstagib && itemid == IT_INVINCIBLE)
977                         precache_sound ("misc/shield_respawn.wav");
978                 else
979                         precache_sound ("misc/itemrespawn.wav");
980
981                 if((itemflags & (FL_POWERUP | FL_WEAPON)) || (itemid & (IT_HEALTH | IT_ARMOR | IT_KEY1 | IT_KEY2)))
982                         self.target = "###item###"; // for finding the nearest item using find()
983         }
984
985         self.bot_pickup = TRUE;
986         self.bot_pickupevalfunc = pickupevalfunc;
987         self.bot_pickupbasevalue = pickupbasevalue;
988         self.mdl = itemmodel;
989         self.item_pickupsound = pickupsound;
990         if(self.weapons)
991                 self.weapon = WEP_FIRST + log2of(lowestbit(self.weapons));
992         else
993                 self.weapon = 0;
994         // let mappers override respawntime
995         if(!self.respawntime) // both set
996         {
997                 self.respawntime = defaultrespawntime;
998                 self.respawntimejitter = defaultrespawntimejitter;
999         }
1000         self.netname = itemname;
1001         self.touch = Item_Touch;
1002         setmodel (self, self.mdl); // precision set below
1003         self.effects |= EF_LOWPRECISION;
1004         
1005         if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
1006             setsize (self, '-16 -16 0', '16 16 48');
1007         else
1008                 setsize (self, '-16 -16 0', '16 16 32');
1009     
1010     if(itemflags & FL_POWERUP) 
1011         self.ItemStatus |= ITS_ANIMATED;
1012         
1013         if(itemflags & FL_WEAPON)
1014         {
1015                 self.modelflags |= MF_ROTATE;
1016                 self.ItemStatus |= ITS_ANIMATED;
1017                 
1018                 if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely
1019             self.colormap = 1024; // color shirt=0 pants=0 grey
1020         }
1021
1022         self.state = 0;
1023         if(self.team)
1024         {
1025                 if(!self.cnt)
1026                         self.cnt = 1; // item probability weight
1027                 self.effects = self.effects | EF_NODRAW; // marker for item team search
1028                 InitializeEntity(self, Item_FindTeam, INITPRIO_FINDTARGET);
1029         }
1030         else
1031                 Item_Reset();
1032     
1033     if(self.classname == "droppedweapon")
1034         return;
1035     
1036     //self.SendFlags = 0xFFFFFF;
1037     if(cvar("g_csqc_items"))
1038         Net_LinkEntity(self, FALSE, 0, ItemSend);
1039
1040 }
1041
1042 /* replace items in minstagib
1043  * IT_STRENGTH   = invisibility
1044  * IT_NAILS      = extra lives
1045  * IT_INVINCIBLE = speed
1046  */
1047 void minstagib_items (float itemid)
1048 {
1049         float rnd;
1050         self.classname = "minstagib";
1051
1052         // replace rocket launchers and nex guns with ammo cells
1053         if (itemid == IT_CELLS)
1054         {
1055                 self.ammo_cells = autocvar_g_minstagib_ammo_drop;
1056                 StartItem ("models/items/a_cells.md3",
1057                         "misc/itempickup.wav", 45, 0,
1058                         "MinstaNex Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100);
1059                 return;
1060         }
1061
1062         // randomize
1063         rnd = random() * 3;
1064         if (rnd <= 1)
1065                 itemid = IT_STRENGTH;
1066         else if (rnd <= 2)
1067                 itemid = IT_NAILS;
1068         else
1069                 itemid = IT_INVINCIBLE;
1070
1071         // replace with invis
1072         if (itemid == IT_STRENGTH)
1073         {
1074                 if(!self.strength_finished)
1075                         self.strength_finished = autocvar_g_balance_powerup_strength_time;
1076                 StartItem ("models/items/g_strength.md3",
1077                         "misc/powerup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup,
1078                         "Invisibility", IT_STRENGTH, 0, FL_POWERUP, generic_pickupevalfunc, BOT_PICKUP_RATING_MID);
1079         }
1080         // replace with extra lives
1081         if (itemid == IT_NAILS)
1082         {
1083                 self.max_health = 1;
1084                 StartItem ("models/items/g_h100.md3",
1085                         "misc/megahealth.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup,
1086                         "Extralife", IT_NAILS, 0, FL_POWERUP, generic_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
1087         }
1088         // replace with speed
1089         if (itemid == IT_INVINCIBLE)
1090         {
1091                 if(!self.invincible_finished)
1092                         self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
1093                 StartItem ("models/items/g_invincible.md3",
1094                         "misc/powerup_shield.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup,
1095                         "Speed", IT_INVINCIBLE, 0, FL_POWERUP, generic_pickupevalfunc, BOT_PICKUP_RATING_MID);
1096         }
1097 }
1098
1099 float minst_no_auto_cells;
1100 void minst_remove_item (void) {
1101         if(minst_no_auto_cells)
1102                 remove(self);
1103 }
1104
1105 float weaponswapping;
1106 float internalteam;
1107
1108 void weapon_defaultspawnfunc(float wpn)
1109 {
1110         entity e;
1111         float t;
1112         var .float ammofield;
1113         string s;
1114         entity oldself;
1115         float i, j;
1116         float f;
1117
1118         if(self.classname != "droppedweapon" && self.classname != "replacedweapon")
1119         {
1120                 e = get_weaponinfo(wpn);
1121                 s = cvar_string(strcat("g_weaponreplace_", e.netname));
1122                 if(s == "0")
1123                 {
1124                         remove(self);
1125                         startitem_failed = TRUE;
1126                         return;
1127                 }
1128                 t = tokenize_console(s);
1129                 if(t >= 2)
1130                 {
1131                         self.team = --internalteam;
1132                         oldself = self;
1133                         for(i = 1; i < t; ++i)
1134                         {
1135                                 s = argv(i);
1136                                 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
1137                                 {
1138                                         e = get_weaponinfo(j);
1139                                         if(e.netname == s)
1140                                         {
1141                                                 self = spawn();
1142                                                 copyentity(oldself, self);
1143                                                 self.classname = "replacedweapon";
1144                                                 weapon_defaultspawnfunc(j);
1145                                                 break;
1146                                         }
1147                                 }
1148                                 if(j > WEP_LAST)
1149                                 {
1150                                         print("The weapon replace list for ", oldself.classname, " contains an unknown weapon ", s, ". Skipped.\n");
1151                                 }
1152                         }
1153                         self = oldself;
1154                 }
1155                 if(t >= 1)
1156                 {
1157                         s = argv(0);
1158                         wpn = 0;
1159                         for(j = WEP_FIRST; j <= WEP_LAST; ++j)
1160                         {
1161                                 e = get_weaponinfo(j);
1162                                 if(e.netname == s)
1163                                 {
1164                                         wpn = j;
1165                                         break;
1166                                 }
1167                         }
1168                         if(j > WEP_LAST)
1169                         {
1170                                 print("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n");
1171                         }
1172                 }
1173                 if(wpn == 0)
1174                 {
1175                         remove(self);
1176                         startitem_failed = TRUE;
1177                         return;
1178                 }
1179         }
1180
1181         e = get_weaponinfo(wpn);
1182
1183         if(!self.respawntime)
1184         {
1185                 if(e.weapons & WEPBIT_SUPERWEAPONS)
1186                 {
1187                         self.respawntime = g_pickup_respawntime_superweapon;
1188                         self.respawntimejitter = g_pickup_respawntimejitter_superweapon;
1189                 }
1190                 else
1191                 {
1192                         self.respawntime = g_pickup_respawntime_weapon;
1193                         self.respawntimejitter = g_pickup_respawntimejitter_weapon;
1194                 }
1195         }
1196
1197         if(e.weapons & WEPBIT_SUPERWEAPONS)
1198                 if(!self.superweapons_finished)
1199                         self.superweapons_finished = autocvar_g_balance_superweapons_time;
1200
1201         if(e.items)
1202         {
1203                 for(i = 0, j = 1; i < 24; ++i, j *= 2)
1204                 {
1205                         if(e.items & j)
1206                         {
1207                                 ammofield = Item_CounterField(j);
1208                                 if(!self.ammofield)
1209                                         self.ammofield = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon"));
1210                         }
1211                 }
1212         }
1213
1214         // pickup anyway
1215         if(g_pickup_weapons_anyway)
1216                 self.pickup_anyway = TRUE;
1217
1218         f = FL_WEAPON;
1219
1220         // no weapon-stay on superweapons
1221         if(e.weapons & WEPBIT_SUPERWEAPONS)
1222                 f |= FL_NO_WEAPON_STAY;
1223
1224         // weapon stay isn't supported for teamed weapons
1225         if(self.team)
1226                 f |= FL_NO_WEAPON_STAY;
1227
1228         // stupid minstagib hack, don't ask
1229         if(g_minstagib)
1230                 if(self.ammo_cells)
1231                         self.ammo_cells = autocvar_g_minstagib_ammo_drop;
1232
1233         StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapons, f, weapon_pickupevalfunc, e.bot_pickupbasevalue);
1234         if (self.modelindex) // don't precache if self was removed
1235                 weapon_action(e.weapon, WR_PRECACHE);
1236 }
1237
1238 void spawnfunc_weapon_shotgun (void);
1239 void spawnfunc_weapon_uzi (void) {
1240         if(autocvar_sv_q3acompat_machineshotgunswap)
1241         if(self.classname != "droppedweapon")
1242         {
1243                 weapon_defaultspawnfunc(WEP_SHOTGUN);
1244                 return;
1245         }
1246         weapon_defaultspawnfunc(WEP_UZI);
1247 }
1248
1249 void spawnfunc_weapon_shotgun (void) {
1250         if(autocvar_sv_q3acompat_machineshotgunswap)
1251         if(self.classname != "droppedweapon")
1252         {
1253                 weapon_defaultspawnfunc(WEP_UZI);
1254                 return;
1255         }
1256         weapon_defaultspawnfunc(WEP_SHOTGUN);
1257 }
1258
1259 void spawnfunc_weapon_nex (void)
1260 {
1261         if (g_minstagib)
1262         {
1263                 minstagib_items(IT_CELLS);
1264                 self.think = minst_remove_item;
1265                 self.nextthink = time;
1266                 return;
1267         }
1268         weapon_defaultspawnfunc(WEP_NEX);
1269 }
1270
1271 void spawnfunc_weapon_minstanex (void)
1272 {
1273         if (g_minstagib)
1274         {
1275                 minstagib_items(IT_CELLS);
1276                 self.think = minst_remove_item;
1277                 self.nextthink = time;
1278                 return;
1279         }
1280         weapon_defaultspawnfunc(WEP_MINSTANEX);
1281 }
1282
1283 void spawnfunc_weapon_rocketlauncher (void)
1284 {
1285         if (g_minstagib)
1286         {
1287                 minstagib_items(IT_CELLS); // replace rocketlauncher with cells
1288                 self.think = minst_remove_item;
1289                 self.nextthink = time;
1290                 return;
1291         }
1292         weapon_defaultspawnfunc(WEP_ROCKET_LAUNCHER);
1293 }
1294
1295 void spawnfunc_item_rockets (void) {
1296         if(!self.ammo_rockets)
1297                 self.ammo_rockets = g_pickup_rockets;
1298         if(!self.pickup_anyway)
1299                 self.pickup_anyway = g_pickup_ammo_anyway;
1300         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);
1301 }
1302
1303 void spawnfunc_item_shells (void);
1304 void spawnfunc_item_bullets (void) {
1305         if(!weaponswapping)
1306         if(autocvar_sv_q3acompat_machineshotgunswap)
1307         if(self.classname != "droppedweapon")
1308         {
1309                 weaponswapping = TRUE;
1310                 spawnfunc_item_shells();
1311                 weaponswapping = FALSE;
1312                 return;
1313         }
1314
1315         if(!self.ammo_nails)
1316                 self.ammo_nails = g_pickup_nails;
1317         if(!self.pickup_anyway)
1318                 self.pickup_anyway = g_pickup_ammo_anyway;
1319         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);
1320 }
1321
1322 void spawnfunc_item_cells (void) {
1323         if(!self.ammo_cells)
1324                 self.ammo_cells = g_pickup_cells;
1325         if(!self.pickup_anyway)
1326                 self.pickup_anyway = g_pickup_ammo_anyway;
1327         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);
1328 }
1329
1330 void spawnfunc_item_shells (void) {
1331         if(!weaponswapping)
1332         if(autocvar_sv_q3acompat_machineshotgunswap)
1333         if(self.classname != "droppedweapon")
1334         {
1335                 weaponswapping = TRUE;
1336                 spawnfunc_item_bullets();
1337                 weaponswapping = FALSE;
1338                 return;
1339         }
1340
1341         if(!self.ammo_shells)
1342                 self.ammo_shells = g_pickup_shells;
1343         if(!self.pickup_anyway)
1344                 self.pickup_anyway = g_pickup_ammo_anyway;
1345         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);
1346 }
1347
1348 void spawnfunc_item_armor_small (void) {
1349         if(!self.armorvalue)
1350                 self.armorvalue = g_pickup_armorsmall;
1351         if(!self.max_armorvalue)
1352                 self.max_armorvalue = g_pickup_armorsmall_max;
1353         if(!self.pickup_anyway)
1354                 self.pickup_anyway = g_pickup_armorsmall_anyway;
1355         StartItem ("models/items/item_armor_small.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);
1356 }
1357
1358 void spawnfunc_item_armor_medium (void) {
1359         if(!self.armorvalue)
1360                 self.armorvalue = g_pickup_armormedium;
1361         if(!self.max_armorvalue)
1362                 self.max_armorvalue = g_pickup_armormedium_max;
1363         if(!self.pickup_anyway)
1364                 self.pickup_anyway = g_pickup_armormedium_anyway;
1365         StartItem ("models/items/item_armor_medium.md3", "misc/armor10.wav", g_pickup_respawntime_medium, g_pickup_respawntimejitter_medium, "25 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_MID);
1366 }
1367
1368 void spawnfunc_item_armor_big (void) {
1369         if(!self.armorvalue)
1370                 self.armorvalue = g_pickup_armorbig;
1371         if(!self.max_armorvalue)
1372                 self.max_armorvalue = g_pickup_armorbig_max;
1373         if(!self.pickup_anyway)
1374                 self.pickup_anyway = g_pickup_armorbig_anyway;
1375         StartItem ("models/items/item_armor_big.md3", "misc/armor17_5.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "50 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, 20000);
1376 }
1377
1378 void spawnfunc_item_armor_large (void) {
1379         if(!self.armorvalue)
1380                 self.armorvalue = g_pickup_armorlarge;
1381         if(!self.max_armorvalue)
1382                 self.max_armorvalue = g_pickup_armorlarge_max;
1383         if(!self.pickup_anyway)
1384                 self.pickup_anyway = g_pickup_armorlarge_anyway;
1385         StartItem ("models/items/item_armor_large.md3", "misc/armor25.wav", g_pickup_respawntime_long, g_pickup_respawntimejitter_long, "100 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, BOT_PICKUP_RATING_HIGH);
1386 }
1387
1388 void spawnfunc_item_health_small (void) {
1389         if(!self.max_health)
1390                 self.max_health = g_pickup_healthsmall_max;
1391         if(!self.health)
1392                 self.health = g_pickup_healthsmall;
1393         if(!self.pickup_anyway)
1394                 self.pickup_anyway = g_pickup_healthsmall_anyway;
1395         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);
1396 }
1397
1398 void spawnfunc_item_health_medium (void) {
1399         if(!self.max_health)
1400                 self.max_health = g_pickup_healthmedium_max;
1401         if(!self.health)
1402                 self.health = g_pickup_healthmedium;
1403         if(!self.pickup_anyway)
1404                 self.pickup_anyway = g_pickup_healthmedium_anyway;
1405         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);
1406 }
1407
1408 void spawnfunc_item_health_large (void) {
1409         if(!self.max_health)
1410                 self.max_health = g_pickup_healthlarge_max;
1411         if(!self.health)
1412                 self.health = g_pickup_healthlarge;
1413         if(!self.pickup_anyway)
1414                 self.pickup_anyway = g_pickup_healthlarge_anyway;
1415         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);
1416 }
1417
1418 void spawnfunc_item_health_mega (void) {
1419         if(g_minstagib) {
1420                 minstagib_items(IT_NAILS);
1421         } else {
1422                 if(!self.max_health)
1423                         self.max_health = g_pickup_healthmega_max;
1424                 if(!self.health)
1425                         self.health = g_pickup_healthmega;
1426                 if(!self.pickup_anyway)
1427                         self.pickup_anyway = g_pickup_healthmega_anyway;
1428                 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);
1429         }
1430 }
1431
1432 // support old misnamed entities
1433 void spawnfunc_item_armor1() { spawnfunc_item_armor_small(); }  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
1434 void spawnfunc_item_armor25() { spawnfunc_item_armor_large(); }
1435 void spawnfunc_item_health1() { spawnfunc_item_health_small(); }
1436 void spawnfunc_item_health25() { spawnfunc_item_health_medium(); }
1437 void spawnfunc_item_health100() { spawnfunc_item_health_mega(); }
1438
1439 void spawnfunc_item_strength (void) {
1440         if(g_minstagib) {
1441                 minstagib_items(IT_STRENGTH);
1442         } else {
1443                 precache_sound("weapons/strength_fire.wav");
1444                 if(!self.strength_finished)
1445                         self.strength_finished = autocvar_g_balance_powerup_strength_time;
1446                 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);
1447         }
1448 }
1449
1450 void spawnfunc_item_invincible (void) {
1451         if(g_minstagib) {
1452                 minstagib_items(IT_INVINCIBLE);
1453         } else {
1454                 if(!self.invincible_finished)
1455                         self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
1456                 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);
1457         }
1458 }
1459
1460 void spawnfunc_item_minst_cells (void) {
1461         if (g_minstagib)
1462         {
1463                 minst_no_auto_cells = 1;
1464                 minstagib_items(IT_CELLS);
1465         }
1466         else
1467                 remove(self);
1468 }
1469
1470 // compatibility:
1471 void spawnfunc_item_quad (void) {self.classname = "item_strength";spawnfunc_item_strength();}
1472
1473 float GiveItems(entity e, float beginarg, float endarg);
1474 void target_items_use (void)
1475 {
1476         if(activator.classname == "droppedweapon")
1477         {
1478                 EXACTTRIGGER_TOUCH;
1479                 remove(activator);
1480                 return;
1481         }
1482
1483         if(activator.classname != "player")
1484                 return;
1485         if(activator.deadflag != DEAD_NO)
1486                 return;
1487         EXACTTRIGGER_TOUCH;
1488
1489         entity e;
1490         for(e = world; (e = find(e, classname, "droppedweapon")); )
1491                 if(e.enemy == activator)
1492                         remove(e);
1493
1494         if(GiveItems(activator, 0, tokenize_console(self.netname)))
1495                 centerprint(activator, self.message);
1496 }
1497
1498 void spawnfunc_target_items (void)
1499 {
1500         float n, i, j;
1501         entity e;
1502
1503         self.use = target_items_use;
1504         if(!self.strength_finished)
1505                 self.strength_finished = autocvar_g_balance_powerup_strength_time;
1506         if(!self.invincible_finished)
1507                 self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
1508         if(!self.superweapons_finished)
1509                 self.superweapons_finished = autocvar_g_balance_superweapons_time;
1510
1511         precache_sound("misc/itempickup.wav");
1512         precache_sound("misc/megahealth.wav");
1513         precache_sound("misc/armor25.wav");
1514         precache_sound("misc/powerup.wav");
1515         precache_sound("misc/poweroff.wav");
1516         precache_sound("weapons/weaponpickup.wav");
1517
1518         n = tokenize_console(self.netname);
1519         if(argv(0) == "give")
1520         {
1521                 self.netname = substring(self.netname, argv_start_index(1), argv_end_index(-1) - argv_start_index(1));
1522         }
1523         else
1524         {
1525                 for(i = 0; i < n; ++i)
1526                 {
1527                         if     (argv(i) == "unlimited_ammo")         self.items |= IT_UNLIMITED_AMMO;
1528                         else if(argv(i) == "unlimited_weapon_ammo")  self.items |= IT_UNLIMITED_WEAPON_AMMO;
1529                         else if(argv(i) == "unlimited_superweapons") self.items |= IT_UNLIMITED_SUPERWEAPONS;
1530                         else if(argv(i) == "strength")               self.items |= IT_STRENGTH;
1531                         else if(argv(i) == "invincible")             self.items |= IT_INVINCIBLE;
1532                         else if(argv(i) == "superweapons")           self.items |= IT_SUPERWEAPON;
1533                         else if(argv(i) == "jetpack")                self.items |= IT_JETPACK;
1534                         else if(argv(i) == "fuel_regen")             self.items |= IT_FUEL_REGEN;
1535                         else
1536                         for(j = WEP_FIRST; j <= WEP_LAST; ++j)
1537                         {
1538                                 e = get_weaponinfo(j);
1539                                 if(argv(i) == e.netname)
1540                                 {
1541                                         self.weapons |= e.weapons;
1542                                         if(self.spawnflags == 0 || self.spawnflags == 2)
1543                                                 weapon_action(e.weapon, WR_PRECACHE);
1544                                         break;
1545                                 }
1546                         }
1547                         if(j > WEP_LAST)
1548                                 print("target_items: invalid item ", argv(i), "\n");
1549                 }
1550
1551                 string itemprefix, valueprefix;
1552                 if(self.spawnflags == 0)
1553                 {
1554                         itemprefix = "";
1555                         valueprefix = "";
1556                 }
1557                 else if(self.spawnflags == 1)
1558                 {
1559                         itemprefix = "max ";
1560                         valueprefix = "max ";
1561                 }
1562                 else if(self.spawnflags == 2)
1563                 {
1564                         itemprefix = "min ";
1565                         valueprefix = "min ";
1566                 }
1567                 else if(self.spawnflags == 4)
1568                 {
1569                         itemprefix = "minus ";
1570                         valueprefix = "max ";
1571                 }
1572                 else
1573                         error("invalid spawnflags");
1574
1575                 self.netname = "";
1576                 self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_UNLIMITED_WEAPON_AMMO), "unlimited_weapon_ammo");
1577                 self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_UNLIMITED_SUPERWEAPONS), "unlimited_superweapons");
1578                 self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.strength_finished * !!(self.items & IT_STRENGTH), "strength");
1579                 self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.invincible_finished * !!(self.items & IT_INVINCIBLE), "invincible");
1580                 self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.superweapons_finished * !!(self.items & IT_SUPERWEAPON), "superweapons");
1581                 self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_JETPACK), "jetpack");
1582                 self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_FUEL_REGEN), "fuel_regen");
1583                 if(self.ammo_shells != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_shells), "shells");
1584                 if(self.ammo_nails != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_nails), "nails");
1585                 if(self.ammo_rockets != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_rockets), "rockets");
1586                 if(self.ammo_cells != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_cells), "cells");
1587                 if(self.ammo_fuel != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_fuel), "fuel");
1588                 if(self.health != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.health), "health");
1589                 if(self.armorvalue != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.health), "armor");
1590                 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
1591                 {
1592                         e = get_weaponinfo(j);
1593                         if(e.weapons)
1594                                 self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.weapons & e.weapons), e.netname);
1595                 }
1596         }
1597         self.netname = strzone(self.netname);
1598         //print(self.netname, "\n");
1599
1600         n = tokenize_console(self.netname);
1601         for(i = 0; i < n; ++i)
1602         {
1603                 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
1604                 {
1605                         e = get_weaponinfo(j);
1606                         if(argv(i) == e.netname)
1607                         {
1608                                 weapon_action(e.weapon, WR_PRECACHE);
1609                                 break;
1610                         }
1611                 }
1612         }
1613 }
1614
1615 void spawnfunc_item_fuel(void)
1616 {
1617         if(!self.ammo_fuel)
1618                 self.ammo_fuel = g_pickup_fuel;
1619         if(!self.pickup_anyway)
1620                 self.pickup_anyway = g_pickup_ammo_anyway;
1621         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);
1622 }
1623
1624 void spawnfunc_item_fuel_regen(void)
1625 {
1626         if(start_items & IT_FUEL_REGEN)
1627         {
1628                 spawnfunc_item_fuel();
1629                 return;
1630         }
1631         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);
1632 }
1633
1634 void spawnfunc_item_jetpack(void)
1635 {
1636         if(g_grappling_hook)
1637                 return; // sorry, but these two can't coexist (same button); spawn fuel instead
1638         if(!self.ammo_fuel)
1639                 self.ammo_fuel = g_pickup_fuel_jetpack;
1640         if(start_items & IT_JETPACK)
1641         {
1642                 spawnfunc_item_fuel();
1643                 return;
1644         }
1645         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);
1646 }
1647
1648
1649 #define OP_SET 0
1650 #define OP_MIN 1
1651 #define OP_MAX 2
1652 #define OP_PLUS 3
1653 #define OP_MINUS 4
1654
1655 float GiveBit(entity e, .float fld, float bit, float op, float val)
1656 {
1657         float v0, v1;
1658         v0 = (e.fld & bit);
1659         switch(op)
1660         {
1661                 case OP_SET:
1662                         if(val > 0)
1663                                 e.fld |= bit;
1664                         else
1665                                 e.fld &~= bit;
1666                         break;
1667                 case OP_MIN:
1668                 case OP_PLUS:
1669                         if(val > 0)
1670                                 e.fld |= bit;
1671                         break;
1672                 case OP_MAX:
1673                         if(val <= 0)
1674                                 e.fld &~= bit;
1675                         break;
1676                 case OP_MINUS:
1677                         if(val > 0)
1678                                 e.fld &~= bit;
1679                         break;
1680         }
1681         v1 = (e.fld & bit);
1682         return (v0 != v1);
1683 }
1684
1685 float GiveValue(entity e, .float fld, float op, float val)
1686 {
1687         float v0, v1;
1688         v0 = e.fld;
1689         switch(op)
1690         {
1691                 case OP_SET:
1692                         e.fld = val;
1693                         break;
1694                 case OP_MIN:
1695                         e.fld = max(e.fld, val); // min 100 cells = at least 100 cells
1696                         break;
1697                 case OP_MAX:
1698                         e.fld = min(e.fld, val);
1699                         break;
1700                 case OP_PLUS:
1701                         e.fld += val;
1702                         break;
1703                 case OP_MINUS:
1704                         e.fld -= val;
1705                         break;
1706         }
1707         v1 = e.fld;
1708         return (v0 != v1);
1709 }
1710
1711 void GiveSound(entity e, float v0, float v1, float t, string snd_incr, string snd_decr)
1712 {
1713         if(v1 == v0)
1714                 return;
1715         if(v1 <= v0 - t)
1716         {
1717                 if(snd_decr != "")
1718                         sound (e, CH_TRIGGER, snd_decr, VOL_BASE, ATTN_NORM);
1719         }
1720         else if(v0 >= v0 + t)
1721         {
1722                 if(snd_incr != "")
1723                         sound (e, CH_TRIGGER, snd_incr, VOL_BASE, ATTN_NORM);
1724         }
1725 }
1726
1727 void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .float regenfield, float regentime)
1728 {
1729         if(v0 < v1)
1730                 e.rotfield = max(e.rotfield, time + rottime);
1731         else if(v0 > v1)
1732                 e.regenfield = max(e.regenfield, time + regentime);
1733 }
1734
1735 #define PREGIVE(e,f) float save_##f; save_##f = (e).f
1736 #define POSTGIVE_BIT(e,f,b,snd_incr,snd_decr) GiveSound((e), save_##f & (b), (e).f & (b), 0, snd_incr, snd_decr)
1737 #define POSTGIVE_VALUE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr)
1738 #define POSTGIVE_VALUE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e), save_##f, (e).f, rotfield, rottime, regenfield, regentime); GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr)
1739
1740 float GiveItems(entity e, float beginarg, float endarg)
1741 {
1742         float got, i, j, val, op;
1743         float _switchweapon;
1744         entity wi;
1745         string cmd;
1746
1747         val = 999;
1748         op = OP_SET;
1749
1750         got = 0;
1751
1752         _switchweapon = FALSE;
1753         if (e.autoswitch)
1754                 if (e.switchweapon == w_getbestweapon(e))
1755                         _switchweapon = TRUE;
1756
1757         e.strength_finished = max(0, e.strength_finished - time);
1758         e.invincible_finished = max(0, e.invincible_finished - time);
1759         e.superweapons_finished = max(0, e.superweapons_finished - time);
1760         
1761         PREGIVE(e, items);
1762         PREGIVE(e, weapons);
1763         PREGIVE(e, strength_finished);
1764         PREGIVE(e, invincible_finished);
1765         PREGIVE(e, superweapons_finished);
1766         PREGIVE(e, ammo_nails);
1767         PREGIVE(e, ammo_cells);
1768         PREGIVE(e, ammo_shells);
1769         PREGIVE(e, ammo_rockets);
1770         PREGIVE(e, ammo_fuel);
1771         PREGIVE(e, armorvalue);
1772         PREGIVE(e, health);
1773
1774         for(i = beginarg; i < endarg; ++i)
1775         {
1776                 cmd = argv(i);
1777
1778                 if(cmd == "0" || stof(cmd))
1779                 {
1780                         val = stof(cmd);
1781                         continue;
1782                 }
1783                 switch(cmd)
1784                 {
1785                         case "no":
1786                                 op = OP_MAX;
1787                                 val = 0;
1788                                 continue;
1789                         case "max":
1790                                 op = OP_MAX;
1791                                 continue;
1792                         case "min":
1793                                 op = OP_MIN;
1794                                 continue;
1795                         case "plus":
1796                                 op = OP_PLUS;
1797                                 continue;
1798                         case "minus":
1799                                 op = OP_MINUS;
1800                                 continue;
1801                         case "ALL":
1802                                 got += GiveBit(e, items, IT_FUEL_REGEN, op, val);
1803                                 got += GiveValue(e, strength_finished, op, val);
1804                                 got += GiveValue(e, invincible_finished, op, val);
1805                                 got += GiveValue(e, superweapons_finished, op, val);
1806                                 got += GiveBit(e, items, IT_UNLIMITED_AMMO, op, val);
1807                         case "all":
1808                                 got += GiveBit(e, items, IT_JETPACK, op, val);
1809                                 got += GiveValue(e, health, op, val);
1810                                 got += GiveValue(e, armorvalue, op, val);
1811                         case "allweapons":
1812                                 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
1813                                 {
1814                                         wi = get_weaponinfo(j);
1815                                         if(wi.weapons)
1816                                                 got += GiveBit(e, weapons, wi.weapons, op, val);
1817                                 }
1818                         case "allammo":
1819                                 got += GiveValue(e, ammo_cells, op, val);
1820                                 got += GiveValue(e, ammo_shells, op, val);
1821                                 got += GiveValue(e, ammo_nails, op, val);
1822                                 got += GiveValue(e, ammo_rockets, op, val);
1823                                 got += GiveValue(e, ammo_fuel, op, val);
1824                                 break;
1825                         case "unlimited_ammo":
1826                                 got += GiveBit(e, items, IT_UNLIMITED_AMMO, op, val);
1827                                 break;
1828                         case "unlimited_weapon_ammo":
1829                                 got += GiveBit(e, items, IT_UNLIMITED_WEAPON_AMMO, op, val);
1830                                 break;
1831                         case "unlimited_superweapons":
1832                                 got += GiveBit(e, items, IT_UNLIMITED_SUPERWEAPONS, op, val);
1833                                 break;
1834                         case "jetpack":
1835                                 got += GiveBit(e, items, IT_JETPACK, op, val);
1836                                 break;
1837                         case "fuel_regen":
1838                                 got += GiveBit(e, items, IT_FUEL_REGEN, op, val);
1839                                 break;
1840                         case "strength":
1841                                 got += GiveValue(e, strength_finished, op, val);
1842                                 break;
1843                         case "invincible":
1844                                 got += GiveValue(e, invincible_finished, op, val);
1845                                 break;
1846                         case "superweapons":
1847                                 got += GiveValue(e, superweapons_finished, op, val);
1848                                 break;
1849                         case "cells":
1850                                 got += GiveValue(e, ammo_cells, op, val);
1851                                 break;
1852                         case "shells":
1853                                 got += GiveValue(e, ammo_shells, op, val);
1854                                 break;
1855                         case "nails":
1856                         case "bullets":
1857                                 got += GiveValue(e, ammo_nails, op, val);
1858                                 break;
1859                         case "rockets":
1860                                 got += GiveValue(e, ammo_rockets, op, val);
1861                                 break;
1862                         case "health":
1863                                 got += GiveValue(e, health, op, val);
1864                                 break;
1865                         case "armor":
1866                                 got += GiveValue(e, armorvalue, op, val);
1867                                 break;
1868                         case "fuel":
1869                                 got += GiveValue(e, ammo_fuel, op, val);
1870                                 break;
1871                         default:
1872                                 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
1873                                 {
1874                                         wi = get_weaponinfo(j);
1875                                         if(cmd == wi.netname)
1876                                         {
1877                                                 got += GiveBit(e, weapons, wi.weapons, op, val);
1878                                                 break;
1879                                         }
1880                                 }
1881                                 if(j > WEP_LAST)
1882                                         print("give: invalid item ", cmd, "\n");
1883                                 break;
1884                 }
1885                 val = 999;
1886                 op = OP_SET;
1887         }
1888
1889         POSTGIVE_BIT(e, items, IT_FUEL_REGEN, "misc/itempickup.wav", string_null);
1890         POSTGIVE_BIT(e, items, IT_UNLIMITED_SUPERWEAPONS, "misc/powerup.wav", "misc/poweroff.wav");
1891         POSTGIVE_BIT(e, items, IT_UNLIMITED_WEAPON_AMMO, "misc/powerup.wav", "misc/poweroff.wav");
1892         POSTGIVE_BIT(e, items, IT_JETPACK, "misc/itempickup.wav", string_null);
1893         for(j = WEP_FIRST; j <= WEP_LAST; ++j)
1894         {
1895                 wi = get_weaponinfo(j);
1896                 if(wi.weapons)
1897                 {
1898                         POSTGIVE_BIT(e, weapons, wi.weapons, "weapons/weaponpickup.wav", string_null);
1899                         if not(save_weapons & wi.weapons)
1900                                 if(e.weapons & wi.weapons)
1901                                         weapon_action(wi.weapon, WR_PRECACHE);
1902                 }
1903         }
1904         POSTGIVE_VALUE(e, strength_finished, 1, "misc/powerup.wav", "misc/poweroff.wav");
1905         POSTGIVE_VALUE(e, invincible_finished, 1, "misc/powerup_shield.wav", "misc/poweroff.wav");
1906         POSTGIVE_VALUE(e, ammo_nails, 0, "misc/itempickup.wav", string_null);
1907         POSTGIVE_VALUE(e, ammo_cells, 0, "misc/itempickup.wav", string_null);
1908         POSTGIVE_VALUE(e, ammo_shells, 0, "misc/itempickup.wav", string_null);
1909         POSTGIVE_VALUE(e, ammo_rockets, 0, "misc/itempickup.wav", string_null);
1910         POSTGIVE_VALUE_ROT(e, ammo_fuel, 1, pauserotfuel_finished, autocvar_g_balance_pause_fuel_rot, pauseregen_finished, autocvar_g_balance_pause_fuel_regen, "misc/itempickup.wav", string_null);
1911         POSTGIVE_VALUE_ROT(e, armorvalue, 1, pauserotarmor_finished, autocvar_g_balance_pause_armor_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, "misc/armor25.wav", string_null);
1912         POSTGIVE_VALUE_ROT(e, health, 1, pauserothealth_finished, autocvar_g_balance_pause_health_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, "misc/megahealth.wav", string_null);
1913
1914         if(e.superweapons_finished <= 0)
1915                 if(self.weapons & WEPBIT_SUPERWEAPONS)
1916                         e.superweapons_finished = autocvar_g_balance_superweapons_time;
1917
1918         if (g_minstagib)
1919         {
1920                 e.health = bound(0, e.health, 100);
1921                 e.armorvalue = bound(0, e.armorvalue, 999);
1922         }
1923
1924         if(e.strength_finished <= 0)
1925                 e.strength_finished = 0;
1926         else
1927                 e.strength_finished += time;
1928         if(e.invincible_finished <= 0)
1929                 e.invincible_finished = 0;
1930         else
1931                 e.invincible_finished += time;
1932         if(e.superweapons_finished <= 0)
1933                 e.superweapons_finished = 0;
1934         else
1935                 e.superweapons_finished += time;
1936
1937         if not(e.weapons & W_WeaponBit(e.switchweapon))
1938                 _switchweapon = TRUE;
1939         if(_switchweapon)
1940                 W_SwitchWeapon_Force(e, w_getbestweapon(e));
1941
1942         return got;
1943 }
1944 #endif