1 ///////////////////////////////////////////////////////////////////////
2 //
3 //  ACE - Quake II Bot Base Code
4 //
5 //  Version 1.0
6 //
7 //  This file is Copyright(c), Steve Yeager 1998, All Rights Reserved
8 //
9 //
10 //	All other files are Copyright(c) Id Software, Inc.
11 //
12 //	Please see liscense.txt in the source directory for the copyright
13 //	information regarding those files belonging to Id Software, Inc.
14 //
15 //	Should you decide to release a modified version of ACE, you MUST
16 //	include the following text (minus the BEGIN and END lines) in the
17 //	documentation for your modification.
18 //
19 //	--- BEGIN ---
20 //
21 //	The ACE Bot is a product of Steve Yeager, and is available from
22 //	the ACE Bot homepage, at http://www.axionfx.com/ace.
23 //
24 //	This program is a modification of the ACE Bot, and is therefore
25 //	in NO WAY supported by Steve Yeager.
26 
27 //	This program MUST NOT be sold in ANY form. If you have paid for
28 //	this product, you should contact Steve Yeager immediately, via
29 //	the ACE Bot homepage.
30 //
31 //	--- END ---
32 //
33 //	I, Steve Yeager, hold no responsibility for any harm caused by the
34 //	use of this source code, especially to small children and animals.
35 //  It is provided as-is with no implied warranty or support.
36 //
37 //  I also wish to thank and acknowledge the great work of others
38 //  that has helped me to develop this code.
39 //
40 //  John Cricket    - For ideas and swapping code.
41 //  Ryan Feltrin    - For ideas and swapping code.
42 //  SABIN           - For showing how to do true client based movement.
43 //  BotEpidemic     - For keeping us up to date.
44 //  Telefragged.com - For giving ACE a home.
45 //  Microsoft       - For giving us such a wonderful crash free OS.
46 //  id              - Need I say more.
47 //
48 //  And to all the other testers, pathers, and players and people
49 //  who I can't remember who the heck they were, but helped out.
50 //
51 ///////////////////////////////////////////////////////////////////////
52 
53 ///////////////////////////////////////////////////////////////////////
54 //
55 //  acebot_items.c - This file contains all of the
56 //                   item handling routines for the
57 //                   ACE bot, including fact table support
58 //
59 ///////////////////////////////////////////////////////////////////////
60 
61 #include "../g_local.h"
62 #include "acebot.h"
63 
64 int	num_players = 0;
65 int num_items = 0;
66 item_table_t item_table[MAX_EDICTS];
67 edict_t *players[MAX_CLIENTS];		// pointers to all players in the game
68 
69 ///////////////////////////////////////////////////////////////////////
70 // Add the player to our list
71 ///////////////////////////////////////////////////////////////////////
ACEIT_PlayerAdded(edict_t * ent)72 void ACEIT_PlayerAdded(edict_t *ent)
73 {
74 	players[num_players++] = ent;
75 }
76 
77 ///////////////////////////////////////////////////////////////////////
78 // Remove player from list
79 ///////////////////////////////////////////////////////////////////////
ACEIT_PlayerRemoved(edict_t * ent)80 void ACEIT_PlayerRemoved(edict_t *ent)
81 {
82 	int i;
83 	int pos;
84 
85 	// watch for 0 players
86 	if(num_players == 0)
87 		return;
88 
89 	// special cas for only one player
90 	if(num_players == 1)
91 	{
92 		num_players = 0;
93 		return;
94 	}
95 
96 	// Find the player
97 	for(i=0;i<num_players;i++)
98 		if(ent == players[i])
99 			pos = i;
100 
101 	// decrement
102 	for(i=pos;i<num_players-1;i++)
103 		players[i] = players[i+1];
104 
105 	num_players--;
106 }
107 
108 ///////////////////////////////////////////////////////////////////////
109 // Can we get there?
110 ///////////////////////////////////////////////////////////////////////
ACEIT_IsReachable(edict_t * self,vec3_t goal)111 qboolean ACEIT_IsReachable(edict_t *self, vec3_t goal)
112 {
113 	trace_t trace;
114 	vec3_t v;
115 
116 	VectorCopy(self->mins,v);
117 	v[2] += 18; // Stepsize
118 
119 	trace = gi.trace (self->s.origin, v, self->maxs, goal, self, MASK_OPAQUE);
120 
121 	// Yes we can see it
122 	if (trace.fraction == 1.0)
123 		return true;
124 	else
125 		return false;
126 
127 }
128 
129 ///////////////////////////////////////////////////////////////////////
130 // Visiblilty check
131 ///////////////////////////////////////////////////////////////////////
ACEIT_IsVisible(edict_t * self,vec3_t goal)132 qboolean ACEIT_IsVisible(edict_t *self, vec3_t goal)
133 {
134 	trace_t trace;
135 
136 	trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, goal, self, MASK_OPAQUE);
137 
138 	// Yes we can see it
139 	if (trace.fraction == 1.0)
140 		return true;
141 	else
142 		return false;
143 
144 }
145 
146 ///////////////////////////////////////////////////////////////////////
147 //  Weapon changing support
148 ///////////////////////////////////////////////////////////////////////
ACEIT_ChangeWeapon(edict_t * ent,gitem_t * item)149 qboolean ACEIT_ChangeWeapon (edict_t *ent, gitem_t *item)
150 {
151 	int			ammo_index;
152 	gitem_t		*ammo_item;
153 
154 	// see if we're already using it
155 	if (item == ent->client->pers.weapon)
156 		return true;
157 
158 	// Has not picked up weapon yet
159 	if(!ent->client->pers.inventory[ITEM_INDEX(item)])
160 		return false;
161 
162 	// Do we have ammo for it?
163 	if (item->ammo)
164 	{
165 		ammo_item = FindItem(item->ammo);
166 		ammo_index = ITEM_INDEX(ammo_item);
167 		if (!ent->client->pers.inventory[ammo_index] && !g_select_empty->value)
168 			return false;
169 	}
170 
171 	// Change to this weapon
172 	ent->client->newweapon = item;
173 
174 	return true;
175 }
176 
177 
178 extern gitem_armor_t jacketarmor_info;
179 extern gitem_armor_t combatarmor_info;
180 extern gitem_armor_t bodyarmor_info;
181 
182 ///////////////////////////////////////////////////////////////////////
183 // Check if we can use the armor
184 ///////////////////////////////////////////////////////////////////////
ACEIT_CanUseArmor(gitem_t * item,edict_t * other)185 qboolean ACEIT_CanUseArmor (gitem_t *item, edict_t *other)
186 {
187 	int				old_armor_index;
188 	gitem_armor_t	*oldinfo;
189 	gitem_armor_t	*newinfo;
190 	int				newcount;
191 	float			salvage;
192 	int				salvagecount;
193 
194 	// get info on new armor
195 	newinfo = (gitem_armor_t *)item->info;
196 
197 	old_armor_index = ArmorIndex (other);
198 
199 	// handle armor shards specially
200 	if (item->tag == ARMOR_SHARD)
201 		return true;
202 
203 	// get info on old armor
204 	if (old_armor_index == ITEM_INDEX(FindItem("Jacket Armor")))
205 		oldinfo = &jacketarmor_info;
206 	else if (old_armor_index == ITEM_INDEX(FindItem("Combat Armor")))
207 		oldinfo = &combatarmor_info;
208 	else // (old_armor_index == body_armor_index)
209 		oldinfo = &bodyarmor_info;
210 
211 	if (newinfo->normal_protection <= oldinfo->normal_protection)
212 	{
213 		// calc new armor values
214 		salvage = newinfo->normal_protection / oldinfo->normal_protection;
215 		salvagecount = salvage * newinfo->base_count;
216 		newcount = other->client->pers.inventory[old_armor_index] + salvagecount;
217 
218 		if (newcount > oldinfo->max_count)
219 			newcount = oldinfo->max_count;
220 
221 		// if we're already maxed out then we don't need the new armor
222 		if (other->client->pers.inventory[old_armor_index] >= newcount)
223 			return false;
224 
225 	}
226 
227 	return true;
228 }
229 
230 
231 ///////////////////////////////////////////////////////////////////////
232 // Determins the NEED for an item
233 //
234 // This function can be modified to support new items to pick up
235 // Any other logic that needs to be added for custom decision making
236 // can be added here. For now it is very simple.
237 ///////////////////////////////////////////////////////////////////////
ACEIT_ItemNeed(edict_t * self,int item)238 float ACEIT_ItemNeed(edict_t *self, int item)
239 {
240 
241 	// Make sure item is at least close to being valid
242 	if(item < 0 || item > 100)
243 		return 0.0;
244 
245 	switch(item)
246 	{
247 		// Health
248 		case ITEMLIST_HEALTH_SMALL:
249 		case ITEMLIST_HEALTH_MEDIUM:
250 		case ITEMLIST_HEALTH_LARGE:
251 		case ITEMLIST_HEALTH_MEGA:
252 			if(self->health < 100)
253 				return 1.0 - (float)self->health/100.0f; // worse off, higher priority
254 			else
255 				return 0.0;
256 
257 		case ITEMLIST_AMMOPACK:
258 		case ITEMLIST_QUADDAMAGE:
259 		case ITEMLIST_INVULNERABILITY:
260 		case ITEMLIST_SILENCER:
261 	//	case ITEMLIST_REBREATHER
262 	//	case ITEMLIST_ENVIRONMENTSUIT
263 		case ITEMLIST_ADRENALINE:
264 		case ITEMLIST_BANDOLIER:
265 			return 0.6;
266 
267 		// Weapons
268 		case ITEMLIST_ROCKETLAUNCHER:
269 		case ITEMLIST_RAILGUN:
270 		case ITEMLIST_MACHINEGUN:
271 		case ITEMLIST_CHAINGUN:
272 		case ITEMLIST_SHOTGUN:
273 		case ITEMLIST_SUPERSHOTGUN:
274 		case ITEMLIST_BFG10K:
275 		case ITEMLIST_GRENADELAUNCHER:
276 		case ITEMLIST_HYPERBLASTER:
277 			if(!self->client->pers.inventory[item])
278 				return 0.7;
279 			else
280 				return 0.0;
281 
282 		// Ammo
283 		case ITEMLIST_SLUGS:
284 			if(self->client->pers.inventory[ITEMLIST_SLUGS] < self->client->pers.max_slugs)
285 				return 0.4;
286 			else
287 				return 0.0;
288 
289 		case ITEMLIST_BULLETS:
290 			if(self->client->pers.inventory[ITEMLIST_BULLETS] < self->client->pers.max_bullets)
291 				return 0.3;
292 			else
293 				return 0.0;
294 
295 		case ITEMLIST_SHELLS:
296 		   if(self->client->pers.inventory[ITEMLIST_SHELLS] < self->client->pers.max_shells)
297 				return 0.3;
298 			else
299 				return 0.0;
300 
301 		case ITEMLIST_CELLS:
302 			if(self->client->pers.inventory[ITEMLIST_CELLS] <	self->client->pers.max_cells)
303 				return 0.3;
304 			else
305 				return 0.0;
306 
307 		case ITEMLIST_ROCKETS:
308 			if(self->client->pers.inventory[ITEMLIST_ROCKETS] < self->client->pers.max_rockets)
309 				return 1.5;
310 			else
311 				return 0.0;
312 
313 		case ITEMLIST_GRENADES:
314 			if(self->client->pers.inventory[ITEMLIST_GRENADES] < self->client->pers.max_grenades)
315 				return 0.3;
316 			else
317 				return 0.0;
318 
319 		case ITEMLIST_BODYARMOR:
320 			if(ACEIT_CanUseArmor (FindItem("Body Armor"), self))
321 				return 0.6;
322 			else
323 				return 0.0;
324 
325 		case ITEMLIST_COMBATARMOR:
326 			if(ACEIT_CanUseArmor (FindItem("Combat Armor"), self))
327 				return 0.6;
328 			else
329 				return 0.0;
330 
331 		case ITEMLIST_JACKETARMOR:
332 			if(ACEIT_CanUseArmor (FindItem("Jacket Armor"), self))
333 				return 0.6;
334 			else
335 				return 0.0;
336 
337 		case ITEMLIST_POWERSCREEN:
338 		case ITEMLIST_POWERSHIELD:
339 			return 0.5;
340 		/*
341 		case ITEMLIST_FLAG1:
342 			// If I am on team one, I want team two's flag
343 			if(!self->client->pers.inventory[item] && self->client->resp.ctf_team == CTF_TEAM2)
344 				return 10.0;
345 			else
346 				return 0.0;
347 
348 		case ITEMLIST_FLAG2:
349 			if(!self->client->pers.inventory[item] && self->client->resp.ctf_team == CTF_TEAM1)
350 				return 10.0;
351 			else
352 				return 0.0;
353 		*/
354 		case ITEMLIST_RESISTANCETECH:
355 		case ITEMLIST_STRENGTHTECH:
356 		case ITEMLIST_HASTETECH:
357 		case ITEMLIST_REGENERATIONTECH:
358 			// Check for other tech
359 			if(!self->client->pers.inventory[ITEMLIST_RESISTANCETECH] &&
360 			   !self->client->pers.inventory[ITEMLIST_STRENGTHTECH] &&
361 			   !self->client->pers.inventory[ITEMLIST_HASTETECH] &&
362 			   !self->client->pers.inventory[ITEMLIST_REGENERATIONTECH])
363 			    return 0.4;
364 			else
365 				return 0.0;
366 
367 		default:
368 			return 0.0;
369 
370 	}
371 
372 }
373 
374 ///////////////////////////////////////////////////////////////////////
375 // Convert a classname to its index value
376 //
377 // I prefer to use integers/defines for simplicity sake. This routine
378 // can lead to some slowdowns I guess, but makes the rest of the code
379 // easier to deal with.
380 ///////////////////////////////////////////////////////////////////////
ACEIT_ClassnameToIndex(char * classname)381 int ACEIT_ClassnameToIndex(char *classname)
382 {
383 	if(strcmp(classname,"item_armor_body")==0)
384 		return ITEMLIST_BODYARMOR;
385 
386 	if(strcmp(classname,"item_armor_combat")==0)
387 		return ITEMLIST_COMBATARMOR;
388 
389 	if(strcmp(classname,"item_armor_jacket")==0)
390 		return ITEMLIST_JACKETARMOR;
391 
392 	if(strcmp(classname,"item_armor_shard")==0)
393 		return ITEMLIST_ARMORSHARD;
394 
395 	if(strcmp(classname,"item_power_screen")==0)
396 		return ITEMLIST_POWERSCREEN;
397 
398 	if(strcmp(classname,"item_power_shield")==0)
399 		return ITEMLIST_POWERSHIELD;
400 
401 	if(strcmp(classname,"weapon_grapple")==0)
402 		return ITEMLIST_GRAPPLE;
403 
404 	if(strcmp(classname,"weapon_blaster")==0)
405 		return ITEMLIST_BLASTER;
406 
407 	if(strcmp(classname,"weapon_shotgun")==0)
408 		return ITEMLIST_SHOTGUN;
409 
410 	if(strcmp(classname,"weapon_supershotgun")==0)
411 		return ITEMLIST_SUPERSHOTGUN;
412 
413 	if(strcmp(classname,"weapon_machinegun")==0)
414 		return ITEMLIST_MACHINEGUN;
415 
416 	if(strcmp(classname,"weapon_chaingun")==0)
417 		return ITEMLIST_CHAINGUN;
418 
419 	if(strcmp(classname,"weapon_chaingun")==0)
420 		return ITEMLIST_CHAINGUN;
421 
422 	if(strcmp(classname,"ammo_grenades")==0)
423 		return ITEMLIST_GRENADES;
424 
425 	if(strcmp(classname,"weapon_grenadelauncher")==0)
426 		return ITEMLIST_GRENADELAUNCHER;
427 
428 	if(strcmp(classname,"weapon_rocketlauncher")==0)
429 		return ITEMLIST_ROCKETLAUNCHER;
430 
431 	if(strcmp(classname,"weapon_hyperblaster")==0)
432 		return ITEMLIST_HYPERBLASTER;
433 
434 	if(strcmp(classname,"weapon_boomer")==0)
435 		return ITEMLIST_BOOMER;
436 
437 	if(strcmp(classname,"weapon_railgun")==0)
438 		return ITEMLIST_RAILGUN;
439 
440 	if(strcmp(classname,"weapon_phalanx")==0)
441 		return ITEMLIST_PHALANX;
442 
443 	if(strcmp(classname,"weapon_bfg10k")==0)
444 		return ITEMLIST_BFG10K;
445 
446 	if(strcmp(classname,"ammo_shells")==0)
447 		return ITEMLIST_SHELLS;
448 
449 	if(strcmp(classname,"ammo_bullets")==0)
450 		return ITEMLIST_BULLETS;
451 
452 	if(strcmp(classname,"ammo_cells")==0)
453 		return ITEMLIST_CELLS;
454 
455 	if(strcmp(classname,"ammo_rockets")==0)
456 		return ITEMLIST_ROCKETS;
457 
458 	if(strcmp(classname,"ammo_slugs")==0)
459 		return ITEMLIST_SLUGS;
460 
461 	if(strcmp(classname,"item_quad")==0)
462 		return ITEMLIST_QUADDAMAGE;
463 
464 	if(strcmp(classname,"item_invunerability")==0)
465 		return ITEMLIST_INVULNERABILITY;
466 
467 	if(strcmp(classname,"item_silencer")==0)
468 		return ITEMLIST_SILENCER;
469 
470 	if(strcmp(classname,"item_rebreather")==0)
471 		return ITEMLIST_REBREATHER;
472 
473 	if(strcmp(classname,"item_enviornmentsuit")==0)
474 		return ITEMLIST_ENVIRONMENTSUIT;
475 
476 	if(strcmp(classname,"item_ancienthead")==0)
477 		return ITEMLIST_ANCIENTHEAD;
478 
479 	if(strcmp(classname,"item_adrenaline")==0)
480 		return ITEMLIST_ADRENALINE;
481 
482 	if(strcmp(classname,"item_bandolier")==0)
483 		return ITEMLIST_BANDOLIER;
484 
485 	if(strcmp(classname,"item_pack")==0)
486 		return ITEMLIST_AMMOPACK;
487 
488 	if(strcmp(classname,"item_datacd")==0)
489 		return ITEMLIST_DATACD;
490 
491 	if(strcmp(classname,"item_powercube")==0)
492 		return ITEMLIST_POWERCUBE;
493 
494 	if(strcmp(classname,"item_pyramidkey")==0)
495 		return ITEMLIST_PYRAMIDKEY;
496 
497 	if(strcmp(classname,"item_dataspinner")==0)
498 		return ITEMLIST_DATASPINNER;
499 
500 	if(strcmp(classname,"item_securitypass")==0)
501 		return ITEMLIST_SECURITYPASS;
502 
503 	if(strcmp(classname,"item_bluekey")==0)
504 		return ITEMLIST_BLUEKEY;
505 
506 	if(strcmp(classname,"item_redkey")==0)
507 		return ITEMLIST_REDKEY;
508 
509 	if(strcmp(classname,"item_commandershead")==0)
510 		return ITEMLIST_COMMANDERSHEAD;
511 
512 	if(strcmp(classname,"item_airstrikemarker")==0)
513 		return ITEMLIST_AIRSTRIKEMARKER;
514 
515 	if(strcmp(classname,"item_health")==0) // ??
516 		return ITEMLIST_HEALTH;
517 
518 	if(strcmp(classname,"item_flag_team1")==0)
519 		return ITEMLIST_FLAG1;
520 
521 	if(strcmp(classname,"item_flag_team2")==0)
522 		return ITEMLIST_FLAG2;
523 
524 	if(strcmp(classname,"item_tech1")==0)
525 		return ITEMLIST_RESISTANCETECH;
526 
527 	if(strcmp(classname,"item_tech2")==0)
528 		return ITEMLIST_STRENGTHTECH;
529 
530 	if(strcmp(classname,"item_tech3")==0)
531 		return ITEMLIST_HASTETECH;
532 
533 	if(strcmp(classname,"item_tech4")==0)
534 		return ITEMLIST_REGENERATIONTECH;
535 
536 	if(strcmp(classname,"item_health_small")==0)
537 		return ITEMLIST_HEALTH_SMALL;
538 
539 	if(strcmp(classname,"item_health_medium")==0)
540 		return ITEMLIST_HEALTH_MEDIUM;
541 
542 	if(strcmp(classname,"item_health_large")==0)
543 		return ITEMLIST_HEALTH_LARGE;
544 
545 	if(strcmp(classname,"item_health_mega")==0)
546 		return ITEMLIST_HEALTH_MEGA;
547 
548 	return INVALID;
549 }
550 
551 
552 ///////////////////////////////////////////////////////////////////////
553 // Only called once per level, when saved will not be called again
554 //
555 // Downside of the routine is that items can not move about. If the level
556 // has been saved before and reloaded, it could cause a problem if there
557 // are items that spawn at random locations.
558 //
559 //#define DEBUG // uncomment to write out items to a file.
560 ///////////////////////////////////////////////////////////////////////
ACEIT_BuildItemNodeTable(qboolean rebuild)561 void ACEIT_BuildItemNodeTable (qboolean rebuild)
562 {
563 	edict_t *items;
564 	int i,item_index;
565 	vec3_t v,v1,v2;
566 
567 #ifdef DEBUG
568 	FILE *pOut; // for testing
569 	if((pOut = fopen("items.txt","wt"))==NULL)
570 		return;
571 #endif
572 
573 	num_items = 0;
574 
575 	// Add game items
576 	for(items = g_edicts; items < &g_edicts[globals.num_edicts]; items++)
577 	{
578 		// filter out crap
579 		if(items->solid == SOLID_NOT)
580 			continue;
581 
582 		if(!items->classname)
583 			continue;
584 
585 		/////////////////////////////////////////////////////////////////
586 		// Items
587 		/////////////////////////////////////////////////////////////////
588 		item_index = ACEIT_ClassnameToIndex(items->classname);
589 
590 		////////////////////////////////////////////////////////////////
591 		// SPECIAL NAV NODE DROPPING CODE
592 		////////////////////////////////////////////////////////////////
593 		// Special node dropping for platforms
594 		if(strcmp(items->classname,"func_plat")==0)
595 		{
596 			if(!rebuild)
597 				ACEND_AddNode(items,NODE_PLATFORM);
598 			item_index = 99; // to allow to pass the item index test
599 		}
600 
601 		// Special node dropping for teleporters
602 		if(strcmp(items->classname,"misc_teleporter_dest")==0 || strcmp(items->classname,"misc_teleporter")==0)
603 		{
604 			if(!rebuild)
605 				ACEND_AddNode(items,NODE_TELEPORTER);
606 			item_index = 99;
607 		}
608 
609 		#ifdef DEBUG
610 		if(item_index == INVALID)
611 			fprintf(pOut,"Rejected item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]);
612 		else
613 			fprintf(pOut,"item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]);
614 		#endif
615 
616 		if(item_index == INVALID)
617 			continue;
618 
619 		// add a pointer to the item entity
620 		item_table[num_items].ent = items;
621 		item_table[num_items].item = item_index;
622 
623 		// If new, add nodes for items
624 		if(!rebuild)
625 		{
626 			// Add a new node at the item's location.
627 			item_table[num_items].node = ACEND_AddNode(items,NODE_ITEM);
628 			num_items++;
629 		}
630 		else // Now if rebuilding, just relink ent structures
631 		{
632 			// Find stored location
633 			for(i=0;i<numnodes;i++)
634 			{
635 				if(nodes[i].type == NODE_ITEM ||
636 				   nodes[i].type == NODE_PLATFORM ||
637 				   nodes[i].type == NODE_TELEPORTER) // valid types
638 				{
639 					VectorCopy(items->s.origin,v);
640 
641 					// Add 16 to item type nodes
642 					if(nodes[i].type == NODE_ITEM)
643 						v[2] += 16;
644 
645 					// Add 32 to teleporter
646 					if(nodes[i].type == NODE_TELEPORTER)
647 						v[2] += 32;
648 
649 					if(nodes[i].type == NODE_PLATFORM)
650 					{
651 						VectorCopy(items->maxs,v1);
652 						VectorCopy(items->mins,v2);
653 
654 						// To get the center
655 						v[0] = (v1[0] - v2[0]) / 2 + v2[0];
656 						v[1] = (v1[1] - v2[1]) / 2 + v2[1];
657 						v[2] = items->mins[2]+64;
658 					}
659 
660 					if(v[0] == nodes[i].origin[0] &&
661  					   v[1] == nodes[i].origin[1] &&
662 					   v[2] == nodes[i].origin[2])
663 					{
664 						// found a match now link to facts
665 						item_table[num_items].node = i;
666 
667 #ifdef DEBUG
668 						fprintf(pOut,"Relink item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]);
669 #endif
670 						num_items++;
671 					}
672 				}
673 			}
674 		}
675 
676 
677 	}
678 
679 #ifdef DEBUG
680 	fclose(pOut);
681 #endif
682 
683 }
684 
685