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