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