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