]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/t_items.qc
support teamed items
[divverent/nexuiz.git] / data / qcsrc / server / t_items.qc
1 floatfield Item_CounterField(float it)
2 {
3         switch(it)
4         {
5                 case IT_SHELLS:      return ammo_shells;
6                 case IT_NAILS:       return ammo_nails;
7                 case IT_ROCKETS:     return ammo_rockets;
8                 case IT_CELLS:       return ammo_cells;
9                 case IT_5HP:         return health;
10                 case IT_25HP:        return health;
11                 case IT_HEALTH:      return health;
12                 case IT_ARMOR_SHARD: return armorvalue;
13                 case IT_ARMOR:       return armorvalue;
14                 // add more things here (health, armor)
15                 default:             error("requested item has no counter field");
16         }
17 }
18
19 string Item_CounterFieldName(float it)
20 {
21         switch(it)
22         {
23                 case IT_SHELLS:      return "shells";
24                 case IT_NAILS:       return "nails";
25                 case IT_ROCKETS:     return "rockets";
26                 case IT_CELLS:       return "cells";
27                 // add more things here (health, armor)
28                 default:             error("requested item has no counter field name");
29         }
30 }
31
32 void Item_SpawnByWeaponCode(float it)
33 {
34         weapon_action(it, WR_SPAWNFUNC);
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 float Item_GiveTo(entity item, entity player)
51 {
52         float _switchweapon;
53         float pickedup;
54         float it;
55         float i;
56         entity e;
57
58         // if nothing happens to player, just return without taking the item
59         pickedup = FALSE;
60         _switchweapon = FALSE;
61
62         if (g_minstagib)
63         {
64                 _switchweapon = TRUE;
65                 if (item.ammo_cells)
66                 {
67                         pickedup = TRUE;
68                         // play some cool sounds ;)
69                         centerprint(player, "\n");
70                         if(player.health <= 5)
71                                 announce(player, "announcer/robotic/lastsecond.ogg");
72                         else if(player.health < 50)
73                                 announce(player, "announcer/robotic/narrowly.ogg");
74                         // sound not available
75                         // else if(item.items == IT_CELLS)
76                         //      play2(player, "announce/robotic/ammo.ogg");
77
78                         if (item.weapons & WEPBIT_MINSTANEX)
79                                 W_GiveWeapon (player, WEP_MINSTANEX, "Nex");
80                         if (item.ammo_cells)
81                                 player.ammo_cells = min (player.ammo_cells + cvar("g_minstagib_ammo_drop"), 999);
82                         player.health = 100;
83                 }
84
85                 // extralife powerup
86                 if (item.max_health)
87                 {
88                         pickedup = TRUE;
89                         // sound not available
90                         // play2(player, "announce/robotic/extra.ogg\nplay2 announce/robotic/_lives.ogg");
91                         player.armorvalue = player.armorvalue + cvar("g_minstagib_extralives");
92                         sprint(player, "^3You picked up some extra lives\n");
93                 }
94
95                 // invis powerup
96                 if (item.strength_finished)
97                 {
98                         pickedup = TRUE;
99                         // sound not available
100                         // play2(player, "announce/robotic/invisible.ogg");
101                         player.strength_finished = max(player.strength_finished, time) + cvar("g_balance_powerup_strength_time");
102                 }
103
104                 // speed powerup
105                 if (item.invincible_finished)
106                 {
107                         pickedup = TRUE;
108                         // sound not available
109                         // play2(player, "announce/robotic/speed.ogg");
110                         player.invincible_finished = max(player.invincible_finished, time) + cvar("g_balance_powerup_strength_time");
111                 }
112         }
113         else
114         {
115                 if (cvar("deathmatch") == 2 || cvar("g_weapon_stay"))
116                 {
117                         if (item.flags & FL_WEAPON && player.weapons & item.weapons && item.classname != "droppedweapon")
118                                 return 0;
119                         if (player.weapons & item.weapons && item.flags & FL_TOSSED)    // don't let players stack ammo by tossing weapons
120                                 return 0;
121                 }
122
123                 // in case the player has autoswitch enabled do the following:
124                 // if the player is using their best weapon before items are given, they
125                 // probably want to switch to an even better weapon after items are given
126                 if (player.autoswitch)
127                 if (player.switchweapon == w_getbestweapon(player))
128                         _switchweapon = TRUE;
129
130                 if (item.ammo_shells)
131                 if (player.ammo_shells < g_pickup_shells_max)
132                 {
133                         pickedup = TRUE;
134                         player.ammo_shells = min (player.ammo_shells + item.ammo_shells, g_pickup_shells_max);
135                 }
136                 if (item.ammo_nails)
137                 if (player.ammo_nails < g_pickup_nails_max)
138                 {
139                         pickedup = TRUE;
140                         player.ammo_nails = min (player.ammo_nails + item.ammo_nails, g_pickup_nails_max);
141                 }
142                 if (item.ammo_rockets)
143                 if (player.ammo_rockets < g_pickup_rockets_max)
144                 {
145                         pickedup = TRUE;
146                         player.ammo_rockets = min (player.ammo_rockets + item.ammo_rockets, g_pickup_rockets_max);
147                 }
148                 if (item.ammo_cells)
149                 if (player.ammo_cells < g_pickup_cells_max)
150                 {
151                         pickedup = TRUE;
152                         player.ammo_cells = min (player.ammo_cells + item.ammo_cells, g_pickup_cells_max);
153                 }
154
155                 if (item.flags & FL_WEAPON)
156                 if ((it = item.weapons - (item.weapons & player.weapons)))
157                 {
158                         pickedup = TRUE;
159                         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
160                         {
161                                 e = get_weaponinfo(i);
162                                 if(it & e.weapons)
163                                         W_GiveWeapon (player, e.weapon, item.netname);
164                         }
165                 }
166
167                 if (item.strength_finished)
168                 {
169                         pickedup = TRUE;
170                         player.strength_finished = max(player.strength_finished, time) + cvar("g_balance_powerup_strength_time");
171                 }
172                 if (item.invincible_finished)
173                 {
174                         pickedup = TRUE;
175                         player.invincible_finished = max(player.invincible_finished, time) + cvar("g_balance_powerup_invincible_time");
176                 }
177                 //if (item.speed_finished)
178                 //{
179                 //      pickedup = TRUE;
180                 //      player.speed_finished = max(player.speed_finished, time) + cvar("g_balance_powerup_speed_time");
181                 //}
182                 //if (item.slowmo_finished)
183                 //{
184                 //      pickedup = TRUE;
185                 //      player.slowmo_finished = max(player.slowmo_finished, time) + (cvar("g_balance_powerup_slowmo_time") * cvar("g_balance_powerup_slowmo_speed"));
186                 //}
187
188                 if (item.health)
189                 if (player.health < item.max_health)
190                 {
191                         pickedup = TRUE;
192                         player.health = min(player.health + item.health, item.max_health);
193                         player.pauserothealth_finished = max(player.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
194                 }
195                 if (item.armorvalue)
196                 if (player.armorvalue < item.max_armorvalue)
197                 {
198                         pickedup = TRUE;
199                         player.armorvalue = min(player.armorvalue + item.armorvalue, item.max_armorvalue);
200                         player.pauserotarmor_finished = max(player.pauserotarmor_finished, time + cvar("g_balance_pause_armor_rot"));
201                 }
202         }
203
204         // always eat teamed entities
205         if(item.team)
206                 pickedup = TRUE;
207
208         if (!pickedup)
209                 return 0;
210
211         sound (player, CHAN_AUTO, item.item_pickupsound, VOL_BASE, ATTN_NORM);
212         if (_switchweapon)
213                 W_SwitchWeapon_Force(player, w_getbestweapon(item));
214
215         return 1;
216 }
217
218 void Item_Touch (void)
219 {
220         entity e, head;
221         float n;
222
223         // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
224         if (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
225         {
226                 remove(self);
227                 return;
228         }
229         if (other.classname != "player")
230                 return;
231         if (other.deadflag)
232                 return;
233         if (self.solid != SOLID_TRIGGER)
234                 return;
235         if (self.owner == other)
236                 return;
237
238         if(!Item_GiveTo(self, other))
239                 return;
240
241         if (self.classname == "droppedweapon")
242                 remove (self);
243         else if((self.flags & FL_WEAPON) && !self.team && (cvar("deathmatch") == 2 || cvar("g_weapon_stay")))
244                 return;
245         else
246         {
247                 self.solid = SOLID_NOT;
248                 self.model = string_null;
249                 if(self.team)
250                 {
251                         RandomSelection_Init();
252                         for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
253                                 RandomSelection_Add(head, 0, 1, 0);
254                         e = RandomSelection_chosen_ent;
255                 }
256                 else
257                         e = self;
258                 e.nextthink = time + self.respawntime;
259                 e.think = Item_Respawn;
260         }
261 }
262
263 void Item_FindTeam()
264 {
265         entity head, e;
266
267         if(self.effects & EF_NODRAW)
268         {
269                 // marker for item team search
270                 dprint("Initializing item team ", ftos(self.team), "\n");
271                 RandomSelection_Init();
272                 for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
273                         RandomSelection_Add(head, 0, 1, 0);
274                 e = RandomSelection_chosen_ent;
275
276                 for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
277                 {
278                         if(head != e)
279                         {
280                                 // make it a non-spawned item
281                                 head.solid = SOLID_NOT;
282                                 head.model = string_null;
283                         }
284                         head.effects = head.effects - (head.effects & EF_NODRAW);
285                 }
286         }
287 }
288
289 // Savage: used for item garbage-collection
290 // TODO: perhaps nice special effect?
291 void RemoveItem(void) /*FIXDECL*/
292 {
293         remove(self);
294 }
295
296 // pickup evaluation functions
297 // these functions decide how desirable an item is to the bots
298
299 float generic_pickupevalfunc(entity player, entity item) {return item.bot_pickupbasevalue;};
300
301 float weapon_pickupevalfunc(entity player, entity item)
302 {
303         // if we already have the weapon, rate it 1/5th normal value
304         if ((player.weapons & item.weapons) == item.weapons)
305                 return item.bot_pickupbasevalue * 0.2;
306         return item.bot_pickupbasevalue;
307 };
308
309 float commodity_pickupevalfunc(entity player, entity item)
310 {
311         float c;
312         c = 0;
313         // TODO: figure out if the player even has the weapon this ammo is for?
314         // may not affect strategy much though...
315         // find out how much more ammo/armor/health the player can hold
316         if (item.ammo_shells)
317         if (player.ammo_shells < g_pickup_shells_max)
318                 c = c + max(0, 1 - player.ammo_shells / g_pickup_shells_max);
319         if (item.ammo_nails)
320         if (player.ammo_nails < g_pickup_nails_max)
321                 c = c + max(0, 1 - player.ammo_nails / g_pickup_nails_max);
322         if (item.ammo_rockets)
323         if (player.ammo_rockets < g_pickup_rockets_max)
324                 c = c + max(0, 1 - player.ammo_rockets / g_pickup_rockets_max);
325         if (item.ammo_cells)
326         if (player.ammo_cells < g_pickup_cells_max)
327                 c = c + max(0, 1 - player.ammo_cells / g_pickup_cells_max);
328         if (item.armorvalue)
329         if (player.armorvalue < item.max_armorvalue)
330                 c = c + max(0, 1 - player.armorvalue / item.max_armorvalue);
331         if (item.health)
332         if (player.health < item.max_health)
333                 c = c + max(0, 1 - player.health / item.max_health);
334
335         return item.bot_pickupbasevalue * c;
336 };
337
338
339 .float is_item;
340 void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
341 {
342         startitem_failed = FALSE;
343
344         // is it a dropped weapon?
345         if (self.classname == "droppedweapon")
346         {
347                 // it's a dropped weapon
348                 self.movetype = MOVETYPE_TOSS;
349                 self.solid = SOLID_TRIGGER;
350                 // Savage: remove thrown items after a certain period of time ("garbage collection")
351                 self.think = RemoveItem;
352                 self.nextthink = time + 60;
353                 // don't drop if in a NODROP zone (such as lava)
354                 traceline(self.origin, self.origin, MOVE_NORMAL, self);
355                 if (trace_dpstartcontents & DPCONTENTS_NODROP)
356                 {
357                         startitem_failed = TRUE;
358                         remove(self);
359                         return;
360                 }
361         }
362         else
363         {
364                 // it's a level item
365                 if(self.spawnflags & 1)
366                         self.noalign = 1;
367                 if (self.noalign)
368                         self.movetype = MOVETYPE_NONE;
369                 else
370                         self.movetype = MOVETYPE_TOSS;
371                 self.solid = SOLID_TRIGGER;
372                 // do item filtering according to game mode and other things
373                 if (!self.noalign)
374                 {
375                         // first nudge it off the floor a little bit to avoid math errors
376                         setorigin(self, self.origin + '0 0 1');
377                         // set item size before we spawn a spawnfunc_waypoint
378                         if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
379                                 setsize (self, '-16 -16 0', '16 16 48');
380                         else
381                                 setsize (self, '-16 -16 0', '16 16 32');
382                         // note droptofloor returns FALSE if stuck/or would fall too far
383                         droptofloor();
384                         waypoint_spawnforitem(self);
385                 }
386
387                 if(teams_matter)
388                 {
389                         if(self.notteam)
390                         {
391                                 print("removed non-teamplay ", self.classname, "\n");
392                                 startitem_failed = TRUE;
393                                 remove (self);
394                                 return;
395                         }
396                 }
397                 else
398                 {
399                         if(self.notfree)
400                         {
401                                 print("removed non-FFA ", self.classname, "\n");
402                                 startitem_failed = TRUE;
403                                 remove (self);
404                                 return;
405                         }
406                 }
407
408                 if(self.notq3a)
409                 {
410                         // We aren't TA or something like that, so we keep the Q3A entities
411                         print("removed non-Q3A ", self.classname, "\n");
412                         startitem_failed = TRUE;
413                         remove (self);
414                         return;
415                 }
416
417                 /*
418                  * can't do it that way, as it would break maps
419                  * TODO make a target_give like entity another way, that perhaps has
420                  * the weapon name in a key
421                 if(self.targetname)
422                 {
423                         // target_give not yet supported; maybe later
424                         print("removed targeted ", self.classname, "\n");
425                         startitem_failed = TRUE;
426                         remove (self);
427                         return;
428                 }
429                 */
430
431                 if(cvar("spawn_debug") >= 2)
432                 {
433                         entity otheritem;
434                         for(otheritem = findradius(self.origin, 3); otheritem; otheritem = otheritem.chain)
435                         {
436                                 if(otheritem.is_item)
437                                 {
438                                         dprint("XXX Found duplicated item: ", itemname, vtos(self.origin));
439                                         dprint(" vs ", otheritem.netname, vtos(otheritem.origin), "\n");
440                                         error("Mapper sucks.");
441                                 }
442                         }
443                         self.is_item = TRUE;
444                 }
445
446                 weaponsInMap |= weaponid;
447
448                 if(g_lms || g_rocketarena)
449                 {
450                         startitem_failed = TRUE;
451                         remove(self);
452                         return;
453                 }
454                 else if (g_minstagib)
455                 {
456                         // don't remove dropped items and powerups
457                         if (self.classname != "minstagib")
458                         {
459                                 startitem_failed = TRUE;
460                                 remove (self);
461                                 return;
462                         }
463                 }
464                 else if ((!cvar("g_pickup_items") || g_nixnex) && itemid != IT_STRENGTH && itemid != IT_INVINCIBLE && itemid != IT_HEALTH)
465                 {
466                         startitem_failed = TRUE;
467                         remove (self);
468                         return;
469                 }
470
471                 precache_model (itemmodel);
472                 precache_sound (pickupsound);
473                 precache_sound ("misc/itemrespawn.wav");
474
475                 if((itemid & (IT_STRENGTH | IT_INVINCIBLE | IT_HEALTH | IT_ARMOR | IT_KEY1 | IT_KEY2)) || (weaponid & WEPBIT_ALL))
476                         self.target = "###item###"; // for finding the nearest item using find()
477         }
478
479         self.bot_pickup = TRUE;
480         self.bot_pickupevalfunc = pickupevalfunc;
481         self.bot_pickupbasevalue = pickupbasevalue;
482         self.mdl = itemmodel;
483         self.item_pickupsound = pickupsound;
484         // let mappers override respawntime
485         if (!self.respawntime)
486                 self.respawntime = defaultrespawntime;
487         self.netname = itemname;
488         self.items = itemid;
489         self.weapons = weaponid;
490         self.flags = FL_ITEM | itemflags;
491         self.touch = Item_Touch;
492         setmodel (self, self.mdl); // precision set below
493         self.effects |= EF_LOWPRECISION;
494         if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
495                 setsize (self, '-16 -16 0', '16 16 48');
496         else
497                 setsize (self, '-16 -16 0', '16 16 32');
498         if (itemflags & FL_WEAPON)
499         {
500                 // neutral team color for pickup weapons
501                 self.colormap = 160 * 1024 + 160;
502         }
503
504         if (cvar("g_fullbrightitems"))
505                 self.effects = self.effects | EF_FULLBRIGHT;
506         
507         if(self.team)
508         {
509                 self.effects = self.effects | EF_NODRAW; // marker for item team search
510                 InitializeEntity(self, Item_FindTeam, INITPRIO_FINDTARGET);
511         }
512 }
513
514 /* replace items in minstagib
515  * IT_STRENGTH   = invisibility
516  * IT_NAILS      = extra lives
517  * IT_INVINCIBLE = speed
518  */
519 void minstagib_items (float itemid)
520 {
521         // we don't want to replace dropped weapons ;)
522         if (self.classname == "droppedweapon")
523         {
524                 self.ammo_cells = 25;
525                 StartItem ("models/weapons/g_nex.md3",
526                         "weapons/weaponpickup.wav", 15,
527                         "MinstaNex", 0, WEPBIT_MINSTANEX, FL_WEAPON, generic_pickupevalfunc, 1000);
528                 return;
529         }
530
531         local float rnd;
532         self.classname = "minstagib";
533
534         // replace rocket launchers and nex guns with ammo cells
535         if (itemid == IT_CELLS)
536         {
537                 self.ammo_cells = 1;
538                 StartItem ("models/items/a_cells.md3",
539                         "misc/itempickup.wav", 45,
540                         "Nex Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100);
541                 return;
542         }
543
544         // randomize
545         rnd = random() * 3;
546         if (rnd <= 1)
547                 itemid = IT_STRENGTH;
548         else if (rnd <= 2)
549                 itemid = IT_NAILS;
550         else
551                 itemid = IT_INVINCIBLE;
552
553         // replace with invis
554         if (itemid == IT_STRENGTH)
555         {
556                 self.effects = EF_ADDITIVE;
557                 self.strength_finished = 30;
558                 StartItem ("models/items/g_strength.md3",
559                         "misc/powerup.wav", 120,
560                         "Invisibility", IT_STRENGTH, 0, FL_POWERUP, generic_pickupevalfunc, 1000);
561         }
562         // replace with extra lives
563         if (itemid == IT_NAILS)
564         {
565                 self.max_health = 1;
566                 StartItem ("models/items/g_h100.md3",
567                         "misc/megahealth.wav", 120,
568                         "Extralife", IT_NAILS, 0, FL_POWERUP, generic_pickupevalfunc, 1000);
569
570         }
571         // replace with speed
572         if (itemid == IT_INVINCIBLE)
573         {
574                 self.effects = EF_ADDITIVE;
575                 self.invincible_finished = 30;
576                 StartItem ("models/items/g_invincible.md3",
577                         "misc/powerup_shield.wav", 120,
578                         "Speed", IT_INVINCIBLE, 0, FL_POWERUP, generic_pickupevalfunc, 1000);
579         }
580
581 }
582
583 float minst_no_auto_cells;
584 void minst_remove_item (void) {
585         if(minst_no_auto_cells)
586                 remove(self);
587 }
588
589 float weaponswapping;
590
591 void weapon_defaultspawnfunc(float wpn, float weight)
592 {
593         entity e;
594         var .float ammofield;
595         e = get_weaponinfo(wpn);
596
597         if(e.items && e.items != IT_SUPERWEAPON)
598         {
599                 ammofield = Item_CounterField(e.items);
600                 if(!self.ammofield)
601                         self.ammofield = cvar(strcat("g_pickup_", Item_CounterFieldName(e.items)));
602         }
603
604         StartItem (e.model, "weapons/weaponpickup.wav", 15, e.message, 0, e.weapons, FL_WEAPON, weapon_pickupevalfunc, weight);
605         if (self.modelindex) // don't precache if self was removed
606                 weapon_action(e.weapon, WR_PRECACHE);
607 }
608
609 void spawnfunc_weapon_shotgun (void);
610 void spawnfunc_weapon_uzi (void) {
611         if(!weaponswapping)
612         if(q3acompat_machineshotgunswap)
613         if(self.classname != "droppedweapon")
614         {
615                 weapon_defaultspawnfunc(WEP_SHOTGUN, 2500);
616                 return;
617         }
618         weapon_defaultspawnfunc(WEP_UZI, 5000);
619 }
620
621 void spawnfunc_weapon_shotgun (void) {
622         if(!weaponswapping)
623         if(q3acompat_machineshotgunswap)
624         if(self.classname != "droppedweapon")
625         {
626                 weapon_defaultspawnfunc(WEP_UZI, 5000);
627                 return;
628         }
629         weapon_defaultspawnfunc(WEP_SHOTGUN, 2500);
630 }
631
632 void spawnfunc_weapon_nex (void)
633 {
634         if (g_minstagib)
635         {
636                 minstagib_items(IT_CELLS);
637                 self.think = minst_remove_item;
638                 self.nextthink = time + cvar("sys_ticrate");
639                 return;
640         }
641         weapon_defaultspawnfunc(WEP_NEX, 10000);
642 }
643
644 void spawnfunc_weapon_minstanex (void)
645 {
646         if (g_minstagib)
647         {
648                 minstagib_items(IT_CELLS);
649                 self.think = minst_remove_item;
650                 self.nextthink = time + cvar("sys_ticrate");
651                 return;
652         }
653         weapon_defaultspawnfunc(WEP_MINSTANEX, 10000);
654 }
655
656 void spawnfunc_weapon_rocketlauncher (void)
657 {
658         if (g_minstagib)
659         {
660                 minstagib_items(IT_CELLS);
661                 self.think = minst_remove_item;
662                 self.nextthink = time + cvar("sys_ticrate");
663                 return;
664         }
665         weapon_defaultspawnfunc(WEP_ROCKET_LAUNCHER, 10000);
666 }
667
668 void spawnfunc_item_rockets (void) {
669         if(!self.ammo_rockets)
670                 self.ammo_rockets = g_pickup_rockets;
671         StartItem ("models/items/a_rockets.md3", "misc/itempickup.wav", 15, "rockets", IT_ROCKETS, 0, 0, commodity_pickupevalfunc, 3000);
672 }
673
674 void spawnfunc_item_shells (void);
675 void spawnfunc_item_bullets (void) {
676         if(!weaponswapping)
677         if(q3acompat_machineshotgunswap)
678         if(self.classname != "droppedweapon")
679         {
680                 weaponswapping = TRUE;
681                 spawnfunc_item_shells();
682                 weaponswapping = FALSE;
683                 return;
684         }
685
686         if(!self.ammo_nails)
687                 self.ammo_nails = g_pickup_nails;
688         StartItem ("models/items/a_bullets.mdl", "misc/itempickup.wav", 15, "bullets", IT_NAILS, 0, 0, commodity_pickupevalfunc, 2000);
689 }
690
691 void spawnfunc_item_cells (void) {
692         if(!self.ammo_cells)
693                 self.ammo_cells = g_pickup_cells;
694         StartItem ("models/items/a_cells.md3", "misc/itempickup.wav", 15, "cells", IT_CELLS, 0, 0, commodity_pickupevalfunc, 2000);
695 }
696
697 void spawnfunc_item_shells (void) {
698         if(!weaponswapping)
699         if(q3acompat_machineshotgunswap)
700         if(self.classname != "droppedweapon")
701         {
702                 weaponswapping = TRUE;
703                 spawnfunc_item_bullets();
704                 weaponswapping = FALSE;
705                 return;
706         }
707
708         if(!self.ammo_shells)
709                 self.ammo_shells = g_pickup_shells;
710         StartItem ("models/items/a_shells.md3", "misc/itempickup.wav", 15, "shells", IT_SHELLS, 0, 0, commodity_pickupevalfunc, 500);
711 }
712
713 void spawnfunc_item_armor_small (void) {
714         if(!self.armorvalue)
715                 self.armorvalue = g_pickup_armorsmall;
716         if(!self.max_armorvalue)
717                 self.max_armorvalue = g_pickup_armorsmall_max;
718         StartItem ("models/items/g_a1.md3", "misc/armor1.wav", 15, "5 Armor", IT_ARMOR_SHARD, 0, 0, commodity_pickupevalfunc, 1000);
719 }
720
721 void spawnfunc_item_armor_medium (void) {
722         if(!self.armorvalue)
723                 self.armorvalue = g_pickup_armormedium;
724         if(!self.max_armorvalue)
725                 self.max_armorvalue = g_pickup_armormedium_max;
726         StartItem ("models/items/g_armormedium.md3", "misc/armor1.wav", 20, "25 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, 20000);
727 }
728
729 void spawnfunc_item_armor_large (void) {
730         if(!self.armorvalue)
731                 self.armorvalue = g_pickup_armorlarge;
732         if(!self.max_armorvalue)
733                 self.max_armorvalue = g_pickup_armorlarge_max;
734         StartItem ("models/items/g_a25.md3", "misc/armor25.wav", 30, "100 Armor", IT_ARMOR, 0, 0, commodity_pickupevalfunc, 20000);
735 }
736
737 void spawnfunc_item_health_small (void) {
738         if(!self.max_health)
739                 self.max_health = g_pickup_healthsmall_max;
740         if(!self.health)
741                 self.health = g_pickup_healthsmall;
742         StartItem ("models/items/g_h1.md3", "misc/minihealth.wav", 15, "5 Health", IT_5HP, 0, 0, commodity_pickupevalfunc, 20000);
743 }
744
745 void spawnfunc_item_health_medium (void) {
746         if(!self.max_health)
747                 self.max_health = g_pickup_healthmedium_max;
748         if(!self.health)
749                 self.health = g_pickup_healthmedium;
750         StartItem ("models/items/g_h25.md3", "misc/mediumhealth.wav", 15, "25 Health", IT_25HP, 0, 0, commodity_pickupevalfunc, 20000);
751 }
752
753 void spawnfunc_item_health_large (void) {
754         if(!self.max_health)
755                 self.max_health = g_pickup_healthlarge_max;
756         if(!self.health)
757                 self.health = g_pickup_healthlarge;
758         StartItem ("models/items/g_h50.md3", "misc/mediumhealth.wav", 20, "50 Health", IT_25HP, 0, 0, commodity_pickupevalfunc, 20000);
759 }
760
761 void spawnfunc_item_health_mega (void) {
762         if(!cvar("g_powerup_superhealth"))
763                 return;
764
765         if(g_arena && !cvar("g_arena_powerups"))
766                 return;
767
768         if(g_minstagib) {
769                 minstagib_items(IT_NAILS);
770         } else {
771                 if(!self.max_health)
772                         self.max_health = g_pickup_healthmega_max;
773                 if(!self.health)
774                         self.health = g_pickup_healthmega;
775                 StartItem ("models/items/g_h100.md3", "misc/megahealth.wav", 30, "100 Health", IT_HEALTH, 0, 0, commodity_pickupevalfunc, 20000);
776         }
777 }
778
779 // support old misnamed entities
780 void spawnfunc_item_armor1() { spawnfunc_item_armor_small(); }  // FIXME: in Quake this is green armor, in Nexuiz maps it is an armor shard
781 void spawnfunc_item_armor25() { spawnfunc_item_armor_large(); }
782 void spawnfunc_item_health1() { spawnfunc_item_health_small(); }
783 void spawnfunc_item_health25() { spawnfunc_item_health_medium(); }
784 void spawnfunc_item_health100() { spawnfunc_item_health_mega(); }
785
786 void spawnfunc_item_strength (void) {
787         if(!cvar("g_powerup_strength"))
788                 return;
789
790         if(g_arena && !cvar("g_arena_powerups"))
791                 return;
792
793         if(g_minstagib) {
794                 minstagib_items(IT_STRENGTH);
795         } else {
796                 precache_sound("weapons/strength_fire.wav");
797                 self.strength_finished = 30;
798                 self.effects = EF_ADDITIVE;
799                 StartItem ("models/items/g_strength.md3", "misc/powerup.wav", 120, "Strength Powerup", IT_STRENGTH, 0, FL_POWERUP, generic_pickupevalfunc, 100000);
800         }
801 }
802
803 void spawnfunc_item_invincible (void) {
804         if(!cvar("g_powerup_shield"))
805                 return;
806
807         if(g_arena && !cvar("g_arena_powerups"))
808                 return;
809
810         if(g_minstagib) {
811                 minstagib_items(IT_INVINCIBLE);
812         } else {
813                 self.invincible_finished = 30;
814                 self.effects = EF_ADDITIVE;
815                 StartItem ("models/items/g_invincible.md3", "misc/powerup_shield.wav", 120, "Invulnerability", IT_INVINCIBLE, 0, FL_POWERUP, generic_pickupevalfunc, 100000);
816         }
817 }
818 //void item_speed (void) {self.speed_finished = 30;StartItem ("models/items/g_speed.md3", "misc/powerup.wav", 120, "Speed Powerup", IT_SPEED, FL_POWERUP, generic_pickupevalfunc, 10000);}
819 //void item_slowmo (void) {self.slowmo_finished = 30;StartItem ("models/items/g_slowmo.md3", "misc/powerup.wav", 120, "Slow Motion", IT_SLOWMO, FL_POWERUP, generic_pickupevalfunc, 10000);}
820
821 void spawnfunc_item_minst_cells (void) {
822         if (g_minstagib)
823         {
824                 minst_no_auto_cells = 1;
825                 minstagib_items(IT_CELLS);
826         }
827         else
828                 remove(self);
829 }
830
831 // compatibility:
832 void spawnfunc_item_quad (void) {self.classname = "item_strength";spawnfunc_item_strength();}
833
834 void spawnfunc_misc_models (void)
835 {
836         SetBrushEntityModel();
837 }
838
839 void func_wall_use (void)
840 {
841         if(teams_matter)
842         {
843                 if(activator.team)
844                         self.colormap = (activator.team - 1) * 0x11;
845                 else
846                         self.colormap = 0x00;
847         }
848         else
849                 self.colormap = ceil(random() * 256) - 1;
850         self.colormap |= 1024; // RENDER_COLORMAPPED
851 }
852
853 void spawnfunc_func_wall (void)
854 {
855         SetBrushEntityModel();
856         self.solid = SOLID_BSP;
857         self.use = func_wall_use;
858 }
859