1 /* $Id: ai.c,v 1.6 2003-06-16 06:57:34 btb Exp $ */
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
17 * Autonomous Individual movement.
20 * Revision 1.1 1995/12/05 14:15:37 allender
23 * Revision 1.10 1995/11/09 09:36:12 allender
24 * cheats not active during demo playback
26 * Revision 1.9 1995/11/03 12:51:55 allender
29 * Revision 1.8 1995/10/31 10:25:07 allender
32 * Revision 1.7 1995/10/26 14:01:38 allender
33 * optimization for doing robot stuff only if anim angles done last frame
35 * Revision 1.6 1995/10/25 09:35:43 allender
36 * prototype some functions causing mcc problems
38 * Revision 1.5 1995/10/17 13:11:40 allender
39 * fix in ai code that makes bots only look for you every so often
41 * Revision 1.4 1995/10/10 11:48:10 allender
44 * Revision 2.11 1995/07/09 11:15:48 john
45 * Put in Mike's code to fix bug where bosses don't gate in bots after
46 * 32767 seconds of playing.
48 * Revision 2.10 1995/06/15 12:31:08 john
49 * Fixed bug with cheats getting enabled when you type
52 * Revision 2.9 1995/05/26 16:16:18 john
53 * Split SATURN into define's for requiring cd, using cd, etc.
54 * Also started adding all the Rockwell stuff.
56 * Revision 2.8 1995/04/06 15:12:27 john
57 * Fixed bug with insane not working.
59 * Revision 2.7 1995/03/30 16:36:44 mike
62 * Revision 2.6 1995/03/28 11:22:24 john
63 * Added cheats to save file. Changed lunacy text.
65 * Revision 2.5 1995/03/27 16:45:07 john
66 * Fixed some cheat bugs. Added astral cheat.
68 * Revision 2.4 1995/03/24 15:29:17 mike
71 * Revision 2.3 1995/03/21 14:39:45 john
72 * Ifdef'd out the NETWORK code.
74 * Revision 2.2 1995/03/14 18:24:39 john
75 * Force Destination Saturn to use CD-ROM drive.
77 * Revision 2.1 1995/03/06 16:47:14 mike
80 * Revision 2.0 1995/02/27 11:30:01 john
81 * New version 2.0, which has no anonymous unions, builds with
82 * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
84 * Revision 1.295 1995/02/22 13:23:04 allender
85 * remove anonymous unions from object structure
87 * Revision 1.294 1995/02/13 11:00:43 rob
88 * Make brain guys high enough to get an open slot.
90 * Revision 1.293 1995/02/13 10:31:55 mike
91 * Make brains understand they can't open locked doors.
93 * Revision 1.292 1995/02/13 10:18:01 rob
94 * Reduced brain guy's level of awareness to keep him from hogging slots.
96 * Revision 1.291 1995/02/11 12:27:12 mike
97 * fix path-to-exit cheat.
99 * Revision 1.290 1995/02/11 01:56:30 mike
100 * robots don't fire cheat.
102 * Revision 1.289 1995/02/10 17:15:09 rob
103 * Fixed some stuff with 64 awareness stuff.
105 * Revision 1.288 1995/02/10 16:31:32 mike
108 * Revision 1.287 1995/02/10 16:24:45 mike
109 * fix the network follow path fix.
111 * Revision 1.286 1995/02/10 16:11:40 mike
112 * in serial or modem games, follow path guys don't move if far away and
115 * Revision 1.285 1995/02/09 13:11:35 mike
116 * comment out a bunch of mprintfs.
117 * add toaster (drops prox bombs, runs away) to boss gate list.
119 * Revision 1.284 1995/02/08 22:44:53 rob
120 * Lowerd anger level for follow path of any sort.
122 * Revision 1.283 1995/02/08 22:30:43 mike
123 * lower awareness on station guys if they are returning home (multiplayer).
125 * Revision 1.282 1995/02/08 17:01:06 rob
126 * Fixed problem with toasters dropping of proximity bombs.
128 * Revision 1.281 1995/02/08 11:49:35 rob
129 * Reduce Green-guy attack awareness level so we don't let him attack us too.
131 * Revision 1.280 1995/02/08 11:37:52 mike
132 * Check for failures in call to obj_create.
134 * Revision 1.279 1995/02/07 20:38:46 mike
135 * fix toasters in multiplayer
138 * Revision 1.278 1995/02/07 16:51:07 mike
139 * fix sound time play bug.
141 * Revision 1.277 1995/02/06 22:33:04 mike
142 * make robots follow path better in cooperative/roboarchy.
144 * Revision 1.276 1995/02/06 18:15:42 rob
145 * Added forced sends for evasion movemnet.
147 * Revision 1.275 1995/02/06 16:41:22 rob
148 * Change some positioning calls.
150 * Revision 1.274 1995/02/06 11:40:33 mike
151 * replace some lint-related hacks with clean, proper code.
153 * Revision 1.273 1995/02/04 17:28:19 mike
154 * make station guys return better.
156 * Revision 1.272 1995/02/03 17:40:55 mike
157 * fix problem with robots falling asleep if you sit in game overnight, not in pause...bah.
159 * Revision 1.271 1995/02/02 21:11:25 rob
160 * Tweaking stuff for multiplayer ai.
162 * Revision 1.270 1995/02/02 17:32:06 john
163 * Added Hack for Assert that Mike put in after using Lint to find
164 * uninitialized variables.
166 * Revision 1.269 1995/02/02 16:46:31 mike
169 * Revision 1.268 1995/02/02 16:27:29 mike
170 * make boss not put out infinite robots.
172 * Revision 1.267 1995/02/01 21:10:02 mike
173 * lint found bug! player_visibility not initialized!
175 * Revision 1.266 1995/02/01 20:51:27 john
178 * Revision 1.265 1995/02/01 17:14:05 mike
181 * Revision 1.264 1995/01/31 16:16:40 mike
182 * Comment out "Darn you, John" Int3().
184 * Revision 1.263 1995/01/30 20:55:04 mike
185 * fix nonsense in robot firing when a player is cloaked.
187 * Revision 1.262 1995/01/30 17:15:10 rob
188 * Fixed problems with bigboss eclip messages.
189 * Tweaked robot position sending for modem purposes.
191 * Revision 1.261 1995/01/30 15:30:31 rob
192 * Prevent non-master players from gating in robots.
194 * Revision 1.260 1995/01/30 13:30:55 mike
195 * new cases for firing at other players were bogus, could send position
196 * without permission.
198 * Revision 1.259 1995/01/30 13:01:17 mike
199 * Make robots fire at player other than one they are controlled by sometimes.
201 * Revision 1.258 1995/01/29 16:09:17 rob
202 * Trying to get robots to shoot at non-controlling players.
204 * Revision 1.257 1995/01/29 13:47:05 mike
205 * Make boss have more fireballs on death, have until end (though silent at end).
206 * Fix bug which was preventing him from teleporting until hit, so he'd always
207 * be in the same place when the player enters the room.
209 * Revision 1.256 1995/01/28 17:40:18 mike
210 * make boss teleport & gate before you see him.
212 * Revision 1.255 1995/01/27 17:02:08 mike
213 * move code around, was sending one frame (or worse!) old robot information.
215 * Revision 1.254 1995/01/26 17:02:43 mike
216 * make fusion cannon have more chrome, make fusion, mega rock you!
218 * Revision 1.253 1995/01/26 15:11:17 rob
219 * Shutup! I fixed it!
221 * Revision 1.252 1995/01/26 15:08:55 rob
222 * Changed robot gating to accomodate multiplayer.
224 * Revision 1.251 1995/01/26 14:49:04 rob
225 * Increase awareness level for firing to 94.
227 * Revision 1.250 1995/01/26 12:41:20 mike
228 * fix bogus multiplayer code, would send permission without getting permission.
230 * Revision 1.249 1995/01/26 12:23:23 rob
231 * Removed defines that were moved to ai.h
233 * Revision 1.248 1995/01/25 23:38:48 mike
234 * modify list of robots gated in by super boss.
236 * Revision 1.247 1995/01/25 21:21:13 rob
237 * Trying to let robots fire at a player even if they're not in control.
239 * Revision 1.246 1995/01/25 13:50:37 mike
240 * Robots make angry sounds.
242 * Revision 1.245 1995/01/25 10:53:47 mike
243 * better handling of robots which poke out of mine and try to recover.
245 * Revision 1.244 1995/01/24 22:03:02 mike
246 * Tricky code to move a robot to a legal position if he is poking out of
247 * the mine, even if it means moving him to another segment.
249 * Revision 1.243 1995/01/24 20:12:06 rob
250 * Changed robot fire awareness level from 74 to 94.
252 * Revision 1.242 1995/01/24 13:22:32 mike
253 * make robots accelerate faster, and Difficulty_level dependent.
255 * Revision 1.241 1995/01/24 12:09:39 mike
256 * make robots animate in multiplayer.
258 * Revision 1.240 1995/01/21 21:21:10 mike
259 * Make boss only gate robots into specified segments.
261 * Revision 1.239 1995/01/20 20:21:26 mike
262 * prevent unnecessary boss cloaking.
271 char ai_rcsid[] = "$Id: ai.c,v 1.6 2003-06-16 06:57:34 btb Exp $";
296 #include "fireball.h"
301 #include "cntrlcen.h"
302 #include "multibot.h"
313 #include "controls.h"
317 #include "editor/editor.h"
326 // ---------- John: These variables must be saved as part of gamesave. --------
327 int Ai_initialized = 0;
328 int Overall_agitation;
329 ai_local Ai_local_info[MAX_OBJECTS];
330 point_seg Point_segs[MAX_POINT_SEGS];
331 point_seg *Point_segs_free_ptr = Point_segs;
332 ai_cloak_info Ai_cloak_info[MAX_AI_CLOAK_INFO];
333 fix Boss_cloak_start_time = 0;
334 fix Boss_cloak_end_time = 0;
335 fix Last_teleport_time = 0;
336 fix Boss_teleport_interval = F1_0*8;
337 fix Boss_cloak_interval = F1_0*10; // Time between cloaks
338 fix Boss_cloak_duration = BOSS_CLOAK_DURATION;
339 fix Last_gate_time = 0;
340 fix Gate_interval = F1_0*6;
341 fix Boss_dying_start_time;
343 byte Boss_dying, Boss_dying_sound_playing, unused123, unused234;
345 // -- MK, 10/21/95, unused! -- int Boss_been_hit=0;
348 // ------ John: End of variables which must be saved as part of gamesave. -----
351 // -- ubyte Boss_cloaks[NUM_D2_BOSSES] = {1,1,1,1,1,1}; // Set byte if this boss can cloak
353 ubyte Boss_teleports[NUM_D2_BOSSES] = {1,1,1,1,1,1, 1,1}; // Set byte if this boss can teleport
354 ubyte Boss_spew_more[NUM_D2_BOSSES] = {0,1,0,0,0,0, 0,0}; // If set, 50% of time, spew two bots.
355 ubyte Boss_spews_bots_energy[NUM_D2_BOSSES] = {1,1,0,1,0,1, 1,1}; // Set byte if boss spews bots when hit by energy weapon.
356 ubyte Boss_spews_bots_matter[NUM_D2_BOSSES] = {0,0,1,1,1,1, 0,1}; // Set byte if boss spews bots when hit by matter weapon.
357 ubyte Boss_invulnerable_energy[NUM_D2_BOSSES] = {0,0,1,1,0,0, 0,0}; // Set byte if boss is invulnerable to energy weapons.
358 ubyte Boss_invulnerable_matter[NUM_D2_BOSSES] = {0,0,0,0,1,1, 1,0}; // Set byte if boss is invulnerable to matter weapons.
359 ubyte Boss_invulnerable_spot[NUM_D2_BOSSES] = {0,0,0,0,0,1, 0,1}; // Set byte if boss is invulnerable in all but a certain spot. (Dot product fvec|vec_to_collision < BOSS_INVULNERABLE_DOT)
363 // -- byte Super_boss_gate_list[MAX_GATE_INDEX] = {0, 1, 8, 9, 10, 11, 12, 15, 16, 18, 19, 20, 22, 0, 8, 11, 19, 20, 8, 20, 8};
365 int Robot_firing_enabled = 1;
366 int Animation_enabled = 1;
369 int Ai_info_enabled=0;
373 // These globals are set by a call to find_vector_intersection, which is a slow routine,
374 // so we don't want to call it again (for this object) unless we have to.
376 int Hit_type, Hit_seg;
379 int Num_awareness_events = 0;
380 awareness_event Awareness_events[MAX_AWARENESS_EVENTS];
382 vms_vector Believed_player_pos;
383 int Believed_player_seg;
386 // Index into this array with ailp->mode
387 char *mode_text[18] = {
408 // Index into this array with aip->behavior
409 char behavior_text[6][9] = {
418 // Index into this array with aip->GOAL_STATE or aip->CURRENT_STATE
419 char state_text[8][5] = {
433 // Current state indicates where the robot current is, or has just done.
434 // Transition table between states for an AI object.
435 // First dimension is trigger event.
436 // Second dimension is current state.
437 // Third dimension is goal state.
438 // Result is new goal state.
439 // ERR_ means something impossible has happened.
440 byte Ai_transition_table[AI_MAX_EVENT][AI_MAX_STATE][AI_MAX_STATE] = {
442 // Event = AIE_FIRE, a nearby object fired
443 // none rest srch lock flin fire reco // CURRENT is rows, GOAL is columns
444 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO }, // none
445 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO }, // rest
446 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO }, // search
447 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO }, // lock
448 { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO }, // flinch
449 { AIS_ERR_, AIS_FIRE, AIS_FIRE, AIS_FIRE, AIS_FLIN, AIS_FIRE, AIS_RECO }, // fire
450 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE } // recoil
453 // Event = AIE_HITT, a nearby object was hit (or a wall was hit)
455 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
456 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
457 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
458 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
459 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FLIN},
460 { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO},
461 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE}
464 // Event = AIE_COLL, player collided with robot
466 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
467 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
468 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
469 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
470 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_LOCK, AIS_FLIN, AIS_FLIN},
471 { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO},
472 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE}
475 // Event = AIE_HURT, player hurt robot (by firing at and hitting it)
476 // Note, this doesn't necessarily mean the robot JUST got hit, only that that is the most recent thing that happened.
478 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
479 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
480 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
481 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
482 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
483 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
484 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN}
490 fix Dist_to_last_fired_upon_player_pos = 0;
492 // ----------------------------------------------------------------------------
493 void init_ai_frame(void)
497 Dist_to_last_fired_upon_player_pos = vm_vec_dist_quick(&Last_fired_upon_player_pos, &Believed_player_pos);
499 ab_state = Afterburner_charge && Controls.afterburner_state && (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER);
501 if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) || (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT_ON) || ab_state) {
506 // ----------------------------------------------------------------------------
507 // Return firing status.
508 // If ready to fire a weapon, return true, else return false.
509 // Ready to fire a weapon if next_fire <= 0 or next_fire2 <= 0.
510 int ready_to_fire(robot_info *robptr, ai_local *ailp)
512 if (robptr->weapon_type2 != -1)
513 return (ailp->next_fire <= 0) || (ailp->next_fire2 <= 0);
515 return (ailp->next_fire <= 0);
518 // ----------------------------------------------------------------------------
519 // Make a robot near the player snipe.
520 #define MNRS_SEG_MAX 70
521 void make_nearby_robot_snipe(void)
524 short bfs_list[MNRS_SEG_MAX];
526 create_bfs_list(ConsoleObject->segnum, bfs_list, &bfs_length, MNRS_SEG_MAX);
528 for (i=0; i<bfs_length; i++) {
529 int objnum = Segments[bfs_list[i]].objects;
531 while (objnum != -1) {
532 object *objp = &Objects[objnum];
533 robot_info *robptr = &Robot_info[objp->id];
535 if ((objp->type == OBJ_ROBOT) && (objp->id != ROBOT_BRAIN)) {
536 if ((objp->ctype.ai_info.behavior != AIB_SNIPE) && (objp->ctype.ai_info.behavior != AIB_RUN_FROM) && !Robot_info[objp->id].boss_flag && !robptr->companion) {
537 objp->ctype.ai_info.behavior = AIB_SNIPE;
538 Ai_local_info[objnum].mode = AIM_SNIPE_ATTACK;
539 mprintf((0, "Making robot #%i go into snipe mode!\n", objnum));
547 mprintf((0, "Couldn't find a robot to make snipe!\n"));
551 int Ai_last_missile_camera;
553 int Robots_kill_robots_cheat = 0;
555 // --------------------------------------------------------------------------------------------------------------------
556 void do_ai_frame(object *obj)
558 int objnum = obj-Objects;
559 ai_static *aip = &obj->ctype.ai_info;
560 ai_local *ailp = &Ai_local_info[objnum];
562 vms_vector vec_to_player;
565 int player_visibility=-1;
569 int visibility_and_vec_computed = 0;
570 int previous_visibility;
571 vms_vector gun_point;
572 vms_vector vis_vec_pos;
574 ailp->next_action_time -= FrameTime;
576 if (aip->SKIP_AI_COUNT) {
577 aip->SKIP_AI_COUNT--;
578 if (obj->mtype.phys_info.flags & PF_USES_THRUST) {
579 obj->mtype.phys_info.rotthrust.x = (obj->mtype.phys_info.rotthrust.x * 15)/16;
580 obj->mtype.phys_info.rotthrust.y = (obj->mtype.phys_info.rotthrust.y * 15)/16;
581 obj->mtype.phys_info.rotthrust.z = (obj->mtype.phys_info.rotthrust.z * 15)/16;
582 if (!aip->SKIP_AI_COUNT)
583 obj->mtype.phys_info.flags &= ~PF_USES_THRUST;
588 robptr = &Robot_info[obj->id];
589 Assert(robptr->always_0xabcd == 0xabcd);
591 if (do_any_robot_dying_frame(obj))
594 // Kind of a hack. If a robot is flinching, but it is time for it to fire, unflinch it.
595 // Else, you can turn a big nasty robot into a wimp by firing flares at it.
596 // This also allows the player to see the cool flinch effect for mechs without unbalancing the game.
597 if ((aip->GOAL_STATE == AIS_FLIN) && ready_to_fire(robptr, ailp)) {
598 aip->GOAL_STATE = AIS_FIRE;
602 if ((aip->behavior == AIB_RUN_FROM) && (ailp->mode != AIM_RUN_FROM_OBJECT))
603 Int3(); // This is peculiar. Behavior is run from, but mode is not. Contact Mike.
605 mprintf_animation_info((obj));
610 if (Break_on_object != -1)
611 if ((obj-Objects) == Break_on_object)
612 Int3(); // Contact Mike: This is a debug break
615 //mprintf((0, "Object %i: behavior = %02x, mode = %i, awareness = %i, time = %7.3f\n", obj-Objects, aip->behavior, ailp->mode, ailp->player_awareness_type, f2fl(ailp->player_awareness_time)));
616 //mprintf((0, "Object %i: behavior = %02x, mode = %i, awareness = %i, cur=%i, goal=%i\n", obj-Objects, aip->behavior, ailp->mode, ailp->player_awareness_type, aip->CURRENT_STATE, aip->GOAL_STATE));
618 //Assert((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR));
619 if (!((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR))) {
620 //mprintf((0, "Object %i behavior is %i, setting to AIB_NORMAL, fix in editor!\n", objnum, aip->behavior));
621 aip->behavior = AIB_NORMAL;
624 Assert(obj->segnum != -1);
625 Assert(obj->id < N_robot_types);
627 obj_ref = objnum ^ FrameCount;
629 if (ailp->next_fire > -F1_0*8)
630 ailp->next_fire -= FrameTime;
632 if (robptr->weapon_type2 != -1) {
633 if (ailp->next_fire2 > -F1_0*8)
634 ailp->next_fire2 -= FrameTime;
636 ailp->next_fire2 = F1_0*8;
638 if (ailp->time_since_processed < F1_0*256)
639 ailp->time_since_processed += FrameTime;
641 previous_visibility = ailp->previous_visibility; // Must get this before we toast the master copy!
643 // -- (No robots have this behavior...)
644 // -- // Deal with cloaking for robots which are cloaked except just before firing.
645 // -- if (robptr->cloak_type == RI_CLOAKED_EXCEPT_FIRING)
646 // -- if (ailp->next_fire < F1_0/2)
647 // -- aip->CLOAKED = 1;
649 // -- aip->CLOAKED = 0;
651 // If only awake because of a camera, make that the believed player position.
652 if ((aip->SUB_FLAGS & SUB_FLAGS_CAMERA_AWAKE) && (Ai_last_missile_camera != -1))
653 Believed_player_pos = Objects[Ai_last_missile_camera].pos;
655 if (Robots_kill_robots_cheat) {
656 vis_vec_pos = obj->pos;
657 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
658 if (player_visibility) {
659 int ii, min_obj = -1;
660 fix min_dist = F1_0*200, cur_dist;
662 for (ii=0; ii<=Highest_object_index; ii++)
663 if ((Objects[ii].type == OBJ_ROBOT) && (ii != objnum)) {
664 cur_dist = vm_vec_dist_quick(&obj->pos, &Objects[ii].pos);
666 if (cur_dist < F1_0*100)
667 if (object_to_object_visibility(obj, &Objects[ii], FQ_TRANSWALL))
668 if (cur_dist < min_dist) {
674 Believed_player_pos = Objects[min_obj].pos;
675 Believed_player_seg = Objects[min_obj].segnum;
676 vm_vec_normalized_dir_quick(&vec_to_player, &Believed_player_pos, &obj->pos);
683 visibility_and_vec_computed = 0;
684 if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
685 Believed_player_pos = ConsoleObject->pos;
687 Believed_player_pos = Ai_cloak_info[objnum & (MAX_AI_CLOAK_INFO-1)].last_position;
690 dist_to_player = vm_vec_dist_quick(&Believed_player_pos, &obj->pos);
691 //if (robptr->companion)
692 // mprintf((0, "%3i: %3i %8.3f %8s %8s [%3i %4i]\n", objnum, obj->segnum, f2fl(dist_to_player), mode_text[ailp->mode], behavior_text[aip->behavior-0x80], aip->hide_index, aip->path_length));
694 // If this robot can fire, compute visibility from gun position.
695 // Don't want to compute visibility twice, as it is expensive. (So is call to calc_gun_point).
696 if ((previous_visibility || !(obj_ref & 3)) && ready_to_fire(robptr, ailp) && (dist_to_player < F1_0*200) && (robptr->n_guns) && !(robptr->attack_type)) {
697 // Since we passed ready_to_fire(), either next_fire or next_fire2 <= 0. calc_gun_point from relevant one.
698 // If both are <= 0, we will deal with the mess in ai_do_actual_firing_stuff
699 if (ailp->next_fire <= 0)
700 calc_gun_point(&gun_point, obj, aip->CURRENT_GUN);
702 calc_gun_point(&gun_point, obj, 0);
703 vis_vec_pos = gun_point;
705 vis_vec_pos = obj->pos;
706 vm_vec_zero(&gun_point);
707 //mprintf((0, "Visibility = %i, computed from center.\n", player_visibility));
710 // MK: Debugging, July 26, 1995!
713 // compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
714 // mprintf((0, "Frame %i: dist=%7.3f, vecdot = %7.3f, mode=%i\n", FrameCount, f2fl(dist_to_player), f2fl(vm_vec_dot(&vec_to_player, &obj->orient.fvec)), ailp->mode));
716 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
717 // Occasionally make non-still robots make a path to the player. Based on agitation and distance from player.
718 if ((aip->behavior != AIB_SNIPE) && (aip->behavior != AIB_RUN_FROM) && (aip->behavior != AIB_STILL) && !(Game_mode & GM_MULTI) && (robptr->companion != 1) && (robptr->thief != 1))
719 if (Overall_agitation > 70) {
720 if ((dist_to_player < F1_0*200) && (d_rand() < FrameTime/4)) {
721 if (d_rand() * (Overall_agitation - 40) > F1_0*5) {
722 // -- mprintf((0, "(1) Object #%i going from still to path in frame %i.\n", objnum, FrameCount));
723 create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
729 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
730 // If retry count not 0, then add it into consecutive_retries.
731 // If it is 0, cut down consecutive_retries.
732 // This is largely a hack to speed up physics and deal with stupid
733 // AI. This is low level communication between systems of a sort
734 // that should not be done.
735 if ((ailp->retry_count) && !(Game_mode & GM_MULTI)) {
736 ailp->consecutive_retries += ailp->retry_count;
737 ailp->retry_count = 0;
738 if (ailp->consecutive_retries > 3) {
739 switch (ailp->mode) {
740 case AIM_GOTO_PLAYER:
741 // -- mprintf((0, "Buddy stuck going to player...\n"));
742 // -- Buddy_got_stuck = 1;
743 move_towards_segment_center(obj);
744 create_path_to_player(obj, 100, 1);
745 // -- Buddy_got_stuck = 0;
747 case AIM_GOTO_OBJECT:
748 // -- mprintf((0, "Buddy stuck going to object...\n"));
749 Escort_goal_object = ESCORT_GOAL_UNSPECIFIED;
750 //if (obj->segnum == ConsoleObject->segnum) {
751 // if (Point_segs[aip->hide_index + aip->cur_path_index].segnum == obj->segnum)
752 // if ((aip->cur_path_index + aip->PATH_DIR >= 0) && (aip->cur_path_index + aip->PATH_DIR < aip->path_length-1))
753 // aip->cur_path_index += aip->PATH_DIR;
756 case AIM_CHASE_OBJECT:
757 // -- mprintf((0, "(2) Object #%i, retries while chasing, creating path to player in frame %i\n", objnum, FrameCount));
758 create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
761 if (robptr->attack_type)
762 move_towards_segment_center(obj);
763 else if (!((aip->behavior == AIB_STILL) || (aip->behavior == AIB_STATION) || (aip->behavior == AIB_FOLLOW))) // Behavior is still, so don't follow path.
764 attempt_to_resume_path(obj);
766 case AIM_FOLLOW_PATH:
767 // mprintf((0, "Object %i following path got %i retries in frame %i\n", obj-Objects, ailp->consecutive_retries, FrameCount));
768 if (Game_mode & GM_MULTI) {
769 ailp->mode = AIM_STILL;
771 attempt_to_resume_path(obj);
773 case AIM_RUN_FROM_OBJECT:
774 move_towards_segment_center(obj);
775 obj->mtype.phys_info.velocity.x = 0;
776 obj->mtype.phys_info.velocity.y = 0;
777 obj->mtype.phys_info.velocity.z = 0;
778 create_n_segment_path(obj, 5, -1);
779 ailp->mode = AIM_RUN_FROM_OBJECT;
782 mprintf((0, "Hiding robot (%i) collided much.\n", obj-Objects));
783 move_towards_segment_center(obj);
784 obj->mtype.phys_info.velocity.x = 0;
785 obj->mtype.phys_info.velocity.y = 0;
786 obj->mtype.phys_info.velocity.z = 0;
789 create_n_segment_path_to_door(obj, 5, -1);
792 case AIM_FOLLOW_PATH_2:
793 Int3(); // Should never happen!
797 ailp->consecutive_retries = 0;
800 ailp->consecutive_retries /= 2;
802 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
803 // If in materialization center, exit
804 if (!(Game_mode & GM_MULTI) && (Segment2s[obj->segnum].special == SEGMENT_IS_ROBOTMAKER)) {
805 if (Station[Segment2s[obj->segnum].value].Enabled) {
806 ai_follow_path(obj, 1, 1, NULL); // 1 = player is visible, which might be a lie, but it works.
811 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
812 // Decrease player awareness due to the passage of time.
813 if (ailp->player_awareness_type) {
814 if (ailp->player_awareness_time > 0) {
815 ailp->player_awareness_time -= FrameTime;
816 if (ailp->player_awareness_time <= 0) {
817 ailp->player_awareness_time = F1_0*2; //new: 11/05/94
818 ailp->player_awareness_type--; //new: 11/05/94
821 ailp->player_awareness_type--;
822 ailp->player_awareness_time = F1_0*2;
823 //aip->GOAL_STATE = AIS_REST;
826 aip->GOAL_STATE = AIS_REST; //new: 12/13/94
829 if (Player_is_dead && (ailp->player_awareness_type == 0))
830 if ((dist_to_player < F1_0*200) && (d_rand() < FrameTime/8)) {
831 if ((aip->behavior != AIB_STILL) && (aip->behavior != AIB_RUN_FROM)) {
832 if (!ai_multiplayer_awareness(obj, 30))
835 ai_multi_send_robot_position(objnum, -1);
838 if (!((ailp->mode == AIM_FOLLOW_PATH) && (aip->cur_path_index < aip->path_length-1)))
839 if ((aip->behavior != AIB_SNIPE) && (aip->behavior != AIB_RUN_FROM)) {
840 if (dist_to_player < F1_0*30)
841 create_n_segment_path(obj, 5, 1);
843 create_path_to_player(obj, 20, 1);
848 // -- // Make sure that if this guy got hit or bumped, then he's chasing player.
849 // -- if ((ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) || (ailp->player_awareness_type >= PA_PLAYER_COLLISION)) {
850 // -- if ((ailp->mode != AIM_BEHIND) && (aip->behavior != AIB_STILL) && (aip->behavior != AIB_SNIPE) && (aip->behavior != AIB_RUN_FROM) && (!robptr->companion) && (!robptr->thief) && (obj->id != ROBOT_BRAIN)) {
851 // -- ailp->mode = AIM_CHASE_OBJECT;
852 // -- ailp->player_awareness_type = 0;
853 // -- ailp->player_awareness_time = 0;
857 // Make sure that if this guy got hit or bumped, then he's chasing player.
858 if ((ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) || (ailp->player_awareness_type >= PA_PLAYER_COLLISION)) {
859 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
860 if (player_visibility == 1) // Only increase visibility if unobstructed, else claw guys attack through doors.
861 player_visibility = 2;
862 } else if (((obj_ref&3) == 0) && !previous_visibility && (dist_to_player < F1_0*100)) {
866 sval = (dist_to_player * (Difficulty_level+1))/64;
868 // -- mprintf((0, "Object #%3i: dist = %7.3f, rval = %8x, sval = %8x", obj-Objects, f2fl(dist_to_player), rval, sval));
869 if ((fixmul(rval, sval) < FrameTime) || (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT_ON)) {
870 ailp->player_awareness_type = PA_PLAYER_COLLISION;
871 ailp->player_awareness_time = F1_0*3;
872 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
873 if (player_visibility == 1) {
874 player_visibility = 2;
875 // -- mprintf((0, "...SWITCH!"));
879 // -- mprintf((0, "\n"));
883 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
884 if ((aip->GOAL_STATE == AIS_FLIN) && (aip->CURRENT_STATE == AIS_FLIN))
885 aip->GOAL_STATE = AIS_LOCK;
887 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
888 // Note: Should only do these two function calls for objects which animate
889 if (Animation_enabled && (dist_to_player < F1_0*100)) { // && !(Game_mode & GM_MULTI)) {
890 object_animates = do_silly_animation(obj);
892 ai_frame_animation(obj);
893 //mprintf((0, "Object %i: goal=%i, current=%i\n", obj-Objects, obj->ctype.ai_info.GOAL_STATE, obj->ctype.ai_info.CURRENT_STATE));
895 // If Object is supposed to animate, but we don't let it animate due to distance, then
896 // we must change its state, else it will never update.
897 aip->CURRENT_STATE = aip->GOAL_STATE;
898 object_animates = 0; // If we're not doing the animation, then should pretend it doesn't animate.
901 switch (Robot_info[obj->id].boss_flag) {
907 mprintf((1, "Warning: D1 boss detected. Not supported!\n"));
913 fix dtp = dist_to_player/4;
915 if (aip->GOAL_STATE == AIS_FLIN)
916 aip->GOAL_STATE = AIS_FIRE;
917 if (aip->CURRENT_STATE == AIS_FLIN)
918 aip->CURRENT_STATE = AIS_FIRE;
920 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
922 pv = player_visibility;
924 // If player cloaked, visibility is screwed up and superboss will gate in robots when not supposed to.
925 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
927 dtp = vm_vec_dist_quick(&ConsoleObject->pos, &obj->pos)/4;
930 do_boss_stuff(obj, pv);
935 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
936 // Time-slice, don't process all the time, purely an efficiency hack.
937 // Guys whose behavior is station and are not at their hide segment get processed anyway.
938 if (!((aip->behavior == AIB_SNIPE) && (ailp->mode != AIM_SNIPE_WAIT)) && !robptr->companion && !robptr->thief && (ailp->player_awareness_type < PA_WEAPON_ROBOT_COLLISION-1)) { // If robot got hit, he gets to attack player always!
940 if (Break_on_object != objnum) { // don't time slice if we're interested in this object.
942 if ((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum)) {
943 if (dist_to_player > F1_0*250) // station guys not at home always processed until 250 units away.
945 } else if ((!ailp->previous_visibility) && ((dist_to_player >> 7) > ailp->time_since_processed)) { // 128 units away (6.4 segments) processed after 1 second.
955 // Reset time since processed, but skew objects so not everything
956 // processed synchronously, else we get fast frames with the
957 // occasional very slow frame.
958 // AI_proc_time = ailp->time_since_processed;
959 ailp->time_since_processed = - ((objnum & 0x03) * FrameTime ) / 2;
961 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
962 // Perform special ability
965 // Robots function nicely if behavior is Station. This
966 // means they won't move until they can see the player, at
967 // which time they will start wandering about opening doors.
968 if (ConsoleObject->segnum == obj->segnum) {
969 if (!ai_multiplayer_awareness(obj, 97))
971 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
972 move_away_from_player(obj, &vec_to_player, 0);
973 ai_multi_send_robot_position(objnum, -1);
974 } else if (ailp->mode != AIM_STILL) {
977 r = openable_doors_in_segment(obj->segnum);
979 ailp->mode = AIM_OPEN_DOOR;
981 } else if (ailp->mode != AIM_FOLLOW_PATH) {
982 if (!ai_multiplayer_awareness(obj, 50))
984 create_n_segment_path_to_door(obj, 8+Difficulty_level, -1); // third parameter is avoid_seg, -1 means avoid nothing.
985 ai_multi_send_robot_position(objnum, -1);
988 if (ailp->next_action_time < 0) {
989 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
990 if (player_visibility) {
991 make_nearby_robot_snipe();
992 ailp->next_action_time = (NDL - Difficulty_level) * 2*F1_0;
996 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
997 if (player_visibility) {
998 if (!ai_multiplayer_awareness(obj, 50))
1000 create_n_segment_path_to_door(obj, 8+Difficulty_level, -1); // third parameter is avoid_seg, -1 means avoid nothing.
1001 ai_multi_send_robot_position(objnum, -1);
1009 if (aip->behavior == AIB_SNIPE) {
1010 if ((Game_mode & GM_MULTI) && !robptr->thief) {
1011 aip->behavior = AIB_NORMAL;
1012 ailp->mode = AIM_CHASE_OBJECT;
1016 if (!(obj_ref & 3) || previous_visibility) {
1017 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1019 // If this sniper is in still mode, if he was hit or can see player, switch to snipe mode.
1020 if (ailp->mode == AIM_STILL)
1021 if (player_visibility || (ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION))
1022 ailp->mode = AIM_SNIPE_ATTACK;
1024 if (!robptr->thief && (ailp->mode != AIM_STILL))
1025 do_snipe_frame(obj, dist_to_player, player_visibility, &vec_to_player);
1026 } else if (!robptr->thief && !robptr->companion)
1030 // More special ability stuff, but based on a property of a robot, not its ID.
1031 if (robptr->companion) {
1033 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1034 do_escort_frame(obj, dist_to_player, player_visibility);
1036 if (obj->ctype.ai_info.danger_laser_num != -1) {
1037 object *dobjp = &Objects[obj->ctype.ai_info.danger_laser_num];
1039 if ((dobjp->type == OBJ_WEAPON) && (dobjp->signature == obj->ctype.ai_info.danger_laser_signature)) {
1040 fix circle_distance;
1041 // -- mprintf((0, "Evading! "));
1042 circle_distance = robptr->circle_distance[Difficulty_level] + ConsoleObject->size;
1043 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, circle_distance, 1, player_visibility);
1047 if (ready_to_fire(robptr, ailp)) {
1049 if (openable_doors_in_segment(obj->segnum) != -1)
1051 else if (openable_doors_in_segment(Point_segs[aip->hide_index + aip->cur_path_index + aip->PATH_DIR].segnum) != -1)
1053 else if (openable_doors_in_segment(Point_segs[aip->hide_index + aip->cur_path_index + 2*aip->PATH_DIR].segnum) != -1)
1055 else if ((ailp->mode == AIM_GOTO_PLAYER) && (dist_to_player < 3*MIN_ESCORT_DISTANCE/2) && (vm_vec_dot(&ConsoleObject->orient.fvec, &vec_to_player) > -F1_0/4)) {
1056 // mprintf((0, "Firing at player because dot = %7.3f\n", f2fl(vm_vec_dot(&ConsoleObject->orient.fvec, &vec_to_player))));
1059 ; // mprintf((0, "Not Firing at player because dot = %7.3f, dist = %7.3f\n", f2fl(vm_vec_dot(&ConsoleObject->orient.fvec, &vec_to_player)), f2fl(dist_to_player)));
1062 Laser_create_new_easy( &obj->orient.fvec, &obj->pos, obj-Objects, FLARE_ID, 1);
1063 ailp->next_fire = F1_0/2;
1064 if (!Buddy_allowed_to_talk) // If buddy not talking, make him fire flares less often.
1065 ailp->next_fire += d_rand()*4;
1071 if (robptr->thief) {
1073 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1074 do_thief_frame(obj, dist_to_player, player_visibility, &vec_to_player);
1076 if (ready_to_fire(robptr, ailp)) {
1078 if (openable_doors_in_segment(obj->segnum) != -1)
1080 else if (openable_doors_in_segment(Point_segs[aip->hide_index + aip->cur_path_index + aip->PATH_DIR].segnum) != -1)
1082 else if (openable_doors_in_segment(Point_segs[aip->hide_index + aip->cur_path_index + 2*aip->PATH_DIR].segnum) != -1)
1086 // @mk, 05/08/95: Firing flare from center of object, this is dumb...
1087 Laser_create_new_easy( &obj->orient.fvec, &obj->pos, obj-Objects, FLARE_ID, 1);
1088 ailp->next_fire = F1_0/2;
1089 if (Stolen_item_index == 0) // If never stolen an item, fire flares less often (bad: Stolen_item_index wraps, but big deal)
1090 ailp->next_fire += d_rand()*4;
1095 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1096 switch (ailp->mode) {
1097 case AIM_CHASE_OBJECT: { // chasing player, sort of, chase if far, back off if close, circle in between
1098 fix circle_distance;
1100 circle_distance = robptr->circle_distance[Difficulty_level] + ConsoleObject->size;
1101 // Green guy doesn't get his circle distance boosted, else he might never attack.
1102 if (robptr->attack_type != 1)
1103 circle_distance += (objnum&0xf) * F1_0/2;
1105 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1107 // @mk, 12/27/94, structure here was strange. Would do both clauses of what are now this if/then/else. Used to be if/then, if/then.
1108 if ((player_visibility < 2) && (previous_visibility == 2)) { // this is redundant: mk, 01/15/95: && (ailp->mode == AIM_CHASE_OBJECT)) {
1109 // -- mprintf((0, "I used to be able to see the player!\n"));
1110 if (!ai_multiplayer_awareness(obj, 53)) {
1111 if (maybe_ai_do_actual_firing_stuff(obj, aip))
1112 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1115 // -- mprintf((0, "(3) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
1116 create_path_to_player(obj, 8, 1);
1117 ai_multi_send_robot_position(objnum, -1);
1118 } else if ((player_visibility == 0) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
1119 // If pretty far from the player, player cannot be seen
1120 // (obstructed) and in chase mode, switch to follow path mode.
1121 // This has one desirable benefit of avoiding physics retries.
1122 if (aip->behavior == AIB_STATION) {
1123 ailp->goal_segment = aip->hide_segment;
1124 // -- mprintf((0, "(1) Object #%i going from chase to STATION in frame %i.\n", objnum, FrameCount));
1125 create_path_to_station(obj, 15);
1126 } // -- this looks like a dumb thing to do...robots following paths far away from you! else create_n_segment_path(obj, 5, -1);
1130 if ((aip->CURRENT_STATE == AIS_REST) && (aip->GOAL_STATE == AIS_REST)) {
1131 if (player_visibility) {
1132 if (d_rand() < FrameTime*player_visibility) {
1133 if (dist_to_player/256 < d_rand()*player_visibility) {
1134 // mprintf((0, "Object %i searching for player.\n", obj-Objects));
1135 aip->GOAL_STATE = AIS_SRCH;
1136 aip->CURRENT_STATE = AIS_SRCH;
1142 if (GameTime - ailp->time_player_seen > CHASE_TIME_LENGTH) {
1144 if (Game_mode & GM_MULTI)
1145 if (!player_visibility && (dist_to_player > F1_0*70)) {
1146 ailp->mode = AIM_STILL;
1150 if (!ai_multiplayer_awareness(obj, 64)) {
1151 if (maybe_ai_do_actual_firing_stuff(obj, aip))
1152 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1155 // -- bad idea, robots charge player they've never seen! -- mprintf((0, "(4) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
1156 // -- bad idea, robots charge player they've never seen! -- create_path_to_player(obj, 10, 1);
1157 // -- bad idea, robots charge player they've never seen! -- ai_multi_send_robot_position(objnum, -1);
1158 } else if ((aip->CURRENT_STATE != AIS_REST) && (aip->GOAL_STATE != AIS_REST)) {
1159 if (!ai_multiplayer_awareness(obj, 70)) {
1160 if (maybe_ai_do_actual_firing_stuff(obj, aip))
1161 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1164 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, circle_distance, 0, player_visibility);
1166 if ((obj_ref & 1) && ((aip->GOAL_STATE == AIS_SRCH) || (aip->GOAL_STATE == AIS_LOCK))) {
1167 if (player_visibility) // == 2)
1168 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1172 ai_multi_send_robot_position(objnum, 1);
1175 ai_multi_send_robot_position(objnum, -1);
1177 do_firing_stuff(obj, player_visibility, &vec_to_player);
1182 case AIM_RUN_FROM_OBJECT:
1183 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1185 if (player_visibility) {
1186 if (ailp->player_awareness_type == 0)
1187 ailp->player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
1191 // If in multiplayer, only do if player visible. If not multiplayer, do always.
1192 if (!(Game_mode & GM_MULTI) || player_visibility)
1193 if (ai_multiplayer_awareness(obj, 75)) {
1194 ai_follow_path(obj, player_visibility, previous_visibility, &vec_to_player);
1195 ai_multi_send_robot_position(objnum, -1);
1198 if (aip->GOAL_STATE != AIS_FLIN)
1199 aip->GOAL_STATE = AIS_LOCK;
1200 else if (aip->CURRENT_STATE == AIS_FLIN)
1201 aip->GOAL_STATE = AIS_LOCK;
1203 // Bad to let run_from robot fire at player because it
1204 // will cause a war in which it turns towards the player
1205 // to fire and then towards its goal to move.
1206 // do_firing_stuff(obj, player_visibility, &vec_to_player);
1207 // Instead, do this:
1208 // (Note, only drop if player is visible. This prevents
1209 // the bombs from being a giveaway, and also ensures that
1210 // the robot is moving while it is dropping. Also means
1211 // fewer will be dropped.)
1212 if ((ailp->next_fire <= 0) && (player_visibility)) {
1213 vms_vector fire_vec, fire_pos;
1215 if (!ai_multiplayer_awareness(obj, 75))
1218 fire_vec = obj->orient.fvec;
1219 vm_vec_negate(&fire_vec);
1220 vm_vec_add(&fire_pos, &obj->pos, &fire_vec);
1222 if (aip->SUB_FLAGS & SUB_FLAGS_SPROX)
1223 Laser_create_new_easy( &fire_vec, &fire_pos, obj-Objects, ROBOT_SUPERPROX_ID, 1);
1225 Laser_create_new_easy( &fire_vec, &fire_pos, obj-Objects, PROXIMITY_ID, 1);
1227 ailp->next_fire = (F1_0/2)*(NDL+5 - Difficulty_level); // Drop a proximity bomb every 5 seconds.
1231 if (Game_mode & GM_MULTI)
1233 ai_multi_send_robot_position(obj-Objects, -1);
1234 if (aip->SUB_FLAGS & SUB_FLAGS_SPROX)
1235 multi_send_robot_fire(obj-Objects, -2, &fire_vec);
1237 multi_send_robot_fire(obj-Objects, -1, &fire_vec);
1244 case AIM_GOTO_PLAYER:
1245 case AIM_GOTO_OBJECT:
1246 ai_follow_path(obj, 2, previous_visibility, &vec_to_player); // Follows path as if player can see robot.
1247 ai_multi_send_robot_position(objnum, -1);
1250 case AIM_FOLLOW_PATH: {
1251 int anger_level = 65;
1253 if (aip->behavior == AIB_STATION)
1254 if (Point_segs[aip->hide_index + aip->path_length - 1].segnum == aip->hide_segment) {
1256 // mprintf((0, "Object %i, station, lowering anger to 64.\n"));
1259 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1261 if (Game_mode & (GM_MODEM | GM_SERIAL))
1262 if (!player_visibility && (dist_to_player > F1_0*70)) {
1263 ailp->mode = AIM_STILL;
1267 if (!ai_multiplayer_awareness(obj, anger_level)) {
1268 if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
1269 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1270 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1275 ai_follow_path(obj, player_visibility, previous_visibility, &vec_to_player);
1277 if (aip->GOAL_STATE != AIS_FLIN)
1278 aip->GOAL_STATE = AIS_LOCK;
1279 else if (aip->CURRENT_STATE == AIS_FLIN)
1280 aip->GOAL_STATE = AIS_LOCK;
1282 if (aip->behavior != AIB_RUN_FROM)
1283 do_firing_stuff(obj, player_visibility, &vec_to_player);
1285 if ((player_visibility == 2) && (aip->behavior != AIB_SNIPE) && (aip->behavior != AIB_FOLLOW) && (aip->behavior != AIB_RUN_FROM) && (obj->id != ROBOT_BRAIN) && (robptr->companion != 1) && (robptr->thief != 1)) {
1286 if (robptr->attack_type == 0)
1287 ailp->mode = AIM_CHASE_OBJECT;
1288 // This should not just be distance based, but also time-since-player-seen based.
1289 } else if ((dist_to_player > F1_0*(20*(2*Difficulty_level + robptr->pursuit)))
1290 && (GameTime - ailp->time_player_seen > (F1_0/2*(Difficulty_level+robptr->pursuit)))
1291 && (player_visibility == 0)
1292 && (aip->behavior == AIB_NORMAL)
1293 && (ailp->mode == AIM_FOLLOW_PATH)) {
1294 ailp->mode = AIM_STILL;
1295 aip->hide_index = -1;
1296 aip->path_length = 0;
1299 ai_multi_send_robot_position(objnum, -1);
1305 if (!ai_multiplayer_awareness(obj, 71)) {
1306 if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
1307 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1308 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1313 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1315 if (player_visibility == 2) {
1316 // Get behind the player.
1318 // If vec_to_player dot player_rear_vector > 0, behind is goal.
1319 // Else choose goal with larger dot from left, right.
1320 vms_vector goal_point, goal_vector, vec_to_goal, rand_vec;
1323 dot = vm_vec_dot(&ConsoleObject->orient.fvec, &vec_to_player);
1324 if (dot > 0) { // Remember, we're interested in the rear vector dot being < 0.
1325 goal_vector = ConsoleObject->orient.fvec;
1326 vm_vec_negate(&goal_vector);
1327 // -- mprintf((0, "Goal is BEHIND\n"));
1330 dot = vm_vec_dot(&ConsoleObject->orient.rvec, &vec_to_player);
1331 goal_vector = ConsoleObject->orient.rvec;
1333 vm_vec_negate(&goal_vector);
1334 // -- mprintf((0, "Goal is LEFT\n"));
1336 ; // -- mprintf((0, "Goal is RIGHT\n"));
1339 vm_vec_scale(&goal_vector, 2*(ConsoleObject->size + obj->size + (((objnum*4 + FrameCount) & 63) << 12)));
1340 vm_vec_add(&goal_point, &ConsoleObject->pos, &goal_vector);
1341 make_random_vector(&rand_vec);
1342 vm_vec_scale_add2(&goal_point, &rand_vec, F1_0*8);
1343 vm_vec_sub(&vec_to_goal, &goal_point, &obj->pos);
1344 vm_vec_normalize_quick(&vec_to_goal);
1345 move_towards_vector(obj, &vec_to_goal, 0);
1346 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1347 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1350 if (aip->GOAL_STATE != AIS_FLIN)
1351 aip->GOAL_STATE = AIS_LOCK;
1352 else if (aip->CURRENT_STATE == AIS_FLIN)
1353 aip->GOAL_STATE = AIS_LOCK;
1355 ai_multi_send_robot_position(objnum, -1);
1359 if ((dist_to_player < F1_0*120+Difficulty_level*F1_0*20) || (ailp->player_awareness_type >= PA_WEAPON_ROBOT_COLLISION-1)) {
1360 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1362 // turn towards vector if visible this time or last time, or rand
1364 if ((player_visibility == 2) || (previous_visibility == 2)) { // -- MK, 06/09/95: || ((d_rand() > 0x4000) && !(Game_mode & GM_MULTI))) {
1365 if (!ai_multiplayer_awareness(obj, 71)) {
1366 if (maybe_ai_do_actual_firing_stuff(obj, aip))
1367 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1370 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1371 ai_multi_send_robot_position(objnum, -1);
1374 do_firing_stuff(obj, player_visibility, &vec_to_player);
1375 if (player_visibility == 2) { // Changed @mk, 09/21/95: Require that they be looking to evade. Change, MK, 01/03/95 for Multiplayer reasons. If robots can't see you (even with eyes on back of head), then don't do evasion.
1376 if (robptr->attack_type == 1) {
1377 aip->behavior = AIB_NORMAL;
1378 if (!ai_multiplayer_awareness(obj, 80)) {
1379 if (maybe_ai_do_actual_firing_stuff(obj, aip))
1380 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1383 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 0, player_visibility);
1385 ai_multi_send_robot_position(objnum, 1);
1389 ai_multi_send_robot_position(objnum, -1);
1391 // Robots in hover mode are allowed to evade at half normal speed.
1392 if (!ai_multiplayer_awareness(obj, 81)) {
1393 if (maybe_ai_do_actual_firing_stuff(obj, aip))
1394 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1397 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 1, player_visibility);
1399 ai_multi_send_robot_position(objnum, -1);
1403 ai_multi_send_robot_position(objnum, -1);
1405 } else if ((obj->segnum != aip->hide_segment) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
1406 // If pretty far from the player, player cannot be
1407 // seen (obstructed) and in chase mode, switch to
1408 // follow path mode.
1409 // This has one desirable benefit of avoiding physics retries.
1410 if (aip->behavior == AIB_STATION) {
1411 ailp->goal_segment = aip->hide_segment;
1412 // -- mprintf((0, "(2) Object #%i going from STILL to STATION in frame %i.\n", objnum, FrameCount));
1413 create_path_to_station(obj, 15);
1420 case AIM_OPEN_DOOR: { // trying to open a door.
1421 vms_vector center_point, goal_vector;
1422 Assert(obj->id == ROBOT_BRAIN); // Make sure this guy is allowed to be in this mode.
1424 if (!ai_multiplayer_awareness(obj, 62))
1426 compute_center_point_on_side(¢er_point, &Segments[obj->segnum], aip->GOALSIDE);
1427 vm_vec_sub(&goal_vector, ¢er_point, &obj->pos);
1428 vm_vec_normalize_quick(&goal_vector);
1429 ai_turn_towards_vector(&goal_vector, obj, robptr->turn_time[Difficulty_level]);
1430 move_towards_vector(obj, &goal_vector, 0);
1431 ai_multi_send_robot_position(objnum, -1);
1436 case AIM_SNIPE_WAIT:
1438 case AIM_SNIPE_RETREAT:
1439 // -- if (ai_multiplayer_awareness(obj, 53))
1440 // -- if (ailp->next_fire < -F1_0)
1441 // -- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1443 case AIM_SNIPE_RETREAT_BACKWARDS:
1444 case AIM_SNIPE_ATTACK:
1445 case AIM_SNIPE_FIRE:
1446 if (ai_multiplayer_awareness(obj, 53)) {
1447 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1449 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 0, player_visibility);
1454 case AIM_THIEF_WAIT:
1455 case AIM_THIEF_ATTACK:
1456 case AIM_THIEF_RETREAT:
1457 case AIM_WANDER: // Used for Buddy Bot
1461 mprintf((0, "Unknown mode = %i in robot %i, behavior = %i\n", ailp->mode, obj-Objects, aip->behavior));
1462 ailp->mode = AIM_CHASE_OBJECT;
1464 } // end: switch (ailp->mode) {
1466 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1467 // If the robot can see you, increase his awareness of you.
1468 // This prevents the problem of a robot looking right at you but doing nothing.
1469 // Assert(player_visibility != -1); // Means it didn't get initialized!
1470 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1471 if ((player_visibility == 2) && (aip->behavior != AIB_FOLLOW) && (!robptr->thief)) {
1472 if ((ailp->player_awareness_type == 0) && (aip->SUB_FLAGS & SUB_FLAGS_CAMERA_AWAKE))
1473 aip->SUB_FLAGS &= ~SUB_FLAGS_CAMERA_AWAKE;
1474 else if (ailp->player_awareness_type == 0)
1475 ailp->player_awareness_type = PA_PLAYER_COLLISION;
1478 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1479 if (!object_animates) {
1480 aip->CURRENT_STATE = aip->GOAL_STATE;
1481 // mprintf((0, "Setting current to goal (%i) because object doesn't animate.\n", aip->GOAL_STATE));
1484 Assert(ailp->player_awareness_type <= AIE_MAX);
1485 Assert(aip->CURRENT_STATE < AIS_MAX);
1486 Assert(aip->GOAL_STATE < AIS_MAX);
1488 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1489 if (ailp->player_awareness_type) {
1490 new_goal_state = Ai_transition_table[ailp->player_awareness_type-1][aip->CURRENT_STATE][aip->GOAL_STATE];
1491 if (ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) {
1492 // Decrease awareness, else this robot will flinch every frame.
1493 ailp->player_awareness_type--;
1494 ailp->player_awareness_time = F1_0*3;
1497 if (new_goal_state == AIS_ERR_)
1498 new_goal_state = AIS_REST;
1500 if (aip->CURRENT_STATE == AIS_NONE)
1501 aip->CURRENT_STATE = AIS_REST;
1503 aip->GOAL_STATE = new_goal_state;
1507 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1508 // If new state = fire, then set all gun states to fire.
1509 if ((aip->GOAL_STATE == AIS_FIRE) ) {
1511 num_guns = Robot_info[obj->id].n_guns;
1512 for (i=0; i<num_guns; i++)
1513 ailp->goal_state[i] = AIS_FIRE;
1516 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1517 // Hack by mk on 01/04/94, if a guy hasn't animated to the firing state, but his next_fire says ok to fire, bash him there
1518 if (ready_to_fire(robptr, ailp) && (aip->GOAL_STATE == AIS_FIRE))
1519 aip->CURRENT_STATE = AIS_FIRE;
1521 if ((aip->GOAL_STATE != AIS_FLIN) && (obj->id != ROBOT_BRAIN)) {
1522 switch (aip->CURRENT_STATE) {
1524 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1526 dot = vm_vec_dot(&obj->orient.fvec, &vec_to_player);
1528 if (aip->GOAL_STATE == AIS_REST)
1529 aip->GOAL_STATE = AIS_SRCH;
1532 if (aip->GOAL_STATE == AIS_REST) {
1533 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1534 if (ready_to_fire(robptr, ailp) && (player_visibility)) {
1535 // mprintf((0, "Setting goal state to fire from rest.\n"));
1536 aip->GOAL_STATE = AIS_FIRE;
1541 if (!ai_multiplayer_awareness(obj, 60))
1544 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1546 if (player_visibility == 2) {
1547 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1548 ai_multi_send_robot_position(objnum, -1);
1552 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1554 if (!(Game_mode & GM_MULTI) || (player_visibility)) {
1555 if (!ai_multiplayer_awareness(obj, 68))
1558 if (player_visibility == 2) { // @mk, 09/21/95, require that they be looking towards you to turn towards you.
1559 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1560 ai_multi_send_robot_position(objnum, -1);
1565 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1567 if (player_visibility == 2) {
1568 if (!ai_multiplayer_awareness(obj, (ROBOT_FIRE_AGITATION-1))) {
1569 if (Game_mode & GM_MULTI) {
1570 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1574 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1575 ai_multi_send_robot_position(objnum, -1);
1578 // Fire at player, if appropriate.
1579 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1583 if (!(obj_ref & 3)) {
1584 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1585 if (player_visibility == 2) {
1586 if (!ai_multiplayer_awareness(obj, 69))
1588 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1589 ai_multi_send_robot_position(objnum, -1);
1590 } // -- MK, 06/09/95: else if (!(Game_mode & GM_MULTI)) {
1594 // mprintf((0, "State = flinch, goal = %i.\n", aip->GOAL_STATE));
1597 mprintf((1, "Unknown mode for AI object #%i\n", objnum));
1598 aip->GOAL_STATE = AIS_REST;
1599 aip->CURRENT_STATE = AIS_REST;
1602 } // end of: if (aip->GOAL_STATE != AIS_FLIN) {
1604 // Switch to next gun for next fire.
1605 if (player_visibility == 0) {
1607 if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
1609 if ((robptr->n_guns == 1) || (robptr->weapon_type2 == -1)) // Two weapon types hack.
1610 aip->CURRENT_GUN = 0;
1612 aip->CURRENT_GUN = 1;
1618 // ----------------------------------------------------------------------------
1619 void ai_do_cloak_stuff(void)
1623 for (i=0; i<MAX_AI_CLOAK_INFO; i++) {
1624 Ai_cloak_info[i].last_position = ConsoleObject->pos;
1625 Ai_cloak_info[i].last_segment = ConsoleObject->segnum;
1626 Ai_cloak_info[i].last_time = GameTime;
1629 // Make work for control centers.
1630 Believed_player_pos = Ai_cloak_info[0].last_position;
1631 Believed_player_seg = Ai_cloak_info[0].last_segment;
1635 // ----------------------------------------------------------------------------
1636 // Returns false if awareness is considered too puny to add, else returns true.
1637 int add_awareness_event(object *objp, int type)
1639 // If player cloaked and hit a robot, then increase awareness
1640 if ((type == PA_WEAPON_ROBOT_COLLISION) || (type == PA_WEAPON_WALL_COLLISION) || (type == PA_PLAYER_COLLISION))
1641 ai_do_cloak_stuff();
1643 if (Num_awareness_events < MAX_AWARENESS_EVENTS) {
1644 if ((type == PA_WEAPON_WALL_COLLISION) || (type == PA_WEAPON_ROBOT_COLLISION))
1645 if (objp->id == VULCAN_ID)
1646 if (d_rand() > 3276)
1647 return 0; // For vulcan cannon, only about 1/10 actually cause awareness
1649 Awareness_events[Num_awareness_events].segnum = objp->segnum;
1650 Awareness_events[Num_awareness_events].pos = objp->pos;
1651 Awareness_events[Num_awareness_events].type = type;
1652 Num_awareness_events++;
1654 //Int3(); // Hey -- Overflowed Awareness_events, make more or something
1655 // This just gets ignored, so you can just
1662 // ----------------------------------------------------------------------------------
1663 // Robots will become aware of the player based on something that occurred.
1664 // The object (probably player or weapon) which created the awareness is objp.
1665 void create_awareness_event(object *objp, int type)
1667 // If not in multiplayer, or in multiplayer with robots, do this, else unnecessary!
1668 if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_ROBOTS)) {
1669 if (add_awareness_event(objp, type)) {
1670 if (((d_rand() * (type+4)) >> 15) > 4)
1671 Overall_agitation++;
1672 if (Overall_agitation > OVERALL_AGITATION_MAX)
1673 Overall_agitation = OVERALL_AGITATION_MAX;
1678 byte New_awareness[MAX_SEGMENTS];
1680 // ----------------------------------------------------------------------------------
1681 void pae_aux(int segnum, int type, int level)
1685 if (New_awareness[segnum] < type)
1686 New_awareness[segnum] = type;
1688 // Process children.
1689 for (j=0; j<MAX_SIDES_PER_SEGMENT; j++)
1690 if (IS_CHILD(Segments[segnum].children[j]))
1695 pae_aux(Segments[segnum].children[j], type-1, level+1);
1697 pae_aux(Segments[segnum].children[j], type, level+1);
1703 // ----------------------------------------------------------------------------------
1704 void process_awareness_events(void)
1708 if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_ROBOTS)) {
1709 memset(New_awareness, 0, sizeof(New_awareness[0]) * (Highest_segment_index+1));
1711 for (i=0; i<Num_awareness_events; i++)
1712 pae_aux(Awareness_events[i].segnum, Awareness_events[i].type, 1);
1716 Num_awareness_events = 0;
1719 // ----------------------------------------------------------------------------------
1720 void set_player_awareness_all(void)
1724 process_awareness_events();
1726 for (i=0; i<=Highest_object_index; i++)
1727 if (Objects[i].control_type == CT_AI) {
1728 if (New_awareness[Objects[i].segnum] > Ai_local_info[i].player_awareness_type) {
1729 Ai_local_info[i].player_awareness_type = New_awareness[Objects[i].segnum];
1730 Ai_local_info[i].player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME;
1733 // Clear the bit that says this robot is only awake because a camera woke it up.
1734 if (New_awareness[Objects[i].segnum] > Ai_local_info[i].player_awareness_type)
1735 Objects[i].ctype.ai_info.SUB_FLAGS &= ~SUB_FLAGS_CAMERA_AWAKE;
1740 int Ai_dump_enable = 0;
1742 FILE *Ai_dump_file = NULL;
1744 char Ai_error_message[128] = "";
1746 // ----------------------------------------------------------------------------------
1747 void force_dump_ai_objects_all(char *msg)
1751 tsave = Ai_dump_enable;
1755 sprintf(Ai_error_message, "%s\n", msg);
1756 //dump_ai_objects_all();
1757 Ai_error_message[0] = 0;
1759 Ai_dump_enable = tsave;
1762 // ----------------------------------------------------------------------------------
1763 void turn_off_ai_dump(void)
1765 if (Ai_dump_file != NULL)
1766 fclose(Ai_dump_file);
1768 Ai_dump_file = NULL;
1773 extern void do_boss_dying_frame(object *objp);
1775 // ----------------------------------------------------------------------------------
1776 // Do things which need to get done for all AI objects each frame.
1778 // Setting player_awareness (a fix, time in seconds which object is aware of player)
1779 void do_ai_frame_all(void)
1782 //dump_ai_objects_all();
1785 set_player_awareness_all();
1787 if (Ai_last_missile_camera != -1) {
1788 // Clear if supposed misisle camera is not a weapon, or just every so often, just in case.
1789 if (((FrameCount & 0x0f) == 0) || (Objects[Ai_last_missile_camera].type != OBJ_WEAPON)) {
1792 Ai_last_missile_camera = -1;
1793 for (i=0; i<=Highest_object_index; i++)
1794 if (Objects[i].type == OBJ_ROBOT)
1795 Objects[i].ctype.ai_info.SUB_FLAGS &= ~SUB_FLAGS_CAMERA_AWAKE;
1799 // (Moved here from do_boss_stuff() because that only gets called if robot aware of player.)
1803 for (i=0; i<=Highest_object_index; i++)
1804 if (Objects[i].type == OBJ_ROBOT)
1805 if (Robot_info[Objects[i].id].boss_flag)
1806 do_boss_dying_frame(&Objects[i]);
1811 extern int Final_boss_is_dead;
1812 extern fix Boss_invulnerable_dot;
1814 // Initializations to be performed for all robots for a new level.
1815 void init_robots_for_level(void)
1817 Overall_agitation = 0;
1818 Final_boss_is_dead=0;
1821 Buddy_allowed_to_talk = 0;
1823 Boss_invulnerable_dot = F1_0/4 - i2f(Difficulty_level)/8;
1824 Boss_dying_start_time = 0;
1827 int ai_save_state(CFILE *fp)
1829 cfwrite(&Ai_initialized, sizeof(int), 1, fp);
1830 cfwrite(&Overall_agitation, sizeof(int), 1, fp);
1831 cfwrite(Ai_local_info, sizeof(ai_local), MAX_OBJECTS, fp);
1832 cfwrite(Point_segs, sizeof(point_seg), MAX_POINT_SEGS, fp);
1833 cfwrite(Ai_cloak_info, sizeof(ai_cloak_info), MAX_AI_CLOAK_INFO, fp);
1834 cfwrite(&Boss_cloak_start_time, sizeof(fix), 1, fp);
1835 cfwrite(&Boss_cloak_end_time , sizeof(fix), 1, fp);
1836 cfwrite(&Last_teleport_time , sizeof(fix), 1, fp);
1837 cfwrite(&Boss_teleport_interval, sizeof(fix), 1, fp);
1838 cfwrite(&Boss_cloak_interval, sizeof(fix), 1, fp);
1839 cfwrite(&Boss_cloak_duration, sizeof(fix), 1, fp);
1840 cfwrite(&Last_gate_time, sizeof(fix), 1, fp);
1841 cfwrite(&Gate_interval, sizeof(fix), 1, fp);
1842 cfwrite(&Boss_dying_start_time, sizeof(fix), 1, fp);
1843 cfwrite(&Boss_dying, sizeof(int), 1, fp);
1844 cfwrite(&Boss_dying_sound_playing, sizeof(int), 1, fp);
1845 cfwrite(&Boss_hit_time, sizeof(fix), 1, fp);
1846 // -- MK, 10/21/95, unused! -- cfwrite( &Boss_been_hit, sizeof(int), 1, fp);
1848 cfwrite(&Escort_kill_object, sizeof(Escort_kill_object), 1, fp);
1849 cfwrite(&Escort_last_path_created, sizeof(Escort_last_path_created), 1, fp);
1850 cfwrite(&Escort_goal_object, sizeof(Escort_goal_object), 1, fp);
1851 cfwrite(&Escort_special_goal, sizeof(Escort_special_goal), 1, fp);
1852 cfwrite(&Escort_goal_index, sizeof(Escort_goal_index), 1, fp);
1853 cfwrite(&Stolen_items, sizeof(Stolen_items[0]), MAX_STOLEN_ITEMS, fp);
1856 temp = Point_segs_free_ptr - Point_segs;
1857 cfwrite(&temp, sizeof(int), 1, fp);
1860 cfwrite(&Num_boss_teleport_segs, sizeof(Num_boss_teleport_segs), 1, fp);
1861 cfwrite(&Num_boss_gate_segs, sizeof(Num_boss_gate_segs), 1, fp);
1863 if (Num_boss_gate_segs)
1864 cfwrite(Boss_gate_segs, sizeof(Boss_gate_segs[0]), Num_boss_gate_segs, fp);
1866 if (Num_boss_teleport_segs)
1867 cfwrite(Boss_teleport_segs, sizeof(Boss_teleport_segs[0]), Num_boss_teleport_segs, fp);
1872 int ai_restore_state(CFILE *fp, int version)
1874 cfread(&Ai_initialized, sizeof(int), 1, fp);
1875 cfread(&Overall_agitation, sizeof(int), 1, fp);
1876 cfread(Ai_local_info, sizeof(ai_local), MAX_OBJECTS, fp);
1877 cfread(Point_segs, sizeof(point_seg), MAX_POINT_SEGS, fp);
1878 cfread(Ai_cloak_info, sizeof(ai_cloak_info), MAX_AI_CLOAK_INFO, fp);
1879 cfread(&Boss_cloak_start_time, sizeof(fix), 1, fp);
1880 cfread(&Boss_cloak_end_time , sizeof(fix), 1, fp);
1881 cfread(&Last_teleport_time , sizeof(fix), 1, fp);
1882 cfread(&Boss_teleport_interval, sizeof(fix), 1, fp);
1883 cfread(&Boss_cloak_interval, sizeof(fix), 1, fp);
1884 cfread(&Boss_cloak_duration, sizeof(fix), 1, fp);
1885 cfread(&Last_gate_time, sizeof(fix), 1, fp);
1886 cfread(&Gate_interval, sizeof(fix), 1, fp);
1887 cfread(&Boss_dying_start_time, sizeof(fix), 1, fp);
1888 cfread(&Boss_dying, sizeof(int), 1, fp);
1889 cfread(&Boss_dying_sound_playing, sizeof(int), 1, fp);
1890 cfread(&Boss_hit_time, sizeof(fix), 1, fp);
1891 // -- MK, 10/21/95, unused! -- cfread(&Boss_been_hit, sizeof(int), 1, fp);
1894 cfread(&Escort_kill_object, sizeof(Escort_kill_object), 1, fp);
1895 cfread(&Escort_last_path_created, sizeof(Escort_last_path_created), 1, fp);
1896 cfread(&Escort_goal_object, sizeof(Escort_goal_object), 1, fp);
1897 cfread(&Escort_special_goal, sizeof(Escort_special_goal), 1, fp);
1898 cfread(&Escort_goal_index, sizeof(Escort_goal_index), 1, fp);
1899 cfread(&Stolen_items, sizeof(Stolen_items[0]), MAX_STOLEN_ITEMS, fp);
1903 Escort_kill_object = -1;
1904 Escort_last_path_created = 0;
1905 Escort_goal_object = ESCORT_GOAL_UNSPECIFIED;
1906 Escort_special_goal = -1;
1907 Escort_goal_index = -1;
1909 for (i=0; i<MAX_STOLEN_ITEMS; i++) {
1910 Stolen_items[i] = 255;
1915 if (version >= 15) {
1917 cfread(&temp, sizeof(int), 1, fp);
1918 Point_segs_free_ptr = &Point_segs[temp];
1920 ai_reset_all_paths();
1922 if (version >= 21) {
1923 cfread(&Num_boss_teleport_segs, sizeof(Num_boss_teleport_segs), 1, fp);
1924 cfread(&Num_boss_gate_segs, sizeof(Num_boss_gate_segs), 1, fp);
1926 if (Num_boss_gate_segs)
1927 cfread(Boss_gate_segs, sizeof(Boss_gate_segs[0]), Num_boss_gate_segs, fp);
1929 if (Num_boss_teleport_segs)
1930 cfread(Boss_teleport_segs, sizeof(Boss_teleport_segs[0]), Num_boss_teleport_segs, fp);
1932 // -- Num_boss_teleport_segs = 1;
1933 // -- Num_boss_gate_segs = 1;
1934 // -- Boss_teleport_segs[0] = 0;
1935 // -- Boss_gate_segs[0] = 0;
1936 // Note: Maybe better to leave alone...will probably be ok.
1937 mprintf((1, "Warning: If you fight the boss, he might teleport to segment #0!\n"));