2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
16 * Autonomous Individual movement.
36 #include "editor/editor.h"
40 // ---------- John: These variables must be saved as part of gamesave. --------
41 int Ai_initialized = 0;
42 int Overall_agitation;
43 ai_local Ai_local_info[MAX_OBJECTS];
44 point_seg Point_segs[MAX_POINT_SEGS];
45 point_seg *Point_segs_free_ptr = Point_segs;
46 ai_cloak_info Ai_cloak_info[MAX_AI_CLOAK_INFO];
47 fix Boss_cloak_start_time = 0;
48 fix Boss_cloak_end_time = 0;
49 fix Last_teleport_time = 0;
50 fix Boss_teleport_interval = F1_0*8;
51 fix Boss_cloak_interval = F1_0*10; // Time between cloaks
52 fix Boss_cloak_duration = BOSS_CLOAK_DURATION;
53 fix Last_gate_time = 0;
54 fix Gate_interval = F1_0*6;
55 fix Boss_dying_start_time;
57 sbyte Boss_dying, Boss_dying_sound_playing, unused123, unused234;
59 // -- MK, 10/21/95, unused! -- int Boss_been_hit=0;
62 // ------ John: End of variables which must be saved as part of gamesave. -----
65 // -- ubyte Boss_cloaks[NUM_D2_BOSSES] = {1,1,1,1,1,1}; // Set byte if this boss can cloak
67 const ubyte Boss_teleports[NUM_D2_BOSSES] = {1,1,1,1,1,1, 1,1}; // Set byte if this boss can teleport
68 const ubyte Boss_spew_more[NUM_D2_BOSSES] = {0,1,0,0,0,0, 0,0}; // If set, 50% of time, spew two bots.
69 const 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.
70 const 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.
71 const ubyte Boss_invulnerable_energy[NUM_D2_BOSSES] = {0,0,1,1,0,0, 0,0}; // Set byte if boss is invulnerable to energy weapons.
72 const ubyte Boss_invulnerable_matter[NUM_D2_BOSSES] = {0,0,0,0,1,1, 1,0}; // Set byte if boss is invulnerable to matter weapons.
73 const 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)
77 sbyte 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};
79 int Robot_firing_enabled = 1;
80 int Animation_enabled = 1;
83 int Ai_info_enabled=0;
87 // These globals are set by a call to find_vector_intersection, which is a slow routine,
88 // so we don't want to call it again (for this object) unless we have to.
90 int Hit_type, Hit_seg;
93 int Num_awareness_events = 0;
94 awareness_event Awareness_events[MAX_AWARENESS_EVENTS];
96 vms_vector Believed_player_pos;
97 int Believed_player_seg;
100 // Index into this array with ailp->mode
101 const char mode_text[18][16] = {
122 // Index into this array with aip->behavior
123 const char behavior_text[6][9] = {
132 // Index into this array with aip->GOAL_STATE or aip->CURRENT_STATE
133 const char state_text[8][5] = {
147 // Current state indicates where the robot current is, or has just done.
148 // Transition table between states for an AI object.
149 // First dimension is trigger event.
150 // Second dimension is current state.
151 // Third dimension is goal state.
152 // Result is new goal state.
153 // ERR_ means something impossible has happened.
154 static const sbyte Ai_transition_table[AI_MAX_EVENT][AI_MAX_STATE][AI_MAX_STATE] = {
156 // Event = AIE_FIRE, a nearby object fired
157 // none rest srch lock flin fire reco // CURRENT is rows, GOAL is columns
158 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO }, // none
159 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO }, // rest
160 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO }, // search
161 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO }, // lock
162 { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO }, // flinch
163 { AIS_ERR_, AIS_FIRE, AIS_FIRE, AIS_FIRE, AIS_FLIN, AIS_FIRE, AIS_RECO }, // fire
164 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE } // recoil
167 // Event = AIE_HITT, a nearby object was hit (or a wall was hit)
169 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
170 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
171 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
172 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
173 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FLIN},
174 { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO},
175 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE}
178 // Event = AIE_COLL, player collided with robot
180 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
181 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
182 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
183 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
184 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_LOCK, AIS_FLIN, AIS_FLIN},
185 { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO},
186 { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE}
189 // Event = AIE_HURT, player hurt robot (by firing at and hitting it)
190 // Note, this doesn't necessarily mean the robot JUST got hit, only that that is the most recent thing that happened.
192 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
193 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
194 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
195 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
196 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
197 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
198 { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN}
204 fix Dist_to_last_fired_upon_player_pos = 0;
206 // ----------------------------------------------------------------------------
207 void init_ai_frame(void)
211 Dist_to_last_fired_upon_player_pos = vm_vec_dist_quick(&Last_fired_upon_player_pos, &Believed_player_pos);
213 ab_state = Afterburner_charge && Controls.state[afterburner] && (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER);
215 if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) || (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT_ON) || ab_state) {
220 // ----------------------------------------------------------------------------
221 // Return firing status.
222 // If ready to fire a weapon, return true, else return false.
223 // Ready to fire a weapon if next_fire <= 0 or next_fire2 <= 0.
224 int ready_to_fire(robot_info *robptr, ai_local *ailp)
226 if (robptr->weapon_type2 != -1)
227 return (ailp->next_fire <= 0) || (ailp->next_fire2 <= 0);
229 return (ailp->next_fire <= 0);
232 // ----------------------------------------------------------------------------
233 // Make a robot near the player snipe.
234 #define MNRS_SEG_MAX 70
235 void make_nearby_robot_snipe(void)
238 short bfs_list[MNRS_SEG_MAX];
240 create_bfs_list(ConsoleObject->segnum, bfs_list, &bfs_length, MNRS_SEG_MAX);
242 for (i=0; i<bfs_length; i++) {
243 int objnum = Segments[bfs_list[i]].objects;
245 while (objnum != -1) {
246 object *objp = &Objects[objnum];
247 robot_info *robptr = &Robot_info[objp->id];
249 if ((objp->type == OBJ_ROBOT) && (objp->id != ROBOT_BRAIN)) {
250 if ((objp->ctype.ai_info.behavior != AIB_SNIPE) && (objp->ctype.ai_info.behavior != AIB_RUN_FROM) && !Robot_info[objp->id].boss_flag && !robptr->companion) {
251 objp->ctype.ai_info.behavior = AIB_SNIPE;
252 Ai_local_info[objnum].mode = AIM_SNIPE_ATTACK;
253 mprintf((0, "Making robot #%i go into snipe mode!\n", objnum));
261 mprintf((0, "Couldn't find a robot to make snipe!\n"));
265 int Ai_last_missile_camera;
267 int Robots_kill_robots_cheat = 0;
269 // --------------------------------------------------------------------------------------------------------------------
270 void do_ai_frame(object *obj)
272 int objnum = OBJECT_NUMBER(obj);
273 ai_static *aip = &obj->ctype.ai_info;
274 ai_local *ailp = &Ai_local_info[objnum];
276 vms_vector vec_to_player;
279 int player_visibility=-1;
283 int visibility_and_vec_computed = 0;
284 int previous_visibility;
285 vms_vector gun_point;
286 vms_vector vis_vec_pos;
288 ailp->next_action_time -= FrameTime;
290 if (aip->SKIP_AI_COUNT) {
291 aip->SKIP_AI_COUNT--;
292 if (obj->mtype.phys_info.flags & PF_USES_THRUST) {
293 obj->mtype.phys_info.rotthrust.x = (obj->mtype.phys_info.rotthrust.x * 15)/16;
294 obj->mtype.phys_info.rotthrust.y = (obj->mtype.phys_info.rotthrust.y * 15)/16;
295 obj->mtype.phys_info.rotthrust.z = (obj->mtype.phys_info.rotthrust.z * 15)/16;
296 if (!aip->SKIP_AI_COUNT)
297 obj->mtype.phys_info.flags &= ~PF_USES_THRUST;
302 robptr = &Robot_info[obj->id];
303 Assert(robptr->always_0xabcd == 0xabcd);
305 if (do_any_robot_dying_frame(obj))
308 // Kind of a hack. If a robot is flinching, but it is time for it to fire, unflinch it.
309 // Else, you can turn a big nasty robot into a wimp by firing flares at it.
310 // This also allows the player to see the cool flinch effect for mechs without unbalancing the game.
311 if ((aip->GOAL_STATE == AIS_FLIN) && ready_to_fire(robptr, ailp)) {
312 aip->GOAL_STATE = AIS_FIRE;
316 if ((aip->behavior == AIB_RUN_FROM) && (ailp->mode != AIM_RUN_FROM_OBJECT))
317 Int3(); // This is peculiar. Behavior is run from, but mode is not. Contact Mike.
319 mprintf_animation_info((obj));
324 if (Break_on_object != -1)
325 if (OBJECT_NUMBER(obj) == Break_on_object)
326 Int3(); // Contact Mike: This is a debug break
329 //mprintf((0, "Object %i: behavior = %02x, mode = %i, awareness = %i, time = %7.3f\n", OBJECT_NUMBER(obj), aip->behavior, ailp->mode, ailp->player_awareness_type, f2fl(ailp->player_awareness_time)));
330 //mprintf((0, "Object %i: behavior = %02x, mode = %i, awareness = %i, cur=%i, goal=%i\n", OBJECT_NUMBER(obj), aip->behavior, ailp->mode, ailp->player_awareness_type, aip->CURRENT_STATE, aip->GOAL_STATE));
332 //Assert((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR));
333 if (!((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR))) {
334 //mprintf((0, "Object %i behavior is %i, setting to AIB_NORMAL, fix in editor!\n", objnum, aip->behavior));
335 aip->behavior = AIB_NORMAL;
338 Assert(obj->segnum != -1);
339 Assert(obj->id < N_robot_types);
341 obj_ref = objnum ^ FrameCount;
343 if (ailp->next_fire > -F1_0*8)
344 ailp->next_fire -= FrameTime;
346 if (robptr->weapon_type2 != -1) {
347 if (ailp->next_fire2 > -F1_0*8)
348 ailp->next_fire2 -= FrameTime;
350 ailp->next_fire2 = F1_0*8;
352 if (ailp->time_since_processed < F1_0*256)
353 ailp->time_since_processed += FrameTime;
355 previous_visibility = ailp->previous_visibility; // Must get this before we toast the master copy!
357 // -- (No robots have this behavior...)
358 // -- // Deal with cloaking for robots which are cloaked except just before firing.
359 // -- if (robptr->cloak_type == RI_CLOAKED_EXCEPT_FIRING)
360 // -- if (ailp->next_fire < F1_0/2)
361 // -- aip->CLOAKED = 1;
363 // -- aip->CLOAKED = 0;
365 // If only awake because of a camera, make that the believed player position.
366 if ((aip->SUB_FLAGS & SUB_FLAGS_CAMERA_AWAKE) && (Ai_last_missile_camera != -1))
367 Believed_player_pos = Objects[Ai_last_missile_camera].pos;
369 if (Robots_kill_robots_cheat) {
370 vis_vec_pos = obj->pos;
371 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
372 if (player_visibility) {
373 int ii, min_obj = -1;
374 fix min_dist = F1_0*200, cur_dist;
376 for (ii=0; ii<=Highest_object_index; ii++)
377 if ((Objects[ii].type == OBJ_ROBOT) && (ii != objnum)) {
378 cur_dist = vm_vec_dist_quick(&obj->pos, &Objects[ii].pos);
380 if (cur_dist < F1_0*100)
381 if (object_to_object_visibility(obj, &Objects[ii], FQ_TRANSWALL))
382 if (cur_dist < min_dist) {
388 Believed_player_pos = Objects[min_obj].pos;
389 Believed_player_seg = Objects[min_obj].segnum;
390 vm_vec_normalized_dir_quick(&vec_to_player, &Believed_player_pos, &obj->pos);
397 visibility_and_vec_computed = 0;
398 if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
399 Believed_player_pos = ConsoleObject->pos;
401 Believed_player_pos = Ai_cloak_info[objnum & (MAX_AI_CLOAK_INFO-1)].last_position;
404 dist_to_player = vm_vec_dist_quick(&Believed_player_pos, &obj->pos);
405 //if (robptr->companion)
406 // 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));
408 // If this robot can fire, compute visibility from gun position.
409 // Don't want to compute visibility twice, as it is expensive. (So is call to calc_gun_point).
410 if ((previous_visibility || !(obj_ref & 3)) && ready_to_fire(robptr, ailp) && (dist_to_player < F1_0*200) && (robptr->n_guns) && !(robptr->attack_type)) {
411 // Since we passed ready_to_fire(), either next_fire or next_fire2 <= 0. calc_gun_point from relevant one.
412 // If both are <= 0, we will deal with the mess in ai_do_actual_firing_stuff
413 if (ailp->next_fire <= 0)
414 calc_gun_point(&gun_point, obj, aip->CURRENT_GUN);
416 calc_gun_point(&gun_point, obj, 0);
417 vis_vec_pos = gun_point;
419 vis_vec_pos = obj->pos;
420 vm_vec_zero(&gun_point);
421 //mprintf((0, "Visibility = %i, computed from center.\n", player_visibility));
424 // MK: Debugging, July 26, 1995!
427 // compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
428 // 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));
430 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
431 // Occasionally make non-still robots make a path to the player. Based on agitation and distance from player.
432 if ((aip->behavior != AIB_SNIPE) && (aip->behavior != AIB_RUN_FROM) && (aip->behavior != AIB_STILL) && !(Game_mode & GM_MULTI) && (robptr->companion != 1) && (robptr->thief != 1))
433 if (Overall_agitation > 70) {
434 if ((dist_to_player < F1_0*200) && (d_rand() < FrameTime/4)) {
435 if (d_rand() * (Overall_agitation - 40) > F1_0*5) {
436 // -- mprintf((0, "(1) Object #%i going from still to path in frame %i.\n", objnum, FrameCount));
437 create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
443 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
444 // If retry count not 0, then add it into consecutive_retries.
445 // If it is 0, cut down consecutive_retries.
446 // This is largely a hack to speed up physics and deal with stupid
447 // AI. This is low level communication between systems of a sort
448 // that should not be done.
449 if ((ailp->retry_count) && !(Game_mode & GM_MULTI)) {
450 ailp->consecutive_retries += ailp->retry_count;
451 ailp->retry_count = 0;
452 if (ailp->consecutive_retries > 3) {
453 switch (ailp->mode) {
454 case AIM_GOTO_PLAYER:
455 // -- mprintf((0, "Buddy stuck going to player...\n"));
456 // -- Buddy_got_stuck = 1;
457 move_towards_segment_center(obj);
458 create_path_to_player(obj, 100, 1);
459 // -- Buddy_got_stuck = 0;
461 case AIM_GOTO_OBJECT:
462 // -- mprintf((0, "Buddy stuck going to object...\n"));
463 Escort_goal_object = ESCORT_GOAL_UNSPECIFIED;
464 //if (obj->segnum == ConsoleObject->segnum) {
465 // if (Point_segs[aip->hide_index + aip->cur_path_index].segnum == obj->segnum)
466 // if ((aip->cur_path_index + aip->PATH_DIR >= 0) && (aip->cur_path_index + aip->PATH_DIR < aip->path_length-1))
467 // aip->cur_path_index += aip->PATH_DIR;
470 case AIM_CHASE_OBJECT:
471 // -- mprintf((0, "(2) Object #%i, retries while chasing, creating path to player in frame %i\n", objnum, FrameCount));
472 create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
475 if (robptr->attack_type)
476 move_towards_segment_center(obj);
477 else if (!((aip->behavior == AIB_STILL) || (aip->behavior == AIB_STATION) || (aip->behavior == AIB_FOLLOW))) // Behavior is still, so don't follow path.
478 attempt_to_resume_path(obj);
480 case AIM_FOLLOW_PATH:
481 // mprintf((0, "Object %i following path got %i retries in frame %i\n", OBJECT_NUMBER(obj), ailp->consecutive_retries, FrameCount));
482 if (Game_mode & GM_MULTI) {
483 ailp->mode = AIM_STILL;
485 attempt_to_resume_path(obj);
487 case AIM_RUN_FROM_OBJECT:
488 move_towards_segment_center(obj);
489 obj->mtype.phys_info.velocity.x = 0;
490 obj->mtype.phys_info.velocity.y = 0;
491 obj->mtype.phys_info.velocity.z = 0;
492 create_n_segment_path(obj, 5, -1);
493 ailp->mode = AIM_RUN_FROM_OBJECT;
496 mprintf((0, "Hiding robot (%i) collided much.\n", OBJECT_NUMBER(obj)));
497 move_towards_segment_center(obj);
498 obj->mtype.phys_info.velocity.x = 0;
499 obj->mtype.phys_info.velocity.y = 0;
500 obj->mtype.phys_info.velocity.z = 0;
503 create_n_segment_path_to_door(obj, 5, -1);
506 case AIM_FOLLOW_PATH_2:
507 Int3(); // Should never happen!
511 ailp->consecutive_retries = 0;
514 ailp->consecutive_retries /= 2;
516 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
517 // If in materialization center, exit
518 if (!(Game_mode & GM_MULTI) && (Segment2s[obj->segnum].special == SEGMENT_IS_ROBOTMAKER)) {
519 if (Station[Segment2s[obj->segnum].value].Enabled) {
520 ai_follow_path(obj, 1, 1, NULL); // 1 = player is visible, which might be a lie, but it works.
525 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
526 // Decrease player awareness due to the passage of time.
527 if (ailp->player_awareness_type) {
528 if (ailp->player_awareness_time > 0) {
529 ailp->player_awareness_time -= FrameTime;
530 if (ailp->player_awareness_time <= 0) {
531 ailp->player_awareness_time = F1_0*2; //new: 11/05/94
532 ailp->player_awareness_type--; //new: 11/05/94
535 ailp->player_awareness_type--;
536 ailp->player_awareness_time = F1_0*2;
537 //aip->GOAL_STATE = AIS_REST;
540 aip->GOAL_STATE = AIS_REST; //new: 12/13/94
543 if (Player_is_dead && (ailp->player_awareness_type == 0))
544 if ((dist_to_player < F1_0*200) && (d_rand() < FrameTime/8)) {
545 if ((aip->behavior != AIB_STILL) && (aip->behavior != AIB_RUN_FROM)) {
546 if (!ai_multiplayer_awareness(obj, 30))
549 ai_multi_send_robot_position(objnum, -1);
552 if (!((ailp->mode == AIM_FOLLOW_PATH) && (aip->cur_path_index < aip->path_length-1)))
553 if ((aip->behavior != AIB_SNIPE) && (aip->behavior != AIB_RUN_FROM)) {
554 if (dist_to_player < F1_0*30)
555 create_n_segment_path(obj, 5, 1);
557 create_path_to_player(obj, 20, 1);
562 // -- // Make sure that if this guy got hit or bumped, then he's chasing player.
563 // -- if ((ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) || (ailp->player_awareness_type >= PA_PLAYER_COLLISION)) {
564 // -- 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)) {
565 // -- ailp->mode = AIM_CHASE_OBJECT;
566 // -- ailp->player_awareness_type = 0;
567 // -- ailp->player_awareness_time = 0;
571 // Make sure that if this guy got hit or bumped, then he's chasing player.
572 if ((ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) || (ailp->player_awareness_type >= PA_PLAYER_COLLISION)) {
573 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
574 if (player_visibility == 1) // Only increase visibility if unobstructed, else claw guys attack through doors.
575 player_visibility = 2;
576 } else if (((obj_ref&3) == 0) && !previous_visibility && (dist_to_player < F1_0*100)) {
580 sval = (dist_to_player * (Difficulty_level+1))/64;
582 // -- mprintf((0, "Object #%3i: dist = %7.3f, rval = %8x, sval = %8x", OBJECT_NUMBER(obj), f2fl(dist_to_player), rval, sval));
583 if ((fixmul(rval, sval) < FrameTime) || (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT_ON)) {
584 ailp->player_awareness_type = PA_PLAYER_COLLISION;
585 ailp->player_awareness_time = F1_0*3;
586 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
587 if (player_visibility == 1) {
588 player_visibility = 2;
589 // -- mprintf((0, "...SWITCH!"));
593 // -- mprintf((0, "\n"));
597 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
598 if ((aip->GOAL_STATE == AIS_FLIN) && (aip->CURRENT_STATE == AIS_FLIN))
599 aip->GOAL_STATE = AIS_LOCK;
601 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
602 // Note: Should only do these two function calls for objects which animate
603 if (Animation_enabled && (dist_to_player < F1_0*100)) { // && !(Game_mode & GM_MULTI)) {
604 object_animates = do_silly_animation(obj);
606 ai_frame_animation(obj);
607 //mprintf((0, "Object %i: goal=%i, current=%i\n", OBJECT_NUMBER(obj), obj->ctype.ai_info.GOAL_STATE, obj->ctype.ai_info.CURRENT_STATE));
609 // If Object is supposed to animate, but we don't let it animate due to distance, then
610 // we must change its state, else it will never update.
611 aip->CURRENT_STATE = aip->GOAL_STATE;
612 object_animates = 0; // If we're not doing the animation, then should pretend it doesn't animate.
615 if (Robot_info[obj->id].boss_flag) {
617 fix dtp = dist_to_player/4;
619 if (aip->GOAL_STATE == AIS_FLIN)
620 aip->GOAL_STATE = AIS_FIRE;
621 if (aip->CURRENT_STATE == AIS_FLIN)
622 aip->CURRENT_STATE = AIS_FIRE;
624 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
626 pv = player_visibility;
628 // If player cloaked, visibility is screwed up and superboss will gate in robots when not supposed to.
629 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
631 dtp = vm_vec_dist_quick(&ConsoleObject->pos, &obj->pos)/4;
634 if (Robot_info[obj->id].boss_flag == BOSS_SUPER)
635 do_super_boss_stuff(obj, dtp, pv);
637 do_boss_stuff(obj, pv);
640 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
641 // Time-slice, don't process all the time, purely an efficiency hack.
642 // Guys whose behavior is station and are not at their hide segment get processed anyway.
643 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!
645 if (Break_on_object != objnum) { // don't time slice if we're interested in this object.
647 if ((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum)) {
648 if (dist_to_player > F1_0*250) // station guys not at home always processed until 250 units away.
650 } else if ((!ailp->previous_visibility) && ((dist_to_player >> 7) > ailp->time_since_processed)) { // 128 units away (6.4 segments) processed after 1 second.
660 // Reset time since processed, but skew objects so not everything
661 // processed synchronously, else we get fast frames with the
662 // occasional very slow frame.
663 // AI_proc_time = ailp->time_since_processed;
664 ailp->time_since_processed = - ((objnum & 0x03) * FrameTime ) / 2;
666 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
667 // Perform special ability
670 // Robots function nicely if behavior is Station. This
671 // means they won't move until they can see the player, at
672 // which time they will start wandering about opening doors.
673 if (ConsoleObject->segnum == obj->segnum) {
674 if (!ai_multiplayer_awareness(obj, 97))
676 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
677 move_away_from_player(obj, &vec_to_player, 0);
678 ai_multi_send_robot_position(objnum, -1);
679 } else if (ailp->mode != AIM_STILL) {
682 r = openable_doors_in_segment(obj->segnum);
684 ailp->mode = AIM_OPEN_DOOR;
686 } else if (ailp->mode != AIM_FOLLOW_PATH) {
687 if (!ai_multiplayer_awareness(obj, 50))
689 create_n_segment_path_to_door(obj, 8+Difficulty_level, -1); // third parameter is avoid_seg, -1 means avoid nothing.
690 ai_multi_send_robot_position(objnum, -1);
693 if (ailp->next_action_time < 0) {
694 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
695 if (player_visibility) {
696 make_nearby_robot_snipe();
697 ailp->next_action_time = (NDL - Difficulty_level) * 2*F1_0;
701 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
702 if (player_visibility) {
703 if (!ai_multiplayer_awareness(obj, 50))
705 create_n_segment_path_to_door(obj, 8+Difficulty_level, -1); // third parameter is avoid_seg, -1 means avoid nothing.
706 ai_multi_send_robot_position(objnum, -1);
714 if (aip->behavior == AIB_SNIPE) {
715 if ((Game_mode & GM_MULTI) && !robptr->thief) {
716 aip->behavior = AIB_NORMAL;
717 ailp->mode = AIM_CHASE_OBJECT;
721 if (!(obj_ref & 3) || previous_visibility) {
722 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
724 // If this sniper is in still mode, if he was hit or can see player, switch to snipe mode.
725 if (ailp->mode == AIM_STILL)
726 if (player_visibility || (ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION))
727 ailp->mode = AIM_SNIPE_ATTACK;
729 if (!robptr->thief && (ailp->mode != AIM_STILL))
730 do_snipe_frame(obj, dist_to_player, player_visibility, &vec_to_player);
731 } else if (!robptr->thief && !robptr->companion)
735 // More special ability stuff, but based on a property of a robot, not its ID.
736 if (robptr->companion) {
738 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
739 do_escort_frame(obj, dist_to_player, player_visibility);
741 if (obj->ctype.ai_info.danger_laser_num != -1) {
742 object *dobjp = &Objects[obj->ctype.ai_info.danger_laser_num];
744 if ((dobjp->type == OBJ_WEAPON) && (dobjp->signature == obj->ctype.ai_info.danger_laser_signature)) {
746 // -- mprintf((0, "Evading! "));
747 circle_distance = robptr->circle_distance[Difficulty_level] + ConsoleObject->size;
748 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, circle_distance, 1, player_visibility);
752 if (ready_to_fire(robptr, ailp)) {
754 if (openable_doors_in_segment(obj->segnum) != -1)
756 else if (openable_doors_in_segment(Point_segs[aip->hide_index + aip->cur_path_index + aip->PATH_DIR].segnum) != -1)
758 else if (openable_doors_in_segment(Point_segs[aip->hide_index + aip->cur_path_index + 2*aip->PATH_DIR].segnum) != -1)
760 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)) {
761 // mprintf((0, "Firing at player because dot = %7.3f\n", f2fl(vm_vec_dot(&ConsoleObject->orient.fvec, &vec_to_player))));
764 ; // 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)));
767 Laser_create_new_easy( &obj->orient.fvec, &obj->pos, OBJECT_NUMBER(obj), FLARE_ID, 1 );
768 ailp->next_fire = F1_0/2;
769 if (!Buddy_allowed_to_talk) // If buddy not talking, make him fire flares less often.
770 ailp->next_fire += d_rand()*4;
778 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
779 do_thief_frame(obj, dist_to_player, player_visibility, &vec_to_player);
781 if (ready_to_fire(robptr, ailp)) {
783 if (openable_doors_in_segment(obj->segnum) != -1)
785 else if (openable_doors_in_segment(Point_segs[aip->hide_index + aip->cur_path_index + aip->PATH_DIR].segnum) != -1)
787 else if (openable_doors_in_segment(Point_segs[aip->hide_index + aip->cur_path_index + 2*aip->PATH_DIR].segnum) != -1)
791 // @mk, 05/08/95: Firing flare from center of object, this is dumb...
792 Laser_create_new_easy( &obj->orient.fvec, &obj->pos, OBJECT_NUMBER(obj), FLARE_ID, 1 );
793 ailp->next_fire = F1_0/2;
794 if (Stolen_item_index == 0) // If never stolen an item, fire flares less often (bad: Stolen_item_index wraps, but big deal)
795 ailp->next_fire += d_rand()*4;
800 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
801 switch (ailp->mode) {
802 case AIM_CHASE_OBJECT: { // chasing player, sort of, chase if far, back off if close, circle in between
805 circle_distance = robptr->circle_distance[Difficulty_level] + ConsoleObject->size;
806 // Green guy doesn't get his circle distance boosted, else he might never attack.
807 if (robptr->attack_type != 1)
808 circle_distance += (objnum&0xf) * F1_0/2;
810 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
812 // @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.
813 if ((player_visibility < 2) && (previous_visibility == 2)) { // this is redundant: mk, 01/15/95: && (ailp->mode == AIM_CHASE_OBJECT)) {
814 // -- mprintf((0, "I used to be able to see the player!\n"));
815 if (!ai_multiplayer_awareness(obj, 53)) {
816 if (maybe_ai_do_actual_firing_stuff(obj, aip))
817 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
820 // -- mprintf((0, "(3) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
821 create_path_to_player(obj, 8, 1);
822 ai_multi_send_robot_position(objnum, -1);
823 } else if ((player_visibility == 0) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
824 // If pretty far from the player, player cannot be seen
825 // (obstructed) and in chase mode, switch to follow path mode.
826 // This has one desirable benefit of avoiding physics retries.
827 if (aip->behavior == AIB_STATION) {
828 ailp->goal_segment = aip->hide_segment;
829 // -- mprintf((0, "(1) Object #%i going from chase to STATION in frame %i.\n", objnum, FrameCount));
830 create_path_to_station(obj, 15);
831 } // -- this looks like a dumb thing to do...robots following paths far away from you! else create_n_segment_path(obj, 5, -1);
835 if ((aip->CURRENT_STATE == AIS_REST) && (aip->GOAL_STATE == AIS_REST)) {
836 if (player_visibility) {
837 if (d_rand() < FrameTime*player_visibility) {
838 if (dist_to_player/256 < d_rand()*player_visibility) {
839 // mprintf((0, "Object %i searching for player.\n", OBJECT_NUMBER(obj)));
840 aip->GOAL_STATE = AIS_SRCH;
841 aip->CURRENT_STATE = AIS_SRCH;
847 if (GameTime - ailp->time_player_seen > CHASE_TIME_LENGTH) {
849 if (Game_mode & GM_MULTI)
850 if (!player_visibility && (dist_to_player > F1_0*70)) {
851 ailp->mode = AIM_STILL;
855 if (!ai_multiplayer_awareness(obj, 64)) {
856 if (maybe_ai_do_actual_firing_stuff(obj, aip))
857 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
860 // -- 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));
861 // -- bad idea, robots charge player they've never seen! -- create_path_to_player(obj, 10, 1);
862 // -- bad idea, robots charge player they've never seen! -- ai_multi_send_robot_position(objnum, -1);
863 } else if ((aip->CURRENT_STATE != AIS_REST) && (aip->GOAL_STATE != AIS_REST)) {
864 if (!ai_multiplayer_awareness(obj, 70)) {
865 if (maybe_ai_do_actual_firing_stuff(obj, aip))
866 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
869 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, circle_distance, 0, player_visibility);
871 if ((obj_ref & 1) && ((aip->GOAL_STATE == AIS_SRCH) || (aip->GOAL_STATE == AIS_LOCK))) {
872 if (player_visibility) // == 2)
873 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
877 ai_multi_send_robot_position(objnum, 1);
880 ai_multi_send_robot_position(objnum, -1);
882 do_firing_stuff(obj, player_visibility, &vec_to_player);
887 case AIM_RUN_FROM_OBJECT:
888 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
890 if (player_visibility) {
891 if (ailp->player_awareness_type == 0)
892 ailp->player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
896 // If in multiplayer, only do if player visible. If not multiplayer, do always.
897 if (!(Game_mode & GM_MULTI) || player_visibility)
898 if (ai_multiplayer_awareness(obj, 75)) {
899 ai_follow_path(obj, player_visibility, previous_visibility, &vec_to_player);
900 ai_multi_send_robot_position(objnum, -1);
903 if (aip->GOAL_STATE != AIS_FLIN)
904 aip->GOAL_STATE = AIS_LOCK;
905 else if (aip->CURRENT_STATE == AIS_FLIN)
906 aip->GOAL_STATE = AIS_LOCK;
908 // Bad to let run_from robot fire at player because it
909 // will cause a war in which it turns towards the player
910 // to fire and then towards its goal to move.
911 // do_firing_stuff(obj, player_visibility, &vec_to_player);
913 // (Note, only drop if player is visible. This prevents
914 // the bombs from being a giveaway, and also ensures that
915 // the robot is moving while it is dropping. Also means
916 // fewer will be dropped.)
917 if ((ailp->next_fire <= 0) && (player_visibility)) {
918 vms_vector fire_vec, fire_pos;
920 if (!ai_multiplayer_awareness(obj, 75))
923 fire_vec = obj->orient.fvec;
924 vm_vec_negate(&fire_vec);
925 vm_vec_add(&fire_pos, &obj->pos, &fire_vec);
927 if (aip->SUB_FLAGS & SUB_FLAGS_SPROX)
928 Laser_create_new_easy( &fire_vec, &fire_pos, OBJECT_NUMBER(obj), ROBOT_SUPERPROX_ID, 1 );
930 Laser_create_new_easy( &fire_vec, &fire_pos, OBJECT_NUMBER(obj), PROXIMITY_ID, 1 );
932 ailp->next_fire = (F1_0/2)*(NDL+5 - Difficulty_level); // Drop a proximity bomb every 5 seconds.
936 if (Game_mode & GM_MULTI)
938 ai_multi_send_robot_position(OBJECT_NUMBER(obj), -1);
939 if (aip->SUB_FLAGS & SUB_FLAGS_SPROX)
940 multi_send_robot_fire(OBJECT_NUMBER(obj), -2, &fire_vec);
942 multi_send_robot_fire(OBJECT_NUMBER(obj), -1, &fire_vec);
949 case AIM_GOTO_PLAYER:
950 case AIM_GOTO_OBJECT:
951 ai_follow_path(obj, 2, previous_visibility, &vec_to_player); // Follows path as if player can see robot.
952 ai_multi_send_robot_position(objnum, -1);
955 case AIM_FOLLOW_PATH: {
956 int anger_level = 65;
958 if (aip->behavior == AIB_STATION)
959 if (Point_segs[aip->hide_index + aip->path_length - 1].segnum == aip->hide_segment) {
961 // mprintf((0, "Object %i, station, lowering anger to 64.\n"));
964 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
966 if (Game_mode & (GM_MODEM | GM_SERIAL))
967 if (!player_visibility && (dist_to_player > F1_0*70)) {
968 ailp->mode = AIM_STILL;
972 if (!ai_multiplayer_awareness(obj, anger_level)) {
973 if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
974 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
975 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
980 ai_follow_path(obj, player_visibility, previous_visibility, &vec_to_player);
982 if (aip->GOAL_STATE != AIS_FLIN)
983 aip->GOAL_STATE = AIS_LOCK;
984 else if (aip->CURRENT_STATE == AIS_FLIN)
985 aip->GOAL_STATE = AIS_LOCK;
987 if (aip->behavior != AIB_RUN_FROM)
988 do_firing_stuff(obj, player_visibility, &vec_to_player);
990 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)) {
991 if (robptr->attack_type == 0)
992 ailp->mode = AIM_CHASE_OBJECT;
993 // This should not just be distance based, but also time-since-player-seen based.
994 } else if ((dist_to_player > F1_0*(20*(2*Difficulty_level + robptr->pursuit)))
995 && (GameTime - ailp->time_player_seen > (F1_0/2*(Difficulty_level+robptr->pursuit)))
996 && (player_visibility == 0)
997 && (aip->behavior == AIB_NORMAL)
998 && (ailp->mode == AIM_FOLLOW_PATH)) {
999 ailp->mode = AIM_STILL;
1000 aip->hide_index = -1;
1001 aip->path_length = 0;
1004 ai_multi_send_robot_position(objnum, -1);
1010 if (!ai_multiplayer_awareness(obj, 71)) {
1011 if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
1012 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1013 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1018 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1020 if (player_visibility == 2) {
1021 // Get behind the player.
1023 // If vec_to_player dot player_rear_vector > 0, behind is goal.
1024 // Else choose goal with larger dot from left, right.
1025 vms_vector goal_point, goal_vector, vec_to_goal, rand_vec;
1028 dot = vm_vec_dot(&ConsoleObject->orient.fvec, &vec_to_player);
1029 if (dot > 0) { // Remember, we're interested in the rear vector dot being < 0.
1030 goal_vector = ConsoleObject->orient.fvec;
1031 vm_vec_negate(&goal_vector);
1032 // -- mprintf((0, "Goal is BEHIND\n"));
1035 dot = vm_vec_dot(&ConsoleObject->orient.rvec, &vec_to_player);
1036 goal_vector = ConsoleObject->orient.rvec;
1038 vm_vec_negate(&goal_vector);
1039 // -- mprintf((0, "Goal is LEFT\n"));
1041 ; // -- mprintf((0, "Goal is RIGHT\n"));
1044 vm_vec_scale(&goal_vector, 2*(ConsoleObject->size + obj->size + (((objnum*4 + FrameCount) & 63) << 12)));
1045 vm_vec_add(&goal_point, &ConsoleObject->pos, &goal_vector);
1046 make_random_vector(&rand_vec);
1047 vm_vec_scale_add2(&goal_point, &rand_vec, F1_0*8);
1048 vm_vec_sub(&vec_to_goal, &goal_point, &obj->pos);
1049 vm_vec_normalize_quick(&vec_to_goal);
1050 move_towards_vector(obj, &vec_to_goal, 0);
1051 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1052 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1055 if (aip->GOAL_STATE != AIS_FLIN)
1056 aip->GOAL_STATE = AIS_LOCK;
1057 else if (aip->CURRENT_STATE == AIS_FLIN)
1058 aip->GOAL_STATE = AIS_LOCK;
1060 ai_multi_send_robot_position(objnum, -1);
1064 if ((dist_to_player < F1_0*120+Difficulty_level*F1_0*20) || (ailp->player_awareness_type >= PA_WEAPON_ROBOT_COLLISION-1)) {
1065 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1067 // turn towards vector if visible this time or last time, or rand
1069 if ((player_visibility == 2) || (previous_visibility == 2)) { // -- MK, 06/09/95: || ((d_rand() > 0x4000) && !(Game_mode & GM_MULTI))) {
1070 if (!ai_multiplayer_awareness(obj, 71)) {
1071 if (maybe_ai_do_actual_firing_stuff(obj, aip))
1072 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1075 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1076 ai_multi_send_robot_position(objnum, -1);
1079 do_firing_stuff(obj, player_visibility, &vec_to_player);
1080 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.
1081 if (robptr->attack_type == 1) {
1082 aip->behavior = AIB_NORMAL;
1083 if (!ai_multiplayer_awareness(obj, 80)) {
1084 if (maybe_ai_do_actual_firing_stuff(obj, aip))
1085 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1088 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 0, player_visibility);
1090 ai_multi_send_robot_position(objnum, 1);
1094 ai_multi_send_robot_position(objnum, -1);
1096 // Robots in hover mode are allowed to evade at half normal speed.
1097 if (!ai_multiplayer_awareness(obj, 81)) {
1098 if (maybe_ai_do_actual_firing_stuff(obj, aip))
1099 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1102 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 1, player_visibility);
1104 ai_multi_send_robot_position(objnum, -1);
1108 ai_multi_send_robot_position(objnum, -1);
1110 } else if ((obj->segnum != aip->hide_segment) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
1111 // If pretty far from the player, player cannot be
1112 // seen (obstructed) and in chase mode, switch to
1113 // follow path mode.
1114 // This has one desirable benefit of avoiding physics retries.
1115 if (aip->behavior == AIB_STATION) {
1116 ailp->goal_segment = aip->hide_segment;
1117 // -- mprintf((0, "(2) Object #%i going from STILL to STATION in frame %i.\n", objnum, FrameCount));
1118 create_path_to_station(obj, 15);
1125 case AIM_OPEN_DOOR: { // trying to open a door.
1126 vms_vector center_point, goal_vector;
1127 Assert(obj->id == ROBOT_BRAIN); // Make sure this guy is allowed to be in this mode.
1129 if (!ai_multiplayer_awareness(obj, 62))
1131 compute_center_point_on_side(¢er_point, &Segments[obj->segnum], aip->GOALSIDE);
1132 vm_vec_sub(&goal_vector, ¢er_point, &obj->pos);
1133 vm_vec_normalize_quick(&goal_vector);
1134 ai_turn_towards_vector(&goal_vector, obj, robptr->turn_time[Difficulty_level]);
1135 move_towards_vector(obj, &goal_vector, 0);
1136 ai_multi_send_robot_position(objnum, -1);
1141 case AIM_SNIPE_WAIT:
1143 case AIM_SNIPE_RETREAT:
1144 // -- if (ai_multiplayer_awareness(obj, 53))
1145 // -- if (ailp->next_fire < -F1_0)
1146 // -- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1148 case AIM_SNIPE_RETREAT_BACKWARDS:
1149 case AIM_SNIPE_ATTACK:
1150 case AIM_SNIPE_FIRE:
1151 if (ai_multiplayer_awareness(obj, 53)) {
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);
1154 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 0, player_visibility);
1159 case AIM_THIEF_WAIT:
1160 case AIM_THIEF_ATTACK:
1161 case AIM_THIEF_RETREAT:
1162 case AIM_WANDER: // Used for Buddy Bot
1166 mprintf((0, "Unknown mode = %i in robot %i, behavior = %i\n", ailp->mode, OBJECT_NUMBER(obj), aip->behavior));
1167 ailp->mode = AIM_CHASE_OBJECT;
1169 } // end: switch (ailp->mode) {
1171 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1172 // If the robot can see you, increase his awareness of you.
1173 // This prevents the problem of a robot looking right at you but doing nothing.
1174 // Assert(player_visibility != -1); // Means it didn't get initialized!
1175 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1176 if ((player_visibility == 2) && (aip->behavior != AIB_FOLLOW) && (!robptr->thief)) {
1177 if ((ailp->player_awareness_type == 0) && (aip->SUB_FLAGS & SUB_FLAGS_CAMERA_AWAKE))
1178 aip->SUB_FLAGS &= ~SUB_FLAGS_CAMERA_AWAKE;
1179 else if (ailp->player_awareness_type == 0)
1180 ailp->player_awareness_type = PA_PLAYER_COLLISION;
1183 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1184 if (!object_animates) {
1185 aip->CURRENT_STATE = aip->GOAL_STATE;
1186 // mprintf((0, "Setting current to goal (%i) because object doesn't animate.\n", aip->GOAL_STATE));
1189 Assert(ailp->player_awareness_type <= AIE_MAX);
1190 Assert(aip->CURRENT_STATE < AIS_MAX);
1191 Assert(aip->GOAL_STATE < AIS_MAX);
1193 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1194 if (ailp->player_awareness_type) {
1195 new_goal_state = Ai_transition_table[ailp->player_awareness_type-1][aip->CURRENT_STATE][aip->GOAL_STATE];
1196 if (ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) {
1197 // Decrease awareness, else this robot will flinch every frame.
1198 ailp->player_awareness_type--;
1199 ailp->player_awareness_time = F1_0*3;
1202 if (new_goal_state == AIS_ERR_)
1203 new_goal_state = AIS_REST;
1205 if (aip->CURRENT_STATE == AIS_NONE)
1206 aip->CURRENT_STATE = AIS_REST;
1208 aip->GOAL_STATE = new_goal_state;
1212 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1213 // If new state = fire, then set all gun states to fire.
1214 if ( aip->GOAL_STATE == AIS_FIRE ) {
1216 num_guns = Robot_info[obj->id].n_guns;
1217 for (i=0; i<num_guns; i++)
1218 ailp->goal_state[i] = AIS_FIRE;
1221 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1222 // 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
1223 if (ready_to_fire(robptr, ailp) && (aip->GOAL_STATE == AIS_FIRE))
1224 aip->CURRENT_STATE = AIS_FIRE;
1226 if ((aip->GOAL_STATE != AIS_FLIN) && (obj->id != ROBOT_BRAIN)) {
1227 switch (aip->CURRENT_STATE) {
1229 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1231 dot = vm_vec_dot(&obj->orient.fvec, &vec_to_player);
1233 if (aip->GOAL_STATE == AIS_REST)
1234 aip->GOAL_STATE = AIS_SRCH;
1237 if (aip->GOAL_STATE == AIS_REST) {
1238 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1239 if (ready_to_fire(robptr, ailp) && (player_visibility)) {
1240 // mprintf((0, "Setting goal state to fire from rest.\n"));
1241 aip->GOAL_STATE = AIS_FIRE;
1246 if (!ai_multiplayer_awareness(obj, 60))
1249 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1251 if (player_visibility == 2) {
1252 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1253 ai_multi_send_robot_position(objnum, -1);
1257 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1259 if (!(Game_mode & GM_MULTI) || (player_visibility)) {
1260 if (!ai_multiplayer_awareness(obj, 68))
1263 if (player_visibility == 2) { // @mk, 09/21/95, require that they be looking towards you to turn towards you.
1264 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1265 ai_multi_send_robot_position(objnum, -1);
1270 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1272 if (player_visibility == 2) {
1273 if (!ai_multiplayer_awareness(obj, (ROBOT_FIRE_AGITATION-1))) {
1274 if (Game_mode & GM_MULTI) {
1275 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1279 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1280 ai_multi_send_robot_position(objnum, -1);
1283 // Fire at player, if appropriate.
1284 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates, aip->CURRENT_GUN);
1288 if (!(obj_ref & 3)) {
1289 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
1290 if (player_visibility == 2) {
1291 if (!ai_multiplayer_awareness(obj, 69))
1293 ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
1294 ai_multi_send_robot_position(objnum, -1);
1295 } // -- MK, 06/09/95: else if (!(Game_mode & GM_MULTI)) {
1299 // mprintf((0, "State = flinch, goal = %i.\n", aip->GOAL_STATE));
1302 mprintf((1, "Unknown mode for AI object #%i\n", objnum));
1303 aip->GOAL_STATE = AIS_REST;
1304 aip->CURRENT_STATE = AIS_REST;
1307 } // end of: if (aip->GOAL_STATE != AIS_FLIN) {
1309 // Switch to next gun for next fire.
1310 if (player_visibility == 0) {
1312 if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
1314 if ((robptr->n_guns == 1) || (robptr->weapon_type2 == -1)) // Two weapon types hack.
1315 aip->CURRENT_GUN = 0;
1317 aip->CURRENT_GUN = 1;
1323 // ----------------------------------------------------------------------------
1324 void ai_do_cloak_stuff(void)
1328 for (i=0; i<MAX_AI_CLOAK_INFO; i++) {
1329 Ai_cloak_info[i].last_position = ConsoleObject->pos;
1330 Ai_cloak_info[i].last_segment = ConsoleObject->segnum;
1331 Ai_cloak_info[i].last_time = GameTime;
1334 // Make work for control centers.
1335 Believed_player_pos = Ai_cloak_info[0].last_position;
1336 Believed_player_seg = Ai_cloak_info[0].last_segment;
1340 // ----------------------------------------------------------------------------
1341 // Returns false if awareness is considered too puny to add, else returns true.
1342 int add_awareness_event(object *objp, int type)
1344 // If player cloaked and hit a robot, then increase awareness
1345 if ((type == PA_WEAPON_ROBOT_COLLISION) || (type == PA_WEAPON_WALL_COLLISION) || (type == PA_PLAYER_COLLISION))
1346 ai_do_cloak_stuff();
1348 if (Num_awareness_events < MAX_AWARENESS_EVENTS) {
1349 if ((type == PA_WEAPON_WALL_COLLISION) || (type == PA_WEAPON_ROBOT_COLLISION))
1350 if (objp->id == VULCAN_ID)
1351 if (d_rand() > 3276)
1352 return 0; // For vulcan cannon, only about 1/10 actually cause awareness
1354 Awareness_events[Num_awareness_events].segnum = objp->segnum;
1355 Awareness_events[Num_awareness_events].pos = objp->pos;
1356 Awareness_events[Num_awareness_events].type = type;
1357 Num_awareness_events++;
1359 //Int3(); // Hey -- Overflowed Awareness_events, make more or something
1360 // This just gets ignored, so you can just
1367 // ----------------------------------------------------------------------------------
1368 // Robots will become aware of the player based on something that occurred.
1369 // The object (probably player or weapon) which created the awareness is objp.
1370 void create_awareness_event(object *objp, int type)
1372 // If not in multiplayer, or in multiplayer with robots, do this, else unnecessary!
1373 if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_ROBOTS)) {
1374 if (add_awareness_event(objp, type)) {
1375 if (((d_rand() * (type+4)) >> 15) > 4)
1376 Overall_agitation++;
1377 if (Overall_agitation > OVERALL_AGITATION_MAX)
1378 Overall_agitation = OVERALL_AGITATION_MAX;
1383 sbyte New_awareness[MAX_SEGMENTS];
1385 // ----------------------------------------------------------------------------------
1386 void pae_aux(int segnum, int type, int level)
1390 if (New_awareness[segnum] < type)
1391 New_awareness[segnum] = type;
1393 // Process children.
1394 for (j=0; j<MAX_SIDES_PER_SEGMENT; j++)
1395 if (IS_CHILD(Segments[segnum].children[j]))
1400 pae_aux(Segments[segnum].children[j], type-1, level+1);
1402 pae_aux(Segments[segnum].children[j], type, level+1);
1408 // ----------------------------------------------------------------------------------
1409 void process_awareness_events(void)
1413 if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_ROBOTS)) {
1414 memset(New_awareness, 0, sizeof(New_awareness[0]) * (Highest_segment_index+1));
1416 for (i=0; i<Num_awareness_events; i++)
1417 pae_aux(Awareness_events[i].segnum, Awareness_events[i].type, 1);
1421 Num_awareness_events = 0;
1424 // ----------------------------------------------------------------------------------
1425 void set_player_awareness_all(void)
1429 process_awareness_events();
1431 for (i=0; i<=Highest_object_index; i++)
1432 if (Objects[i].control_type == CT_AI) {
1433 if (New_awareness[Objects[i].segnum] > Ai_local_info[i].player_awareness_type) {
1434 Ai_local_info[i].player_awareness_type = New_awareness[Objects[i].segnum];
1435 Ai_local_info[i].player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME;
1438 // Clear the bit that says this robot is only awake because a camera woke it up.
1439 if (New_awareness[Objects[i].segnum] > Ai_local_info[i].player_awareness_type)
1440 Objects[i].ctype.ai_info.SUB_FLAGS &= ~SUB_FLAGS_CAMERA_AWAKE;
1445 int Ai_dump_enable = 0;
1447 FILE *Ai_dump_file = NULL;
1449 char Ai_error_message[128] = "";
1451 // ----------------------------------------------------------------------------------
1452 void force_dump_ai_objects_all(char *msg)
1456 tsave = Ai_dump_enable;
1460 sprintf(Ai_error_message, "%s\n", msg);
1461 //dump_ai_objects_all();
1462 Ai_error_message[0] = 0;
1464 Ai_dump_enable = tsave;
1467 // ----------------------------------------------------------------------------------
1468 void turn_off_ai_dump(void)
1470 if (Ai_dump_file != NULL)
1471 fclose(Ai_dump_file);
1473 Ai_dump_file = NULL;
1478 extern void do_boss_dying_frame(object *objp);
1480 // ----------------------------------------------------------------------------------
1481 // Do things which need to get done for all AI objects each frame.
1483 // Setting player_awareness (a fix, time in seconds which object is aware of player)
1484 void do_ai_frame_all(void)
1487 //dump_ai_objects_all();
1490 set_player_awareness_all();
1492 if (Ai_last_missile_camera != -1) {
1493 // Clear if supposed misisle camera is not a weapon, or just every so often, just in case.
1494 if (((FrameCount & 0x0f) == 0) || (Objects[Ai_last_missile_camera].type != OBJ_WEAPON)) {
1497 Ai_last_missile_camera = -1;
1498 for (i=0; i<=Highest_object_index; i++)
1499 if (Objects[i].type == OBJ_ROBOT)
1500 Objects[i].ctype.ai_info.SUB_FLAGS &= ~SUB_FLAGS_CAMERA_AWAKE;
1504 // (Moved here from do_boss_stuff() because that only gets called if robot aware of player.)
1508 for (i=0; i<=Highest_object_index; i++)
1509 if (Objects[i].type == OBJ_ROBOT)
1510 if (Robot_info[Objects[i].id].boss_flag)
1511 do_boss_dying_frame(&Objects[i]);
1516 extern int Final_boss_is_dead;
1517 extern fix Boss_invulnerable_dot;
1519 // Initializations to be performed for all robots for a new level.
1520 void init_robots_for_level(void)
1522 Overall_agitation = 0;
1523 Final_boss_is_dead=0;
1526 Buddy_allowed_to_talk = 0;
1528 Boss_invulnerable_dot = F1_0/4 - i2f(Difficulty_level)/8;
1529 Boss_dying_start_time = 0;
1532 int ai_save_state(PHYSFS_file *fp)
1534 PHYSFS_write(fp, &Ai_initialized, sizeof(int), 1);
1535 PHYSFS_write(fp, &Overall_agitation, sizeof(int), 1);
1536 PHYSFS_write(fp, Ai_local_info, sizeof(ai_local) * MAX_OBJECTS, 1);
1537 PHYSFS_write(fp, Point_segs, sizeof(point_seg) * MAX_POINT_SEGS, 1);
1538 PHYSFS_write(fp, Ai_cloak_info, sizeof(ai_cloak_info) * MAX_AI_CLOAK_INFO, 1);
1539 PHYSFS_write(fp, &Boss_cloak_start_time, sizeof(fix), 1);
1540 PHYSFS_write(fp, &Boss_cloak_end_time, sizeof(fix), 1);
1541 PHYSFS_write(fp, &Last_teleport_time, sizeof(fix), 1);
1542 PHYSFS_write(fp, &Boss_teleport_interval, sizeof(fix), 1);
1543 PHYSFS_write(fp, &Boss_cloak_interval, sizeof(fix), 1);
1544 PHYSFS_write(fp, &Boss_cloak_duration, sizeof(fix), 1);
1545 PHYSFS_write(fp, &Last_gate_time, sizeof(fix), 1);
1546 PHYSFS_write(fp, &Gate_interval, sizeof(fix), 1);
1547 PHYSFS_write(fp, &Boss_dying_start_time, sizeof(fix), 1);
1548 PHYSFS_write(fp, &Boss_dying, sizeof(int), 1);
1549 PHYSFS_write(fp, &Boss_dying_sound_playing, sizeof(int), 1);
1550 PHYSFS_write(fp, &Boss_hit_time, sizeof(fix), 1);
1551 // -- MK, 10/21/95, unused! -- PHYSFS_write(fp, &Boss_been_hit, sizeof(int), 1);
1553 PHYSFS_write(fp, &Escort_kill_object, sizeof(Escort_kill_object), 1);
1554 PHYSFS_write(fp, &Escort_last_path_created, sizeof(Escort_last_path_created), 1);
1555 PHYSFS_write(fp, &Escort_goal_object, sizeof(Escort_goal_object), 1);
1556 PHYSFS_write(fp, &Escort_special_goal, sizeof(Escort_special_goal), 1);
1557 PHYSFS_write(fp, &Escort_goal_index, sizeof(Escort_goal_index), 1);
1558 PHYSFS_write(fp, &Stolen_items, sizeof(Stolen_items[0])*MAX_STOLEN_ITEMS, 1);
1562 temp = POINT_SEG_NUMBER(Point_segs_free_ptr);
1563 PHYSFS_write(fp, &temp, sizeof(int), 1);
1566 PHYSFS_write(fp, &Num_boss_teleport_segs, sizeof(Num_boss_teleport_segs), 1);
1567 PHYSFS_write(fp, &Num_boss_gate_segs, sizeof(Num_boss_gate_segs), 1);
1569 if (Num_boss_gate_segs)
1570 PHYSFS_write(fp, Boss_gate_segs, sizeof(Boss_gate_segs[0]), Num_boss_gate_segs);
1572 if (Num_boss_teleport_segs)
1573 PHYSFS_write(fp, Boss_teleport_segs, sizeof(Boss_teleport_segs[0]), Num_boss_teleport_segs);
1578 int ai_restore_state(PHYSFS_file *fp, int version)
1580 PHYSFS_read(fp, &Ai_initialized, sizeof(int), 1);
1581 PHYSFS_read(fp, &Overall_agitation, sizeof(int), 1);
1582 PHYSFS_read(fp, Ai_local_info, sizeof(ai_local) * MAX_OBJECTS, 1);
1583 PHYSFS_read(fp, Point_segs, sizeof(point_seg) * MAX_POINT_SEGS, 1);
1584 PHYSFS_read(fp, Ai_cloak_info, sizeof(ai_cloak_info) * MAX_AI_CLOAK_INFO, 1);
1585 PHYSFS_read(fp, &Boss_cloak_start_time, sizeof(fix), 1);
1586 PHYSFS_read(fp, &Boss_cloak_end_time, sizeof(fix), 1);
1587 PHYSFS_read(fp, &Last_teleport_time, sizeof(fix), 1);
1588 PHYSFS_read(fp, &Boss_teleport_interval, sizeof(fix), 1);
1589 PHYSFS_read(fp, &Boss_cloak_interval, sizeof(fix), 1);
1590 PHYSFS_read(fp, &Boss_cloak_duration, sizeof(fix), 1);
1591 PHYSFS_read(fp, &Last_gate_time, sizeof(fix), 1);
1592 PHYSFS_read(fp, &Gate_interval, sizeof(fix), 1);
1593 PHYSFS_read(fp, &Boss_dying_start_time, sizeof(fix), 1);
1594 PHYSFS_read(fp, &Boss_dying, sizeof(int), 1);
1595 PHYSFS_read(fp, &Boss_dying_sound_playing, sizeof(int), 1);
1596 PHYSFS_read(fp, &Boss_hit_time, sizeof(fix), 1);
1597 // -- MK, 10/21/95, unused! -- PHYSFS_read(fp, &Boss_been_hit, sizeof(int), 1);
1600 PHYSFS_read(fp, &Escort_kill_object, sizeof(Escort_kill_object), 1);
1601 PHYSFS_read(fp, &Escort_last_path_created, sizeof(Escort_last_path_created), 1);
1602 PHYSFS_read(fp, &Escort_goal_object, sizeof(Escort_goal_object), 1);
1603 PHYSFS_read(fp, &Escort_special_goal, sizeof(Escort_special_goal), 1);
1604 PHYSFS_read(fp, &Escort_goal_index, sizeof(Escort_goal_index), 1);
1605 PHYSFS_read(fp, &Stolen_items, sizeof(Stolen_items[0]) * MAX_STOLEN_ITEMS, 1);
1609 Escort_kill_object = -1;
1610 Escort_last_path_created = 0;
1611 Escort_goal_object = ESCORT_GOAL_UNSPECIFIED;
1612 Escort_special_goal = -1;
1613 Escort_goal_index = -1;
1615 for (i=0; i<MAX_STOLEN_ITEMS; i++) {
1616 Stolen_items[i] = 255;
1621 if (version >= 15) {
1623 PHYSFS_read(fp, &temp, sizeof(int), 1);
1624 Point_segs_free_ptr = &Point_segs[temp];
1626 ai_reset_all_paths();
1628 if (version >= 21) {
1629 PHYSFS_read(fp, &Num_boss_teleport_segs, sizeof(Num_boss_teleport_segs), 1);
1630 PHYSFS_read(fp, &Num_boss_gate_segs, sizeof(Num_boss_gate_segs), 1);
1632 if (Num_boss_gate_segs)
1633 PHYSFS_read(fp, Boss_gate_segs, sizeof(Boss_gate_segs[0]), Num_boss_gate_segs);
1635 if (Num_boss_teleport_segs)
1636 PHYSFS_read(fp, Boss_teleport_segs, sizeof(Boss_teleport_segs[0]), Num_boss_teleport_segs);
1638 // -- Num_boss_teleport_segs = 1;
1639 // -- Num_boss_gate_segs = 1;
1640 // -- Boss_teleport_segs[0] = 0;
1641 // -- Boss_gate_segs[0] = 0;
1642 // Note: Maybe better to leave alone...will probably be ok.
1643 mprintf((1, "Warning: If you fight the boss, he might teleport to segment #0!\n"));