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_nodes.c - This file contains all of the
56 // pathing routines for the ACE bot.
57 //
58 ///////////////////////////////////////////////////////////////////////
59
60 #include "../g_local.h"
61 #include "acebot.h"
62
63 // flags
64 qboolean newmap=true;
65
66 // Total number of nodes that are items
67 int numitemnodes;
68
69 // Total number of nodes
70 int numnodes;
71
72 // For debugging paths
73 int show_path_from = -1;
74 int show_path_to = -1;
75
76 // array for node data
77 node_t nodes[MAX_NODES];
78 short int path_table[MAX_NODES][MAX_NODES];
79
80 ///////////////////////////////////////////////////////////////////////
81 // NODE INFORMATION FUNCTIONS
82 ///////////////////////////////////////////////////////////////////////
83
84 ///////////////////////////////////////////////////////////////////////
85 // Determin cost of moving from one node to another
86 ///////////////////////////////////////////////////////////////////////
ACEND_FindCost(int from,int to)87 int ACEND_FindCost(int from, int to)
88 {
89 int curnode;
90 int cost=1; // Shortest possible is 1
91
92 // If we can not get there then return invalid
93 if (path_table[from][to] == INVALID)
94 return INVALID;
95
96 // Otherwise check the path and return the cost
97 curnode = path_table[from][to];
98
99 // Find a path (linear time, very fast)
100 while(curnode != to)
101 {
102 curnode = path_table[curnode][to];
103 if(curnode == INVALID) // something has corrupted the path abort
104 return INVALID;
105 cost++;
106 }
107
108 return cost;
109 }
110
111 ///////////////////////////////////////////////////////////////////////
112 // Find a close node to the player within dist.
113 //
114 // Faster than looking for the closest node, but not very
115 // accurate.
116 ///////////////////////////////////////////////////////////////////////
ACEND_FindCloseReachableNode(edict_t * self,int range,int type)117 int ACEND_FindCloseReachableNode(edict_t *self, int range, int type)
118 {
119 vec3_t v;
120 int i;
121 trace_t tr;
122 float dist;
123
124 range *= range;
125
126 for(i=0;i<numnodes;i++)
127 {
128 if(type == NODE_ALL || type == nodes[i].type) // check node type
129 {
130
131 VectorSubtract(nodes[i].origin,self->s.origin,v); // subtract first
132
133 dist = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
134
135 if(dist < range) // square range instead of sqrt
136 {
137 // make sure it is visible
138 //trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, nodes[i].origin, self, MASK_OPAQUE);
139 tr = gi.trace (self->s.origin, self->mins, self->maxs, nodes[i].origin, self, MASK_OPAQUE);
140
141 if(tr.fraction == 1.0)
142 return i;
143 }
144 }
145 }
146
147 return -1;
148 }
149
150 ///////////////////////////////////////////////////////////////////////
151 // Find the closest node to the player within a certain range
152 ///////////////////////////////////////////////////////////////////////
ACEND_FindClosestReachableNode(edict_t * self,int range,int type)153 int ACEND_FindClosestReachableNode(edict_t *self, int range, int type)
154 {
155 int i;
156 float closest = 99999;
157 float dist;
158 int node=-1;
159 vec3_t v;
160 trace_t tr;
161 float rng;
162 vec3_t maxs,mins;
163
164 VectorCopy(self->mins,mins);
165 VectorCopy(self->maxs,maxs);
166
167 // For Ladders, do not worry so much about reachability
168 if(type == NODE_LADDER)
169 {
170 VectorCopy(vec3_origin,maxs);
171 VectorCopy(vec3_origin,mins);
172 }
173 else
174 mins[2] += 18; // Stepsize
175
176 rng = (float)(range * range); // square range for distance comparison (eliminate sqrt)
177
178 for(i=0;i<numnodes;i++)
179 {
180 if(type == NODE_ALL || type == nodes[i].type) // check node type
181 {
182 VectorSubtract(nodes[i].origin, self->s.origin,v); // subtract first
183
184 dist = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
185
186 if(dist < closest && dist < rng)
187 {
188 // make sure it is visible
189 tr = gi.trace (self->s.origin, mins, maxs, nodes[i].origin, self, MASK_OPAQUE);
190 if(tr.fraction == 1.0)
191 {
192 node = i;
193 closest = dist;
194 }
195 }
196 }
197 }
198
199 return node;
200 }
201
202 ///////////////////////////////////////////////////////////////////////
203 // BOT NAVIGATION ROUTINES
204 ///////////////////////////////////////////////////////////////////////
205
206 ///////////////////////////////////////////////////////////////////////
207 // Set up the goal
208 ///////////////////////////////////////////////////////////////////////
ACEND_SetGoal(edict_t * self,int goal_node)209 void ACEND_SetGoal(edict_t *self, int goal_node)
210 {
211 int node;
212
213 self->goal_node = goal_node;
214 node = ACEND_FindClosestReachableNode(self, NODE_DENSITY*3, NODE_ALL);
215
216 if(node == -1)
217 return;
218
219 if(debug_mode)
220 debug_printf("%s new start node selected %d\n",self->client->pers.netname,node);
221
222
223 self->current_node = node;
224 self->next_node = self->current_node; // make sure we get to the nearest node first
225 self->node_timeout = 0;
226
227 }
228
229 ///////////////////////////////////////////////////////////////////////
230 // Move closer to goal by pointing the bot to the next node
231 // that is closer to the goal
232 ///////////////////////////////////////////////////////////////////////
ACEND_FollowPath(edict_t * self)233 qboolean ACEND_FollowPath(edict_t *self)
234 {
235 vec3_t v;
236
237 //////////////////////////////////////////
238 // Show the path (uncomment for debugging)
239 // show_path_from = self->current_node;
240 // show_path_to = self->goal_node;
241 // ACEND_DrawPath();
242 //////////////////////////////////////////
243
244 // Try again?
245 if(self->node_timeout ++ > 30)
246 {
247 if(self->tries++ > 3)
248 return false;
249 else
250 ACEND_SetGoal(self,self->goal_node);
251 }
252
253 // Are we there yet?
254 VectorSubtract(self->s.origin,nodes[self->next_node].origin,v);
255
256 if(VectorLength(v) < 32)
257 {
258 // reset timeout
259 self->node_timeout = 0;
260
261 if(self->next_node == self->goal_node)
262 {
263 if(debug_mode)
264 debug_printf("%s reached goal!\n",self->client->pers.netname);
265
266 ACEAI_PickLongRangeGoal(self); // Pick a new goal
267 }
268 else
269 {
270 self->current_node = self->next_node;
271 self->next_node = path_table[self->current_node][self->goal_node];
272 }
273 }
274
275 if(self->current_node == -1 || self->next_node ==-1)
276 return false;
277
278 // Set bot's movement vector
279 VectorSubtract (nodes[self->next_node].origin, self->s.origin , self->move_vector);
280
281 return true;
282 }
283
284
285 ///////////////////////////////////////////////////////////////////////
286 // MAPPING CODE
287 ///////////////////////////////////////////////////////////////////////
288
289 ///////////////////////////////////////////////////////////////////////
290 // Capture when the grappling hook has been fired for mapping purposes.
291 ///////////////////////////////////////////////////////////////////////
292 /*void ACEND_GrapFired(edict_t *self)
293 {
294 int closest_node;
295
296 if(!self->owner)
297 return; // should not be here
298
299 // Check to see if the grapple is in pull mode
300 if(self->owner->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL)
301 {
302 // Look for the closest node of type grapple
303 closest_node = ACEND_FindClosestReachableNode(self,NODE_DENSITY,NODE_GRAPPLE);
304 if(closest_node == -1 ) // we need to drop a node
305 {
306 closest_node = ACEND_AddNode(self,NODE_GRAPPLE);
307
308 // Add an edge
309 ACEND_UpdateNodeEdge(self->owner->last_node,closest_node);
310
311 self->owner->last_node = closest_node;
312 }
313 else
314 self->owner->last_node = closest_node; // zero out so other nodes will not be linked
315 }
316 }
317 */
318
319 ///////////////////////////////////////////////////////////////////////
320 // Check for adding ladder nodes
321 ///////////////////////////////////////////////////////////////////////
ACEND_CheckForLadder(edict_t * self)322 qboolean ACEND_CheckForLadder(edict_t *self)
323 {
324 int closest_node;
325
326 // If there is a ladder and we are moving up, see if we should add a ladder node
327 if (gi.pointcontents(self->s.origin) & CONTENTS_LADDER && self->velocity[2] > 0)
328 {
329 //debug_printf("contents: %x\n",tr.contents);
330
331 closest_node = ACEND_FindClosestReachableNode(self,NODE_DENSITY,NODE_LADDER);
332 if(closest_node == -1)
333 {
334 closest_node = ACEND_AddNode(self,NODE_LADDER);
335
336 // Now add link
337 ACEND_UpdateNodeEdge(self->last_node,closest_node);
338
339 // Set current to last
340 self->last_node = closest_node;
341 }
342 else
343 {
344 ACEND_UpdateNodeEdge(self->last_node,closest_node);
345 self->last_node = closest_node; // set visited to last
346 }
347 return true;
348 }
349 return false;
350 }
351
352 ///////////////////////////////////////////////////////////////////////
353 // This routine is called to hook in the pathing code and sets
354 // the current node if valid.
355 ///////////////////////////////////////////////////////////////////////
ACEND_PathMap(edict_t * self)356 void ACEND_PathMap(edict_t *self)
357 {
358 int closest_node;
359 static float last_update=0; // start off low
360 vec3_t v;
361
362 if(level.time < last_update)
363 return;
364
365 last_update = level.time + 0.15; // slow down updates a bit
366
367 // Special node drawing code for debugging
368 if(show_path_to != -1)
369 ACEND_DrawPath();
370
371 ////////////////////////////////////////////////////////
372 // Special check for ladder nodes
373 ///////////////////////////////////////////////////////
374 if(ACEND_CheckForLadder(self)) // check for ladder nodes
375 return;
376
377 // Not on ground, and not in the water, so bail
378 if(!self->groundentity && !self->waterlevel)
379 return;
380
381 ////////////////////////////////////////////////////////
382 // Lava/Slime
383 ////////////////////////////////////////////////////////
384 VectorCopy(self->s.origin,v);
385 v[2] -= 18;
386 if(gi.pointcontents(v) & (CONTENTS_LAVA|CONTENTS_SLIME))
387 return; // no nodes in slime
388
389 ////////////////////////////////////////////////////////
390 // Jumping
391 ///////////////////////////////////////////////////////
392 if(self->is_jumping)
393 {
394 // See if there is a closeby jump landing node (prevent adding too many)
395 closest_node = ACEND_FindClosestReachableNode(self, 64, NODE_JUMP);
396
397 if(closest_node == INVALID)
398 closest_node = ACEND_AddNode(self,NODE_JUMP);
399
400 // Now add link
401 if(self->last_node != -1)
402 ACEND_UpdateNodeEdge(self->last_node, closest_node);
403
404 self->is_jumping = false;
405 return;
406 }
407
408 ////////////////////////////////////////////////////////////
409 // Grapple
410 // Do not add nodes during grapple, added elsewhere manually
411 ////////////////////////////////////////////////////////////
412 /*if(ctf->value && self->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL)
413 return;*/
414
415 // Iterate through all nodes to make sure far enough apart
416 closest_node = ACEND_FindClosestReachableNode(self, NODE_DENSITY, NODE_ALL);
417
418 ////////////////////////////////////////////////////////
419 // Special Check for Platforms
420 ////////////////////////////////////////////////////////
421 if(self->groundentity && self->groundentity->use == Use_Plat)
422 {
423 if(closest_node == INVALID)
424 return; // Do not want to do anything here.
425
426 // Here we want to add links
427 if(closest_node != self->last_node && self->last_node != INVALID)
428 ACEND_UpdateNodeEdge(self->last_node,closest_node);
429
430 self->last_node = closest_node; // set visited to last
431 return;
432 }
433
434 ////////////////////////////////////////////////////////
435 // Add Nodes as needed
436 ////////////////////////////////////////////////////////
437 if(closest_node == INVALID)
438 {
439 // Add nodes in the water as needed
440 if(self->waterlevel)
441 closest_node = ACEND_AddNode(self,NODE_WATER);
442 else
443 closest_node = ACEND_AddNode(self,NODE_MOVE);
444
445 // Now add link
446 if(self->last_node != -1)
447 ACEND_UpdateNodeEdge(self->last_node, closest_node);
448
449 }
450 else if(closest_node != self->last_node && self->last_node != INVALID)
451 ACEND_UpdateNodeEdge(self->last_node,closest_node);
452
453 self->last_node = closest_node; // set visited to last
454
455 }
456
457 ///////////////////////////////////////////////////////////////////////
458 // Init node array (set all to INVALID)
459 ///////////////////////////////////////////////////////////////////////
ACEND_InitNodes(void)460 void ACEND_InitNodes(void)
461 {
462 numnodes = 1;
463 numitemnodes = 1;
464 memset(nodes,0,sizeof(node_t) * MAX_NODES);
465 memset(path_table,INVALID,sizeof(short int)*MAX_NODES*MAX_NODES);
466
467 }
468
469 ///////////////////////////////////////////////////////////////////////
470 // Show the node for debugging (utility function)
471 ///////////////////////////////////////////////////////////////////////
ACEND_ShowNode(int node)472 void ACEND_ShowNode(int node)
473 {
474 edict_t *ent;
475
476 return; // commented out for now. uncommend to show nodes during debugging,
477 // but too many will cause overflows. You have been warned.
478
479 ent = G_Spawn();
480
481 ent->movetype = MOVETYPE_NONE;
482 ent->solid = SOLID_NOT;
483
484 if(nodes[node].type == NODE_MOVE)
485 ent->s.renderfx = RF_SHELL_BLUE;
486 else if (nodes[node].type == NODE_WATER)
487 ent->s.renderfx = RF_SHELL_RED;
488 else
489 ent->s.renderfx = RF_SHELL_GREEN; // action nodes
490
491 ent->s.modelindex = gi.modelindex ("models/items/ammo/grenades/medium/tris.md2");
492 ent->owner = ent;
493 ent->nextthink = level.time + 200000.0;
494 ent->think = G_FreeEdict;
495 ent->dmg = 0;
496
497 VectorCopy(nodes[node].origin,ent->s.origin);
498 gi.linkentity (ent);
499
500 }
501
502 ///////////////////////////////////////////////////////////////////////
503 // Draws the current path (utility function)
504 ///////////////////////////////////////////////////////////////////////
ACEND_DrawPath()505 void ACEND_DrawPath()
506 {
507 int current_node, goal_node, next_node;
508
509 current_node = show_path_from;
510 goal_node = show_path_to;
511
512 next_node = path_table[current_node][goal_node];
513
514 // Now set up and display the path
515 while(current_node != goal_node && current_node != -1)
516 {
517 gi.WriteByte (svc_temp_entity);
518 gi.WriteByte (TE_BFG_LASER);
519 gi.WritePosition (nodes[current_node].origin);
520 gi.WritePosition (nodes[next_node].origin);
521 gi.multicast (nodes[current_node].origin, MULTICAST_PVS);
522 current_node = next_node;
523 next_node = path_table[current_node][goal_node];
524 }
525 }
526
527 ///////////////////////////////////////////////////////////////////////
528 // Turns on showing of the path, set goal to -1 to
529 // shut off. (utility function)
530 ///////////////////////////////////////////////////////////////////////
ACEND_ShowPath(edict_t * self,int goal_node)531 void ACEND_ShowPath(edict_t *self, int goal_node)
532 {
533 show_path_from = ACEND_FindClosestReachableNode(self, NODE_DENSITY, NODE_ALL);
534 show_path_to = goal_node;
535 }
536
537 ///////////////////////////////////////////////////////////////////////
538 // Add a node of type ?
539 ///////////////////////////////////////////////////////////////////////
ACEND_AddNode(edict_t * self,int type)540 int ACEND_AddNode(edict_t *self, int type)
541 {
542 vec3_t v1,v2;
543
544 // Block if we exceed maximum
545 if (numnodes + 1 > MAX_NODES)
546 return false;
547
548 // Set location
549 VectorCopy(self->s.origin,nodes[numnodes].origin);
550
551 // Set type
552 nodes[numnodes].type = type;
553
554 /////////////////////////////////////////////////////
555 // ITEMS
556 // Move the z location up just a bit.
557 if(type == NODE_ITEM)
558 {
559 nodes[numnodes].origin[2] += 16;
560 numitemnodes++;
561 }
562
563 // Teleporters
564 if(type == NODE_TELEPORTER)
565 {
566 // Up 32
567 nodes[numnodes].origin[2] += 32;
568 }
569
570 if(type == NODE_LADDER)
571 {
572 nodes[numnodes].type = NODE_LADDER;
573
574 if(debug_mode)
575 {
576 debug_printf("Node added %d type: Ladder\n",numnodes);
577 ACEND_ShowNode(numnodes);
578 }
579
580 numnodes++;
581 return numnodes-1; // return the node added
582
583 }
584
585 // For platforms drop two nodes one at top, one at bottom
586 if(type == NODE_PLATFORM)
587 {
588 VectorCopy(self->maxs,v1);
589 VectorCopy(self->mins,v2);
590
591 // To get the center
592 nodes[numnodes].origin[0] = (v1[0] - v2[0]) / 2 + v2[0];
593 nodes[numnodes].origin[1] = (v1[1] - v2[1]) / 2 + v2[1];
594 nodes[numnodes].origin[2] = self->maxs[2];
595
596 if(debug_mode)
597 ACEND_ShowNode(numnodes);
598
599 numnodes++;
600
601 nodes[numnodes].origin[0] = nodes[numnodes-1].origin[0];
602 nodes[numnodes].origin[1] = nodes[numnodes-1].origin[1];
603 nodes[numnodes].origin[2] = self->mins[2]+64;
604
605 nodes[numnodes].type = NODE_PLATFORM;
606
607 // Add a link
608 ACEND_UpdateNodeEdge(numnodes,numnodes-1);
609
610 if(debug_mode)
611 {
612 debug_printf("Node added %d type: Platform\n",numnodes);
613 ACEND_ShowNode(numnodes);
614 }
615
616 numnodes++;
617
618 return numnodes -1;
619 }
620
621 if(debug_mode)
622 {
623 if(nodes[numnodes].type == NODE_MOVE)
624 debug_printf("Node added %d type: Move\n",numnodes);
625 else if(nodes[numnodes].type == NODE_TELEPORTER)
626 debug_printf("Node added %d type: Teleporter\n",numnodes);
627 else if(nodes[numnodes].type == NODE_ITEM)
628 debug_printf("Node added %d type: Item\n",numnodes);
629 else if(nodes[numnodes].type == NODE_WATER)
630 debug_printf("Node added %d type: Water\n",numnodes);
631 else if(nodes[numnodes].type == NODE_GRAPPLE)
632 debug_printf("Node added %d type: Grapple\n",numnodes);
633
634 ACEND_ShowNode(numnodes);
635 }
636
637 numnodes++;
638
639 return numnodes-1; // return the node added
640 }
641
642 ///////////////////////////////////////////////////////////////////////
643 // Add/Update node connections (paths)
644 ///////////////////////////////////////////////////////////////////////
ACEND_UpdateNodeEdge(int from,int to)645 void ACEND_UpdateNodeEdge(int from, int to)
646 {
647 int i;
648
649 if(from == -1 || to == -1 || from == to)
650 return; // safety
651
652 // Add the link
653 path_table[from][to] = to;
654
655 // Now for the self-referencing part, linear time for each link added
656 for(i=0;i<numnodes;i++)
657 if(path_table[i][from] != INVALID)
658 if(i == to)
659 path_table[i][to] = INVALID; // make sure we terminate
660 else
661 path_table[i][to] = path_table[i][from];
662
663 if(debug_mode)
664 debug_printf("Link %d -> %d\n", from, to);
665 }
666
667 ///////////////////////////////////////////////////////////////////////
668 // Remove a node edge
669 ///////////////////////////////////////////////////////////////////////
ACEND_RemoveNodeEdge(edict_t * self,int from,int to)670 void ACEND_RemoveNodeEdge(edict_t *self, int from, int to)
671 {
672 int i;
673
674 if(debug_mode)
675 debug_printf("%s: Removing Edge %d -> %d\n", self->client->pers.netname, from, to);
676
677 path_table[from][to] = INVALID; // set to invalid
678
679 // Make sure this gets updated in our path array
680 for(i=0;i<numnodes;i++)
681 if(path_table[from][i] == to)
682 path_table[from][i] = INVALID;
683 }
684
685 ///////////////////////////////////////////////////////////////////////
686 // This function will resolve all paths that are incomplete
687 // usually called before saving to disk
688 ///////////////////////////////////////////////////////////////////////
ACEND_ResolveAllPaths()689 void ACEND_ResolveAllPaths()
690 {
691 int i, from, to;
692 int num=0;
693
694 safe_bprintf(PRINT_HIGH,"Resolving all paths...");
695
696 for(from=0;from<numnodes;from++)
697 for(to=0;to<numnodes;to++)
698 {
699 // update unresolved paths
700 // Not equal to itself, not equal to -1 and equal to the last link
701 if(from != to && path_table[from][to] == to)
702 {
703 num++;
704
705 // Now for the self-referencing part linear time for each link added
706 for(i=0;i<numnodes;i++)
707 if(path_table[i][from] != -1)
708 if(i == to)
709 path_table[i][to] = -1; // make sure we terminate
710 else
711 path_table[i][to] = path_table[i][from];
712 }
713 }
714
715 safe_bprintf(PRINT_MEDIUM,"done (%d updated)\n",num);
716 }
717
718 ///////////////////////////////////////////////////////////////////////
719 // Save to disk file
720 //
721 // Since my compression routines are one thing I did not want to
722 // release, I took out the compressed format option. Most levels will
723 // save out to a node file around 50-200k, so compression is not really
724 // a big deal.
725 ///////////////////////////////////////////////////////////////////////
ACEND_SaveNodes()726 void ACEND_SaveNodes()
727 {
728 FILE *pOut;
729 char filename[60];
730 int i,j;
731 int version = 1;
732
733 // Resolve paths
734 ACEND_ResolveAllPaths();
735
736 safe_bprintf(PRINT_MEDIUM,"Saving node table...");
737
738 strcpy(filename,"lights/nav/");
739 strcat(filename,level.mapname);
740 strcat(filename,".nod");
741
742 if((pOut = fopen(filename, "wb" )) == NULL)
743 return; // bail
744
745 fwrite(&version,sizeof(int),1,pOut); // write version
746 fwrite(&numnodes,sizeof(int),1,pOut); // write count
747 fwrite(&num_items,sizeof(int),1,pOut); // write facts count
748
749 fwrite(nodes,sizeof(node_t),numnodes,pOut); // write nodes
750
751 for(i=0;i<numnodes;i++)
752 for(j=0;j<numnodes;j++)
753 fwrite(&path_table[i][j],sizeof(short int),1,pOut); // write count
754
755 fwrite(item_table,sizeof(item_table_t),num_items,pOut); // write out the fact table
756
757 fclose(pOut);
758
759 safe_bprintf(PRINT_MEDIUM,"done.\n");
760 }
761
762 ///////////////////////////////////////////////////////////////////////
763 // Read from disk file
764 ///////////////////////////////////////////////////////////////////////
ACEND_LoadNodes(void)765 void ACEND_LoadNodes(void)
766 {
767 FILE *pIn;
768 int i,j;
769 char filename[60];
770 int version;
771
772 strcpy(filename,"lights/nav/");
773 strcat(filename,level.mapname);
774 strcat(filename,".nod");
775
776 if((pIn = fopen(filename, "rb" )) == NULL)
777 {
778 // Create item table
779 safe_bprintf(PRINT_MEDIUM, "ACE: No node file found, creating new one...");
780 ACEIT_BuildItemNodeTable(false);
781 safe_bprintf(PRINT_MEDIUM, "done.\n");
782 return;
783 }
784
785 // determin version
786 fread(&version,sizeof(int),1,pIn); // read version
787
788 if(version == 1)
789 {
790 safe_bprintf(PRINT_MEDIUM,"ACE: Loading node table...");
791
792 fread(&numnodes,sizeof(int),1,pIn); // read count
793 fread(&num_items,sizeof(int),1,pIn); // read facts count
794
795 fread(nodes,sizeof(node_t),numnodes,pIn);
796
797 for(i=0;i<numnodes;i++)
798 for(j=0;j<numnodes;j++)
799 fread(&path_table[i][j],sizeof(short int),1,pIn); // write count
800
801 fread(item_table,sizeof(item_table_t),num_items,pIn);
802 fclose(pIn);
803 }
804 else
805 {
806 // Create item table
807 safe_bprintf(PRINT_MEDIUM, "ACE: No node file found, creating new one...");
808 ACEIT_BuildItemNodeTable(false);
809 safe_bprintf(PRINT_MEDIUM, "done.\n");
810 return; // bail
811 }
812
813 safe_bprintf(PRINT_MEDIUM, "done.\n");
814
815 ACEIT_BuildItemNodeTable(true);
816
817 }
818
819
820