1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: b_look.c 1564 2020-12-19 06:21:07Z wesleyjohnson $
5 //
6 // Copyright (C) 2002-2016 by DooM Legacy Team.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
17 //
18 //
19 // $Log: b_look.c,v $
20 // Revision 1.5  2003/06/11 04:20:45  ssntails
21 // Keep stupid bots from trying to get to items on 3d Floors.
22 //
23 // Revision 1.4  2003/06/11 04:04:50  ssntails
24 // Rellik's Bot Code!
25 //
26 // Revision 1.3  2002/09/28 06:53:11  tonyd
27 // fixed CR problem, fixed game options crash
28 //
29 // Revision 1.2  2002/09/27 16:40:08  tonyd
30 // First commit of acbot
31 //
32 //-----------------------------------------------------------------------------
33 
34 #include "b_bot.h"
35 #include "b_game.h"
36 #include "b_look.h"
37 //#include "bot_move.h"
38 #include "b_node.h"
39 //#include "bot_ctf.h"
40 
41 #include "g_game.h"
42 #include "r_defs.h"
43 #include "p_local.h"
44 #include "m_random.h"
45 #include "r_main.h"
46 #include "z_zone.h"
47 
48 #define MAX_TRAVERSE_DIST 100000000 //10 meters, used within b_func.c
49 
50 extern int maxsoul;
51 extern int max_armor;
52 extern thinker_t thinkercap;
53 
54 //Used with Reachable().
55 static mobj_t	*bot_looker_mobj, *bot_dest_mobj;
56 static sector_t *bot_last_sector;
57 
PTR_QuickReachable(intercept_t * in)58 static boolean PTR_QuickReachable (intercept_t *in)
59 {
60     fixed_t floorheight, ceilingheight;
61     line_t *line;
62     mobj_t* thing;
63     sector_t *s;
64 
65     if (in->isaline)
66     {
67         line = in->d.line;
68 
69         if (!(line->flags & ML_TWOSIDED) || (line->flags & ML_BLOCKING))
70             return false; //Cannot continue.
71 
72         //Determine if going to use backsector/frontsector.
73         s = (line->backsector == bot_last_sector) ? line->frontsector : line->backsector;
74         ceilingheight = s->ceilingheight;
75         floorheight = s->floorheight;
76 
77         if( (((floorheight <= (bot_last_sector->floorheight+(37<<FRACBITS)))
78               || (((floorheight <= (bot_last_sector->floorheight+(45<<FRACBITS)))
79                    && (bot_last_sector->floortype != FLOOR_WATER))))
80              && (((ceilingheight == floorheight) && line->special)
81                  || ((ceilingheight - floorheight) >= bot_looker_mobj->height)))) //Does it fit?
82         {
83             bot_last_sector = s;
84             return true;
85         }
86         return false;
87     }
88     else
89     {
90         thing = in->d.thing;
91         // Care about solid things that can block our path.
92         // Cannot jump over a solid corpse yet.
93         if( (thing != bot_looker_mobj) && (thing != bot_dest_mobj) && (thing->flags & MF_SOLID) )
94              return false;
95     }
96 
97     return true;
98 }
99 
B_Reachable(player_t * p,mobj_t * mo)100 boolean B_Reachable(player_t* p, mobj_t* mo)
101 {
102     bot_looker_mobj = p->mo;
103     bot_dest_mobj = mo;
104     bot_last_sector = p->mo->subsector->sector;
105 
106     // Bots shouldn't try to get stuff that's on a 3dfloor they can't get to. SSNTails 06-10-2003
107     if(p->mo->subsector == mo->subsector && p->mo->subsector->sector->ffloors)
108     {
109       ffloor_t*  rover;
110 
111       for(rover = mo->subsector->sector->ffloors; rover; rover = rover->next)
112       {
113         if(!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue;
114 
115         if( *rover->topheight <= p->mo->z && mo->z < *rover->topheight )
116             return false;
117 
118         if( *rover->bottomheight >= p->mo->z + p->mo->height
119             && mo->z > *rover->bottomheight)
120             return false;
121       }
122     }
123 
124     return P_PathTraverse (p->mo->x, p->mo->y, mo->x, mo->y,
125                            PT_ADDLINES|PT_ADDTHINGS, PTR_QuickReachable);
126 }
127 
128 //Checks TRUE reachability from
129 //one actor to another. First mobj (actor) is bot_looker_mobj.
B_ReachablePoint(player_t * p,sector_t * destSector,fixed_t x,fixed_t y)130 boolean B_ReachablePoint (player_t *p, sector_t* destSector,
131                           fixed_t x, fixed_t y)
132 {
133 /*  if((destSector->ceilingheight - destSector->floorheight)
134         < p->mo->height) //Where target is, bot_looker_mobj can't be.
135         return false;
136  */
137 
138     //if (p->mo->subsector->sector == destSector)
139     //	return true;
140 
141     bot_looker_mobj = p->mo;
142     bot_dest_mobj = NULL;
143     bot_last_sector = p->mo->subsector->sector;
144 
145     return P_PathTraverse (p->mo->x, p->mo->y, x, y,
146                            PT_ADDLINES|PT_ADDTHINGS, PTR_QuickReachable);
147 }
148 
149 
150 //
151 // B_LookForSpecialLine
152 //
153 // This function looks through the sector the bot is in,
154 // and one sector level outwards.
155 // Very inefficient cause searches of sectors are done multiple times.
156 // when a sector has many linedefs between a single sector-sector boundary
157 // Must fix this, perhaps use the visited boolean.
158 // Maybe should do search through the switches array instead.
159 //
160 
161 static
B_Is_Usable_Special_Line(short line_special)162 boolean B_Is_Usable_Special_Line( short line_special )
163 {
164   return
165     //edge->special && !(edge->special & ML_REPEAT_SPECIAL)
166     //P_CheckTag(edge) && (!specialsector || !specialsector->ceilingdata))
167     //!(line->flags & ML_TWOSIDED) || (line->flags & ML_BLOCKING)
168     //((edge->special & TriggerType) >> TriggerTypeShift) == SwitchOnce)
169     //|| ((edge->special & TriggerType) >> TriggerTypeShift) == PushOnce)
170     (line_special == 31)   // Door
171     // || (edge->special == 1)
172     || (line_special == 23)    // SW: Lower floor to lowest
173     || (line_special == 102)   // SW: Lower floor to surrounding floor height
174     || (line_special == 103)   // SW: Open Door
175     || (line_special == 71) ;  // SW: Turbo lower floor
176 }
177 
B_LookForSpecialLine(player_t * p,fixed_t * x,fixed_t * y)178 boolean B_LookForSpecialLine(player_t* p, fixed_t* x, fixed_t* y)
179 {
180     int  i, j;
181     sector_t  *in_sector, *sector2;
182     line_t  *edge;
183     msecnode_t  *in_sector_node;
184 
185     in_sector_node = p->mo->touching_sectorlist;
186     while (in_sector_node)
187     {
188         in_sector = in_sector_node->m_sector;
189         for (i = 0; i < in_sector->linecount; i++)
190         {
191             // for all lines in sector linelist
192             edge = in_sector->linelist[i];
193             // sector_t * specialsector = (in_sector == edge->frontsector) ? edge->backsector : edge->frontsector;
194             if( B_Is_Usable_Special_Line( edge->special ) )  goto ret_edge_center;
195 
196             if (edge->sidenum[1] != NULL_INDEX)
197             {
198                 // its a double sided linedef
199                 sector2 = (edge->frontsector == in_sector) ? edge->backsector : edge->frontsector;
200 
201                 for (j = 0; j < sector2->linecount; j++)
202                 {
203                     // for all lines in sector linelist
204                     edge = sector2->linelist[j];
205                     // sector_t * specialsector = (sector == edge->frontsector) ? edge->backsector : edge->frontsector;
206                     if( B_Is_Usable_Special_Line( edge->special ) )  goto ret_edge_center;
207                 }
208             }
209         }
210         in_sector_node = in_sector_node->m_snext;
211     }
212 
213     return false;
214 
215 ret_edge_center:
216     *x = (edge->v1->x + edge->v2->x)/2;
217     *y = (edge->v1->y + edge->v2->y)/2;
218     return true;
219 }
220 
221 // id : any identifier
222 // on_time, period_time : tics
223 // Return periodic value, 0..255
regulate(mobj_t * mo,int id,int on_time,int period_time)224 byte regulate( mobj_t * mo, int id, int on_time, int period_time )
225 {
226     // Periodic, individualized for each id and mo.
227     int mobjid = (intptr_t) mo;
228     int pr = (gametic + id + (mobjid>>1)) % period_time;  // periodic ramp
229 
230     if( mo->health < 5 ) // more desperate
231     {
232         pr -= TICRATE;  // add a second
233     }
234 
235     if( pr < on_time )  return 255;
236     return 0;
237 }
238 
239 typedef enum {
240   WB_SHOT = 0x01,  // shotgun
241   WB_SSG  = 0x02,  // supershotgun
242   WB_CHAIN = 0x04, // chaingun
243   WB_ROCKET = 0x08, // rocket launcher
244   WB_PLASMA = 0x10, // plasma
245   WB_BFG = 0x20    // BFG
246 } weapon_bits_e;
247 
248 //
249 // B_LookForThings
250 //
B_LookForThings(player_t * p)251 void B_LookForThings (player_t* p)
252 {
253     fixed_t  bestItemDistance = 0;
254     fixed_t  bestSeenItemDistance = 0;
255     fixed_t  closestEnemyDistance = 0;
256     fixed_t  closestMissileDistance = 0;
257     fixed_t  closestUnseenEnemyDistance = 0;
258     fixed_t  closestUnseenTeammateDistance = 0;
259     fixed_t  furthestTeammateDistance = 0;
260     fixed_t  thingDistance = 0;
261 
262     // ItemWeight is usefulness of item, byte 0..10
263     byte  bestItemWeight = 0; //used to determine best object to get
264     byte  bestSeenItemWeight = 0;
265     byte  itemWeight = 0;
266     int   enemy_weight = 0;
267 
268     mobj_t   *bestSeenItem = NULL;
269     mobj_t   *bestItem = NULL;
270     mobj_t   *mo;
271     bot_t  * pbot = p->bot;  // player bot
272     thinker_t*	 currentthinker;
273 
274     byte item_respawn = cv_itemrespawn.EV || (deathmatch == 2);  // DM_items
275     byte item_getable;
276 
277     byte weapon = 0;
278     byte ammo = 0;
279     byte out_of_ammo = 0;
280 
281     int health_index =   // 0..5
282      (p->health < 40) ? 0:
283      (p->health < 50) ? 1:
284      (p->health < 60) ? 2:
285      (p->health < 80) ? 3:
286      (p->health < 100) ? 4:  5;
287 
288     pbot->closestEnemy = NULL;
289     pbot->closestMissile = NULL;
290     pbot->closestUnseenEnemy = NULL;
291     pbot->closestUnseenTeammate = NULL;
292     pbot->teammate = NULL;
293     pbot->bestSeenItem = NULL;
294     pbot->bestItem = NULL;
295 
296     // For simpler tests
297     if( p->weaponowned[wp_shotgun] )  weapon |= WB_SHOT;
298     if( p->weaponowned[wp_supershotgun] )  weapon |= WB_SSG;
299     if( p->weaponowned[wp_chaingun] )  weapon |= WB_CHAIN;
300     if( p->weaponowned[wp_missile] )  weapon |= WB_ROCKET;
301     if( p->weaponowned[wp_plasma] )  weapon |= WB_PLASMA;
302     if( p->weaponowned[wp_bfg] )  weapon |= WB_BFG;
303     if( p->ammo[am_shell] >= 2 )  ammo |= WB_SHOT | WB_SSG;
304     if( p->ammo[am_clip] >= 4 )  ammo |= WB_CHAIN;
305     if( p->ammo[am_misl] )  ammo |= WB_ROCKET;
306     if( p->ammo[am_cell] >= 4 )  ammo |= WB_PLASMA | WB_BFG;
307     if((p->readyweapon == wp_fist) || (p->readyweapon == wp_chainsaw))  out_of_ammo = 1;
308 
309     //search through the list of all thinkers
310     for( currentthinker = thinkercap.next; currentthinker != &thinkercap; currentthinker = currentthinker->next )
311     {
312         if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)
313         {
314             enemy_weight = 0;
315             itemWeight = 0;  // initialize to no weight, best items have greatest weight
316             mo = (mobj_t *)currentthinker;
317             thingDistance = P_AproxDistance (p->mo->x - mo->x, p->mo->y - mo->y);
318 
319 
320             if((mo->flags & MF_COUNTKILL) || (mo->type == MT_SKULL) )
321             {
322                  // Corpse may be solid, so check health.
323                  if( mo->health <= 0 )  continue;
324                  enemy_weight = mo->health | 128;  // estimate of importance
325             }
326             else if( (mo->type == MT_BARREL) || (mo->type == MT_POD) || (mo->flags & MF_TOUCHY) )
327             {
328                 // lessen bot fixation with shooting barrels
329                 if((thingDistance > (80*FRACUNIT)) && !out_of_ammo)
330                 {
331                     if( regulate(p->mo, MT_BARREL, 4*TICRATE, 15*TICRATE ) )  // 0..255
332                         enemy_weight = 64;  // fire 1/4 of time
333                 }
334             }
335             else if (mo->player)
336             {
337                 if( p != mo->player)
338                 {
339                     if( mo->health <= 0 )  continue;
340 
341                     if( deathmatch )
342                     {
343                         enemy_weight = 250;
344                     }
345                     else
346                     {
347                         if (B_Reachable(p, mo))	//i can reach this teammate
348                         {
349                             if ((thingDistance > furthestTeammateDistance)
350                                 && (!pbot->teammate && !mo->player->bot))
351                             {
352                                 furthestTeammateDistance = thingDistance;
353                                 pbot->teammate = mo;
354                                 //debug_Printf("found a teammate\n");
355                             }
356                         }
357                         else //i can not reach this teammate
358                         {
359                             SearchNode_t* tempNode = B_GetNodeAt(mo->x, mo->y);
360                             if (tempNode
361                                 && (!closestUnseenTeammateDistance
362                                     || ((thingDistance < closestUnseenTeammateDistance)
363                                         && (!pbot->teammate
364                                             || (!mo->player->bot
365                                                 && pbot->teammate->player->bot))))
366                                 )
367                             {
368                                 closestUnseenTeammateDistance = thingDistance;
369                                 pbot->closestUnseenTeammate = mo;
370                                 //debug_Printf("found a teammate\n");
371                             }
372                         }
373                     }
374                 }
375             }
376             else if (mo->flags & MF_MISSILE)	//is it a threatening missile
377             {
378                 if (mo->target != p->mo) //if its an enemies missile I had better avoid it
379                 {  // important: a missiles "target" is actually its owner...
380                    // see if the missile is heading my way, if the missile will be closer to me, next tick
381                    // then its heading at least somewhat towards me, so better dodge it
382                     if (P_AproxDistance (p->mo->x + p->mo->momx - (mo->x + mo->momx), p->mo->y+p->mo->momy - (mo->y+mo->momy)) < thingDistance)
383                     {
384                         //if its the closest missile and its reasonably close I should try and avoid it
385                         if (thingDistance
386                             && (!closestMissileDistance || (thingDistance < closestMissileDistance))
387                             && (thingDistance <= (300<<FRACBITS)))
388                         {
389                             closestMissileDistance = thingDistance;
390                             pbot->closestMissile = mo;
391                         }
392                     }
393                     thingDistance = 0;
394                 }
395             }
396             else if (((mo->flags & MF_SPECIAL)
397                       || (mo->flags & MF_DROPPED))) //most likely a pickup
398             {
399                 item_getable = (mo->flags & MF_DROPPED) || item_respawn;
400                 if(EN_heretic)
401                 {
402                     switch (mo->type)
403                     {
404 //  HERETIC??? --> ///////// bonuses/powerups ////////////////////////
405                      case MT_ARTIINVULNERABILITY:  //invulnerability, always run to get it
406                         if( deathmatch || !p->powers[pw_invulnerability])
407                             itemWeight = 10;
408                         break;
409                      case MT_ARTIINVISIBILITY:	//invisability
410                         if( deathmatch || !p->powers[pw_invisibility])
411                             itemWeight = 9;
412                         break;
413                      case MT_ARTISUPERHEAL:	//soul sphere
414                         if( deathmatch || p->health < maxsoul)
415                             itemWeight = 8;
416                         break;
417                      case MT_ITEMSHIELD2:	//blue armour, if we have >= maxarmour, its impossible to get
418                         if (p->armorpoints < max_armor)
419                             itemWeight = 8;
420                         break;
421                      case MT_ITEMSHIELD1:	//green armour
422                         if (p->armorpoints < (max_armor/2))
423                             itemWeight = 5;
424                         break;
425                      case SPR_MEDI: case SPR_STIM: //medication
426                         if (health_index < 5)
427                         {
428                             // index by health_index
429                             static const byte  stim_weight[5] = { 6, 6, 5, 4, 3 };
430                             itemWeight = stim_weight[ health_index ];
431                         }
432                         break;
433                      case SPR_BON1:	//health potion
434                         if (p->mo->health < maxsoul)
435                             itemWeight = 1;
436                         break;
437                      case SPR_BON2:	//armour bonus
438                         if (p->armorpoints < max_armor)
439                             itemWeight = 1;
440                         break;
441 
442         /////////////// weapons ////////////////////////////
443                      case SPR_SHOT:
444                         if (!p->weaponowned[wp_shotgun])
445                         {
446                             if( weapon & ammo & (WB_SSG | WB_CHAIN | WB_ROCKET | WB_PLASMA) )
447                                 itemWeight = 4;
448                             else
449                                 itemWeight = 6;
450                         }
451                         else if( item_getable
452                                  && (p->ammo[am_shell] < p->maxammo[am_shell])
453                                  )
454                             itemWeight = 3;
455                         break;
456                      case SPR_MGUN:
457                         if (!p->weaponowned[wp_chaingun])
458                         {
459                             if( weapon & ammo & (WB_SSG | WB_ROCKET | WB_PLASMA) )
460                                 itemWeight = 5;
461                             else
462                                 itemWeight = 6;
463                         }
464                         else if( item_getable
465                                  && (p->ammo[am_clip] < p->maxammo[am_clip]))
466                             itemWeight = 3;
467                         break;
468                      case SPR_LAUN:
469                         if (!p->weaponowned[wp_missile])
470                         {
471                             if( weapon & ammo & (WB_SSG | WB_CHAIN | WB_PLASMA) )
472                                 itemWeight = 5;
473                             else
474                                 itemWeight = 7;
475                         }
476                         else if( item_getable
477                                  && (p->ammo[am_misl] < p->maxammo[am_misl]))
478                             itemWeight = 3;
479                         break;
480                      case SPR_PLAS:
481                         if (!p->weaponowned[wp_plasma])
482                         {
483                             if( weapon & ammo & (WB_SSG | WB_CHAIN | WB_ROCKET) )
484                                 itemWeight = 5;
485                             else
486                                 itemWeight = 7;
487                         }
488                         else if( item_getable
489                                  && (p->ammo[am_cell] < p->maxammo[am_cell]))
490                             itemWeight = 3;
491                         break;
492                      case SPR_BFUG:
493                         if (!p->weaponowned[wp_bfg])
494                         {
495                             if( weapon & ammo & (WB_SSG | WB_CHAIN | WB_ROCKET | WB_PLASMA) )
496                                 itemWeight = 5;
497                             else
498                                 itemWeight = 7;
499                         }
500                         else if( item_getable
501                                  && (p->ammo[am_cell] < p->maxammo[am_cell]))
502                             itemWeight = 3;
503                         break;
504                      case SPR_SGN2:
505                         if (!p->weaponowned[wp_supershotgun])
506                         {
507                             if( weapon & ammo & (WB_CHAIN | WB_ROCKET | WB_PLASMA) )
508                                 itemWeight = 5;
509                             else
510                                 itemWeight = 7;
511                         }
512                         else if( item_getable
513                                  && (p->ammo[am_shell] < p->maxammo[am_shell]))
514                             itemWeight = 3;
515                         break;
516 
517         /////////////////////ammo
518                      case SPR_CLIP: case SPR_AMMO:
519                         if( (p->ammo[am_clip]==0) && out_of_ammo )
520                             itemWeight = 6;
521                         else if( p->ammo[am_clip] < p->maxammo[am_clip])
522                             itemWeight = 3;
523                         break;
524                      case SPR_SHEL: case SPR_SBOX:
525                         if( (weapon & (WB_SHOT | WB_SSG) & ~ammo )  // shotgun without ammo
526                             && out_of_ammo )
527                             itemWeight = 6;
528                         else if(p->ammo[am_shell] < p->maxammo[am_shell])
529                             itemWeight = 3;
530                         break;
531                      case SPR_ROCK: case SPR_BROK:
532                         if( (weapon & WB_ROCKET & ~ammo )  // launcher without ammo
533                             && out_of_ammo )
534                             itemWeight = 6;
535                         else if(p->ammo[am_misl] < p->maxammo[am_misl])
536                             itemWeight = 3;
537                         break;
538                      case SPR_CELL: case SPR_CELP:
539                         if( (weapon & WB_PLASMA & ~ammo )  // plasma without ammo
540                             && out_of_ammo )
541                             itemWeight = 6;
542                         else if(p->ammo[am_cell] < p->maxammo[am_cell])
543                             itemWeight = 3;
544                         break;
545 
546         ///////////////////////keys
547                      case SPR_BKEY:
548                         if (!(p->cards & it_bluecard))
549                             itemWeight = 5;
550                         break;
551                      case SPR_BSKU:
552                         if (!(p->cards & it_blueskull))
553                             itemWeight = 5;
554                         break;
555                      case SPR_RKEY:
556                         if (!(p->cards & it_redcard))
557                             itemWeight = 5;
558                         break;
559                      case SPR_RSKU:
560                         if (!(p->cards & it_redskull))
561                             itemWeight = 5;
562                         break;
563                      case SPR_YKEY:
564                         if (!(p->cards & it_yellowcard))
565                             itemWeight = 5;
566                         break;
567                      case SPR_YSKU:
568                         if (!(p->cards & it_yellowskull))
569                             itemWeight = 5;
570                         break;
571                      default:
572                         itemWeight = 0;	//dont want it
573                         break;
574                     }
575                 }
576                 else switch (mo->sprite)
577                 {
578 //NON-HERETIC???////////// bonuses/powerups now checks for skill level
579                  case SPR_PINV:	//invulnrability always run to get it
580                     if( deathmatch || !p->powers[pw_invulnerability])
581                     {
582                         // index by gameskill
583                         static const byte  pinv_weight[5] = {2, 5, 6, 8, 10};
584                         itemWeight = pinv_weight[ gameskill ];
585                     }
586                     break;
587                  case SPR_MEGA: //megasphere
588                     if( deathmatch
589                         || (p->health < maxsoul || p->armorpoints < max_armor) )
590                     {
591                         static const byte  mega_weight[5] = {2, 4, 5, 7, 9};
592                         itemWeight = mega_weight[ gameskill ];
593                     }
594                     break;
595                  case SPR_PINS:	//invisibility
596                     if( deathmatch || !p->powers[pw_invisibility] )
597                     {
598                         static const byte  pins_weight[5] = {2, 3, 5, 7, 9};
599                         itemWeight = pins_weight[ gameskill ];
600                     }
601                     break;
602                  case SPR_SOUL:	//soul sphere
603                     if( deathmatch || p->health < maxsoul )
604                     {
605                         static const byte  soul_weight[5] = {1, 2, 4, 6, 9};
606                         itemWeight = soul_weight[ gameskill ];
607                     }
608                     break;
609                  case SPR_ARM2:	//blue armour, if we have >= maxarmour, its impossible to get
610                     if (p->armorpoints < max_armor)
611                     {
612                         static const byte arm2_weight[5] = {1, 2, 4, 6, 8};
613                         itemWeight = arm2_weight[ gameskill ];
614                     }
615                     break;
616                  case SPR_PSTR:	//berserk pack
617                     if (health_index < 5)
618                     {
619                         // index by gameskill, health test
620                         static const byte  pstr_weight[5][5] =
621                         {
622                             {9, 9, 9, 8, 7},  // sk_baby
623                             {9, 9, 8, 7, 6},  // sk_easy
624                             {9, 8, 7, 6, 5},  // sk_medium
625                             {9, 8, 7, 5, 4},  // sk_hard
626                             {7, 6, 5, 4, 3}   // sk_nightmare
627                         };
628                         itemWeight = pstr_weight[ gameskill ][ health_index ];
629                     }
630                     else if (!p->powers[pw_strength])
631                         itemWeight = 2;
632                     break;
633 
634                  case SPR_ARM1:	//green armour
635                     if (p->armorpoints < max_armor/2)
636                     {
637                         static const byte arm1_weight[5] = {1, 2, 3, 4, 5};
638                         itemWeight = arm1_weight[ gameskill ];
639                     }
640                     break;
641 
642                  case SPR_MEDI: case SPR_STIM: //medication  MEDIKIT or STIMPACK
643                     if (health_index < 5)
644                     {
645                         // index by gameskill, health test
646                         static const byte  medi_weight[5][5] =
647                         {
648                             {2, 2, 1, 1, 1},  // sk_baby
649                             {3, 3, 2, 1, 1},  // sk_easy
650                             {4, 4, 3, 2, 1},  // sk_medium
651                             {5, 5, 4, 3, 2},  // sk_hard
652                             {6, 6, 5, 4, 3}   // sk_nightmare
653                         };
654                         itemWeight = medi_weight[ gameskill ][ health_index ];
655                     }
656                     break;
657 
658                  case SPR_BON1:	//health potion
659                     if (p->mo->health < maxsoul)
660                         itemWeight = 1;
661                     break;
662                  case SPR_BON2:	//armour bonus
663                     if (p->armorpoints < max_armor)
664                         itemWeight = 1;
665                     break;
666 
667 /////////////// weapons ////////////////////////////
668                  case SPR_SHOT:
669                     if (!p->weaponowned[wp_shotgun])
670                     {
671                         if( weapon & ammo & (WB_SSG | WB_CHAIN | WB_ROCKET | WB_PLASMA) )
672                             itemWeight = 4;
673                         else
674                             itemWeight = 6;
675                     }
676                     else if( item_getable
677                              && (p->ammo[am_shell] < p->maxammo[am_shell]))
678                         itemWeight = 3;
679                     break;
680                  case SPR_MGUN:
681                     if (!p->weaponowned[wp_chaingun])
682                     {
683                         if( weapon & ammo & (WB_SSG | WB_ROCKET | WB_PLASMA) )
684                             itemWeight = 5;
685                         else
686                             itemWeight = 6;
687                     }
688                     else if( item_getable
689                              && (p->ammo[am_clip] < p->maxammo[am_clip]))
690                         itemWeight = 3;
691                     break;
692                  case SPR_LAUN:
693                     if (!p->weaponowned[wp_missile])
694                     {
695                         if( weapon & ammo & (WB_SSG | WB_CHAIN | WB_PLASMA) )
696                             itemWeight = 5;
697                        else
698                             itemWeight = 7;
699                     }
700                     else if( item_getable
701                              && (p->ammo[am_misl] < p->maxammo[am_misl]))
702                        itemWeight = 3;
703                     break;
704                  case SPR_PLAS:
705                     if (!p->weaponowned[wp_plasma])
706                     {
707                         if( weapon & ammo & (WB_SSG | WB_CHAIN | WB_ROCKET) )
708                             itemWeight = 5;
709                         else
710                             itemWeight = 7;
711                     }
712                     else if( item_getable
713                              && (p->ammo[am_cell] < p->maxammo[am_cell]))
714                         itemWeight = 3;
715                     break;
716                  case SPR_BFUG:
717                     if (!p->weaponowned[wp_bfg])
718                     {
719                         if( weapon & ammo & (WB_SSG | WB_CHAIN | WB_ROCKET | WB_PLASMA) )
720                             itemWeight = 5;
721                         else
722                             itemWeight = 7;
723                     }
724                     else if( item_getable
725                              && (p->ammo[am_cell] < p->maxammo[am_cell]))
726                         itemWeight = 3;
727                     break;
728                  case SPR_SGN2:
729                     if (!p->weaponowned[wp_supershotgun])
730                     {
731                         if( weapon & ammo & (WB_CHAIN | WB_ROCKET | WB_PLASMA) )
732                             itemWeight = 5;
733                         else
734                             itemWeight = 7;
735                     }
736                     else if( item_getable
737                              && (p->ammo[am_shell] < p->maxammo[am_shell]))
738                         itemWeight = 3;
739                     break;
740 
741 /////////////////////ammo
742                  case SPR_CLIP: case SPR_AMMO:
743                     if( (p->ammo[am_clip] == 0) && out_of_ammo )
744                         itemWeight = 6;
745                     else if(p->ammo[am_clip] < p->maxammo[am_clip])
746                         itemWeight = 3;
747                     break;
748                  case SPR_SHEL: case SPR_SBOX:
749                     if( (weapon & (WB_SHOT | WB_SSG) & ~ammo )  // shotgun without ammo
750                         && out_of_ammo )
751                         itemWeight = 6;
752                     else if(p->ammo[am_shell] < p->maxammo[am_shell])
753                         itemWeight = 3;
754                     break;
755                  case SPR_ROCK: case SPR_BROK:
756                     if( (weapon & WB_ROCKET & ~ammo )  // launcher without ammo
757                         && out_of_ammo )
758                         itemWeight = 6;
759                     else if(p->ammo[am_misl] < p->maxammo[am_misl])
760                         itemWeight = 3;
761                     break;
762                  case SPR_CELL: case SPR_CELP:
763                     if( (weapon & (WB_PLASMA | WB_BFG) & ~ammo )  // plasma without ammo
764                         && out_of_ammo )
765                         itemWeight = 6;
766                     else if(p->ammo[am_cell] < p->maxammo[am_cell])
767                         itemWeight = 3;
768                     break;
769 
770 ///////////////////////keys
771                  case SPR_BKEY:
772                     if (!(p->cards & it_bluecard))
773                         itemWeight = 5;
774                     break;
775                  case SPR_BSKU:
776                     if (!(p->cards & it_blueskull))
777                         itemWeight = 5;
778                     break;
779                  case SPR_RKEY:
780                     if (!(p->cards & it_redcard))
781                         itemWeight = 5;
782                     break;
783                  case SPR_RSKU:
784                     if (!(p->cards & it_redskull))
785                         itemWeight = 5;
786                     break;
787                  case SPR_YKEY:
788                     if (!(p->cards & it_yellowcard))
789                         itemWeight = 5;
790                     break;
791                  case SPR_YSKU:
792                     if (!(p->cards & it_yellowskull))
793                         itemWeight = 5;
794                     break;
795                  default:
796                     itemWeight = 0;	//don't want it
797                     break;
798                 }
799 
800                 if (P_CheckSight(p->mo, mo) && B_Reachable(p, mo))
801                 {
802                     if (((itemWeight > bestSeenItemWeight)
803                          || ((itemWeight == bestSeenItemWeight)
804                              && (thingDistance < bestSeenItemDistance))))
805                     {
806                         // Select this item.
807                         bestSeenItem = mo;
808                         bestSeenItemDistance = thingDistance;
809                         bestSeenItemWeight = itemWeight;
810                     }
811                 }
812                 else // this item is not getable atm, may use a search later to find a path to it
813                 {
814                     SearchNode_t* tempNode = B_GetNodeAt(mo->x, mo->y);
815                     // if there is a node near the item wanted, and its the best item
816                     if (tempNode
817                         // && ((P_AproxDistance(posX2x(tempNode->x) - mo->x, posY2y(tempNode->y) - mo->y) < (BOTNODEGRIDSIZE<<1))
818                         && ((((itemWeight > bestItemWeight)
819                               || ((itemWeight == bestItemWeight)
820                                   && (thingDistance < bestItemDistance)))))
821                         )
822                     {
823                         bestItem = mo;
824                         bestItemDistance = thingDistance;
825                         bestItemWeight = itemWeight;
826                         //debug_Printf("best item set to x:%d y:%d for type:%d\n", mo->x>>FRACBITS, mo->y>>FRACBITS, mo->type);
827                     }
828 
829                     //if (!tempNode)
830                     //	debug_Printf("could not find a node here x:%d y:%d for type:%d\n", mo->x>>FRACBITS, mo->y>>FRACBITS, mo->type);
831                 }
832             }
833 
834             // Reduce constant firing
835             if( enemy_weight && (enemy_weight > B_Random()))
836 //            if( enemy_weight )
837             {
838                 if (P_CheckSight(p->mo, mo))
839                 {
840                     // if I have seen an enemy, if its deathmatch,
841                     // players have priority, so closest player targeted
842                     // otherwise make closest target the closest monster
843                     if (thingDistance
844                         && (!closestEnemyDistance || (thingDistance < closestEnemyDistance)
845                             || (mo->player && !pbot->closestEnemy->player)))
846                     {
847                         closestEnemyDistance = thingDistance;
848                         pbot->closestEnemy = mo;
849                     }
850                 }
851                 else
852                 {
853                     SearchNode_t* tempNode = B_GetNodeAt(mo->x, mo->y);
854                     if (tempNode
855                         && ((!closestUnseenEnemyDistance || (thingDistance < closestUnseenEnemyDistance)
856                              || (mo->player && !pbot->closestUnseenEnemy->player))))
857                     {
858                         closestUnseenEnemyDistance = thingDistance;
859                         pbot->closestUnseenEnemy = mo;
860                     }
861                 }
862 
863                 enemy_weight = 0;
864                 thingDistance = 0;
865             }
866         }
867     }
868 
869     // if a item has a good weight, get it no matter what.
870     // Else only if we have no target/enemy get it.
871     pbot->bestSeenItem =
872      ((bestSeenItemWeight > 5)
873       || (bestSeenItemWeight && !pbot->closestEnemy)) ?
874          bestSeenItem : NULL;
875 
876     pbot->bestItem = (bestItemWeight) ? bestItem : NULL;
877 }
878