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