home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / ai.c next >
Text File  |  1998-06-08  |  137KB  |  3,829 lines

  1. /*
  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-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  12. */
  13. /*
  14.  * $Source: f:/miner/source/main/rcs/ai.c $
  15.  * $Revision: 2.11 $
  16.  * $Author: john $
  17.  * $Date: 1995/07/09 11:15:48 $
  18.  *
  19.  * Autonomous Individual movement.
  20.  *
  21.  * $Log: ai.c $
  22.  * Revision 2.11  1995/07/09  11:15:48  john
  23.  * Put in Mike's code to fix bug where bosses don't gate in bots after
  24.  * 32767 seconds of playing.
  25.  * 
  26.  * Revision 2.10  1995/06/15  12:31:08  john
  27.  * Fixed bug with cheats getting enabled when you type
  28.  * the whole alphabet.
  29.  * 
  30.  * Revision 2.9  1995/05/26  16:16:18  john
  31.  * Split SATURN into define's for requiring cd, using cd, etc.
  32.  * Also started adding all the Rockwell stuff.
  33.  * 
  34.  * Revision 2.8  1995/04/06  15:12:27  john
  35.  * Fixed bug with insane not working.
  36.  * 
  37.  * Revision 2.7  1995/03/30  16:36:44  mike
  38.  * text localization.
  39.  * 
  40.  * Revision 2.6  1995/03/28  11:22:24  john
  41.  * Added cheats to save file. Changed lunacy text.
  42.  * 
  43.  * Revision 2.5  1995/03/27  16:45:07  john
  44.  * Fixed some cheat bugs.  Added astral cheat.
  45.  * 
  46.  * Revision 2.4  1995/03/24  15:29:17  mike
  47.  * add new cheats.
  48.  * 
  49.  * Revision 2.3  1995/03/21  14:39:45  john
  50.  * Ifdef'd out the NETWORK code.
  51.  * 
  52.  * Revision 2.2  1995/03/14  18:24:39  john
  53.  * Force Destination Saturn to use CD-ROM drive.
  54.  * 
  55.  * Revision 2.1  1995/03/06  16:47:14  mike
  56.  * destination saturn
  57.  * 
  58.  * Revision 2.0  1995/02/27  11:30:01  john
  59.  * New version 2.0, which has no anonymous unions, builds with
  60.  * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
  61.  * 
  62.  * Revision 1.295  1995/02/22  13:23:04  allender
  63.  * remove anonymous unions from object structure
  64.  * 
  65.  * Revision 1.294  1995/02/13  11:00:43  rob
  66.  * Make brain guys high enough to get an open slot.
  67.  * 
  68.  * Revision 1.293  1995/02/13  10:31:55  mike
  69.  * Make brains understand they can't open locked doors.
  70.  * 
  71.  * Revision 1.292  1995/02/13  10:18:01  rob
  72.  * Reduced brain guy's level of awareness to keep him from hogging slots.
  73.  * 
  74.  * Revision 1.291  1995/02/11  12:27:12  mike
  75.  * fix path-to-exit cheat.
  76.  * 
  77.  * Revision 1.290  1995/02/11  01:56:30  mike
  78.  * robots don't fire cheat.
  79.  * 
  80.  * Revision 1.289  1995/02/10  17:15:09  rob
  81.  * Fixed some stuff with 64 awareness stuff.
  82.  * 
  83.  * Revision 1.288  1995/02/10  16:31:32  mike
  84.  * oops.
  85.  * 
  86.  * Revision 1.287  1995/02/10  16:24:45  mike
  87.  * fix the network follow path fix.
  88.  * 
  89.  * Revision 1.286  1995/02/10  16:11:40  mike
  90.  * in serial or modem games, follow path guys don't move if far away and
  91.  * can't see player.
  92.  * 
  93.  * Revision 1.285  1995/02/09  13:11:35  mike
  94.  * comment out a bunch of mprintfs.
  95.  * add toaster (drops prox bombs, runs away) to boss gate list.
  96.  * 
  97.  * Revision 1.284  1995/02/08  22:44:53  rob
  98.  * Lowerd anger level for follow path of any sort.
  99.  * 
  100.  * Revision 1.283  1995/02/08  22:30:43  mike
  101.  * lower awareness on station guys if they are returning home (multiplayer).
  102.  * 
  103.  * Revision 1.282  1995/02/08  17:01:06  rob
  104.  * Fixed problem with toasters dropping of proximity bombs.
  105.  * 
  106.  * Revision 1.281  1995/02/08  11:49:35  rob
  107.  * Reduce Green-guy attack awareness level so we don't let him attack us too.
  108.  * 
  109.  * Revision 1.280  1995/02/08  11:37:52  mike
  110.  * Check for failures in call to obj_create.
  111.  * 
  112.  * Revision 1.279  1995/02/07  20:38:46  mike
  113.  * fix toasters in multiplayer
  114.  * 
  115.  * 
  116.  * Revision 1.278  1995/02/07  16:51:07  mike
  117.  * fix sound time play bug.
  118.  * 
  119.  * Revision 1.277  1995/02/06  22:33:04  mike
  120.  * make robots follow path better in cooperative/roboarchy.
  121.  * 
  122.  * Revision 1.276  1995/02/06  18:15:42  rob
  123.  * Added forced sends for evasion movemnet.
  124.  * 
  125.  * Revision 1.275  1995/02/06  16:41:22  rob
  126.  * Change some positioning calls.
  127.  * 
  128.  * Revision 1.274  1995/02/06  11:40:33  mike
  129.  * replace some lint-related hacks with clean, proper code.
  130.  * 
  131.  * Revision 1.273  1995/02/04  17:28:19  mike
  132.  * make station guys return better.
  133.  * 
  134.  * Revision 1.272  1995/02/03  17:40:55  mike
  135.  * fix problem with robots falling asleep if you sit in game overnight, not in pause...bah.
  136.  * 
  137.  * Revision 1.271  1995/02/02  21:11:25  rob
  138.  * Tweaking stuff for multiplayer ai.
  139.  * 
  140.  * Revision 1.270  1995/02/02  17:32:06  john
  141.  * Added Hack for Assert that Mike put in after using Lint to find
  142.  * uninitialized variables.
  143.  * 
  144.  * Revision 1.269  1995/02/02  16:46:31  mike
  145.  * fix boss gating.
  146.  * 
  147.  * Revision 1.268  1995/02/02  16:27:29  mike
  148.  * make boss not put out infinite robots.
  149.  * 
  150.  * Revision 1.267  1995/02/01  21:10:02  mike
  151.  * lint found bug! player_visibility not initialized!
  152.  * 
  153.  * Revision 1.266  1995/02/01  20:51:27  john
  154.  * Lintized
  155.  * 
  156.  * Revision 1.265  1995/02/01  17:14:05  mike
  157.  * fix robot sounds.
  158.  * 
  159.  * Revision 1.264  1995/01/31  16:16:40  mike
  160.  * Comment out "Darn you, John" Int3().
  161.  * 
  162.  * Revision 1.263  1995/01/30  20:55:04  mike
  163.  * fix nonsense in robot firing when a player is cloaked.
  164.  * 
  165.  * Revision 1.262  1995/01/30  17:15:10  rob
  166.  * Fixed problems with bigboss eclip messages.
  167.  * Tweaked robot position sending for modem purposes.
  168.  * 
  169.  * Revision 1.261  1995/01/30  15:30:31  rob
  170.  * Prevent non-master players from gating in robots.
  171.  * 
  172.  * Revision 1.260  1995/01/30  13:30:55  mike
  173.  * new cases for firing at other players were bogus, could send position
  174.  * without permission.
  175.  * 
  176.  * Revision 1.259  1995/01/30  13:01:17  mike
  177.  * Make robots fire at player other than one they are controlled by sometimes.
  178.  * 
  179.  * Revision 1.258  1995/01/29  16:09:17  rob
  180.  * Trying to get robots to shoot at non-controlling players.
  181.  * 
  182.  * Revision 1.257  1995/01/29  13:47:05  mike
  183.  * Make boss have more fireballs on death, have until end (though silent at end).
  184.  * Fix bug which was preventing him from teleporting until hit, so he'd always
  185.  * be in the same place when the player enters the room.
  186.  * 
  187.  * Revision 1.256  1995/01/28  17:40:18  mike
  188.  * make boss teleport & gate before you see him.
  189.  * 
  190.  * Revision 1.255  1995/01/27  17:02:08  mike
  191.  * move code around, was sending one frame (or worse!) old robot information.
  192.  * 
  193.  * Revision 1.254  1995/01/26  17:02:43  mike
  194.  * make fusion cannon have more chrome, make fusion, mega rock you!
  195.  * 
  196.  * Revision 1.253  1995/01/26  15:11:17  rob
  197.  * Shutup!  I fixed it!
  198.  * 
  199.  * Revision 1.252  1995/01/26  15:08:55  rob
  200.  * Changed robot gating to accomodate multiplayer.
  201.  * 
  202.  * Revision 1.251  1995/01/26  14:49:04  rob
  203.  * Increase awareness level for firing to 94.
  204.  * 
  205.  * Revision 1.250  1995/01/26  12:41:20  mike
  206.  * fix bogus multiplayer code, would send permission without getting permission.
  207.  * 
  208.  * Revision 1.249  1995/01/26  12:23:23  rob
  209.  * Removed defines that were moved to ai.h
  210.  * 
  211.  * Revision 1.248  1995/01/25  23:38:48  mike
  212.  * modify list of robots gated in by super boss.
  213.  * 
  214.  * Revision 1.247  1995/01/25  21:21:13  rob
  215.  * Trying to let robots fire at a player even if they're not in control.
  216.  * 
  217.  * Revision 1.246  1995/01/25  13:50:37  mike
  218.  * Robots make angry sounds.
  219.  * 
  220.  * Revision 1.245  1995/01/25  10:53:47  mike
  221.  * better handling of robots which poke out of mine and try to recover.
  222.  * 
  223.  * Revision 1.244  1995/01/24  22:03:02  mike
  224.  * Tricky code to move a robot to a legal position if he is poking out of
  225.  * the mine, even if it means moving him to another segment.
  226.  * 
  227.  * Revision 1.243  1995/01/24  20:12:06  rob
  228.  * Changed robot fire awareness level from 74 to 94.
  229.  * 
  230.  * Revision 1.242  1995/01/24  13:22:32  mike
  231.  * make robots accelerate faster, and Difficulty_level dependent.
  232.  * 
  233.  * Revision 1.241  1995/01/24  12:09:39  mike
  234.  * make robots animate in multiplayer.
  235.  * 
  236.  * Revision 1.240  1995/01/21  21:21:10  mike
  237.  * Make boss only gate robots into specified segments.
  238.  * 
  239.  * Revision 1.239  1995/01/20  20:21:26  mike
  240.  * prevent unnecessary boss cloaking.
  241.  * 
  242.  */
  243.  
  244. #pragma off (unreferenced)
  245. static char rcsid[] = "$Id: ai.c 2.11 1995/07/09 11:15:48 john Exp $";
  246. #pragma on (unreferenced)
  247.  
  248. #include <stdio.h>
  249. #include <stdlib.h>
  250.  
  251. #include "inferno.h"
  252. #include "game.h"
  253. #include "mono.h"
  254. #include "3d.h"
  255.  
  256. #include "object.h"
  257. #include "render.h"
  258. #include "error.h"
  259. #include "ai.h"
  260. #include "laser.h"
  261. #include "fvi.h"
  262. #include "polyobj.h"
  263. #include "bm.h"
  264. #include "weapon.h"
  265. #include "physics.h"
  266. #include "collide.h"
  267. #include "fuelcen.h"
  268. #include "player.h"
  269. #include "wall.h"
  270. #include "vclip.h"
  271. #include "digi.h"
  272. #include "fireball.h"
  273. #include "morph.h"
  274. #include "effects.h"
  275. #include "timer.h"
  276. #include "sounds.h"
  277. #include "cntrlcen.h"
  278. #include "multibot.h"
  279. #include "multi.h"
  280. #include "network.h"
  281. #include "gameseq.h"
  282. #include "key.h"
  283. #include "powerup.h"
  284. #include "gauges.h"
  285. #include "text.h"
  286.  
  287. #ifdef EDITOR
  288. #include "editor\editor.h"
  289. #endif
  290.  
  291. #ifndef NDEBUG
  292. #include "string.h"
  293. #include <time.h>
  294. #endif
  295.  
  296. #define    JOHN_CHEATS_SIZE_1    6
  297. #define    JOHN_CHEATS_SIZE_2    6
  298. #define    JOHN_CHEATS_SIZE_3    6
  299.  
  300. ubyte    john_cheats_1[JOHN_CHEATS_SIZE_1] = {     KEY_P ^ 0x00 ^ 0x34, 
  301.                                                             KEY_O ^ 0x10 ^ 0x34, 
  302.                                                             KEY_B ^ 0x20 ^ 0x34, 
  303.                                                             KEY_O ^ 0x30 ^ 0x34, 
  304.                                                             KEY_Y ^ 0x40 ^ 0x34, 
  305.                                                             KEY_S ^ 0x50 ^ 0x34 };
  306.  
  307. #define    PARALLAX    0        //    If !0, then special debugging info for Parallax eyes only enabled.
  308.  
  309. #define MIN_D 0x100
  310.  
  311. int    Flinch_scale = 4;
  312. int    john_cheats_index_1;        //    POBOYS        detonate reactor
  313. int    Attack_scale = 24;
  314. #define    ANIM_RATE        (F1_0/16)
  315. #define    DELTA_ANG_SCALE    16
  316.  
  317. byte Mike_to_matt_xlate[] = {AS_REST, AS_REST, AS_ALERT, AS_ALERT, AS_FLINCH, AS_FIRE, AS_RECOIL, AS_REST};
  318. int    john_cheats_index_2;        //    PORGYS        high speed weapon firing
  319.  
  320. // int    No_ai_flag=0;
  321.  
  322. #define    OVERALL_AGITATION_MAX    100
  323.  
  324. #define        MAX_AI_CLOAK_INFO    8    //    Must be a power of 2!
  325.  
  326. typedef struct {
  327.     fix            last_time;
  328.     vms_vector    last_position;
  329. } ai_cloak_info;
  330.  
  331. #define    BOSS_CLOAK_DURATION    (F1_0*7)
  332. #define    BOSS_DEATH_DURATION    (F1_0*6)
  333. #define    BOSS_DEATH_SOUND_DURATION    0x2ae14        //    2.68 seconds
  334.  
  335. //    Amount of time since the current robot was last processed for things such as movement.
  336. //    It is not valid to use FrameTime because robots do not get moved every frame.
  337. //fix    AI_proc_time;
  338.  
  339. int    Num_boss_teleport_segs;
  340. short    Boss_teleport_segs[MAX_BOSS_TELEPORT_SEGS];
  341. #ifndef SHAREWARE
  342. int    Num_boss_gate_segs;
  343. short    Boss_gate_segs[MAX_BOSS_TELEPORT_SEGS];
  344. #endif
  345.  
  346. int    john_cheats_index_3;        //    LUNACY        lunacy (insane behavior, rookie firing)
  347.  
  348. //    ---------- John: These variables must be saved as part of gamesave. ----------
  349. int                Ai_initialized = 0;
  350. int                Overall_agitation;
  351. ai_local            Ai_local_info[MAX_OBJECTS];
  352. point_seg        Point_segs[MAX_POINT_SEGS];
  353. point_seg        *Point_segs_free_ptr = Point_segs;
  354. ai_cloak_info    Ai_cloak_info[MAX_AI_CLOAK_INFO];
  355. fix                Boss_cloak_start_time = 0;
  356. fix                Boss_cloak_end_time = 0;
  357. fix                Last_teleport_time = 0;
  358. fix                Boss_teleport_interval = F1_0*8;
  359. fix                Boss_cloak_interval = F1_0*10;                    //    Time between cloaks
  360. fix                Boss_cloak_duration = BOSS_CLOAK_DURATION;
  361. fix                Last_gate_time = 0;
  362. fix                Gate_interval = F1_0*6;
  363. fix                Boss_dying_start_time;
  364. int                Boss_dying, Boss_dying_sound_playing, Boss_hit_this_frame;
  365. int                Boss_been_hit=0;
  366.  
  367.  
  368. //    ---------- John: End of variables which must be saved as part of gamesave. ----------
  369.  
  370. int    john_cheats_index_4;        //    PLETCHnnn    paint robots
  371. int                ai_evaded=0;
  372.  
  373. #ifndef SHAREWARE
  374. //    0    mech
  375. //    1    green claw
  376. //    2    spider
  377. //    3    josh
  378. //    4    violet
  379. //    5    cloak vulcan
  380. //    6    cloak mech
  381. //    7    brain
  382. //    8    onearm
  383. //    9    plasma
  384. //    10    toaster
  385. //    11    bird
  386. //    12    missile bird
  387. //    13    polyhedron
  388. //    14    baby spider
  389. //    15    mini boss
  390. //    16    super mech
  391. //    17    shareware boss
  392. //    18    cloak-green    ; note, gating in this guy benefits player, cloak objects
  393. //    19    vulcan
  394. //    20    toad
  395. //    21    4-claw
  396. //    22    quad-laser
  397. // 23 super boss
  398.  
  399. // byte    Super_boss_gate_list[] = {0, 1, 2, 9, 11, 16, 18, 19, 21, 22, 0, 9, 9, 16, 16, 18, 19, 19, 22, 22};
  400. byte    Super_boss_gate_list[] = {0, 1, 8, 9, 10, 11, 12, 15, 16, 18, 19, 20, 22, 0, 8, 11, 19, 20, 8, 20, 8};
  401. #define    MAX_GATE_INDEX    ( sizeof(Super_boss_gate_list) / sizeof(Super_boss_gate_list[0]) )
  402. #endif
  403.  
  404. int    Ai_info_enabled=0;
  405. int    Robot_firing_enabled = 1;
  406.  
  407. extern    int    Ugly_robot_cheat, Ugly_robot_texture, Laser_rapid_fire;
  408. extern    byte    Enable_john_cheat_1, Enable_john_cheat_2, Enable_john_cheat_3, Enable_john_cheat_4;
  409.  
  410. ubyte    john_cheats_3[2*JOHN_CHEATS_SIZE_3+1] = { KEY_Y ^ 0x67, 
  411.                                                                 KEY_E ^ 0x66, 
  412.                                                                 KEY_C ^ 0x65, 
  413.                                                                 KEY_A ^ 0x64, 
  414.                                                                 KEY_N ^ 0x63, 
  415.                                                                 KEY_U ^ 0x62, 
  416.                                                                 KEY_L ^ 0x61 };
  417.  
  418.  
  419. #define    MAX_AWARENESS_EVENTS    64
  420. typedef struct awareness_event {
  421.     short         segnum;                // segment the event occurred in
  422.     short            type;                    // type of event, defines behavior
  423.     vms_vector    pos;                    // absolute 3 space location of event
  424. } awareness_event;
  425.  
  426.  
  427. //    These globals are set by a call to find_vector_intersection, which is a slow routine,
  428. //    so we don't want to call it again (for this object) unless we have to.
  429. vms_vector    Hit_pos;
  430. int            Hit_type, Hit_seg;
  431. fvi_info        Hit_data;
  432.  
  433. int                    Num_awareness_events = 0;
  434. awareness_event    Awareness_events[MAX_AWARENESS_EVENTS];
  435.  
  436. vms_vector        Believed_player_pos;
  437.  
  438. #define    AIS_MAX    8
  439. #define    AIE_MAX    4
  440.  
  441. //--unused-- int    Processed_this_frame, LastFrameCount;
  442. #ifndef NDEBUG
  443. //    Index into this array with ailp->mode
  444. char    mode_text[8][9] = {
  445.     "STILL   ",
  446.     "WANDER  ",
  447.     "FOL_PATH",
  448.     "CHASE_OB",
  449.     "RUN_FROM",
  450.     "HIDE    ",
  451.     "FOL_PAT2",
  452.     "OPENDOR2"
  453. };
  454.  
  455. //    Index into this array with aip->behavior
  456. char    behavior_text[6][9] = {
  457.     "STILL   ",
  458.     "NORMAL  ",
  459.     "HIDE    ",
  460.     "RUN_FROM",
  461.     "FOLPATH ",
  462.     "STATION "
  463. };
  464.  
  465. //    Index into this array with aip->GOAL_STATE or aip->CURRENT_STATE
  466. char    state_text[8][5] = {
  467.     "NONE",
  468.     "REST",
  469.     "SRCH",
  470.     "LOCK",
  471.     "FLIN",
  472.     "FIRE",
  473.     "RECO",
  474.     "ERR_",
  475. };
  476.  
  477.  
  478. int    Ai_animation_test=0;
  479. #endif
  480.  
  481. // Current state indicates where the robot current is, or has just done.
  482. //    Transition table between states for an AI object.
  483. //     First dimension is trigger event.
  484. //    Second dimension is current state.
  485. //     Third dimension is goal state.
  486. //    Result is new goal state.
  487. //    ERR_ means something impossible has happened.
  488. byte Ai_transition_table[AI_MAX_EVENT][AI_MAX_STATE][AI_MAX_STATE] = {
  489.     {
  490.     //    Event = AIE_FIRE, a nearby object fired
  491.     //    none            rest            srch            lock            flin            fire            reco                // CURRENT is rows, GOAL is columns
  492.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},        //    none
  493.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},        //    rest
  494.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},        //    search
  495.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},        //    lock
  496.     {    AIS_ERR_,    AIS_REST,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FIRE,    AIS_RECO},        //    flinch
  497.     {    AIS_ERR_,    AIS_FIRE,    AIS_FIRE,    AIS_FIRE,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},        //    fire
  498.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_FIRE}        //    recoil
  499.     },
  500.  
  501.     //    Event = AIE_HITT, a nearby object was hit (or a wall was hit)
  502.     {
  503.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},
  504.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},
  505.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},
  506.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},
  507.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FLIN},
  508.     {    AIS_ERR_,    AIS_REST,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FIRE,    AIS_RECO},
  509.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_FIRE}
  510.     },
  511.  
  512.     //    Event = AIE_COLL, player collided with robot
  513.     {
  514.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},
  515.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},
  516.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},
  517.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_RECO},
  518.     {    AIS_ERR_,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_LOCK,    AIS_FLIN,    AIS_FLIN},
  519.     {    AIS_ERR_,    AIS_REST,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FIRE,    AIS_RECO},
  520.     {    AIS_ERR_,    AIS_LOCK,    AIS_LOCK,    AIS_LOCK,    AIS_FLIN,    AIS_FIRE,    AIS_FIRE}
  521.     },
  522.  
  523.     //    Event = AIE_HURT, player hurt robot (by firing at and hitting it)
  524.     //    Note, this doesn't necessarily mean the robot JUST got hit, only that that is the most recent thing that happened.
  525.     {
  526.     {    AIS_ERR_,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN},
  527.     {    AIS_ERR_,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN},
  528.     {    AIS_ERR_,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN},
  529.     {    AIS_ERR_,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN},
  530.     {    AIS_ERR_,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN},
  531.     {    AIS_ERR_,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN},
  532.     {    AIS_ERR_,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN,    AIS_FLIN}
  533.     }
  534. };
  535.  
  536. ubyte    john_cheats_2[2*JOHN_CHEATS_SIZE_2] = {     KEY_P ^ 0x00 ^ 0x43, 0x66, 
  537.                                                                 KEY_O ^ 0x10 ^ 0x43, 0x11, 
  538.                                                                 KEY_R ^ 0x20 ^ 0x43, 0x8, 
  539.                                                                 KEY_G ^ 0x30 ^ 0x43, 0x2, 
  540.                                                                 KEY_Y ^ 0x40 ^ 0x43, 0x0, 
  541.                                                                 KEY_S ^ 0x50 ^ 0x43 };
  542.  
  543. // ---------------------------------------------------------
  544. //    On entry, N_robot_types had darn sure better be set.
  545. //    Mallocs N_robot_types robot_info structs into global Robot_info.
  546. void init_ai_system(void)
  547. {
  548. #if 0
  549.     int    i;
  550.  
  551.     mprintf((0, "Trying to malloc %i bytes for Robot_info.\n", N_robot_types * sizeof(*Robot_info)));
  552.     Robot_info = (robot_info *) malloc( N_robot_types * sizeof(*Robot_info) );
  553.     mprintf((0, "Robot_info = %i\n", Robot_info));
  554.  
  555.     for (i=0; i<N_robot_types; i++) {
  556.         Robot_info[i].field_of_view = F1_0/2;
  557.         Robot_info[i].firing_wait = F1_0;
  558.         Robot_info[i].turn_time = F1_0*2;
  559.         Robot_info[i].fire_power = F1_0;
  560.         Robot_info[i].shield = F1_0/2;
  561.         Robot_info[i].max_speed = F1_0*10;
  562.         Robot_info[i].always_0xabcd = 0xabcd;
  563.     }
  564. #endif
  565.  
  566. }
  567.  
  568. void john_cheat_func_1(int key)
  569. {
  570.     if (!Cheats_enabled)
  571.         return;
  572.  
  573.     if (key == (john_cheats_1[john_cheats_index_1] ^ (john_cheats_index_1 << 4) ^ 0x34)) {
  574.         john_cheats_index_1++;
  575.         if (john_cheats_index_1 == JOHN_CHEATS_SIZE_1)    {
  576.             do_controlcen_destroyed_stuff(NULL);
  577.             john_cheats_index_1 = 0;
  578.             digi_play_sample( SOUND_CHEATER, F1_0);
  579.         }
  580.     } else
  581.         john_cheats_index_1 = 0;
  582. }
  583.  
  584. // ---------------------------------------------------------------------------------------------------------------------
  585. //    Given a behavior, set initial mode.
  586. int ai_behavior_to_mode(int behavior)
  587. {
  588.     switch (behavior) {
  589.         case AIB_STILL:            return AIM_STILL;
  590.         case AIB_NORMAL:            return AIM_CHASE_OBJECT;
  591.         case AIB_HIDE:                return AIM_HIDE;
  592.         case AIB_RUN_FROM:        return AIM_RUN_FROM_OBJECT;
  593.         case AIB_FOLLOW_PATH:    return AIM_FOLLOW_PATH;
  594.         case AIB_STATION:            return AIM_STILL;
  595.         default:    Int3();    //    Contact Mike: Error, illegal behavior type
  596.     }
  597.  
  598.     return AIM_STILL;
  599. }
  600.  
  601. // ---------------------------------------------------------------------------------------------------------------------
  602. //    Call every time the player starts a new ship.
  603. void ai_init_boss_for_ship(void)
  604. {
  605.     Boss_been_hit = 0;
  606. }
  607.  
  608. // ---------------------------------------------------------------------------------------------------------------------
  609. //    initial_mode == -1 means leave mode unchanged.
  610. void init_ai_object(int objnum, int behavior, int hide_segment)
  611. {
  612.     object    *objp = &Objects[objnum];
  613.     ai_static    *aip = &objp->ctype.ai_info;
  614.     ai_local        *ailp = &Ai_local_info[objnum];
  615.  
  616. #ifdef DEST_SAT
  617.         if (!(Game_mode & GM_MULTI) && Robot_info[objp->id].boss_flag) {
  618.             mprintf((0, "Current_level_num = %i, Last_level = %i\n", Current_level_num, Last_level));
  619.             if (Current_level_num != Last_level) {
  620.                 mprintf((0, "Removing boss, object num = %i\n", objnum));
  621.                 objp->id = 0;
  622.                 objp->flags |= OF_SHOULD_BE_DEAD;
  623.             }
  624.         }
  625. #endif
  626.  
  627.     if (behavior == 0) {
  628.         // mprintf((0, "Behavior of 0 for object #%i, bashing to AIB_NORMAL.\n", objnum));
  629.         behavior = AIB_NORMAL;
  630.         objp->ctype.ai_info.behavior = behavior;
  631.     }
  632.     // mprintf((0, "Initializing object #%i\n", objnum));
  633.  
  634.     //    mode is now set from the Robot dialog, so this should get overwritten.
  635.     ailp->mode = AIM_STILL;
  636.  
  637.     ailp->previous_visibility = 0;
  638.  
  639.     if (behavior != -1) {
  640.         aip->behavior = behavior;
  641.         ailp->mode = ai_behavior_to_mode(aip->behavior);
  642.     } else if (!((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR))) {
  643.         mprintf((0, "[obj %i -> normal] ", objnum));
  644.         aip->behavior = AIB_NORMAL;
  645.     }
  646.  
  647.     // This is astonishingly stupid!  This routine gets called by matcens! KILL KILL KILL!!! Point_segs_free_ptr = Point_segs;
  648.  
  649.     vm_vec_zero(&objp->mtype.phys_info.velocity);
  650.     // -- ailp->wait_time = F1_0*5;
  651.     ailp->player_awareness_time = 0;
  652.     ailp->player_awareness_type = 0;
  653.     aip->GOAL_STATE = AIS_SRCH;
  654.     aip->CURRENT_STATE = AIS_REST;
  655.     ailp->time_player_seen = GameTime;
  656.     ailp->next_misc_sound_time = GameTime;
  657.     ailp->time_player_sound_attacked = GameTime;
  658.  
  659.     if ((behavior == AIB_HIDE) || (behavior == AIB_FOLLOW_PATH) || (behavior == AIB_STATION) || (behavior == AIB_RUN_FROM)) {
  660.         aip->hide_segment = hide_segment;
  661.         ailp->goal_segment = hide_segment;
  662.         aip->hide_index = -1;            // This means the path has not yet been created.
  663.         aip->cur_path_index = 0;
  664.     }
  665.  
  666.     aip->SKIP_AI_COUNT = 0;
  667.  
  668.     if (Robot_info[objp->id].cloak_type == RI_CLOAKED_ALWAYS)
  669.         aip->CLOAKED = 1;
  670.     else
  671.         aip->CLOAKED = 0;
  672.  
  673.     objp->mtype.phys_info.flags |= (PF_BOUNCE | PF_TURNROLL);
  674.     
  675.     aip->REMOTE_OWNER = -1;
  676. }
  677.  
  678. void john_cheat_func_2(int key)
  679. {
  680.     if (!Cheats_enabled)
  681.         return;
  682.  
  683.     if (key == (john_cheats_2[2*john_cheats_index_2] ^ (john_cheats_index_2 << 4) ^ 0x43)) {
  684.         john_cheats_index_2++;
  685.         if (john_cheats_index_2 == JOHN_CHEATS_SIZE_2) {
  686.             Laser_rapid_fire = 0xBADA55;
  687.             do_megawow_powerup(200);
  688.             john_cheats_index_2 = 0;
  689.             digi_play_sample( SOUND_CHEATER, F1_0);
  690.         }
  691.     } else
  692.         john_cheats_index_2 = 0;
  693. }
  694.  
  695. // ---------------------------------------------------------------------------------------------------------------------
  696. void init_ai_objects(void)
  697. {
  698.     int    i;
  699.  
  700.     Point_segs_free_ptr = Point_segs;
  701.  
  702.     for (i=0; i<MAX_OBJECTS; i++) {
  703.         object *objp = &Objects[i];
  704.  
  705.         if (objp->control_type == CT_AI)
  706.             init_ai_object(i, objp->ctype.ai_info.behavior, objp->ctype.ai_info.hide_segment);
  707.     }
  708.  
  709.     init_boss_segments(Boss_teleport_segs, &Num_boss_teleport_segs, 1);
  710.  
  711.     #ifndef SHAREWARE
  712.         init_boss_segments(Boss_gate_segs, &Num_boss_gate_segs, 0);
  713.     #endif
  714.  
  715.     Boss_dying_sound_playing = 0;
  716.     Boss_dying = 0;
  717.     Boss_been_hit = 0;
  718.     #ifndef SHAREWARE
  719.     Gate_interval = F1_0*5 - Difficulty_level*F1_0/2;
  720.     #endif
  721.  
  722.     Ai_initialized = 1;
  723. }
  724.  
  725. int    Lunacy = 0;
  726. int    Diff_save = 1;
  727.  
  728. fix    Firing_wait_copy[MAX_ROBOT_TYPES];
  729. byte    Rapidfire_count_copy[MAX_ROBOT_TYPES];
  730.  
  731. void do_lunacy_on(void)
  732. {
  733.     int    i;
  734.  
  735.     if ( !Lunacy )    {
  736.         Lunacy = 1;
  737.         Diff_save = Difficulty_level;
  738.         Difficulty_level = NDL-1;
  739.  
  740.         for (i=0; i<MAX_ROBOT_TYPES; i++) {
  741.             Firing_wait_copy[i] = Robot_info[i].firing_wait[NDL-1];
  742.             Rapidfire_count_copy[i] = Robot_info[i].rapidfire_count[NDL-1];
  743.     
  744.             Robot_info[i].firing_wait[NDL-1] = Robot_info[i].firing_wait[1];
  745.             Robot_info[i].rapidfire_count[NDL-1] = Robot_info[i].rapidfire_count[1];
  746.         }
  747.     }
  748. }
  749.  
  750. void do_lunacy_off(void)
  751. {
  752.     int    i;
  753.  
  754.     if ( Lunacy )    {
  755.         Lunacy = 0;
  756.         for (i=0; i<MAX_ROBOT_TYPES; i++) {
  757.             Robot_info[i].firing_wait[NDL-1] = Firing_wait_copy[i];
  758.             Robot_info[i].rapidfire_count[NDL-1] = Rapidfire_count_copy[i];
  759.         }
  760.         Difficulty_level = Diff_save;
  761.     }
  762. }
  763.  
  764. void john_cheat_func_3(int key)
  765. {
  766.     if (!Cheats_enabled)
  767.         return;
  768.  
  769.     if (key == (john_cheats_3[JOHN_CHEATS_SIZE_3 - john_cheats_index_3] ^ (0x61 + john_cheats_index_3))) {
  770.         if (john_cheats_index_3 == 4)
  771.             john_cheats_index_3++;
  772.         john_cheats_index_3++;
  773.         if (john_cheats_index_3 == JOHN_CHEATS_SIZE_3+1) {
  774.             if (Lunacy) {
  775.                 do_lunacy_off();
  776.                 HUD_init_message( TXT_NO_LUNACY );
  777.             } else {
  778.                 do_lunacy_on();
  779.                 HUD_init_message( TXT_LUNACY );
  780.                 digi_play_sample( SOUND_CHEATER, F1_0);
  781.             }
  782.             john_cheats_index_3 = 0;
  783.         }
  784.     } else
  785.         john_cheats_index_3 = 0;
  786. }
  787.  
  788. //    ----------------------------------------------------------------
  789. //    Do *dest = *delta unless:
  790. //                *delta is pretty small
  791. //        and    they are of different signs.
  792. void set_rotvel_and_saturate(fix *dest, fix delta)
  793. {
  794.     if ((delta ^ *dest) < 0) {
  795.         if (abs(delta) < F1_0/8) {
  796.             // mprintf((0, "D"));
  797.             *dest = delta/4;
  798.         } else
  799.             // mprintf((0, "d"));
  800.             *dest = delta;
  801.     } else {
  802.         // mprintf((0, "!"));
  803.         *dest = delta;
  804.     }
  805. }
  806.  
  807. //--debug-- #ifndef NDEBUG
  808. //--debug-- int    Total_turns=0;
  809. //--debug-- int    Prevented_turns=0;
  810. //--debug-- #endif
  811.  
  812. #define    AI_TURN_SCALE    1
  813. #define    BABY_SPIDER_ID    14
  814.  
  815. extern void physics_turn_towards_vector(vms_vector *goal_vector, object *obj, fix rate);
  816.  
  817. //-------------------------------------------------------------------------------------------
  818. void ai_turn_towards_vector(vms_vector *goal_vector, object *objp, fix rate)
  819. {
  820.     vms_vector    new_fvec;
  821.     fix            dot;
  822.  
  823.     if ((objp->id == BABY_SPIDER_ID) && (objp->type == OBJ_ROBOT)) {
  824.         physics_turn_towards_vector(goal_vector, objp, rate);
  825.         return;
  826.     }
  827.  
  828.     new_fvec = *goal_vector;
  829.  
  830.     dot = vm_vec_dot(goal_vector, &objp->orient.fvec);
  831.  
  832.     if (dot < (F1_0 - FrameTime/2)) {
  833.         fix    mag;
  834.         fix    new_scale = fixdiv(FrameTime * AI_TURN_SCALE, rate);
  835.         vm_vec_scale(&new_fvec, new_scale);
  836.         vm_vec_add2(&new_fvec, &objp->orient.fvec);
  837.         mag = vm_vec_normalize_quick(&new_fvec);
  838.         if (mag < F1_0/256) {
  839.             mprintf((1, "Degenerate vector in ai_turn_towards_vector (mag = %7.3f)\n", f2fl(mag)));
  840.             new_fvec = *goal_vector;        //    if degenerate vector, go right to goal
  841.         }
  842.     }
  843.  
  844. //    //    Every 8th time, do a correct matrix create, 7/8 time, do a quick one.
  845. //    if (rand() < 0x1000)
  846.         vm_vector_2_matrix(&objp->orient, &new_fvec, NULL, &objp->orient.rvec);
  847. //    else
  848. //        vm_vector_2_matrix_norm(&objp->orient, &new_fvec, NULL, &objp->orient.rvec);
  849.  
  850. //--{
  851. //--vms_vector tvec;
  852. //--fix mag;
  853. //--tvec = objp->orient.fvec;
  854. //--mag = vm_vec_mag(&tvec);
  855. //--mprintf((0, "mags = %7.3f ", f2fl(mag)));
  856. //--
  857. //--tvec = objp->orient.uvec;
  858. //--mag = vm_vec_mag(&tvec);
  859. //--mprintf((0, "%7.3f ", f2fl(mag)));
  860. //--
  861. //--tvec = objp->orient.rvec;
  862. //--mag = vm_vec_mag(&tvec);
  863. //--mprintf((0, "%7.3f\n", f2fl(mag)));
  864. //--}
  865. //--simpler, but buggy:    //    The cross product of the forward vector with the right vector is the up vector
  866. //--simpler, but buggy:    vm_vec_cross(&new_uvec, &new_fvec, &objp->orient.rvec);
  867. //--simpler, but buggy:    vm_vec_cross(&new_rvec, &new_uvec, &new_fvec);
  868. //--simpler, but buggy:
  869. //--simpler, but buggy:    objp->orient.fvec = new_fvec;
  870. //--simpler, but buggy:    objp->orient.rvec = new_rvec;
  871. //--simpler, but buggy:    objp->orient.uvec = new_uvec;
  872. }
  873.  
  874. // --------------------------------------------------------------------------------------------------------------------
  875. void ai_turn_randomly(vms_vector *vec_to_player, object *obj, fix rate, int previous_visibility)
  876. {
  877.     vms_vector    curvec;
  878.  
  879.     //    Random turning looks too stupid, so 1/4 of time, cheat.
  880.     if (previous_visibility)
  881.         if (rand() > 0x7400) {
  882.             ai_turn_towards_vector(vec_to_player, obj, rate);
  883.             return;
  884.         }
  885. //--debug--     if (rand() > 0x6000)
  886. //--debug--         Prevented_turns++;
  887.  
  888.     curvec = obj->mtype.phys_info.rotvel;
  889.  
  890.     curvec.y += F1_0/64;
  891.  
  892.     curvec.x += curvec.y/6;
  893.     curvec.y += curvec.z/4;
  894.     curvec.z += curvec.x/10;
  895.  
  896.     if (abs(curvec.x) > F1_0/8) curvec.x /= 4;
  897.     if (abs(curvec.y) > F1_0/8) curvec.y /= 4;
  898.     if (abs(curvec.z) > F1_0/8) curvec.z /= 4;
  899.  
  900.     obj->mtype.phys_info.rotvel = curvec;
  901.  
  902. }
  903.  
  904. //    Overall_agitation affects:
  905. //        Widens field of view.  Field of view is in range 0..1 (specified in bitmaps.tbl as N/360 degrees).
  906. //            Overall_agitation/128 subtracted from field of view, making robots see wider.
  907. //        Increases distance to which robot will search to create path to player by Overall_agitation/8 segments.
  908. //        Decreases wait between fire times by Overall_agitation/64 seconds.
  909.  
  910. void john_cheat_func_4(int key)
  911. {
  912.     if (!Cheats_enabled)
  913.         return;
  914.  
  915.     switch (john_cheats_index_4) {
  916.         case 3:
  917.             if (key == KEY_T)
  918.                 john_cheats_index_4++;
  919.             else
  920.                 john_cheats_index_4 = 0;
  921.             break;
  922.  
  923.         case 1:
  924.             if (key == KEY_L)
  925.                 john_cheats_index_4++;
  926.             else
  927.                 john_cheats_index_4 = 0;
  928.             break;
  929.     
  930.         case 2:
  931.             if (key == KEY_E)
  932.                 john_cheats_index_4++;
  933.             else
  934.                 john_cheats_index_4 = 0;
  935.             break;
  936.     
  937.         case 0:
  938.             if (key == KEY_P)
  939.                 john_cheats_index_4++;
  940.             break;
  941.  
  942.  
  943.         case 4:
  944.             if (key == KEY_C)
  945.                 john_cheats_index_4++;
  946.             else
  947.                 john_cheats_index_4 = 0;
  948.             break;
  949.     
  950.         case 5:
  951.             if (key == KEY_H)
  952.                 john_cheats_index_4++;
  953.             else
  954.                 john_cheats_index_4 = 0;
  955.             break;
  956.     
  957.         case 6:
  958.             Ugly_robot_texture = 0;
  959.         case 7:
  960.         case 8:
  961.             if ((key >= KEY_1) && (key <= KEY_0)) {
  962.                 john_cheats_index_4++;
  963.                 Ugly_robot_texture *= 10;
  964.                 if (key != KEY_0)
  965.                     Ugly_robot_texture += key - 1;
  966.                 if (john_cheats_index_4 == 9) {
  967.                     if (Ugly_robot_texture == 999)    {
  968.                         Ugly_robot_cheat = 0;
  969.                         HUD_init_message( TXT_ROBOT_PAINTING_OFF );
  970.                     } else {
  971.                         HUD_init_message( TXT_ROBOT_PAINTING_ON, Ugly_robot_texture );
  972.                         Ugly_robot_cheat = 0xBADA55;
  973.                     }
  974.                     mprintf((0, "Paint value = %i\n", Ugly_robot_texture));
  975.                     john_cheats_index_4 = 0;
  976.                 }
  977.             } else
  978.                 john_cheats_index_4 = 0;
  979.         
  980.             break;
  981.         default:
  982.             john_cheats_index_4 = 0;
  983.     }
  984. }
  985.  
  986. // --------------------------------------------------------------------------------------------------------------------
  987. //    Returns:
  988. //        0        Player is not visible from object, obstruction or something.
  989. //        1        Player is visible, but not in field of view.
  990. //        2        Player is visible and in field of view.
  991. //    Note: Uses Believed_player_pos as player's position for cloak effect.
  992. //    NOTE: Will destructively modify *pos if *pos is outside the mine.
  993. int player_is_visible_from_object(object *objp, vms_vector *pos, fix field_of_view, vms_vector *vec_to_player)
  994. {
  995.     fix            dot;
  996.     fvi_query    fq;
  997.  
  998.     fq.p0                        = pos;
  999.     if ((pos->x != objp->pos.x) || (pos->y != objp->pos.y) || (pos->z != objp->pos.z)) {
  1000.         int    segnum = find_point_seg(pos, objp->segnum);
  1001.         if (segnum == -1) {
  1002.             fq.startseg = objp->segnum;
  1003.             *pos = objp->pos;
  1004.             mprintf((1, "Object %i, gun is outside mine, moving towards center.\n", objp-Objects));
  1005.             move_towards_segment_center(objp);
  1006.         } else
  1007.             fq.startseg = segnum;
  1008.     } else
  1009.         fq.startseg            = objp->segnum;
  1010.     fq.p1                        = &Believed_player_pos;
  1011.     fq.rad                    = F1_0/4;
  1012.     fq.thisobjnum            = objp-Objects;
  1013.     fq.ignore_obj_list    = NULL;
  1014.     fq.flags                    = FQ_TRANSWALL | FQ_CHECK_OBJS;        //what about trans walls???
  1015.  
  1016.     Hit_type = find_vector_intersection(&fq,&Hit_data);
  1017.  
  1018.     Hit_pos = Hit_data.hit_pnt;
  1019.     Hit_seg = Hit_data.hit_seg;
  1020.  
  1021.     if ((Hit_type == HIT_NONE) || ((Hit_type == HIT_OBJECT) && (Hit_data.hit_object == Players[Player_num].objnum))) {
  1022.         dot = vm_vec_dot(vec_to_player, &objp->orient.fvec);
  1023.         // mprintf((0, "Fvec = [%5.2f %5.2f %5.2f], vec_to_player = [%5.2f %5.2f %5.2f], dot = %7.3f\n", f2fl(objp->orient.fvec.x), f2fl(objp->orient.fvec.y), f2fl(objp->orient.fvec.z), f2fl(vec_to_player->x), f2fl(vec_to_player->y), f2fl(vec_to_player->z), f2fl(dot)));
  1024.         if (dot > field_of_view - (Overall_agitation << 9)) {
  1025.             // mprintf((0, "I can see you!\n"));
  1026.             return 2;
  1027.         } else {
  1028.             // mprintf((0, "Damn, I could see you if I were looking...\n"));
  1029.             return 1;
  1030.         }
  1031.     } else {
  1032.         // mprintf((0, " ** Where are you? **\n"));
  1033.         return 0;
  1034.     }
  1035. }
  1036.  
  1037. // ------------------------------------------------------------------------------------------------------------------
  1038. //    Return 1 if animates, else return 0
  1039. int do_silly_animation(object *objp)
  1040. {
  1041.     int                objnum = objp-Objects;
  1042.     jointpos         *jp_list;
  1043.     int                robot_type, gun_num, robot_state, num_joint_positions;
  1044.     polyobj_info    *pobj_info = &objp->rtype.pobj_info;
  1045.     ai_static        *aip = &objp->ctype.ai_info;
  1046.     // ai_local            *ailp = &Ai_local_info[objnum];
  1047.     int                num_guns, at_goal;
  1048.     int                attack_type;
  1049.     int                flinch_attack_scale = 1;
  1050.  
  1051.     robot_type = objp->id;
  1052.     num_guns = Robot_info[robot_type].n_guns;
  1053.     attack_type = Robot_info[robot_type].attack_type;
  1054.  
  1055.     if (num_guns == 0) {
  1056.         // mprintf((0, "Object #%i of type #%i has 0 guns.\n", objp-Objects, robot_type));
  1057.         return 0;
  1058.     }
  1059.  
  1060.     //    This is a hack.  All positions should be based on goal_state, not GOAL_STATE.
  1061.     robot_state = Mike_to_matt_xlate[aip->GOAL_STATE];
  1062.     // previous_robot_state = Mike_to_matt_xlate[aip->CURRENT_STATE];
  1063.  
  1064.     if (attack_type) // && ((robot_state == AS_FIRE) || (robot_state == AS_RECOIL)))
  1065.         flinch_attack_scale = Attack_scale;
  1066.     else if ((robot_state == AS_FLINCH) || (robot_state == AS_RECOIL))
  1067.         flinch_attack_scale = Flinch_scale;
  1068.  
  1069.     at_goal = 1;
  1070.     for (gun_num=0; gun_num <= num_guns; gun_num++) {
  1071.         int    joint;
  1072.  
  1073.         num_joint_positions = robot_get_anim_state(&jp_list, robot_type, gun_num, robot_state);
  1074.  
  1075.         for (joint=0; joint<num_joint_positions; joint++) {
  1076.             fix            delta_angle, delta_2;
  1077.             int            jointnum = jp_list[joint].jointnum;
  1078.             vms_angvec    *jp = &jp_list[joint].angles;
  1079.             vms_angvec    *pobjp = &pobj_info->anim_angles[jointnum];
  1080.  
  1081.             if (jointnum >= Polygon_models[objp->rtype.pobj_info.model_num].n_models) {
  1082.                 Int3();        // Contact Mike: incompatible data, illegal jointnum, problem in pof file?
  1083.                 continue;
  1084.             }
  1085.             if (jp->p != pobjp->p) {
  1086.                 if (gun_num == 0)
  1087.                     at_goal = 0;
  1088.                 Ai_local_info[objnum].goal_angles[jointnum].p = jp->p;
  1089.  
  1090.                 delta_angle = jp->p - pobjp->p;
  1091.                 if (delta_angle >= F1_0/2)
  1092.                     delta_2 = -ANIM_RATE;
  1093.                 else if (delta_angle >= 0)
  1094.                     delta_2 = ANIM_RATE;
  1095.                 else if (delta_angle >= -F1_0/2)
  1096.                     delta_2 = -ANIM_RATE;
  1097.                 else
  1098.                     delta_2 = ANIM_RATE;
  1099.  
  1100.                 if (flinch_attack_scale != 1)
  1101.                     delta_2 *= flinch_attack_scale;
  1102.  
  1103.                 Ai_local_info[objnum].delta_angles[jointnum].p = delta_2/DELTA_ANG_SCALE;        // complete revolutions per second
  1104.             }
  1105.  
  1106.             if (jp->b != pobjp->b) {
  1107.                 if (gun_num == 0)
  1108.                     at_goal = 0;
  1109.                 Ai_local_info[objnum].goal_angles[jointnum].b = jp->b;
  1110.  
  1111.                 delta_angle = jp->b - pobjp->b;
  1112.                 if (delta_angle >= F1_0/2)
  1113.                     delta_2 = -ANIM_RATE;
  1114.                 else if (delta_angle >= 0)
  1115.                     delta_2 = ANIM_RATE;
  1116.                 else if (delta_angle >= -F1_0/2)
  1117.                     delta_2 = -ANIM_RATE;
  1118.                 else
  1119.                     delta_2 = ANIM_RATE;
  1120.  
  1121.                 if (flinch_attack_scale != 1)
  1122.                     delta_2 *= flinch_attack_scale;
  1123.  
  1124.                 Ai_local_info[objnum].delta_angles[jointnum].b = delta_2/DELTA_ANG_SCALE;        // complete revolutions per second
  1125.             }
  1126.  
  1127.             if (jp->h != pobjp->h) {
  1128.                 if (gun_num == 0)
  1129.                     at_goal = 0;
  1130.                 Ai_local_info[objnum].goal_angles[jointnum].h = jp->h;
  1131.  
  1132.                 delta_angle = jp->h - pobjp->h;
  1133.                 if (delta_angle >= F1_0/2)
  1134.                     delta_2 = -ANIM_RATE;
  1135.                 else if (delta_angle >= 0)
  1136.                     delta_2 = ANIM_RATE;
  1137.                 else if (delta_angle >= -F1_0/2)
  1138.                     delta_2 = -ANIM_RATE;
  1139.                 else
  1140.                     delta_2 = ANIM_RATE;
  1141.  
  1142.                 if (flinch_attack_scale != 1)
  1143.                     delta_2 *= flinch_attack_scale;
  1144.  
  1145.                 Ai_local_info[objnum].delta_angles[jointnum].h = delta_2/DELTA_ANG_SCALE;        // complete revolutions per second
  1146.             }
  1147.         }
  1148.  
  1149.         if (at_goal) {
  1150.             //ai_static    *aip = &objp->ctype.ai_info;
  1151.             ai_local        *ailp = &Ai_local_info[objp-Objects];
  1152.             ailp->achieved_state[gun_num] = ailp->goal_state[gun_num];
  1153.             if (ailp->achieved_state[gun_num] == AIS_RECO)
  1154.                 ailp->goal_state[gun_num] = AIS_FIRE;
  1155.  
  1156.             if (ailp->achieved_state[gun_num] == AIS_FLIN)
  1157.                 ailp->goal_state[gun_num] = AIS_LOCK;
  1158.  
  1159.         }
  1160.     }
  1161.  
  1162.     if (at_goal == 1) //num_guns)
  1163.         aip->CURRENT_STATE = aip->GOAL_STATE;
  1164.  
  1165.     return 1;
  1166. }
  1167.  
  1168. //    ------------------------------------------------------------------------------------------
  1169. //    Move all sub-objects in an object towards their goals.
  1170. //    Current orientation of object is at:    pobj_info.anim_angles
  1171. //    Goal orientation of object is at:        ai_info.goal_angles
  1172. //    Delta orientation of object is at:        ai_info.delta_angles
  1173. void ai_frame_animation(object *objp)
  1174. {
  1175.     int    objnum = objp-Objects;
  1176.     int    joint;
  1177.     int    num_joints;
  1178.  
  1179.     num_joints = Polygon_models[objp->rtype.pobj_info.model_num].n_models;
  1180.  
  1181.     for (joint=1; joint<num_joints; joint++) {
  1182.         fix            delta_to_goal;
  1183.         fix            scaled_delta_angle;
  1184.         vms_angvec    *curangp = &objp->rtype.pobj_info.anim_angles[joint];
  1185.         vms_angvec    *goalangp = &Ai_local_info[objnum].goal_angles[joint];
  1186.         vms_angvec    *deltaangp = &Ai_local_info[objnum].delta_angles[joint];
  1187.  
  1188. #ifndef NDEBUG
  1189. if (Ai_animation_test) {
  1190.     printf("%i: [%7.3f %7.3f %7.3f]  [%7.3f %7.3f %7.3f]\n", joint, f2fl(curangp->p), f2fl(curangp->b), f2fl(curangp->h), f2fl(goalangp->p), f2fl(goalangp->b), f2fl(goalangp->h), f2fl(curangp->p), f2fl(curangp->b), f2fl(curangp->h));
  1191. }
  1192. #endif
  1193.         delta_to_goal = goalangp->p - curangp->p;
  1194.         if (delta_to_goal > 32767)
  1195.             delta_to_goal = delta_to_goal - 65536;
  1196.         else if (delta_to_goal < -32767)
  1197.             delta_to_goal = 65536 + delta_to_goal;
  1198.  
  1199.         if (delta_to_goal) {
  1200.             scaled_delta_angle = fixmul(deltaangp->p, FrameTime) * DELTA_ANG_SCALE;
  1201.             curangp->p += scaled_delta_angle;
  1202.             if (abs(delta_to_goal) < abs(scaled_delta_angle))
  1203.                 curangp->p = goalangp->p;
  1204.         }
  1205.  
  1206.         delta_to_goal = goalangp->b - curangp->b;
  1207.         if (delta_to_goal > 32767)
  1208.             delta_to_goal = delta_to_goal - 65536;
  1209.         else if (delta_to_goal < -32767)
  1210.             delta_to_goal = 65536 + delta_to_goal;
  1211.  
  1212.         if (delta_to_goal) {
  1213.             scaled_delta_angle = fixmul(deltaangp->b, FrameTime) * DELTA_ANG_SCALE;
  1214.             curangp->b += scaled_delta_angle;
  1215.             if (abs(delta_to_goal) < abs(scaled_delta_angle))
  1216.                 curangp->b = goalangp->b;
  1217.         }
  1218.  
  1219.         delta_to_goal = goalangp->h - curangp->h;
  1220.         if (delta_to_goal > 32767)
  1221.             delta_to_goal = delta_to_goal - 65536;
  1222.         else if (delta_to_goal < -32767)
  1223.             delta_to_goal = 65536 + delta_to_goal;
  1224.  
  1225.         if (delta_to_goal) {
  1226.             scaled_delta_angle = fixmul(deltaangp->h, FrameTime) * DELTA_ANG_SCALE;
  1227.             curangp->h += scaled_delta_angle;
  1228.             if (abs(delta_to_goal) < abs(scaled_delta_angle))
  1229.                 curangp->h = goalangp->h;
  1230.         }
  1231.  
  1232.     }
  1233.  
  1234. }
  1235.  
  1236. // ----------------------------------------------------------------------------------
  1237. void set_next_fire_time(ai_local *ailp, robot_info *robptr)
  1238. {
  1239.     ailp->rapidfire_count++;
  1240.  
  1241.     if (ailp->rapidfire_count < robptr->rapidfire_count[Difficulty_level]) {
  1242.         ailp->next_fire = min(F1_0/8, robptr->firing_wait[Difficulty_level]/2);
  1243.     } else {
  1244.         ailp->rapidfire_count = 0;
  1245.         ailp->next_fire = robptr->firing_wait[Difficulty_level];
  1246.     }
  1247. }
  1248.  
  1249. // ----------------------------------------------------------------------------------
  1250. //    When some robots collide with the player, they attack.
  1251. //    If player is cloaked, then robot probably didn't actually collide, deal with that here.
  1252. void do_ai_robot_hit_attack(object *robot, object *player, vms_vector *collision_point)
  1253. {
  1254.     ai_local        *ailp = &Ai_local_info[robot-Objects];
  1255.     robot_info *robptr = &Robot_info[robot->id];
  1256.  
  1257. //#ifndef NDEBUG
  1258.     if (!Robot_firing_enabled)
  1259.         return;
  1260. //#endif
  1261.  
  1262.     //    If player is dead, stop firing.
  1263.     if (Objects[Players[Player_num].objnum].type == OBJ_GHOST)
  1264.         return;
  1265.  
  1266.     if (robptr->attack_type == 1) {
  1267.         if (ailp->next_fire <= 0) {
  1268.             if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
  1269.                 if (vm_vec_dist_quick(&ConsoleObject->pos, &robot->pos) < robot->size + ConsoleObject->size + F1_0*2)
  1270.                     collide_player_and_nasty_robot( player, robot, collision_point );
  1271.  
  1272.             robot->ctype.ai_info.GOAL_STATE = AIS_RECO;
  1273.             set_next_fire_time(ailp, robptr);
  1274.         }
  1275.     }
  1276.  
  1277. }
  1278.  
  1279. extern int Player_exploded;
  1280.  
  1281. // --------------------------------------------------------------------------------------------------------------------
  1282. //    Note: Parameter vec_to_player is only passed now because guns which aren't on the forward vector from the
  1283. //    center of the robot will not fire right at the player.  We need to aim the guns at the player.  Barring that, we cheat.
  1284. //    When this routine is complete, the parameter vec_to_player should not be necessary.
  1285. void ai_fire_laser_at_player(object *obj, vms_vector *fire_point)
  1286. {
  1287.     int            objnum = obj-Objects;
  1288.     ai_local        *ailp = &Ai_local_info[objnum];
  1289.     robot_info    *robptr = &Robot_info[obj->id];
  1290.     vms_vector    fire_vec;
  1291.     vms_vector    bpp_diff;
  1292.  
  1293.     if (!Robot_firing_enabled)
  1294.         return;
  1295.  
  1296. #ifndef NDEBUG
  1297.     //    We should never be coming here for the green guy, as he has no laser!
  1298.     if (robptr->attack_type == 1)
  1299.         Int3();    // Contact Mike: This is impossible.
  1300. #endif
  1301.  
  1302.     if (obj->control_type == CT_MORPH)
  1303.         return;
  1304.  
  1305.     //    If player is exploded, stop firing.
  1306.     if (Player_exploded)
  1307.         return;
  1308.  
  1309.     //    If player is cloaked, maybe don't fire based on how long cloaked and randomness.
  1310.     if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
  1311.         fix    cloak_time = Ai_cloak_info[objnum % MAX_AI_CLOAK_INFO].last_time;
  1312.  
  1313.         if (GameTime - cloak_time > CLOAK_TIME_MAX/4)
  1314.             if (rand() > fixdiv(GameTime - cloak_time, CLOAK_TIME_MAX)/2) {
  1315.                 set_next_fire_time(ailp, robptr);
  1316.                 return;
  1317.             }
  1318.     }
  1319.  
  1320. //--    //    Find segment containing laser fire position.  If the robot is straddling a segment, the position from
  1321. //--    //    which it fires may be in a different segment, which is bad news for find_vector_intersection.  So, cast
  1322. //--    //    a ray from the object center (whose segment we know) to the laser position.  Then, in the call to Laser_create_new
  1323. //--    //    use the data returned from this call to find_vector_intersection.
  1324. //--    //    Note that while find_vector_intersection is pretty slow, it is not terribly slow if the destination point is
  1325. //--    //    in the same segment as the source point.
  1326. //--
  1327. //--    fq.p0                        = &obj->pos;
  1328. //--    fq.startseg                = obj->segnum;
  1329. //--    fq.p1                        = fire_point;
  1330. //--    fq.rad                    = 0;
  1331. //--    fq.thisobjnum            = obj-Objects;
  1332. //--    fq.ignore_obj_list    = NULL;
  1333. //--    fq.flags                    = FQ_TRANSWALL | FQ_CHECK_OBJS;        //what about trans walls???
  1334. //--
  1335. //--    fate = find_vector_intersection(&fq, &hit_data);
  1336. //--    if (fate != HIT_NONE)
  1337. //--        return;
  1338.  
  1339.     //    Set position to fire at based on difficulty level.
  1340.     bpp_diff.x = Believed_player_pos.x + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
  1341.     bpp_diff.y = Believed_player_pos.y + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
  1342.     bpp_diff.z = Believed_player_pos.z + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
  1343.  
  1344.     //    Half the time fire at the player, half the time lead the player.
  1345.     if (rand() > 16384) {
  1346.  
  1347.         vm_vec_normalized_dir_quick(&fire_vec, &bpp_diff, fire_point);
  1348.  
  1349.     } else {
  1350.         vms_vector    player_direction_vector;
  1351.  
  1352.         vm_vec_sub(&player_direction_vector, &bpp_diff, &bpp_diff);
  1353.  
  1354.         // If player is not moving, fire right at him!
  1355.         //    Note: If the robot fires in the direction of its forward vector, this is bad because the weapon does not
  1356.         //    come out from the center of the robot; it comes out from the side.  So it is common for the weapon to miss
  1357.         //    its target.  Ideally, we want to point the guns at the player.  For now, just fire right at the player.
  1358.         if ((abs(player_direction_vector.x < 0x10000)) && (abs(player_direction_vector.y < 0x10000)) && (abs(player_direction_vector.z < 0x10000))) {
  1359.  
  1360.             vm_vec_normalized_dir_quick(&fire_vec, &bpp_diff, fire_point);
  1361.  
  1362.         // Player is moving.  Determine where the player will be at the end of the next frame if he doesn't change his
  1363.         //    behavior.  Fire at exactly that point.  This isn't exactly what you want because it will probably take the laser
  1364.         //    a different amount of time to get there, since it will probably be a different distance from the player.
  1365.         //    So, that's why we write games, instead of guiding missiles...
  1366.         } else {
  1367.             vm_vec_sub(&fire_vec, &bpp_diff, fire_point);
  1368.             vm_vec_scale(&fire_vec,fixmul(Weapon_info[Robot_info[obj->id].weapon_type].speed[Difficulty_level], FrameTime));
  1369.  
  1370.             vm_vec_add2(&fire_vec, &player_direction_vector);
  1371.             vm_vec_normalize_quick(&fire_vec);
  1372.  
  1373.         }
  1374.     }
  1375.  
  1376. //#ifndef NDEBUG
  1377. //    if (robptr->boss_flag)
  1378. //        mprintf((0, "Boss (%i) fires!\n", obj-Objects));
  1379. //#endif
  1380.  
  1381.     Laser_create_new_easy( &fire_vec, fire_point, obj-Objects, robptr->weapon_type, 1);
  1382.  
  1383. #ifndef SHAREWARE
  1384. #ifdef NETWORK
  1385.     if (Game_mode & GM_MULTI)
  1386.     {
  1387.         ai_multi_send_robot_position(objnum, -1);
  1388.         multi_send_robot_fire(objnum, obj->ctype.ai_info.CURRENT_GUN, &fire_vec);
  1389.     }
  1390. #endif
  1391. #endif
  1392.  
  1393.     create_awareness_event(obj, PA_NEARBY_ROBOT_FIRED);
  1394.  
  1395.     set_next_fire_time(ailp, robptr);
  1396.  
  1397.     //    If the boss fired, allow him to teleport very soon (right after firing, cool!), pending other factors.
  1398.     if (robptr->boss_flag)
  1399.         Last_teleport_time -= Boss_teleport_interval/2;
  1400. }
  1401.  
  1402. // --------------------------------------------------------------------------------------------------------------------
  1403. //    vec_goal must be normalized, or close to it.
  1404. void move_towards_vector(object *objp, vms_vector *vec_goal)
  1405. {
  1406.     physics_info    *pptr = &objp->mtype.phys_info;
  1407.     fix                speed, dot, max_speed;
  1408.     robot_info        *robptr = &Robot_info[objp->id];
  1409.     vms_vector        vel;
  1410.  
  1411.     //    Trying to move towards player.  If forward vector much different than velocity vector,
  1412.     //    bash velocity vector twice as much towards player as usual.
  1413.  
  1414.     vel = pptr->velocity;
  1415.     vm_vec_normalize_quick(&vel);
  1416.     dot = vm_vec_dot(&vel, &objp->orient.fvec);
  1417.  
  1418.     if (dot < 3*F1_0/4) {
  1419.         //    This funny code is supposed to slow down the robot and move his velocity towards his direction
  1420.         //    more quickly than the general code
  1421.         //-! mprintf((0, "Th  "));
  1422.         pptr->velocity.x = pptr->velocity.x/2 + fixmul(vec_goal->x, FrameTime*32);
  1423.         pptr->velocity.y = pptr->velocity.y/2 + fixmul(vec_goal->y, FrameTime*32);
  1424.         pptr->velocity.z = pptr->velocity.z/2 + fixmul(vec_goal->z, FrameTime*32);
  1425.     } else {
  1426.         //-! mprintf((0, "Tn  "));
  1427.         pptr->velocity.x += fixmul(vec_goal->x, FrameTime*64) * (Difficulty_level+5)/4;
  1428.         pptr->velocity.y += fixmul(vec_goal->y, FrameTime*64) * (Difficulty_level+5)/4;
  1429.         pptr->velocity.z += fixmul(vec_goal->z, FrameTime*64) * (Difficulty_level+5)/4;
  1430.     }
  1431.  
  1432.     speed = vm_vec_mag_quick(&pptr->velocity);
  1433.     max_speed = robptr->max_speed[Difficulty_level];
  1434.  
  1435.     //    Green guy attacks twice as fast as he moves away.
  1436.     if (robptr->attack_type == 1)
  1437.         max_speed *= 2;
  1438.  
  1439.     if (speed > max_speed) {
  1440.         pptr->velocity.x = (pptr->velocity.x*3)/4;
  1441.         pptr->velocity.y = (pptr->velocity.y*3)/4;
  1442.         pptr->velocity.z = (pptr->velocity.z*3)/4;
  1443.     }
  1444. }
  1445.  
  1446. // --------------------------------------------------------------------------------------------------------------------
  1447. void move_towards_player(object *objp, vms_vector *vec_to_player)
  1448. //    vec_to_player must be normalized, or close to it.
  1449. {
  1450.     move_towards_vector(objp, vec_to_player);
  1451. }
  1452.  
  1453. // --------------------------------------------------------------------------------------------------------------------
  1454. //    I am ashamed of this: fast_flag == -1 means normal slide about.  fast_flag = 0 means no evasion.
  1455. void move_around_player(object *objp, vms_vector *vec_to_player, int fast_flag)
  1456. {
  1457.     physics_info    *pptr = &objp->mtype.phys_info;
  1458.     fix                speed;
  1459.     robot_info        *robptr = &Robot_info[objp->id];
  1460.     int                objnum = objp-Objects;
  1461.     int                dir;
  1462.     int                dir_change;
  1463.     fix                ft;
  1464.     vms_vector        evade_vector;
  1465.     int                count=0;
  1466.  
  1467.     if (fast_flag == 0)
  1468.         return;
  1469.  
  1470.     dir_change = 48;
  1471.     ft = FrameTime;
  1472.     if (ft < F1_0/32) {
  1473.         dir_change *= 8;
  1474.         count += 3;
  1475.     } else
  1476.         while (ft < F1_0/4) {
  1477.             dir_change *= 2;
  1478.             ft *= 2;
  1479.             count++;
  1480.         }
  1481.  
  1482.     dir = (FrameCount + (count+1) * (objnum*8 + objnum*4 + objnum)) & dir_change;
  1483.     dir >>= (4+count);
  1484.  
  1485.     Assert((dir >= 0) && (dir <= 3));
  1486.  
  1487.     switch (dir) {
  1488.         case 0:
  1489.             evade_vector.x = fixmul(vec_to_player->z, FrameTime*32);
  1490.             evade_vector.y = fixmul(vec_to_player->y, FrameTime*32);
  1491.             evade_vector.z = fixmul(-vec_to_player->x, FrameTime*32);
  1492.             break;
  1493.         case 1:
  1494.             evade_vector.x = fixmul(-vec_to_player->z, FrameTime*32);
  1495.             evade_vector.y = fixmul(vec_to_player->y, FrameTime*32);
  1496.             evade_vector.z = fixmul(vec_to_player->x, FrameTime*32);
  1497.             break;
  1498.         case 2:
  1499.             evade_vector.x = fixmul(-vec_to_player->y, FrameTime*32);
  1500.             evade_vector.y = fixmul(vec_to_player->x, FrameTime*32);
  1501.             evade_vector.z = fixmul(vec_to_player->z, FrameTime*32);
  1502.             break;
  1503.         case 3:
  1504.             evade_vector.x = fixmul(vec_to_player->y, FrameTime*32);
  1505.             evade_vector.y = fixmul(-vec_to_player->x, FrameTime*32);
  1506.             evade_vector.z = fixmul(vec_to_player->z, FrameTime*32);
  1507.             break;
  1508.     }
  1509.  
  1510.     //    Note: -1 means normal circling about the player.  > 0 means fast evasion.
  1511.     if (fast_flag > 0) {
  1512.         fix    dot;
  1513.  
  1514.         //    Only take evasive action if looking at player.
  1515.         //    Evasion speed is scaled by percentage of shields left so wounded robots evade less effectively.
  1516.  
  1517.         dot = vm_vec_dot(vec_to_player, &objp->orient.fvec);
  1518.         if ((dot > robptr->field_of_view[Difficulty_level]) && !(ConsoleObject->flags & PLAYER_FLAGS_CLOAKED)) {
  1519.             fix    damage_scale;
  1520.  
  1521.             damage_scale = fixdiv(objp->shields, robptr->strength);
  1522.             if (damage_scale > F1_0)
  1523.                 damage_scale = F1_0;        //    Just in case...
  1524.             else if (damage_scale < 0)
  1525.                 damage_scale = 0;            //    Just in case...
  1526.  
  1527.             vm_vec_scale(&evade_vector, i2f(fast_flag) + damage_scale);
  1528.         }
  1529.     }
  1530.  
  1531.     pptr->velocity.x += evade_vector.x;
  1532.     pptr->velocity.y += evade_vector.y;
  1533.     pptr->velocity.z += evade_vector.z;
  1534.  
  1535.     speed = vm_vec_mag_quick(&pptr->velocity);
  1536.     if (speed > robptr->max_speed[Difficulty_level]) {
  1537.         pptr->velocity.x = (pptr->velocity.x*3)/4;
  1538.         pptr->velocity.y = (pptr->velocity.y*3)/4;
  1539.         pptr->velocity.z = (pptr->velocity.z*3)/4;
  1540.     }
  1541.  
  1542. }
  1543.  
  1544. // --------------------------------------------------------------------------------------------------------------------
  1545. void move_away_from_player(object *objp, vms_vector *vec_to_player, int attack_type)
  1546. {
  1547.     fix                speed;
  1548.     physics_info    *pptr = &objp->mtype.phys_info;
  1549.     robot_info        *robptr = &Robot_info[objp->id];
  1550.     int                objref;
  1551.  
  1552.     pptr->velocity.x -= fixmul(vec_to_player->x, FrameTime*16);
  1553.     pptr->velocity.y -= fixmul(vec_to_player->y, FrameTime*16);
  1554.     pptr->velocity.z -= fixmul(vec_to_player->z, FrameTime*16);
  1555.  
  1556.     if (attack_type) {
  1557.         //    Get value in 0..3 to choose evasion direction.
  1558.         objref = ((objp-Objects) ^ ((FrameCount + 3*(objp-Objects)) >> 5)) & 3;
  1559.  
  1560.         switch (objref) {
  1561.             case 0:    vm_vec_scale_add2(&pptr->velocity, &objp->orient.uvec, FrameTime << 5);    break;
  1562.             case 1:    vm_vec_scale_add2(&pptr->velocity, &objp->orient.uvec, -FrameTime << 5);    break;
  1563.             case 2:    vm_vec_scale_add2(&pptr->velocity, &objp->orient.rvec, FrameTime << 5);    break;
  1564.             case 3:    vm_vec_scale_add2(&pptr->velocity, &objp->orient.rvec, -FrameTime << 5);    break;
  1565.             default:    Int3();    //    Impossible, bogus value on objref, must be in 0..3
  1566.         }
  1567.     }
  1568.  
  1569.  
  1570.     speed = vm_vec_mag_quick(&pptr->velocity);
  1571.  
  1572.     if (speed > robptr->max_speed[Difficulty_level]) {
  1573.         pptr->velocity.x = (pptr->velocity.x*3)/4;
  1574.         pptr->velocity.y = (pptr->velocity.y*3)/4;
  1575.         pptr->velocity.z = (pptr->velocity.z*3)/4;
  1576.     }
  1577.  
  1578. //--old--    fix                speed, dot;
  1579. //--old--    physics_info    *pptr = &objp->mtype.phys_info;
  1580. //--old--    robot_info        *robptr = &Robot_info[objp->id];
  1581. //--old--
  1582. //--old--    //    Trying to move away from player.  If forward vector much different than velocity vector,
  1583. //--old--    //    bash velocity vector twice as much away from player as usual.
  1584. //--old--    dot = vm_vec_dot(&pptr->velocity, &objp->orient.fvec);
  1585. //--old--    if (dot > -3*F1_0/4) {
  1586. //--old--        //    This funny code is supposed to slow down the robot and move his velocity towards his direction
  1587. //--old--        //    more quickly than the general code
  1588. //--old--        pptr->velocity.x = pptr->velocity.x/2 - fixmul(vec_to_player->x, FrameTime*16);
  1589. //--old--        pptr->velocity.y = pptr->velocity.y/2 - fixmul(vec_to_player->y, FrameTime*16);
  1590. //--old--        pptr->velocity.z = pptr->velocity.z/2 - fixmul(vec_to_player->z, FrameTime*16);
  1591. //--old--    } else {
  1592. //--old--        pptr->velocity.x -= fixmul(vec_to_player->x, FrameTime*16);
  1593. //--old--        pptr->velocity.y -= fixmul(vec_to_player->y, FrameTime*16);
  1594. //--old--        pptr->velocity.z -= fixmul(vec_to_player->z, FrameTime*16);
  1595. //--old--    }
  1596. //--old--
  1597. //--old--    speed = vm_vec_mag_quick(&pptr->velocity);
  1598. //--old--
  1599. //--old--    if (speed > robptr->max_speed[Difficulty_level]) {
  1600. //--old--        pptr->velocity.x = (pptr->velocity.x*3)/4;
  1601. //--old--        pptr->velocity.y = (pptr->velocity.y*3)/4;
  1602. //--old--        pptr->velocity.z = (pptr->velocity.z*3)/4;
  1603. //--old--    }
  1604. }
  1605.  
  1606. // --------------------------------------------------------------------------------------------------------------------
  1607. //    Move towards, away_from or around player.
  1608. //    Also deals with evasion.
  1609. //    If the flag evade_only is set, then only allowed to evade, not allowed to move otherwise (must have mode == AIM_STILL).
  1610. void ai_move_relative_to_player(object *objp, ai_local *ailp, fix dist_to_player, vms_vector *vec_to_player, fix circle_distance, int evade_only)
  1611. {
  1612.     object        *dobjp;
  1613.     robot_info    *robptr = &Robot_info[objp->id];
  1614.  
  1615.     //    See if should take avoidance.
  1616.  
  1617. // New way, green guys don't evade:    if ((robptr->attack_type == 0) && (objp->ctype.ai_info.danger_laser_num != -1)) {
  1618.     if (objp->ctype.ai_info.danger_laser_num != -1) {
  1619.         dobjp = &Objects[objp->ctype.ai_info.danger_laser_num];
  1620.  
  1621.         if ((dobjp->type == OBJ_WEAPON) && (dobjp->signature == objp->ctype.ai_info.danger_laser_signature)) {
  1622.             fix            dot, dist_to_laser, field_of_view;
  1623.             vms_vector    vec_to_laser, laser_fvec;
  1624.  
  1625.             field_of_view = Robot_info[objp->id].field_of_view[Difficulty_level];
  1626.  
  1627.             vm_vec_sub(&vec_to_laser, &dobjp->pos, &objp->pos);
  1628.             dist_to_laser = vm_vec_normalize_quick(&vec_to_laser);
  1629.             dot = vm_vec_dot(&vec_to_laser, &objp->orient.fvec);
  1630.  
  1631.             if (dot > field_of_view) {
  1632.                 fix            laser_robot_dot;
  1633.                 vms_vector    laser_vec_to_robot;
  1634.  
  1635.                 //    The laser is seen by the robot, see if it might hit the robot.
  1636.                 //    Get the laser's direction.  If it's a polyobj, it can be gotten cheaply from the orientation matrix.
  1637.                 if (dobjp->render_type == RT_POLYOBJ)
  1638.                     laser_fvec = dobjp->orient.fvec;
  1639.                 else {        //    Not a polyobj, get velocity and normalize.
  1640.                     laser_fvec = dobjp->mtype.phys_info.velocity;    //dobjp->orient.fvec;
  1641.                     vm_vec_normalize_quick(&laser_fvec);
  1642.                 }
  1643.                 vm_vec_sub(&laser_vec_to_robot, &objp->pos, &dobjp->pos);
  1644.                 vm_vec_normalize_quick(&laser_vec_to_robot);
  1645.                 laser_robot_dot = vm_vec_dot(&laser_fvec, &laser_vec_to_robot);
  1646.  
  1647.                 if ((laser_robot_dot > F1_0*7/8) && (dist_to_laser < F1_0*80)) {
  1648.                     int    evade_speed;
  1649.  
  1650.                     ai_evaded = 1;
  1651.                     evade_speed = Robot_info[objp->id].evade_speed[Difficulty_level];
  1652.  
  1653.                     move_around_player(objp, vec_to_player, evade_speed);
  1654.                 }
  1655.             }
  1656.             return;
  1657.         }
  1658.     }
  1659.  
  1660.     //    If only allowed to do evade code, then done.
  1661.     //    Hmm, perhaps brilliant insight.  If want claw-type guys to keep coming, don't return here after evasion.
  1662.     if ((!robptr->attack_type) && evade_only)
  1663.         return;
  1664.  
  1665.     //    If we fall out of above, then no object to be avoided.
  1666.     objp->ctype.ai_info.danger_laser_num = -1;
  1667.  
  1668.     //    Green guy selects move around/towards/away based on firing time, not distance.
  1669.     if (robptr->attack_type == 1) {
  1670.         if (((ailp->next_fire > robptr->firing_wait[Difficulty_level]/4) && (dist_to_player < F1_0*30)) || Player_is_dead) {
  1671.             //    1/4 of time, move around player, 3/4 of time, move away from player
  1672.             if (rand() < 8192) {
  1673.                 move_around_player(objp, vec_to_player, -1);
  1674.             } else {
  1675.                 move_away_from_player(objp, vec_to_player, 1);
  1676.             }
  1677.         } else {
  1678.             move_towards_player(objp, vec_to_player);
  1679.         }
  1680.     } else {
  1681.         if (dist_to_player < circle_distance)
  1682.             move_away_from_player(objp, vec_to_player, 0);
  1683.         else if (dist_to_player < circle_distance*2)
  1684.             move_around_player(objp, vec_to_player, -1);
  1685.         else
  1686.             move_towards_player(objp, vec_to_player);
  1687.     }
  1688.  
  1689. }
  1690.  
  1691. // --------------------------------------------------------------------------------------------------------------------
  1692. //    Compute a somewhat random, normalized vector.
  1693. void make_random_vector(vms_vector *vec)
  1694. {
  1695.     vec->x = (rand() - 16384) | 1;    // make sure we don't create null vector
  1696.     vec->y = rand() - 16384;
  1697.     vec->z = rand() - 16384;
  1698.  
  1699.     vm_vec_normalize_quick(vec);
  1700. }
  1701.  
  1702. #ifndef NDEBUG
  1703. void mprintf_animation_info(object *objp)
  1704. {
  1705.     ai_static    *aip = &objp->ctype.ai_info;
  1706.     ai_local        *ailp = &Ai_local_info[objp-Objects];
  1707.  
  1708.     if (!Ai_info_enabled)
  1709.         return;
  1710.  
  1711.     mprintf((0, "Goal = "));
  1712.  
  1713.     switch (aip->GOAL_STATE) {
  1714.         case AIS_NONE:    mprintf((0, "NONE "));    break;
  1715.         case AIS_REST:    mprintf((0, "REST "));    break;
  1716.         case AIS_SRCH:    mprintf((0, "SRCH "));    break;
  1717.         case AIS_LOCK:    mprintf((0, "LOCK "));    break;
  1718.         case AIS_FLIN:    mprintf((0, "FLIN "));    break;
  1719.         case AIS_FIRE:    mprintf((0, "FIRE "));    break;
  1720.         case AIS_RECO:    mprintf((0, "RECO "));    break;
  1721.         case AIS_ERR_:    mprintf((0, "ERR_ "));    break;
  1722.     }
  1723.  
  1724.     mprintf((0, " Cur = "));
  1725.  
  1726.     switch (aip->CURRENT_STATE) {
  1727.         case AIS_NONE:    mprintf((0, "NONE "));    break;
  1728.         case AIS_REST:    mprintf((0, "REST "));    break;
  1729.         case AIS_SRCH:    mprintf((0, "SRCH "));    break;
  1730.         case AIS_LOCK:    mprintf((0, "LOCK "));    break;
  1731.         case AIS_FLIN:    mprintf((0, "FLIN "));    break;
  1732.         case AIS_FIRE:    mprintf((0, "FIRE "));    break;
  1733.         case AIS_RECO:    mprintf((0, "RECO "));    break;
  1734.         case AIS_ERR_:    mprintf((0, "ERR_ "));    break;
  1735.     }
  1736.  
  1737.     mprintf((0, " Aware = "));
  1738.  
  1739.     switch (ailp->player_awareness_type) {
  1740.         case AIE_FIRE: mprintf((0, "FIRE ")); break;
  1741.         case AIE_HITT: mprintf((0, "HITT ")); break;
  1742.         case AIE_COLL: mprintf((0, "COLL ")); break;
  1743.         case AIE_HURT: mprintf((0, "HURT ")); break;
  1744.     }
  1745.  
  1746.     mprintf((0, "Next fire = %6.3f, Time = %6.3f\n", f2fl(ailp->next_fire), f2fl(ailp->player_awareness_time)));
  1747.  
  1748. }
  1749. #endif
  1750.  
  1751. //    -------------------------------------------------------------------------------------------------------------------
  1752. int    Break_on_object = -1;
  1753.  
  1754. void do_firing_stuff(object *obj, int player_visibility, vms_vector *vec_to_player)
  1755. {
  1756. //mprintf((0, "!"));
  1757.     if (player_visibility >= 1) {
  1758.         //    Now, if in robot's field of view, lock onto player
  1759.         fix    dot = vm_vec_dot(&obj->orient.fvec, vec_to_player);
  1760. //mprintf((0, "dot = %8x ", dot));
  1761.         if ((dot >= 7*F1_0/8) || (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
  1762.             ai_static    *aip = &obj->ctype.ai_info;
  1763.             ai_local        *ailp = &Ai_local_info[obj-Objects];
  1764.  
  1765.             switch (aip->GOAL_STATE) {
  1766.                 case AIS_NONE:
  1767.                 case AIS_REST:
  1768.                 case AIS_SRCH:
  1769.                 case AIS_LOCK:
  1770.                     aip->GOAL_STATE = AIS_FIRE;
  1771.                     if (ailp->player_awareness_type <= PA_NEARBY_ROBOT_FIRED) {
  1772.                         ailp->player_awareness_type = PA_NEARBY_ROBOT_FIRED;
  1773.                         ailp->player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME;
  1774.                     }
  1775.                     break;
  1776.             }
  1777.         } else if (dot >= F1_0/2) {
  1778.             ai_static    *aip = &obj->ctype.ai_info;
  1779.             switch (aip->GOAL_STATE) {
  1780.                 case AIS_NONE:
  1781.                 case AIS_REST:
  1782.                 case AIS_SRCH:
  1783.                     aip->GOAL_STATE = AIS_LOCK;
  1784.                     break;
  1785.             }
  1786.         }
  1787.     }
  1788. }
  1789.  
  1790. // --------------------------------------------------------------------------------------------------------------------
  1791. //    If a hiding robot gets bumped or hit, he decides to find another hiding place.
  1792. void do_ai_robot_hit(object *objp, int type)
  1793. {
  1794.     if (objp->control_type == CT_AI) {
  1795.         if ((type == PA_WEAPON_ROBOT_COLLISION) || (type == PA_PLAYER_COLLISION))
  1796.             switch (objp->ctype.ai_info.behavior) {
  1797.                 case AIM_HIDE:
  1798.                     objp->ctype.ai_info.SUBMODE = AISM_GOHIDE;
  1799.                     break;
  1800.                 case AIM_STILL:
  1801.                     Ai_local_info[objp-Objects].mode = AIM_CHASE_OBJECT;
  1802.                     break;
  1803.             }
  1804.     }
  1805.  
  1806. }
  1807. #ifndef NDEBUG
  1808. int    Do_ai_flag=1;
  1809. int    Cvv_test=0;
  1810. int    Cvv_last_time[MAX_OBJECTS];
  1811. int    Gun_point_hack=0;
  1812. #endif
  1813.  
  1814. #define    CHASE_TIME_LENGTH        (F1_0*8)
  1815. #define    DEFAULT_ROBOT_SOUND_VOLUME        F1_0
  1816. int        Robot_sound_volume=DEFAULT_ROBOT_SOUND_VOLUME;
  1817.  
  1818. // --------------------------------------------------------------------------------------------------------------------
  1819. //    Note: This function could be optimized.  Surely player_is_visible_from_object would benefit from the
  1820. //    information of a normalized vec_to_player.
  1821. //    Return player visibility:
  1822. //        0        not visible
  1823. //        1        visible, but robot not looking at player (ie, on an unobstructed vector)
  1824. //        2        visible and in robot's field of view
  1825. //        -1        player is cloaked
  1826. //    If the player is cloaked, set vec_to_player based on time player cloaked and last uncloaked position.
  1827. //    Updates ailp->previous_visibility if player is not cloaked, in which case the previous visibility is left unchanged
  1828. //    and is copied to player_visibility
  1829. void compute_vis_and_vec(object *objp, vms_vector *pos, ai_local *ailp, vms_vector *vec_to_player, int *player_visibility, robot_info *robptr, int *flag)
  1830. {
  1831.     if (!*flag) {
  1832.         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
  1833.             fix            delta_time, dist;
  1834.             int            cloak_index = (objp-Objects) % MAX_AI_CLOAK_INFO;
  1835.  
  1836.             delta_time = GameTime - Ai_cloak_info[cloak_index].last_time;
  1837.             if (delta_time > F1_0*2) {
  1838.                 vms_vector    randvec;
  1839.  
  1840.                 Ai_cloak_info[cloak_index].last_time = GameTime;
  1841.                 make_random_vector(&randvec);
  1842.                 vm_vec_scale_add2(&Ai_cloak_info[cloak_index].last_position, &randvec, 8*delta_time );
  1843.             }
  1844.  
  1845.             dist = vm_vec_normalized_dir_quick(vec_to_player, &Ai_cloak_info[cloak_index].last_position, pos);
  1846.             *player_visibility = player_is_visible_from_object(objp, pos, robptr->field_of_view[Difficulty_level], vec_to_player);
  1847.             // *player_visibility = 2;
  1848.  
  1849.             if ((ailp->next_misc_sound_time < GameTime) && (ailp->next_fire < F1_0) && (dist < F1_0*20)) {
  1850.                 mprintf((0, "ANGRY! "));
  1851.                 ailp->next_misc_sound_time = GameTime + (rand() + F1_0) * (7 - Difficulty_level) / 1;
  1852.                 digi_link_sound_to_pos( robptr->see_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1853.             }
  1854.         } else {
  1855.             //    Compute expensive stuff -- vec_to_player and player_visibility
  1856.             vm_vec_normalized_dir_quick(vec_to_player, &Believed_player_pos, pos);
  1857.             if ((vec_to_player->x == 0) && (vec_to_player->y == 0) && (vec_to_player->z == 0)) {
  1858.                 mprintf((0, "Warning: Player and robot at exactly the same location.\n"));
  1859.                 vec_to_player->x = F1_0;
  1860.             }
  1861.             *player_visibility = player_is_visible_from_object(objp, pos, robptr->field_of_view[Difficulty_level], vec_to_player);
  1862.  
  1863.             //    This horrible code added by MK in desperation on 12/13/94 to make robots wake up as soon as they
  1864.             //    see you without killing frame rate.
  1865.             {
  1866.                 ai_static    *aip = &objp->ctype.ai_info;
  1867.             if ((*player_visibility == 2) && (ailp->previous_visibility != 2))
  1868.                 if ((aip->GOAL_STATE == AIS_REST) || (aip->CURRENT_STATE == AIS_REST)) {
  1869.                     aip->GOAL_STATE = AIS_FIRE;
  1870.                     aip->CURRENT_STATE = AIS_FIRE;
  1871.                 }
  1872.             }
  1873.  
  1874.             if (!Player_exploded && (ailp->previous_visibility != *player_visibility) && (*player_visibility == 2)) {
  1875.                 if (ailp->previous_visibility == 0) {
  1876.                     if (ailp->time_player_seen + F1_0/2 < GameTime) {
  1877.                         // mprintf((0, "SEE! "));
  1878.                         digi_link_sound_to_pos( robptr->see_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1879.                         ailp->time_player_sound_attacked = GameTime;
  1880.                         ailp->next_misc_sound_time = GameTime + F1_0 + rand()*4;
  1881.                     }
  1882.                 } else if (ailp->time_player_sound_attacked + F1_0/4 < GameTime) {
  1883.                     // mprintf((0, "ANGRY! "));
  1884.                     digi_link_sound_to_pos( robptr->attack_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1885.                     ailp->time_player_sound_attacked = GameTime;
  1886.                 }
  1887.             } 
  1888.  
  1889.             if ((*player_visibility == 2) && (ailp->next_misc_sound_time < GameTime)) {
  1890.                 // mprintf((0, "ATTACK! "));
  1891.                 ailp->next_misc_sound_time = GameTime + (rand() + F1_0) * (7 - Difficulty_level) / 2;
  1892.                 digi_link_sound_to_pos( robptr->attack_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1893.             }
  1894.             ailp->previous_visibility = *player_visibility;
  1895.         }
  1896.  
  1897.         *flag = 1;
  1898.  
  1899.         if (*player_visibility) {
  1900.             ailp->time_player_seen = GameTime;
  1901.         }
  1902.     }
  1903.  
  1904. }
  1905.  
  1906. // --------------------------------------------------------------------------------------------------------------------
  1907. //    Move the object objp to a spot in which it doesn't intersect a wall.
  1908. //    It might mean moving it outside its current segment.
  1909. void move_object_to_legal_spot(object *objp)
  1910. {
  1911.     vms_vector    original_pos = objp->pos;
  1912.     int        i;
  1913.     segment    *segp = &Segments[objp->segnum];
  1914.  
  1915.     for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
  1916.         if (WALL_IS_DOORWAY(segp, i) & WID_FLY_FLAG) {
  1917.             vms_vector    segment_center, goal_dir;
  1918.             fix            dist_to_center;
  1919.  
  1920.             compute_segment_center(&segment_center, &Segments[segp->children[i]]);
  1921.             vm_vec_sub(&goal_dir, &segment_center, &objp->pos);
  1922.             dist_to_center = vm_vec_normalize_quick(&goal_dir);
  1923.             vm_vec_scale(&goal_dir, objp->size);
  1924.             vm_vec_add2(&objp->pos, &goal_dir);
  1925.             if (!object_intersects_wall(objp)) {
  1926.                 int    new_segnum = find_point_seg(&objp->pos, objp->segnum);
  1927.  
  1928.                 if (new_segnum != -1) {
  1929.                     obj_relink(objp-Objects, new_segnum);
  1930.                     return;
  1931.                 }
  1932.             } else
  1933.                 objp->pos = original_pos;
  1934.         }
  1935.     }
  1936.  
  1937.     // Int3();        //    Darn you John, you done it again!  (But contact Mike)
  1938.     mprintf((0, "Note: Killing robot #%i because he's badly stuck outside the mine.\n", objp-Objects));
  1939.  
  1940.     apply_damage_to_robot(objp, objp->shields*2, objp-Objects);
  1941. }
  1942.  
  1943. // --------------------------------------------------------------------------------------------------------------------
  1944. //    Move object one object radii from current position towards segment center.
  1945. //    If segment center is nearer than 2 radii, move it to center.
  1946. void move_towards_segment_center(object *objp)
  1947. {
  1948.     int            segnum = objp->segnum;
  1949.     fix            dist_to_center;
  1950.     vms_vector    segment_center, goal_dir;
  1951.  
  1952.     compute_segment_center(&segment_center, &Segments[segnum]);
  1953.  
  1954.     vm_vec_sub(&goal_dir, &segment_center, &objp->pos);
  1955.     dist_to_center = vm_vec_normalize_quick(&goal_dir);
  1956.  
  1957.     if (dist_to_center < objp->size) {
  1958.         //    Center is nearer than the distance we want to move, so move to center.
  1959.         objp->pos = segment_center;
  1960.         mprintf((0, "Object #%i moved to center of segment #%i (%7.3f %7.3f %7.3f)\n", objp-Objects, objp->segnum, f2fl(objp->pos.x), f2fl(objp->pos.y), f2fl(objp->pos.z)));
  1961.         if (object_intersects_wall(objp)) {
  1962.             mprintf((0, "Object #%i still illegal, trying trickier move.\n"));
  1963.             move_object_to_legal_spot(objp);
  1964.         }
  1965.     } else {
  1966.         int    new_segnum;
  1967.         //    Move one radii towards center.
  1968.         vm_vec_scale(&goal_dir, objp->size);
  1969.         vm_vec_add2(&objp->pos, &goal_dir);
  1970.         new_segnum = find_point_seg(&objp->pos, objp->segnum);
  1971.         if (new_segnum == -1) {
  1972.             objp->pos = segment_center;
  1973.             move_object_to_legal_spot(objp);
  1974.         }
  1975.         mprintf((0, "Obj %i moved twrds seg %i (%6.2f %6.2f %6.2f), dists: [%6.2f %6.2f]\n", objp-Objects, objp->segnum, f2fl(objp->pos.x), f2fl(objp->pos.y), f2fl(objp->pos.z), f2fl(vm_vec_dist_quick(&objp->pos, &segment_center)), f2fl(vm_vec_dist_quick(&objp->pos, &segment_center))));
  1976.     }
  1977.  
  1978. }
  1979.  
  1980. //    -----------------------------------------------------------------------------------------------------------
  1981. //    Return true if door can be flown through by a suitable type robot.
  1982. //    Only brains and avoid robots can open doors.
  1983. int ai_door_is_openable(object *objp, segment *segp, int sidenum)
  1984. {
  1985.     int    wall_num;
  1986.  
  1987.     //    The mighty console object can open all doors (for purposes of determining paths).
  1988.     if (objp == ConsoleObject) {
  1989.         int    wall_num = segp->sides[sidenum].wall_num;
  1990.  
  1991.         if (Walls[wall_num].type == WALL_DOOR)
  1992.             return 1;
  1993.     }
  1994.  
  1995.     if ((objp->id == ROBOT_BRAIN) || (objp->ctype.ai_info.behavior == AIB_RUN_FROM)) {
  1996.         wall_num = segp->sides[sidenum].wall_num;
  1997.  
  1998.         if (wall_num != -1)
  1999.             if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && !(Walls[wall_num].flags & WALL_DOOR_LOCKED))
  2000.                 return 1;
  2001.     }
  2002.  
  2003.     return 0;
  2004. }
  2005.  
  2006. //--//    -----------------------------------------------------------------------------------------------------------
  2007. //--//    Return true if object *objp is allowed to open door at wall_num
  2008. //--int door_openable_by_robot(object *objp, int wall_num)
  2009. //--{
  2010. //--    if (objp->id == ROBOT_BRAIN)
  2011. //--        if (Walls[wall_num].keys == KEY_NONE)
  2012. //--            return 1;
  2013. //--
  2014. //--    return 0;
  2015. //--}
  2016.  
  2017. //    -----------------------------------------------------------------------------------------------------------
  2018. //    Return side of openable door in segment, if any.  If none, return -1.
  2019. int openable_doors_in_segment(object *objp)
  2020. {
  2021.     int    i;
  2022.     int    segnum = objp->segnum;
  2023.  
  2024.     for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
  2025.         if (Segments[segnum].sides[i].wall_num != -1) {
  2026.             int    wall_num = Segments[segnum].sides[i].wall_num;
  2027.             if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && (Walls[wall_num].state == WALL_DOOR_CLOSED) && !(Walls[wall_num].flags & WALL_DOOR_LOCKED))
  2028.                 return i;
  2029.         }
  2030.     }
  2031.  
  2032.     return -1;
  2033.  
  2034. }
  2035.  
  2036. //--unused-- //    -----------------------------------------------------------------------------------------------------------
  2037. //--unused-- //    For all doors this guy can open in his segment, open them.
  2038. //--unused-- void ai_open_doors_in_segment(object *objp)
  2039. //--unused-- {
  2040. //--unused--     int    i;
  2041. //--unused--     int    segnum = objp->segnum;
  2042. //--unused-- 
  2043. //--unused--     for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
  2044. //--unused--         if (Segments[segnum].sides[i].wall_num != -1) {
  2045. //--unused--             int    wall_num = Segments[segnum].sides[i].wall_num;
  2046. //--unused--             if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && (Walls[wall_num].state == WALL_DOOR_CLOSED))
  2047. //--unused--                 if (door_openable_by_robot(objp, wall_num)) {
  2048. //--unused--                     mprintf((0, "Trying to open door at segment %i, side %i\n", segnum, i));
  2049. //--unused--                     wall_open_door(&Segments[segnum], i);
  2050. //--unused--                 }
  2051. //--unused--         }
  2052. //--unused-- }
  2053.  
  2054. // --------------------------------------------------------------------------------------------------------------------
  2055. //    Return true if a special object (player or control center) is in this segment.
  2056. int special_object_in_seg(int segnum)
  2057. {
  2058.     int    objnum;
  2059.  
  2060.     objnum = Segments[segnum].objects;
  2061.  
  2062.     while (objnum != -1) {
  2063.         if ((Objects[objnum].type == OBJ_PLAYER) || (Objects[objnum].type == OBJ_CNTRLCEN)) {
  2064.             mprintf((0, "Special object of type %i in segment %i\n", Objects[objnum].type, segnum));
  2065.             return 1;
  2066.         } else
  2067.             objnum = Objects[objnum].next;
  2068.     }
  2069.  
  2070.     return 0;
  2071. }
  2072.  
  2073. // --------------------------------------------------------------------------------------------------------------------
  2074. //    Randomly select a segment attached to *segp, reachable by flying.
  2075. int get_random_child(int segnum)
  2076. {
  2077.     int    sidenum;
  2078.     segment    *segp = &Segments[segnum];
  2079.  
  2080.     sidenum = (rand() * 6) >> 15;
  2081.  
  2082.     while (!(WALL_IS_DOORWAY(segp, sidenum) & WID_FLY_FLAG))
  2083.         sidenum = (rand() * 6) >> 15;
  2084.  
  2085.     segnum = segp->children[sidenum];
  2086.  
  2087.     return segnum;
  2088. }
  2089.  
  2090. // --------------------------------------------------------------------------------------------------------------------
  2091. //    Return true if placing an object of size size at pos *pos intersects a (player or robot or control center) in segment *segp.
  2092. int check_object_object_intersection(vms_vector *pos, fix size, segment *segp)
  2093. {
  2094.     int        curobjnum;
  2095.  
  2096.     //    If this would intersect with another object (only check those in this segment), then try to move.
  2097.     curobjnum = segp->objects;
  2098.     while (curobjnum != -1) {
  2099.         object *curobjp = &Objects[curobjnum];
  2100.         if ((curobjp->type == OBJ_PLAYER) || (curobjp->type == OBJ_ROBOT) || (curobjp->type == OBJ_CNTRLCEN)) {
  2101.             if (vm_vec_dist_quick(pos, &curobjp->pos) < size + curobjp->size)
  2102.                 return 1;
  2103.         }
  2104.         curobjnum = curobjp->next;
  2105.     }
  2106.  
  2107.     return 0;
  2108.  
  2109. }
  2110.  
  2111. #ifndef SHAREWARE
  2112.  
  2113. // --------------------------------------------------------------------------------------------------------------------
  2114. //    Return true if object created, else return false.
  2115. int create_gated_robot( int segnum, int object_id)
  2116. {
  2117.     int        objnum;
  2118.     object    *objp;
  2119.     segment    *segp = &Segments[segnum];
  2120.     vms_vector    object_pos;
  2121.     robot_info    *robptr = &Robot_info[object_id];
  2122.     int        i, count=0;
  2123.     fix        objsize = Polygon_models[robptr->model_num].rad;
  2124.     int        default_behavior;
  2125.  
  2126.     for (i=0; i<=Highest_object_index; i++)
  2127.         if (Objects[i].type == OBJ_ROBOT)
  2128.             if (Objects[i].matcen_creator == BOSS_GATE_MATCEN_NUM)
  2129.                 count++;
  2130.  
  2131.     if (count > 2*Difficulty_level + 3) {
  2132.         // mprintf((0, "Cannot gate in a robot until you kill one.\n"));
  2133.         Last_gate_time = GameTime - 3*Gate_interval/4;
  2134.         return 0;
  2135.     }
  2136.  
  2137.     compute_segment_center(&object_pos, segp);
  2138.     pick_random_point_in_seg(&object_pos, segp-Segments);
  2139.  
  2140.     //    See if legal to place object here.  If not, move about in segment and try again.
  2141.     if (check_object_object_intersection(&object_pos, objsize, segp)) {
  2142.         // mprintf((0, "Can't get in because object collides with something.\n"));
  2143.         Last_gate_time = GameTime - 3*Gate_interval/4;
  2144.         return 0;
  2145.     }
  2146.  
  2147.     objnum = obj_create(OBJ_ROBOT, object_id, segnum, &object_pos, &vmd_identity_matrix, objsize, CT_AI, MT_PHYSICS, RT_POLYOBJ);
  2148.  
  2149.     if ( objnum < 0 ) {
  2150.         // mprintf((1, "Can't get object to gate in robot.  Not gating in.\n"));
  2151.         Last_gate_time = GameTime - 3*Gate_interval/4;
  2152.         return 0;
  2153.     }
  2154.  
  2155.     mprintf((0, "Gating in object %i in segment %i\n", objnum, segp-Segments));
  2156.  
  2157.     #ifdef NETWORK
  2158.     Net_create_objnums[0] = objnum; // A convenient global to get objnum back to caller for multiplayer
  2159.     #endif
  2160.  
  2161.     objp = &Objects[objnum];
  2162.  
  2163.     //Set polygon-object-specific data
  2164.  
  2165.     objp->rtype.pobj_info.model_num = robptr->model_num;
  2166.     objp->rtype.pobj_info.subobj_flags = 0;
  2167.  
  2168.     //set Physics info
  2169.  
  2170.     objp->mtype.phys_info.mass = robptr->mass;
  2171.     objp->mtype.phys_info.drag = robptr->drag;
  2172.  
  2173.     objp->mtype.phys_info.flags |= (PF_LEVELLING);
  2174.  
  2175.     objp->shields = robptr->strength;
  2176.     objp->matcen_creator = BOSS_GATE_MATCEN_NUM;    //    flag this robot as having been created by the boss.
  2177.  
  2178.     default_behavior = AIB_NORMAL;
  2179.     if (object_id == 10)                        //    This is a toaster guy!
  2180.         default_behavior = AIB_RUN_FROM;
  2181.  
  2182.     init_ai_object(objp-Objects, default_behavior, -1 );        //    Note, -1 = segment this robot goes to to hide, should probably be something useful
  2183.  
  2184.     object_create_explosion(segnum, &object_pos, i2f(10), VCLIP_MORPHING_ROBOT );
  2185.     digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, segnum, 0, &object_pos, 0 , F1_0);
  2186.     morph_start(objp);
  2187.  
  2188.     Last_gate_time = GameTime;
  2189.  
  2190.     Players[Player_num].num_robots_level++;
  2191.     Players[Player_num].num_robots_total++;
  2192.  
  2193.     return 1;
  2194. }
  2195.  
  2196. // --------------------------------------------------------------------------------------------------------------------
  2197. //    Make object objp gate in a robot.
  2198. //    The process of him bringing in a robot takes one second.
  2199. //    Then a robot appears somewhere near the player.
  2200. //    Return true if robot successfully created, else return false
  2201. int gate_in_robot(int type, int segnum)
  2202. {
  2203.     if (segnum < 0)
  2204.         segnum = Boss_gate_segs[(rand() * Num_boss_gate_segs) >> 15];
  2205.  
  2206.     Assert((segnum >= 0) && (segnum <= Highest_segment_index));
  2207.  
  2208.     return create_gated_robot(segnum, type);
  2209. }
  2210.  
  2211. #endif
  2212.  
  2213. //// --------------------------------------------------------------------------------------------------------------------
  2214. ////    Return true if some distance function of vertices implies segment is too small for object.
  2215. //int segment_too_small_for_object(object *objp, segment *segp)
  2216. //{
  2217. //    int    i;
  2218. //    fix    threshold_distance;
  2219. //
  2220. //    threshold_distance = objp->size*2;
  2221. //
  2222. //    for (i=1; i<MAX_VERTICES_PER_SEGMENT; i++)
  2223. //        if (vm_vec_dist_quick(&Vertices[segp->verts[i]], &Vertices[segp->verts[i-1]]) < threshold_distance) {
  2224. //#ifndef NDEBUG
  2225. //            fix dist = vm_vec_dist_quick(&Vertices[segp->verts[i]], &Vertices[segp->verts[i-1]]);
  2226. //#endif
  2227. //            mprintf((0, "Seg %i too small for obj %i (sz=%7.3f), verts %i, %i only %7.3f apart\n", segp-Segments, objp-Objects, f2fl(objp->size), segp->verts[i], segp->verts[i-1], f2fl(dist)));
  2228. //            return 1;
  2229. //        }
  2230. //
  2231. //    return 0;
  2232. //}
  2233.  
  2234. //--unused-- int    Shown_all_segments=0;
  2235.  
  2236. // --------------------------------------------------------------------------------------------------------------------
  2237. int boss_fits_in_seg(object *boss_objp, int segnum)
  2238. {
  2239.     vms_vector    segcenter;
  2240.     int            boss_objnum = boss_objp-Objects;
  2241.     int            posnum;
  2242.  
  2243.     compute_segment_center(&segcenter, &Segments[segnum]);
  2244.  
  2245.     for (posnum=0; posnum<9; posnum++) {
  2246.         if (posnum > 0) {
  2247.             vms_vector    vertex_pos;
  2248.  
  2249.             Assert((posnum-1 >= 0) && (posnum-1 < 8));
  2250.             vertex_pos = Vertices[Segments[segnum].verts[posnum-1]];
  2251.             vm_vec_avg(&boss_objp->pos, &vertex_pos, &segcenter);
  2252.         } else
  2253.             boss_objp->pos = segcenter;
  2254.  
  2255.         obj_relink(boss_objnum, segnum);
  2256.         if (!object_intersects_wall(boss_objp))
  2257.             return 1;
  2258.     }
  2259.  
  2260.     return 0;
  2261. }
  2262.  
  2263. #define    QUEUE_SIZE    256
  2264.  
  2265. // --------------------------------------------------------------------------------------------------------------------
  2266. //    Create list of segments boss is allowed to teleport to at segptr.
  2267. //    Set *num_segs.
  2268. //    Boss is allowed to teleport to segments he fits in (calls object_intersects_wall) and
  2269. //    he can reach from his initial position (calls find_connected_distance).
  2270. //    If size_check is set, then only add segment if boss can fit in it, else any segment is legal.
  2271. void init_boss_segments(short segptr[], int *num_segs, int size_check)
  2272. {
  2273.     int            boss_objnum=-1;
  2274.     int            i;
  2275.  
  2276.     *num_segs = 0;
  2277. #ifdef EDITOR
  2278.     N_selected_segs = 0;
  2279. #endif
  2280.  
  2281.  
  2282.     //    See if there is a boss.  If not, quick out.
  2283.     for (i=0; i<=Highest_object_index; i++)
  2284.         if ((Objects[i].type == OBJ_ROBOT) && (Robot_info[Objects[i].id].boss_flag)) {
  2285.             Assert(boss_objnum == -1);        //    There are two bosses in this mine!  i and boss_objnum!
  2286.             boss_objnum = i;
  2287.         }
  2288.  
  2289.     if (boss_objnum != -1) {
  2290.         int            original_boss_seg;
  2291.         vms_vector    original_boss_pos;
  2292.         object        *boss_objp = &Objects[boss_objnum];
  2293.         int            head, tail;
  2294.         int            seg_queue[QUEUE_SIZE];
  2295. //ALREADY IN RENDER.H        byte            visited[MAX_SEGMENTS];
  2296.         fix            boss_size_save;
  2297.  
  2298.         boss_size_save = boss_objp->size;
  2299.         boss_objp->size = fixmul((F1_0/4)*3, boss_objp->size);
  2300.         original_boss_seg = boss_objp->segnum;
  2301.         original_boss_pos = boss_objp->pos;
  2302.         head = 0;
  2303.         tail = 0;
  2304.         seg_queue[head++] = original_boss_seg;
  2305.  
  2306.         segptr[(*num_segs)++] = original_boss_seg;
  2307.         #ifdef EDITOR
  2308.         Selected_segs[N_selected_segs++] = original_boss_seg;
  2309.         #endif
  2310.  
  2311.         for (i=0; i<=Highest_segment_index; i++)
  2312.             visited[i] = 0;
  2313.  
  2314.         while (tail != head) {
  2315.             int        sidenum;
  2316.             segment    *segp = &Segments[seg_queue[tail++]];
  2317.  
  2318.             tail &= QUEUE_SIZE-1;
  2319.  
  2320.             for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
  2321.                 if (WALL_IS_DOORWAY(segp, sidenum) & WID_FLY_FLAG) {
  2322.                     if (visited[segp->children[sidenum]] == 0) {
  2323.                         seg_queue[head++] = segp->children[sidenum];
  2324.                         visited[segp->children[sidenum]] = 1;
  2325.                         head &= QUEUE_SIZE-1;
  2326.                         if (head > tail) {
  2327.                             if (head == tail + QUEUE_SIZE-1)
  2328.                                 Int3();    //    queue overflow.  Make it bigger!
  2329.                         } else
  2330.                             if (head+QUEUE_SIZE == tail + QUEUE_SIZE-1)
  2331.                                 Int3();    //    queue overflow.  Make it bigger!
  2332.     
  2333.                         if ((!size_check) || boss_fits_in_seg(boss_objp, segp->children[sidenum])) {
  2334.                             segptr[(*num_segs)++] = segp->children[sidenum];
  2335.                             #ifdef EDITOR
  2336.                             Selected_segs[N_selected_segs++] = segp->children[sidenum];
  2337.                             #endif
  2338.                             if (*num_segs >= MAX_BOSS_TELEPORT_SEGS) {
  2339.                                 mprintf((1, "Warning: Too many boss teleport segments.  Found %i after searching %i/%i segments.\n", MAX_BOSS_TELEPORT_SEGS, segp->children[sidenum], Highest_segment_index+1));
  2340.                                 tail = head;
  2341.                             }
  2342.                         }
  2343.                     }
  2344.                 }
  2345.             }
  2346.  
  2347.         }
  2348.  
  2349.         boss_objp->size = boss_size_save;
  2350.         boss_objp->pos = original_boss_pos;
  2351.         obj_relink(boss_objnum, original_boss_seg);
  2352.  
  2353.     }
  2354.  
  2355. }
  2356.  
  2357. // --------------------------------------------------------------------------------------------------------------------
  2358. void teleport_boss(object *objp)
  2359. {
  2360.     int            rand_segnum;
  2361.     vms_vector    boss_dir;
  2362.     int            rand_seg;
  2363.     Assert(Num_boss_teleport_segs > 0);
  2364.  
  2365.     //    Pick a random segment from the list of boss-teleportable-to segments.
  2366.     rand_seg = (rand() * Num_boss_teleport_segs) >> 15;    
  2367.     rand_segnum = Boss_teleport_segs[rand_seg];
  2368.     Assert((rand_segnum >= 0) && (rand_segnum <= Highest_segment_index));
  2369.  
  2370. #ifndef SHAREWARE
  2371. #ifdef NETWORK
  2372.     if (Game_mode & GM_MULTI)
  2373.         multi_send_boss_actions(objp-Objects, 1, rand_seg, 0);
  2374. #endif
  2375. #endif
  2376.  
  2377.     compute_segment_center(&objp->pos, &Segments[rand_segnum]);
  2378.     obj_relink(objp-Objects, rand_segnum);
  2379.  
  2380.     Last_teleport_time = GameTime;
  2381.  
  2382.     //    make boss point right at player
  2383.     vm_vec_sub(&boss_dir, &Objects[Players[Player_num].objnum].pos, &objp->pos);
  2384.     vm_vector_2_matrix(&objp->orient, &boss_dir, NULL, NULL);
  2385.  
  2386.     digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, rand_segnum, 0, &objp->pos, 0 , F1_0);
  2387.     digi_kill_sound_linked_to_object( objp-Objects);
  2388.     digi_link_sound_to_object2( SOUND_BOSS_SHARE_SEE, objp-Objects, 1, F1_0, F1_0*512 );    //    F1_0*512 means play twice as loud
  2389.     #ifndef NDEBUG
  2390.     mprintf((0, "Boss teleported to segment %i\n", rand_segnum));
  2391.     #endif
  2392.  
  2393.     //    After a teleport, boss can fire right away.
  2394.     Ai_local_info[objp-Objects].next_fire = 0;
  2395.  
  2396. }
  2397.  
  2398. //    ----------------------------------------------------------------------
  2399. void start_boss_death_sequence(object *objp)
  2400. {
  2401.     if (Robot_info[objp->id].boss_flag) {
  2402.         Boss_dying = 1;
  2403.         Boss_dying_start_time = GameTime;
  2404.     }
  2405.  
  2406. }
  2407.  
  2408. //    ----------------------------------------------------------------------
  2409. void do_boss_dying_frame(object *objp)
  2410. {
  2411.     fix    boss_roll_val, temp;
  2412.  
  2413.     boss_roll_val = fixdiv(GameTime - Boss_dying_start_time, BOSS_DEATH_DURATION);
  2414.  
  2415.     fix_sincos(fixmul(boss_roll_val, boss_roll_val), &temp, &objp->mtype.phys_info.rotvel.x);
  2416.     fix_sincos(boss_roll_val, &temp, &objp->mtype.phys_info.rotvel.y);
  2417.     fix_sincos(boss_roll_val-F1_0/8, &temp, &objp->mtype.phys_info.rotvel.z);
  2418.  
  2419.     objp->mtype.phys_info.rotvel.x = (GameTime - Boss_dying_start_time)/9;
  2420.     objp->mtype.phys_info.rotvel.y = (GameTime - Boss_dying_start_time)/5;
  2421.     objp->mtype.phys_info.rotvel.z = (GameTime - Boss_dying_start_time)/7;
  2422.  
  2423.     if (Boss_dying_start_time + BOSS_DEATH_DURATION - BOSS_DEATH_SOUND_DURATION < GameTime) {
  2424.         if (!Boss_dying_sound_playing) {
  2425.             mprintf((0, "Starting boss death sound!\n"));
  2426.             Boss_dying_sound_playing = 1;
  2427.             digi_link_sound_to_object2( SOUND_BOSS_SHARE_DIE, objp-Objects, 0, F1_0*4, F1_0*1024 );    //    F1_0*512 means play twice as loud
  2428.         } else if (rand() < FrameTime*16)
  2429.             create_small_fireball_on_object(objp, (F1_0 + rand()) * 8, 0);
  2430.     } else if (rand() < FrameTime*8)
  2431.         create_small_fireball_on_object(objp, (F1_0/2 + rand()) * 8, 1);
  2432.  
  2433.     if (Boss_dying_start_time + BOSS_DEATH_DURATION < GameTime) {
  2434.         do_controlcen_destroyed_stuff(NULL);
  2435.         explode_object(objp, F1_0/4);
  2436.         digi_link_sound_to_object2(SOUND_BADASS_EXPLOSION, objp-Objects, 0, F2_0, F1_0*512);
  2437.     }
  2438. }
  2439.  
  2440. #ifndef SHAREWARE 
  2441. #ifdef NETWORK
  2442. // --------------------------------------------------------------------------------------------------------------------
  2443. //    Called for an AI object if it is fairly aware of the player.
  2444. //    awareness_level is in 0..100.  Larger numbers indicate greater awareness (eg, 99 if firing at player).
  2445. //    In a given frame, might not get called for an object, or might be called more than once.
  2446. //    The fact that this routine is not called for a given object does not mean that object is not interested in the player.
  2447. //    Objects are moved by physics, so they can move even if not interested in a player.  However, if their velocity or
  2448. //    orientation is changing, this routine will be called.
  2449. //    Return value:
  2450. //        0    this player IS NOT allowed to move this robot.
  2451. //        1    this player IS allowed to move this robot.
  2452. int ai_multiplayer_awareness(object *objp, int awareness_level)
  2453. {
  2454.     int    rval=1;
  2455.  
  2456. #ifndef SHAREWARE
  2457.     if (Game_mode & GM_MULTI) {
  2458.         if (awareness_level == 0)
  2459.             return 0;
  2460.         rval = multi_can_move_robot(objp-Objects, awareness_level);
  2461.     }
  2462. #endif
  2463.  
  2464.     return rval;
  2465.  
  2466. }
  2467. #else
  2468. #define ai_multiplayer_awareness(a, b) 1
  2469. #endif
  2470. #else
  2471. #define ai_multiplayer_awareness(a, b) 1
  2472. #endif
  2473.  
  2474. #ifndef NDEBUG
  2475. fix    Prev_boss_shields = -1;
  2476. #endif
  2477.  
  2478. // --------------------------------------------------------------------------------------------------------------------
  2479. //    Do special stuff for a boss.
  2480. void do_boss_stuff(object *objp)
  2481. {
  2482.     //  New code, fixes stupid bug which meant boss never gated in robots if > 32767 seconds played.
  2483.     if (Last_teleport_time > GameTime)
  2484.         Last_teleport_time = GameTime;
  2485.  
  2486.     if (Last_gate_time > GameTime)
  2487.         Last_gate_time = GameTime;
  2488.  
  2489. #ifndef NDEBUG
  2490.     if (objp->shields != Prev_boss_shields) {
  2491.         mprintf((0, "Boss shields = %7.3f, object %i\n", f2fl(objp->shields), objp-Objects));
  2492.         Prev_boss_shields = objp->shields;
  2493.     }
  2494. #endif
  2495.  
  2496.     if (!Boss_dying) {
  2497.         if (objp->ctype.ai_info.CLOAKED == 1) {
  2498.             if ((GameTime - Boss_cloak_start_time > BOSS_CLOAK_DURATION/3) && (Boss_cloak_end_time - GameTime > BOSS_CLOAK_DURATION/3) && (GameTime - Last_teleport_time > Boss_teleport_interval)) {
  2499.                 if (ai_multiplayer_awareness(objp, 98))
  2500.                     teleport_boss(objp);
  2501.             } else if (Boss_hit_this_frame) {
  2502.                 Boss_hit_this_frame = 0;
  2503.                 Last_teleport_time -= Boss_teleport_interval/4;
  2504.             }
  2505.  
  2506.             if (GameTime > Boss_cloak_end_time)
  2507.                 objp->ctype.ai_info.CLOAKED = 0;
  2508.         } else {
  2509.             if ((GameTime - Boss_cloak_end_time > Boss_cloak_interval) || Boss_hit_this_frame) {
  2510.                 if (ai_multiplayer_awareness(objp, 95))
  2511.                 {
  2512.                     Boss_hit_this_frame = 0;
  2513.                     Boss_cloak_start_time = GameTime;
  2514.                     Boss_cloak_end_time = GameTime+Boss_cloak_duration;
  2515.                     objp->ctype.ai_info.CLOAKED = 1;
  2516. #ifndef SHAREWARE
  2517. #ifdef NETWORK
  2518.                     if (Game_mode & GM_MULTI)
  2519.                         multi_send_boss_actions(objp-Objects, 2, 0, 0);
  2520. #endif
  2521. #endif
  2522.                 }
  2523.             }
  2524.         }
  2525.     } else
  2526.         do_boss_dying_frame(objp);
  2527.  
  2528. }
  2529.  
  2530. #define    BOSS_TO_PLAYER_GATE_DISTANCE    (F1_0*150)
  2531.  
  2532. #ifndef SHAREWARE
  2533.  
  2534. // --------------------------------------------------------------------------------------------------------------------
  2535. //    Do special stuff for a boss.
  2536. void do_super_boss_stuff(object *objp, fix dist_to_player, int player_visibility)
  2537. {
  2538.     static int eclip_state = 0;
  2539.     do_boss_stuff(objp);
  2540.  
  2541.     // Only master player can cause gating to occur.
  2542.     #ifdef NETWORK
  2543.     if ((Game_mode & GM_MULTI) && !network_i_am_master())
  2544.         return; 
  2545.     #endif
  2546.  
  2547.     if ((dist_to_player < BOSS_TO_PLAYER_GATE_DISTANCE) || player_visibility || (Game_mode & GM_MULTI)) {
  2548.         if (GameTime - Last_gate_time > Gate_interval/2) {
  2549.             restart_effect(BOSS_ECLIP_NUM);
  2550. #ifndef SHAREWARE
  2551. #ifdef NETWORK
  2552.             if (eclip_state == 0) {
  2553.                 multi_send_boss_actions(objp-Objects, 4, 0, 0);
  2554.                 eclip_state = 1;
  2555.             }
  2556. #endif
  2557. #endif
  2558.         }
  2559.         else {
  2560.             stop_effect(BOSS_ECLIP_NUM);
  2561. #ifndef SHAREWARE
  2562. #ifdef NETWORK
  2563.             if (eclip_state == 1) {
  2564.                 multi_send_boss_actions(objp-Objects, 5, 0, 0);
  2565.                 eclip_state = 0;
  2566.             }
  2567. #endif
  2568. #endif
  2569.         }
  2570.  
  2571.         if (GameTime - Last_gate_time > Gate_interval)
  2572.             if (ai_multiplayer_awareness(objp, 99)) {
  2573.                 int    rtval;
  2574.                 int    randtype = (rand() * MAX_GATE_INDEX) >> 15;
  2575.  
  2576.                 Assert(randtype < MAX_GATE_INDEX);
  2577.                 randtype = Super_boss_gate_list[randtype];
  2578.                 Assert(randtype < N_robot_types);
  2579.  
  2580.                 rtval = gate_in_robot(randtype, -1);
  2581. #ifndef SHAREWARE
  2582. #ifdef NETWORK
  2583.                 if (rtval && (Game_mode & GM_MULTI))
  2584.                 {
  2585.                     multi_send_boss_actions(objp-Objects, 3, randtype, Net_create_objnums[0]);
  2586.                     map_objnum_local_to_local(Net_create_objnums[0]);
  2587.                 }
  2588. #endif
  2589. #endif
  2590.             }    
  2591.     }
  2592. }
  2593.  
  2594. #endif
  2595.  
  2596. //int multi_can_move_robot(object *objp, int awareness_level)
  2597. //{
  2598. //    return 0;
  2599. //}
  2600.  
  2601. #ifndef SHAREWARE
  2602. void ai_multi_send_robot_position(int objnum, int force)
  2603. {
  2604. #ifndef SHAREWARE
  2605. #ifdef NETWORK
  2606.     if (Game_mode & GM_MULTI) 
  2607.     {
  2608.         if (force != -1)
  2609.             multi_send_robot_position(objnum, 1);
  2610.         else
  2611.             multi_send_robot_position(objnum, 0);
  2612.     }
  2613. #endif
  2614. #endif
  2615.     return;
  2616. }
  2617. #else
  2618. #define ai_multi_send_robot_position(a, b)
  2619. #endif
  2620.  
  2621. // --------------------------------------------------------------------------------------------------------------------
  2622. //    Returns true if this object should be allowed to fire at the player.
  2623. int maybe_ai_do_actual_firing_stuff(object *obj, ai_static *aip)
  2624. {
  2625.     if (Game_mode & GM_MULTI)
  2626.         if ((aip->GOAL_STATE != AIS_FLIN) && (obj->id != ROBOT_BRAIN))
  2627.             if (aip->CURRENT_STATE == AIS_FIRE)
  2628.                 return 1;
  2629.  
  2630.     return 0;
  2631. }
  2632.  
  2633. // --------------------------------------------------------------------------------------------------------------------
  2634. void ai_do_actual_firing_stuff(object *obj, ai_static *aip, ai_local *ailp, robot_info *robptr, vms_vector *vec_to_player, fix dist_to_player, vms_vector *gun_point, int player_visibility, int object_animates)
  2635. {
  2636.     fix    dot;
  2637.  
  2638.     if (player_visibility == 2) {
  2639.         //    Changed by mk, 01/04/94, onearm would take about 9 seconds until he can fire at you.
  2640.         // if (((!object_animates) || (ailp->achieved_state[aip->CURRENT_GUN] == AIS_FIRE)) && (ailp->next_fire <= 0)) {
  2641.         if (!object_animates || (ailp->next_fire <= 0)) {
  2642.             dot = vm_vec_dot(&obj->orient.fvec, vec_to_player);
  2643.             if (dot >= 7*F1_0/8) {
  2644.  
  2645.                 if (aip->CURRENT_GUN < Robot_info[obj->id].n_guns) {
  2646.                     if (robptr->attack_type == 1) {
  2647.                         if (!Player_exploded && (dist_to_player < obj->size + ConsoleObject->size + F1_0*2)) {        // robptr->circle_distance[Difficulty_level] + ConsoleObject->size) {
  2648.                             if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION-2))
  2649.                                 return;
  2650.                             do_ai_robot_hit_attack(obj, ConsoleObject, &obj->pos);
  2651.                         } else {
  2652.                             // mprintf((0, "Green won't fire: Too far: dist = %7.3f, threshold = %7.3f\n", f2fl(dist_to_player), f2fl(obj->size + ConsoleObject->size + F1_0*2)));
  2653.                             return;
  2654.                         }
  2655.                     } else {
  2656.                         if ((gun_point->x == 0) && (gun_point->y == 0) && (gun_point->z == 0)) {
  2657.                             ; //mprintf((0, "Would like to fire gun, but gun not selected.\n"));
  2658.                         } else {
  2659.                             if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION))
  2660.                                 return;
  2661.                             ai_fire_laser_at_player(obj, gun_point);
  2662.                         }
  2663.                     }
  2664.  
  2665.                     //    Wants to fire, so should go into chase mode, probably.
  2666.                     if ( (aip->behavior != AIB_RUN_FROM) && (aip->behavior != AIB_STILL) && (aip->behavior != AIB_FOLLOW_PATH) && ((ailp->mode == AIM_FOLLOW_PATH) || (ailp->mode == AIM_STILL)))
  2667.                         ailp->mode = AIM_CHASE_OBJECT;
  2668.                 }
  2669.  
  2670.                 aip->GOAL_STATE = AIS_RECO;
  2671.                 ailp->goal_state[aip->CURRENT_GUN] = AIS_RECO;
  2672.  
  2673.                 // Switch to next gun for next fire.
  2674.                 aip->CURRENT_GUN++;
  2675.                 if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  2676.                     aip->CURRENT_GUN = 0;
  2677.             }
  2678.         }
  2679.     } else if (Weapon_info[Robot_info[obj->id].weapon_type].homing_flag == 1) {
  2680.         //    Robots which fire homing weapons might fire even if they don't have a bead on the player.
  2681.         if (((!object_animates) || (ailp->achieved_state[aip->CURRENT_GUN] == AIS_FIRE)) && (ailp->next_fire <= 0) && (vm_vec_dist_quick(&Hit_pos, &obj->pos) > F1_0*40)) {
  2682.             if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION))
  2683.                 return;
  2684.             ai_fire_laser_at_player(obj, gun_point);
  2685.  
  2686.             aip->GOAL_STATE = AIS_RECO;
  2687.             ailp->goal_state[aip->CURRENT_GUN] = AIS_RECO;
  2688.  
  2689.             // Switch to next gun for next fire.
  2690.             aip->CURRENT_GUN++;
  2691.             if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  2692.                 aip->CURRENT_GUN = 0;
  2693.         } else {
  2694.             // Switch to next gun for next fire.
  2695.             aip->CURRENT_GUN++;
  2696.             if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  2697.                 aip->CURRENT_GUN = 0;
  2698.         }
  2699.     }
  2700. }
  2701.  
  2702. // --------------------------------------------------------------------------------------------------------------------
  2703. void do_ai_frame(object *obj)
  2704. {
  2705.     int            objnum = obj-Objects;
  2706.     ai_static    *aip = &obj->ctype.ai_info;
  2707.     ai_local        *ailp = &Ai_local_info[objnum];
  2708.     fix            dist_to_player;
  2709.     vms_vector    vec_to_player;
  2710.     fix            dot;
  2711.     robot_info    *robptr;
  2712.     int            player_visibility=-1;
  2713.     int            obj_ref;
  2714.     int            object_animates;
  2715.     int            new_goal_state;
  2716.     int            visibility_and_vec_computed = 0;
  2717.     int            previous_visibility;
  2718.     vms_vector    gun_point;
  2719.     vms_vector    vis_vec_pos;
  2720.  
  2721.     if (aip->SKIP_AI_COUNT) {
  2722.         aip->SKIP_AI_COUNT--;
  2723.         return;
  2724.     }
  2725.  
  2726.     //    Kind of a hack.  If a robot is flinching, but it is time for it to fire, unflinch it.
  2727.     //    Else, you can turn a big nasty robot into a wimp by firing flares at it.
  2728.     //    This also allows the player to see the cool flinch effect for mechs without unbalancing the game.
  2729.     if ((aip->GOAL_STATE == AIS_FLIN) && (ailp->next_fire < 0)) {
  2730.         aip->GOAL_STATE = AIS_FIRE;
  2731.     }
  2732.  
  2733. #ifndef NDEBUG
  2734.     if ((aip->behavior == AIB_RUN_FROM) && (ailp->mode != AIM_RUN_FROM_OBJECT))
  2735.         Int3();    //    This is peculiar.  Behavior is run from, but mode is not.  Contact Mike.
  2736.  
  2737.     mprintf_animation_info((obj));
  2738.  
  2739.     if (Ai_animation_test) {
  2740.         if (aip->GOAL_STATE == aip->CURRENT_STATE) {
  2741.             aip->GOAL_STATE++;
  2742.             if (aip->GOAL_STATE > AIS_RECO)
  2743.                 aip->GOAL_STATE = AIS_REST;
  2744.         }
  2745.  
  2746.         mprintf((0, "Frame %4i, current = %i, goal = %i\n", FrameCount, aip->CURRENT_STATE, aip->GOAL_STATE));
  2747.         object_animates = do_silly_animation(obj);
  2748.         if (object_animates)
  2749.             ai_frame_animation(obj);
  2750.         return;
  2751.     }
  2752.  
  2753.     if (!Do_ai_flag)
  2754.         return;
  2755.  
  2756.     if (Break_on_object != -1)
  2757.         if ((obj-Objects) == Break_on_object)
  2758.             Int3();    //    Contact Mike: This is a debug break
  2759. #endif
  2760.  
  2761.     Believed_player_pos = Ai_cloak_info[objnum & (MAX_AI_CLOAK_INFO-1)].last_position;
  2762.  
  2763.     // 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)));
  2764.     // 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));
  2765.  
  2766. //    Assert((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR));
  2767.     if (!((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR))) {
  2768.         // mprintf((0, "Object %i behavior is %i, setting to AIB_NORMAL, fix in editor!\n", objnum, aip->behavior));
  2769.         aip->behavior = AIB_NORMAL;
  2770.     }
  2771.  
  2772.     Assert(obj->segnum != -1);
  2773.     Assert(obj->id < N_robot_types);
  2774.  
  2775.     robptr = &Robot_info[obj->id];
  2776.     Assert(robptr->always_0xabcd == 0xabcd);
  2777.     obj_ref = objnum ^ FrameCount;
  2778.     // -- if (ailp->wait_time > -F1_0*8)
  2779.     // --     ailp->wait_time -= FrameTime;
  2780.     if (ailp->next_fire > -F1_0*8)
  2781.         ailp->next_fire -= FrameTime;
  2782.     if (ailp->time_since_processed < F1_0*256)
  2783.         ailp->time_since_processed += FrameTime;
  2784.     previous_visibility = ailp->previous_visibility;    //    Must get this before we toast the master copy!
  2785.  
  2786.     //    Deal with cloaking for robots which are cloaked except just before firing.
  2787.     if (robptr->cloak_type == RI_CLOAKED_EXCEPT_FIRING)
  2788.         if (ailp->next_fire < F1_0/2)
  2789.             aip->CLOAKED = 1;
  2790.         else
  2791.             aip->CLOAKED = 0;
  2792.  
  2793.     if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
  2794.         Believed_player_pos = ConsoleObject->pos;
  2795.  
  2796.     dist_to_player = vm_vec_dist_quick(&Believed_player_pos, &obj->pos);
  2797.  
  2798. //--!--     mprintf((0, "%2i: %s, [vel = %5.1f %5.1f %5.1f] spd = %4.1f dtp=%5.1f ", objnum, mode_text[ailp->mode], f2fl(obj->mtype.phys_info.velocity.x), f2fl(obj->mtype.phys_info.velocity.y), f2fl(obj->mtype.phys_info.velocity.z), f2fl(vm_vec_mag(&obj->mtype.phys_info.velocity)), f2fl(dist_to_player)));
  2799. //--!--     if (ailp->mode == AIM_FOLLOW_PATH) {
  2800. //--!--         mprintf((0, "gseg = %i\n", Point_segs[aip->hide_index+aip->cur_path_index].segnum));
  2801. //--!--     } else
  2802. //--!--         mprintf((0, "\n"));
  2803.  
  2804.     //    If this robot can fire, compute visibility from gun position.
  2805.     //    Don't want to compute visibility twice, as it is expensive.  (So is call to calc_gun_point).
  2806.     if ((ailp->next_fire <= 0) && (dist_to_player < F1_0*200) && (robptr->n_guns) && !(robptr->attack_type)) {
  2807.         calc_gun_point(&gun_point, obj, aip->CURRENT_GUN);
  2808.         vis_vec_pos = gun_point;
  2809.         // mprintf((0, "Visibility = %i, computed from gun #%i\n", player_visibility, aip->CURRENT_GUN));
  2810.     } else {
  2811.         vis_vec_pos = obj->pos;
  2812.         vm_vec_zero(&gun_point);
  2813.         // mprintf((0, "Visibility = %i, computed from center.\n", player_visibility));
  2814.     }
  2815.  
  2816.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2817.     //    Occasionally make non-still robots make a path to the player.  Based on agitation and distance from player.
  2818.     if ((aip->behavior != AIB_RUN_FROM) && (aip->behavior != AIB_STILL) && !(Game_mode & GM_MULTI))
  2819.         if (Overall_agitation > 70) {
  2820.             if ((dist_to_player < F1_0*200) && (rand() < FrameTime/4)) {
  2821.                 if (rand() * (Overall_agitation - 40) > F1_0*5) {
  2822.                     // -- mprintf((0, "(1) Object #%i going from still to path in frame %i.\n", objnum, FrameCount));
  2823.                     create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
  2824.                     // -- show_path_and_other(obj);
  2825.                     return;
  2826.                 }
  2827.             }
  2828.         }
  2829.  
  2830.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2831.     //    If retry count not 0, then add it into consecutive_retries.
  2832.     //    If it is 0, cut down consecutive_retries.
  2833.     //    This is largely a hack to speed up physics and deal with stupid AI.  This is low level
  2834.     //    communication between systems of a sort that should not be done.
  2835.     if ((ailp->retry_count) && !(Game_mode & GM_MULTI)) {
  2836.         ailp->consecutive_retries += ailp->retry_count;
  2837.         ailp->retry_count = 0;
  2838.         if (ailp->consecutive_retries > 3) {
  2839.             switch (ailp->mode) {
  2840.                 case AIM_CHASE_OBJECT:
  2841.                     // -- mprintf((0, "(2) Object #%i, retries while chasing, creating path to player in frame %i\n", objnum, FrameCount));
  2842.                     create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
  2843.                     break;
  2844.                 case AIM_STILL:
  2845.                     if (!((aip->behavior == AIB_STILL) || (aip->behavior == AIB_STATION)))    //    Behavior is still, so don't follow path.
  2846.                         attempt_to_resume_path(obj);
  2847.                     break;    
  2848.                 case AIM_FOLLOW_PATH:
  2849.                         // mprintf((0, "Object %i following path got %i retries in frame %i\n", obj-Objects, ailp->consecutive_retries, FrameCount));
  2850.                     if (Game_mode & GM_MULTI)
  2851.                         ailp->mode = AIM_STILL;
  2852.                     else
  2853.                         attempt_to_resume_path(obj);
  2854.                     break;
  2855.                 case AIM_RUN_FROM_OBJECT:
  2856.                     move_towards_segment_center(obj);
  2857.                     obj->mtype.phys_info.velocity.x = 0;
  2858.                     obj->mtype.phys_info.velocity.y = 0;
  2859.                     obj->mtype.phys_info.velocity.z = 0;
  2860.                     create_n_segment_path(obj, 5, -1);
  2861.                     ailp->mode = AIM_RUN_FROM_OBJECT;
  2862.                     break;
  2863.                 case AIM_HIDE:
  2864.                     move_towards_segment_center(obj);
  2865.                     obj->mtype.phys_info.velocity.x = 0;
  2866.                     obj->mtype.phys_info.velocity.y = 0;
  2867.                     obj->mtype.phys_info.velocity.z = 0;
  2868.                     // -- mprintf((0, "Hiding, yet creating path to player.\n"));
  2869.                     if (Overall_agitation > (50 - Difficulty_level*4))
  2870.                         create_path_to_player(obj, 4 + Overall_agitation/8, 1);
  2871.                     else {
  2872.                         create_n_segment_path(obj, 5, -1);
  2873.                     }
  2874.                     break;
  2875.                 case AIM_OPEN_DOOR:
  2876.                     create_n_segment_path_to_door(obj, 5, -1);
  2877.                     break;
  2878.                 #ifndef NDEBUG
  2879.                 case AIM_FOLLOW_PATH_2:
  2880.                     Int3();    //    Should never happen!
  2881.                     break;
  2882.                 #endif
  2883.             }
  2884.             ailp->consecutive_retries = 0;
  2885.         }
  2886.     } else
  2887.         ailp->consecutive_retries /= 2;
  2888.  
  2889.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2890.      //    If in materialization center, exit
  2891.      if (!(Game_mode & GM_MULTI) && (Segments[obj->segnum].special == SEGMENT_IS_ROBOTMAKER)) {
  2892.          ai_follow_path(obj, 1);        // 1 = player is visible, which might be a lie, but it works.
  2893.          return;
  2894.      }
  2895.  
  2896.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2897.     //    Decrease player awareness due to the passage of time.
  2898. //-----old, faster way from about 11/06/94-----    if (ailp->player_awareness_type) {
  2899. //-----old, faster way from about 11/06/94-----        if (ailp->player_awareness_time > 0) {
  2900. //-----old, faster way from about 11/06/94-----            ailp->player_awareness_time -= FrameTime;
  2901. //-----old, faster way from about 11/06/94-----            if (ailp->player_awareness_time <= 0) {
  2902. //-----old, faster way from about 11/06/94-----                 ailp->player_awareness_time = F1_0*2;
  2903. //-----old, faster way from about 11/06/94-----                 ailp->player_awareness_type--;
  2904. //-----old, faster way from about 11/06/94-----                if (ailp->player_awareness_type < 0) {
  2905. //-----old, faster way from about 11/06/94-----                     aip->GOAL_STATE = AIS_REST;
  2906. //-----old, faster way from about 11/06/94-----                     ailp->player_awareness_type = 0;
  2907. //-----old, faster way from about 11/06/94-----                }
  2908. //-----old, faster way from about 11/06/94-----
  2909. //-----old, faster way from about 11/06/94-----            }
  2910. //-----old, faster way from about 11/06/94-----        } else {
  2911. //-----old, faster way from about 11/06/94-----             ailp->player_awareness_type = 0;
  2912. //-----old, faster way from about 11/06/94-----             aip->GOAL_STATE = AIS_REST;
  2913. //-----old, faster way from about 11/06/94-----        }
  2914. //-----old, faster way from about 11/06/94-----    } else
  2915. //-----old, faster way from about 11/06/94-----        aip->GOAL_STATE = AIS_REST;
  2916.  
  2917.  
  2918.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2919.     //    Decrease player awareness due to the passage of time.
  2920.     if (ailp->player_awareness_type) {
  2921.         if (ailp->player_awareness_time > 0) {
  2922.             ailp->player_awareness_time -= FrameTime;
  2923.             if (ailp->player_awareness_time <= 0) {
  2924.                  ailp->player_awareness_time = F1_0*2;    //new: 11/05/94
  2925.                  ailp->player_awareness_type--;    //new: 11/05/94
  2926.             }
  2927.         } else {
  2928.             ailp->player_awareness_type--;
  2929.             ailp->player_awareness_time = F1_0*2;
  2930.             // aip->GOAL_STATE = AIS_REST;
  2931.         }
  2932.     } else
  2933.         aip->GOAL_STATE = AIS_REST;                            //new: 12/13/94
  2934.  
  2935.  
  2936.     if (Player_is_dead && (ailp->player_awareness_type == 0))
  2937.         if ((dist_to_player < F1_0*200) && (rand() < FrameTime/8)) {
  2938.             if ((aip->behavior != AIB_STILL) && (aip->behavior != AIB_RUN_FROM)) {
  2939.                 if (!ai_multiplayer_awareness(obj, 30))
  2940.                     return;
  2941.                 ai_multi_send_robot_position(objnum, -1);
  2942.  
  2943.                 if (!((ailp->mode == AIM_FOLLOW_PATH) && (aip->cur_path_index < aip->path_length-1)))
  2944.                     if (dist_to_player < F1_0*30)
  2945.                         create_n_segment_path(obj, 5, 1);
  2946.                     else
  2947.                         create_path_to_player(obj, 20, 1);
  2948.             }
  2949.         }
  2950.  
  2951.     //    Make sure that if this guy got hit or bumped, then he's chasing player.
  2952.     if ((ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) || (ailp->player_awareness_type >= PA_PLAYER_COLLISION)) {
  2953.         if ((aip->behavior != AIB_STILL) && (aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM) && (obj->id != ROBOT_BRAIN))
  2954.             ailp->mode = AIM_CHASE_OBJECT;
  2955.     }
  2956.  
  2957.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2958.     if ((aip->GOAL_STATE == AIS_FLIN) && (aip->CURRENT_STATE == AIS_FLIN))
  2959.         aip->GOAL_STATE = AIS_LOCK;
  2960.  
  2961.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2962.     //    Note: Should only do these two function calls for objects which animate
  2963.     if ((dist_to_player < F1_0*100)) { // && !(Game_mode & GM_MULTI)) {
  2964.         object_animates = do_silly_animation(obj);
  2965.         if (object_animates)
  2966.             ai_frame_animation(obj);
  2967.         //mprintf((0, "Object %i: goal=%i, current=%i\n", obj-Objects, obj->ctype.ai_info.GOAL_STATE, obj->ctype.ai_info.CURRENT_STATE));
  2968.     } else {
  2969.         //    If Object is supposed to animate, but we don't let it animate due to distance, then
  2970.         //    we must change its state, else it will never update.
  2971.         aip->CURRENT_STATE = aip->GOAL_STATE;
  2972.         object_animates = 0;        //    If we're not doing the animation, then should pretend it doesn't animate.
  2973.     }
  2974.  
  2975. //Processed_this_frame++;
  2976. //if (FrameCount != LastFrameCount) {
  2977. //    LastFrameCount = FrameCount;
  2978. //    mprintf((0, "Processed in frame %i = %i robots\n", FrameCount-1, Processed_this_frame));
  2979. //    Processed_this_frame = 0;
  2980. //}
  2981.  
  2982.     switch (Robot_info[obj->id].boss_flag) {
  2983.         case 0:
  2984.             break;
  2985.         case 1:
  2986.             if (aip->GOAL_STATE == AIS_FLIN)
  2987.                 aip->GOAL_STATE = AIS_FIRE;
  2988.             if (aip->CURRENT_STATE == AIS_FLIN)
  2989.                 aip->CURRENT_STATE = AIS_FIRE;
  2990.             dist_to_player /= 4;
  2991.  
  2992.             do_boss_stuff(obj);
  2993.             dist_to_player *= 4;
  2994.             break;
  2995. #ifndef SHAREWARE
  2996.         case 2:
  2997.             if (aip->GOAL_STATE == AIS_FLIN)
  2998.                 aip->GOAL_STATE = AIS_FIRE;
  2999.             if (aip->CURRENT_STATE == AIS_FLIN)
  3000.                 aip->CURRENT_STATE = AIS_FIRE;
  3001.             compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3002.  
  3003.             {    int pv = player_visibility;
  3004.                 fix    dtp = dist_to_player/4;
  3005.  
  3006.             //    If player cloaked, visibility is screwed up and superboss will gate in robots when not supposed to.
  3007.             if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
  3008.                 pv = 0;
  3009.                 dtp = vm_vec_dist_quick(&ConsoleObject->pos, &obj->pos)/4;
  3010.             }
  3011.  
  3012.             do_super_boss_stuff(obj, dtp, pv);
  3013.             }
  3014.             break;
  3015. #endif
  3016.         default:
  3017.             Int3();    //    Bogus boss flag value.
  3018.     }
  3019.  
  3020.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3021.     //    Time-slice, don't process all the time, purely an efficiency hack.
  3022.     //    Guys whose behavior is station and are not at their hide segment get processed anyway.
  3023.     if (ailp->player_awareness_type < PA_WEAPON_ROBOT_COLLISION-1) { // If robot got hit, he gets to attack player always!
  3024.         #ifndef NDEBUG
  3025.         if (Break_on_object != objnum) {    //    don't time slice if we're interested in this object.
  3026.         #endif
  3027.             if ((dist_to_player > F1_0*250) && (ailp->time_since_processed <= F1_0*2))
  3028.                 return;
  3029.             else if (!((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum))) {
  3030.                 if ((dist_to_player > F1_0*150) && (ailp->time_since_processed <= F1_0))
  3031.                     return;
  3032.                 else if ((dist_to_player > F1_0*100) && (ailp->time_since_processed <= F1_0/2))
  3033.                     return;
  3034.             }
  3035.         #ifndef NDEBUG
  3036.         }
  3037.         #endif
  3038.     }
  3039.  
  3040. // --     if ((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum))
  3041. // --         mprintf((0, "[%i] ", obj-Objects));
  3042.  
  3043.     //    Reset time since processed, but skew objects so not everything processed synchronously, else
  3044.     //    we get fast frames with the occasional very slow frame.
  3045.     // AI_proc_time = ailp->time_since_processed;
  3046.     ailp->time_since_processed = - ((objnum & 0x03) * FrameTime ) / 2;
  3047.  
  3048.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3049.     //    Perform special ability
  3050.     switch (obj->id) {
  3051.         case ROBOT_BRAIN:
  3052.             //    Robots function nicely if behavior is Station.  This means they won't move until they
  3053.             //    can see the player, at which time they will start wandering about opening doors.
  3054.             if (ConsoleObject->segnum == obj->segnum) {
  3055.                 if (!ai_multiplayer_awareness(obj, 97))
  3056.                     return;
  3057.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3058.                 move_away_from_player(obj, &vec_to_player, 0);
  3059.                 ai_multi_send_robot_position(objnum, -1);
  3060.             } else if (ailp->mode != AIM_STILL) {
  3061.                 int    r;
  3062.  
  3063.                 r = openable_doors_in_segment(obj);
  3064.                 if (r != -1) {
  3065.                     ailp->mode = AIM_OPEN_DOOR;
  3066.                     aip->GOALSIDE = r;
  3067.                 } else if (ailp->mode != AIM_FOLLOW_PATH) {
  3068.                     if (!ai_multiplayer_awareness(obj, 50))
  3069.                         return;
  3070.                     create_n_segment_path_to_door(obj, 8+Difficulty_level, -1);        //    third parameter is avoid_seg, -1 means avoid nothing.
  3071.                     ai_multi_send_robot_position(objnum, -1);
  3072.                 }
  3073.             } else {
  3074.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3075.                 if (player_visibility) {
  3076.                     if (!ai_multiplayer_awareness(obj, 50))
  3077.                         return;
  3078.                     create_n_segment_path_to_door(obj, 8+Difficulty_level, -1);        //    third parameter is avoid_seg, -1 means avoid nothing.
  3079.                     ai_multi_send_robot_position(objnum, -1);
  3080.                 }
  3081.             }
  3082.             break;
  3083.         default:
  3084.             break;
  3085.     }
  3086.  
  3087.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3088.     switch (ailp->mode) {
  3089.         case AIM_CHASE_OBJECT: {        // chasing player, sort of, chase if far, back off if close, circle in between
  3090.             fix    circle_distance;
  3091.  
  3092.             circle_distance = robptr->circle_distance[Difficulty_level] + ConsoleObject->size;
  3093.             //    Green guy doesn't get his circle distance boosted, else he might never attack.
  3094.             if (robptr->attack_type != 1)
  3095.                 circle_distance += (objnum&0xf) * F1_0/2;
  3096.  
  3097.             compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3098.  
  3099.             //    @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.
  3100.             if ((player_visibility < 2) && (previous_visibility == 2)) { // this is redundant: mk, 01/15/95: && (ailp->mode == AIM_CHASE_OBJECT)) {
  3101.                 // mprintf((0, "I used to be able to see the player!\n"));
  3102.                 if (!ai_multiplayer_awareness(obj, 53)) {
  3103.                     if (maybe_ai_do_actual_firing_stuff(obj, aip))
  3104.                         ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3105.                     return;
  3106.                 }
  3107.                 // -- mprintf((0, "(3) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
  3108.                 create_path_to_player(obj, 8, 1);
  3109.                 // -- show_path_and_other(obj);
  3110.                 ai_multi_send_robot_position(objnum, -1);
  3111.             } else if ((player_visibility == 0) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
  3112.                 //    If pretty far from the player, player cannot be seen (obstructed) and in chase mode, switch to follow path mode.
  3113.                 //    This has one desirable benefit of avoiding physics retries.
  3114.                 if (aip->behavior == AIB_STATION) {
  3115.                     ailp->goal_segment = aip->hide_segment;
  3116.                     // -- mprintf((0, "(1) Object #%i going from chase to STATION in frame %i.\n", objnum, FrameCount));
  3117.                     create_path_to_station(obj, 15);
  3118.                     // -- show_path_and_other(obj);
  3119.                 } else
  3120.                     create_n_segment_path(obj, 5, -1);
  3121.                 break;
  3122.             }
  3123.  
  3124.             if ((aip->CURRENT_STATE == AIS_REST) && (aip->GOAL_STATE == AIS_REST)) {
  3125.                 if (player_visibility) {
  3126.                     if (rand() < FrameTime*player_visibility) {
  3127.                         if (dist_to_player/256 < rand()*player_visibility) {
  3128.                             // mprintf((0, "Object %i searching for player.\n", obj-Objects));
  3129.                             aip->GOAL_STATE = AIS_SRCH;
  3130.                             aip->CURRENT_STATE = AIS_SRCH;
  3131.                         }
  3132.                     }
  3133.                 }
  3134.             }
  3135.  
  3136.             if (GameTime - ailp->time_player_seen > CHASE_TIME_LENGTH) {
  3137.  
  3138.                 if (Game_mode & GM_MULTI)
  3139.                     if (!player_visibility && (dist_to_player > F1_0*70)) {
  3140.                         ailp->mode = AIM_STILL;
  3141.                         return;
  3142.                     }
  3143.  
  3144.                 if (!ai_multiplayer_awareness(obj, 64)) {
  3145.                     if (maybe_ai_do_actual_firing_stuff(obj, aip))
  3146.                         ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3147.                     return;
  3148.                 }
  3149.                 // -- mprintf((0, "(4) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
  3150.                 create_path_to_player(obj, 10, 1);
  3151.                 // -- show_path_and_other(obj);
  3152.                 ai_multi_send_robot_position(objnum, -1);
  3153.             } else if ((aip->CURRENT_STATE != AIS_REST) && (aip->GOAL_STATE != AIS_REST)) {
  3154.                 if (!ai_multiplayer_awareness(obj, 70)) {
  3155.                     if (maybe_ai_do_actual_firing_stuff(obj, aip))
  3156.                         ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3157.                     return;
  3158.                 }
  3159.                 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, circle_distance, 0);
  3160.  
  3161.                 if ((obj_ref & 1) && ((aip->GOAL_STATE == AIS_SRCH) || (aip->GOAL_STATE == AIS_LOCK))) {
  3162.                     if (player_visibility) // == 2)
  3163.                         ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3164.                     else
  3165.                         ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  3166.                 }
  3167.  
  3168.                 if (ai_evaded) {
  3169.                     ai_multi_send_robot_position(objnum, 1);
  3170.                     ai_evaded = 0;
  3171.                 }
  3172.                 else 
  3173.                     ai_multi_send_robot_position(objnum, -1);
  3174.                 
  3175.                 do_firing_stuff(obj, player_visibility, &vec_to_player);
  3176.             }
  3177.             break;
  3178.         }
  3179.  
  3180.         case AIM_RUN_FROM_OBJECT:
  3181.             compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3182.  
  3183.             if (player_visibility) {
  3184.                 if (ailp->player_awareness_type == 0)
  3185.                     ailp->player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
  3186.  
  3187.             }
  3188.  
  3189.             //    If in multiplayer, only do if player visible.  If not multiplayer, do always.
  3190.             if (!(Game_mode & GM_MULTI) || player_visibility)
  3191.                 if (ai_multiplayer_awareness(obj, 75)) {
  3192.                     ai_follow_path(obj, player_visibility);
  3193.                     ai_multi_send_robot_position(objnum, -1);
  3194.                 }
  3195.  
  3196.             if (aip->GOAL_STATE != AIS_FLIN)
  3197.                 aip->GOAL_STATE = AIS_LOCK;
  3198.             else if (aip->CURRENT_STATE == AIS_FLIN)
  3199.                 aip->GOAL_STATE = AIS_LOCK;
  3200.  
  3201.             //    Bad to let run_from robot fire at player because it will cause a war in which it turns towards the
  3202.             //    player to fire and then towards its goal to move.
  3203.             // do_firing_stuff(obj, player_visibility, &vec_to_player);
  3204.             //    Instead, do this:
  3205.             //    (Note, only drop if player is visible.  This prevents the bombs from being a giveaway, and
  3206.             //    also ensures that the robot is moving while it is dropping.  Also means fewer will be dropped.)
  3207.             if ((ailp->next_fire <= 0) && (player_visibility)) {
  3208.                 vms_vector    fire_vec, fire_pos;
  3209.  
  3210.                 if (!ai_multiplayer_awareness(obj, 75))
  3211.                     return;
  3212.  
  3213.                 fire_vec = obj->orient.fvec;
  3214.                 vm_vec_negate(&fire_vec);
  3215.                 vm_vec_add(&fire_pos, &obj->pos, &fire_vec);
  3216.  
  3217.                 Laser_create_new_easy( &fire_vec, &fire_pos, obj-Objects, PROXIMITY_ID, 1);
  3218.                 ailp->next_fire = F1_0*5;        //    Drop a proximity bomb every 5 seconds.
  3219.                 
  3220.                 #ifdef NETWORK
  3221.                 if (Game_mode & GM_MULTI)
  3222.                 {
  3223.                     ai_multi_send_robot_position(obj-Objects, -1);
  3224.                     multi_send_robot_fire(obj-Objects, -1, &fire_vec);
  3225.                 }                  
  3226.                 #endif    
  3227.             }
  3228.             break;
  3229.  
  3230.         case AIM_FOLLOW_PATH: {
  3231.             int    anger_level = 65;
  3232.  
  3233.             if (aip->behavior == AIB_STATION)
  3234.                 if (Point_segs[aip->hide_index + aip->path_length - 1].segnum == aip->hide_segment) {
  3235.                     anger_level = 64;
  3236.                     // mprintf((0, "Object %i, station, lowering anger to 64.\n"));
  3237.                 }
  3238.  
  3239.             compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3240.  
  3241.             if (Game_mode & (GM_MODEM | GM_SERIAL))
  3242.                 if (!player_visibility && (dist_to_player > F1_0*70)) {
  3243.                     ailp->mode = AIM_STILL;
  3244.                     return;
  3245.                 }
  3246.  
  3247.             if (!ai_multiplayer_awareness(obj, anger_level)) {
  3248.                 if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
  3249.                     compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3250.                     ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3251.                 }
  3252.                 return;
  3253.             }
  3254.  
  3255.             ai_follow_path(obj, player_visibility);
  3256.  
  3257.             if (aip->GOAL_STATE != AIS_FLIN)
  3258.                 aip->GOAL_STATE = AIS_LOCK;
  3259.             else if (aip->CURRENT_STATE == AIS_FLIN)
  3260.                 aip->GOAL_STATE = AIS_LOCK;
  3261.  
  3262.             if ((aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM))
  3263.                 do_firing_stuff(obj, player_visibility, &vec_to_player);
  3264.  
  3265.             if ((player_visibility == 2) && (aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM) && (obj->id != ROBOT_BRAIN)) {
  3266.                 if (robptr->attack_type == 0)
  3267.                     ailp->mode = AIM_CHASE_OBJECT;
  3268.             } else if ((player_visibility == 0) && (aip->behavior == AIB_NORMAL) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM)) {
  3269.                 ailp->mode = AIM_STILL;
  3270.                 aip->hide_index = -1;
  3271.                 aip->path_length = 0;
  3272.             }
  3273.  
  3274.             ai_multi_send_robot_position(objnum, -1);
  3275.  
  3276.             break;
  3277.         }
  3278.  
  3279.         case AIM_HIDE:
  3280.             if (!ai_multiplayer_awareness(obj, 71)) {
  3281.                 if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
  3282.                     compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3283.                     ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3284.                 }
  3285.                 return;
  3286.             }
  3287.  
  3288.             compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3289.  
  3290.              ai_follow_path(obj, player_visibility);
  3291.  
  3292.             if (aip->GOAL_STATE != AIS_FLIN)
  3293.                 aip->GOAL_STATE = AIS_LOCK;
  3294.             else if (aip->CURRENT_STATE == AIS_FLIN)
  3295.                 aip->GOAL_STATE = AIS_LOCK;
  3296.  
  3297.             ai_multi_send_robot_position(objnum, -1);
  3298.             break;
  3299.  
  3300.         case AIM_STILL:
  3301.             if ((dist_to_player < F1_0*120+Difficulty_level*F1_0*20) || (ailp->player_awareness_type >= PA_WEAPON_ROBOT_COLLISION-1)) {
  3302.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3303.  
  3304.                 //    turn towards vector if visible this time or last time, or rand
  3305.                 // new!
  3306.                 if ((player_visibility) || (previous_visibility) || ((rand() > 0x4000) && !(Game_mode & GM_MULTI))) {
  3307.                     if (!ai_multiplayer_awareness(obj, 71)) {
  3308.                         if (maybe_ai_do_actual_firing_stuff(obj, aip))
  3309.                             ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3310.                         return;
  3311.                     }
  3312.                     ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3313.                     ai_multi_send_robot_position(objnum, -1);
  3314.                 }
  3315.  
  3316.                 do_firing_stuff(obj, player_visibility, &vec_to_player);
  3317.                 //    This is debugging code!  Remove it!  It's to make the green guy attack without doing other kinds of movement.
  3318.                 if (player_visibility) {        //    Change, MK, 01/03/94 for Multiplayer reasons.  If robots can't see you (even with eyes on back of head), then don't do evasion.
  3319.                     if (robptr->attack_type == 1) {
  3320.                         aip->behavior = AIB_NORMAL;
  3321.                         if (!ai_multiplayer_awareness(obj, 80)) {
  3322.                             if (maybe_ai_do_actual_firing_stuff(obj, aip))
  3323.                                 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3324.                             return;
  3325.                         }
  3326.                         ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 0);
  3327.                         if (ai_evaded) {
  3328.                             ai_multi_send_robot_position(objnum, 1);
  3329.                             ai_evaded = 0;
  3330.                         }
  3331.                         else
  3332.                             ai_multi_send_robot_position(objnum, -1);
  3333.                     } else {
  3334.                         //    Robots in hover mode are allowed to evade at half normal speed.
  3335.                         if (!ai_multiplayer_awareness(obj, 81)) {
  3336.                             if (maybe_ai_do_actual_firing_stuff(obj, aip))
  3337.                                 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3338.                             return;
  3339.                         }
  3340.                         ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 1);
  3341.                         if (ai_evaded) {
  3342.                             ai_multi_send_robot_position(objnum, -1);
  3343.                             ai_evaded = 0;
  3344.                         }
  3345.                         else                
  3346.                             ai_multi_send_robot_position(objnum, -1);
  3347.                     }
  3348.                 } else if ((obj->segnum != aip->hide_segment) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
  3349.                     //    If pretty far from the player, player cannot be seen (obstructed) and in chase mode, switch to follow path mode.
  3350.                     //    This has one desirable benefit of avoiding physics retries.
  3351.                     if (aip->behavior == AIB_STATION) {
  3352.                         ailp->goal_segment = aip->hide_segment;
  3353.                         // -- mprintf((0, "(2) Object #%i going from STILL to STATION in frame %i.\n", objnum, FrameCount));
  3354.                         create_path_to_station(obj, 15);
  3355.                         // -- show_path_and_other(obj);
  3356.                     }
  3357.                     break;
  3358.                 }
  3359.             }
  3360.  
  3361.             break;
  3362.         case AIM_OPEN_DOOR: {        // trying to open a door.
  3363.             vms_vector    center_point, goal_vector;
  3364.             Assert(obj->id == ROBOT_BRAIN);        //    Make sure this guy is allowed to be in this mode.
  3365.  
  3366.             if (!ai_multiplayer_awareness(obj, 62))
  3367.                 return;
  3368.             compute_center_point_on_side(¢er_point, &Segments[obj->segnum], aip->GOALSIDE);
  3369.             vm_vec_sub(&goal_vector, ¢er_point, &obj->pos);
  3370.             vm_vec_normalize_quick(&goal_vector);
  3371.             ai_turn_towards_vector(&goal_vector, obj, robptr->turn_time[Difficulty_level]);
  3372.             move_towards_vector(obj, &goal_vector);
  3373.             ai_multi_send_robot_position(objnum, -1);
  3374.  
  3375.             break;
  3376.         }
  3377.  
  3378.         default:
  3379.             mprintf((0, "Unknown mode = %i in robot %i, behavior = %i\n", ailp->mode, obj-Objects, aip->behavior));
  3380.             ailp->mode = AIM_CHASE_OBJECT;
  3381.             break;
  3382.     }        // end:    switch (ailp->mode) {
  3383.  
  3384.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3385.     //    If the robot can see you, increase his awareness of you.
  3386.     //    This prevents the problem of a robot looking right at you but doing nothing.
  3387.     // Assert(player_visibility != -1);    //    Means it didn't get initialized!
  3388.     compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3389.     if (player_visibility == 2)
  3390.         if (ailp->player_awareness_type == 0)
  3391.             ailp->player_awareness_type = PA_PLAYER_COLLISION;
  3392.  
  3393.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3394.     if (!object_animates) {
  3395.         aip->CURRENT_STATE = aip->GOAL_STATE;
  3396.         // mprintf((0, "Setting current to goal (%i) because object doesn't animate.\n", aip->GOAL_STATE));
  3397.     }
  3398.  
  3399.     Assert(ailp->player_awareness_type <= AIE_MAX);
  3400.     Assert(aip->CURRENT_STATE < AIS_MAX);
  3401.     Assert(aip->GOAL_STATE < AIS_MAX);
  3402.  
  3403.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3404.     if (ailp->player_awareness_type) {
  3405.         new_goal_state = Ai_transition_table[ailp->player_awareness_type-1][aip->CURRENT_STATE][aip->GOAL_STATE];
  3406.         if (ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) {
  3407.             //    Decrease awareness, else this robot will flinch every frame.
  3408.             ailp->player_awareness_type--;
  3409.             ailp->player_awareness_time = F1_0*3;
  3410.         }
  3411.  
  3412.         if (new_goal_state == AIS_ERR_)
  3413.             new_goal_state = AIS_REST;
  3414.  
  3415.         if (aip->CURRENT_STATE == AIS_NONE)
  3416.             aip->CURRENT_STATE = AIS_REST;
  3417.  
  3418.         aip->GOAL_STATE = new_goal_state;
  3419.  
  3420.     }
  3421.  
  3422.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3423.     //    If new state = fire, then set all gun states to fire.
  3424.     if ((aip->GOAL_STATE == AIS_FIRE) ) {
  3425.         int    i,num_guns;
  3426.         num_guns = Robot_info[obj->id].n_guns;
  3427.         for (i=0; i<num_guns; i++)
  3428.             ailp->goal_state[i] = AIS_FIRE;
  3429.     }
  3430.  
  3431.     //    - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3432.     //    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
  3433.     if ((ailp->next_fire < 0) && (aip->GOAL_STATE == AIS_FIRE))
  3434.         aip->CURRENT_STATE = AIS_FIRE;
  3435.  
  3436.     if ((aip->GOAL_STATE != AIS_FLIN)  && (obj->id != ROBOT_BRAIN)) {
  3437.         switch (aip->CURRENT_STATE) {
  3438.             case    AIS_NONE:
  3439.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3440.  
  3441.                 dot = vm_vec_dot(&obj->orient.fvec, &vec_to_player);
  3442.                 if (dot >= F1_0/2)
  3443.                     if (aip->GOAL_STATE == AIS_REST)
  3444.                         aip->GOAL_STATE = AIS_SRCH;
  3445.                 //mprintf((0, "State = none, goal = %i.\n", aip->GOAL_STATE));
  3446.                 break;
  3447.             case    AIS_REST:
  3448.                 if (aip->GOAL_STATE == AIS_REST) {
  3449.                     compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3450.                     if ((ailp->next_fire <= 0) && (player_visibility)) {
  3451.                         // mprintf((0, "Setting goal state to fire from rest.\n"));
  3452.                         aip->GOAL_STATE = AIS_FIRE;
  3453.                     }
  3454.                     //mprintf((0, "State = rest, goal = %i.\n", aip->GOAL_STATE));
  3455.                 }
  3456.                 break;
  3457.             case    AIS_SRCH:
  3458.                 if (!ai_multiplayer_awareness(obj, 60))
  3459.                     return;
  3460.  
  3461.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3462.  
  3463.                 if (player_visibility) {
  3464.                     ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3465.                     ai_multi_send_robot_position(objnum, -1);
  3466.                 } else if (!(Game_mode & GM_MULTI))
  3467.                     ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  3468.                 break;
  3469.             case    AIS_LOCK:
  3470.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3471.  
  3472.                 if (!(Game_mode & GM_MULTI) || (player_visibility)) {
  3473.                     if (!ai_multiplayer_awareness(obj, 68))
  3474.                         return;
  3475.  
  3476.                     if (player_visibility) {
  3477.                         ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3478.                         ai_multi_send_robot_position(objnum, -1);
  3479.                     } else if (!(Game_mode & GM_MULTI))
  3480.                         ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  3481.                 }
  3482.                 break;
  3483.             case    AIS_FIRE:
  3484.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3485.  
  3486.                 if (player_visibility) {
  3487.                     if (!ai_multiplayer_awareness(obj, (ROBOT_FIRE_AGITATION-1))) 
  3488.                     {
  3489.                         if (Game_mode & GM_MULTI) {
  3490.                             ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3491.                             return;
  3492.                         }
  3493.                     }
  3494.                     ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3495.                     ai_multi_send_robot_position(objnum, -1);
  3496.                 } else if (!(Game_mode & GM_MULTI)) {
  3497.                     ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  3498.                 }
  3499.  
  3500.                 //    Fire at player, if appropriate.
  3501.                 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3502.  
  3503.                 break;
  3504.             case    AIS_RECO:
  3505.                 if (!(obj_ref & 3)) {
  3506.                     compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3507.                     if (player_visibility) {
  3508.                         if (!ai_multiplayer_awareness(obj, 69))
  3509.                             return;
  3510.                         ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3511.                         ai_multi_send_robot_position(objnum, -1);
  3512.                     } else if (!(Game_mode & GM_MULTI)) {
  3513.                         ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  3514.                     }
  3515.                 }
  3516.                 break;
  3517.             case    AIS_FLIN:
  3518.                 // mprintf((0, "State = flinch, goal = %i.\n", aip->GOAL_STATE));
  3519.                 break;
  3520.             default:
  3521.                 mprintf((1, "Unknown mode for AI object #%i\n", objnum));
  3522.                 aip->GOAL_STATE = AIS_REST;
  3523.                 aip->CURRENT_STATE = AIS_REST;
  3524.                 break;
  3525.         }
  3526.     } // end of: if (aip->GOAL_STATE != AIS_FLIN) {
  3527.  
  3528.     // Switch to next gun for next fire.
  3529.     if (player_visibility == 0) {
  3530.         aip->CURRENT_GUN++;
  3531.         if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  3532.             aip->CURRENT_GUN = 0;
  3533.     }
  3534.  
  3535. }
  3536.  
  3537. //--mk, 121094 -- // ----------------------------------------------------------------------------------
  3538. //--mk, 121094 -- void spin_robot(object *robot, vms_vector *collision_point)
  3539. //--mk, 121094 -- {
  3540. //--mk, 121094 --     if (collision_point->x != 3) {
  3541. //--mk, 121094 --         robot->phys_info.rotvel.x = 0x1235;
  3542. //--mk, 121094 --         robot->phys_info.rotvel.y = 0x2336;
  3543. //--mk, 121094 --         robot->phys_info.rotvel.z = 0x3737;
  3544. //--mk, 121094 --     }
  3545. //--mk, 121094 -- 
  3546. //--mk, 121094 -- }
  3547.  
  3548. //    -----------------------------------------------------------------------------------
  3549. void ai_do_cloak_stuff(void)
  3550. {
  3551.     int    i;
  3552.  
  3553.     for (i=0; i<MAX_AI_CLOAK_INFO; i++) {
  3554.         Ai_cloak_info[i].last_position = ConsoleObject->pos;
  3555.         Ai_cloak_info[i].last_time = GameTime;
  3556.     }
  3557.  
  3558.     //    Make work for control centers.
  3559.     Believed_player_pos = Ai_cloak_info[0].last_position;
  3560.  
  3561. }
  3562.  
  3563. //    -----------------------------------------------------------------------------------
  3564. //    Returns false if awareness is considered too puny to add, else returns true.
  3565. int add_awareness_event(object *objp, int type)
  3566. {
  3567.     //    If player cloaked and hit a robot, then increase awareness
  3568.     if ((type == PA_WEAPON_ROBOT_COLLISION) || (type == PA_WEAPON_WALL_COLLISION) || (type == PA_PLAYER_COLLISION))
  3569.         ai_do_cloak_stuff();
  3570.  
  3571.     if (Num_awareness_events < MAX_AWARENESS_EVENTS) {
  3572.         if ((type == PA_WEAPON_WALL_COLLISION) || (type == PA_WEAPON_ROBOT_COLLISION))
  3573.             if (objp->id == VULCAN_ID)
  3574.                 if (rand() > 3276)
  3575.                     return 0;        //    For vulcan cannon, only about 1/10 actually cause awareness
  3576.  
  3577.         Awareness_events[Num_awareness_events].segnum = objp->segnum;
  3578.         Awareness_events[Num_awareness_events].pos = objp->pos;
  3579.         Awareness_events[Num_awareness_events].type = type;
  3580.         Num_awareness_events++;
  3581.     } else
  3582.         Assert(0);        // Hey -- Overflowed Awareness_events, make more or something
  3583.                             // This just gets ignored, so you can just continue.
  3584.     return 1;
  3585.  
  3586. }
  3587.  
  3588. // ----------------------------------------------------------------------------------
  3589. //    Robots will become aware of the player based on something that occurred.
  3590. //    The object (probably player or weapon) which created the awareness is objp.
  3591. void create_awareness_event(object *objp, int type)
  3592. {
  3593.     if (add_awareness_event(objp, type)) {
  3594.         if (((rand() * (type+4)) >> 15) > 4)
  3595.             Overall_agitation++;
  3596.         if (Overall_agitation > OVERALL_AGITATION_MAX)
  3597.             Overall_agitation = OVERALL_AGITATION_MAX;
  3598.     }
  3599. }
  3600.  
  3601. byte    New_awareness[MAX_SEGMENTS];
  3602.  
  3603. // ----------------------------------------------------------------------------------
  3604. void pae_aux(int segnum, int type, int level)
  3605. {
  3606.     int    j;
  3607.  
  3608.     if (New_awareness[segnum] < type)
  3609.         New_awareness[segnum] = type;
  3610.  
  3611.     // Process children.
  3612.     if (level <= 4)
  3613.         for (j=0; j<MAX_SIDES_PER_SEGMENT; j++)
  3614.             if (IS_CHILD(Segments[segnum].children[j]))
  3615.                 if (type == 4)
  3616.                     pae_aux(Segments[segnum].children[j], type-1, level+1);
  3617.                 else
  3618.                     pae_aux(Segments[segnum].children[j], type, level+1);
  3619. }
  3620.  
  3621.  
  3622. // ----------------------------------------------------------------------------------
  3623. void process_awareness_events(void)
  3624. {
  3625.     int    i;
  3626.  
  3627.     for (i=0; i<=Highest_segment_index; i++)
  3628.         New_awareness[i] = 0;
  3629.  
  3630.     for (i=0; i<Num_awareness_events; i++)
  3631.         pae_aux(Awareness_events[i].segnum, Awareness_events[i].type, 1);
  3632.  
  3633.     Num_awareness_events = 0;
  3634. }
  3635.  
  3636. // ----------------------------------------------------------------------------------
  3637. void set_player_awareness_all(void)
  3638. {
  3639.     int    i;
  3640.  
  3641.     process_awareness_events();
  3642.  
  3643.     for (i=0; i<=Highest_object_index; i++)
  3644.         if (Objects[i].control_type == CT_AI)
  3645.             if (New_awareness[Objects[i].segnum] > Ai_local_info[i].player_awareness_type) {
  3646.                 Ai_local_info[i].player_awareness_type = New_awareness[Objects[i].segnum];
  3647.                 Ai_local_info[i].player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME;
  3648.             }
  3649. }
  3650.  
  3651. #ifndef NDEBUG
  3652. int    Ai_dump_enable = 0;
  3653.  
  3654. FILE *Ai_dump_file = NULL;
  3655.  
  3656. char    Ai_error_message[128] = "";
  3657.  
  3658. // ----------------------------------------------------------------------------------
  3659. void dump_ai_objects_all()
  3660. {
  3661. #if PARALLAX
  3662.     int    objnum;
  3663.     int    total=0;
  3664.     time_t    time_of_day;
  3665.  
  3666.     time_of_day = time(NULL);
  3667.  
  3668.     if (!Ai_dump_enable)
  3669.         return;
  3670.  
  3671.     if (Ai_dump_file == NULL)
  3672.         Ai_dump_file = fopen("ai.out","a+t");
  3673.  
  3674.     fprintf(Ai_dump_file, "\nnum: seg distance __mode__ behav.    [velx vely velz] (Frame = %i)\n", FrameCount);
  3675.     fprintf(Ai_dump_file, "Date & Time = %s\n", ctime(&time_of_day));
  3676.  
  3677.     if (Ai_error_message[0])
  3678.         fprintf(Ai_dump_file, "Error message: %s\n", Ai_error_message);
  3679.  
  3680.     for (objnum=0; objnum <= Highest_object_index; objnum++) {
  3681.         object        *objp = &Objects[objnum];
  3682.         ai_static    *aip = &objp->ctype.ai_info;
  3683.         ai_local        *ailp = &Ai_local_info[objnum];
  3684.         fix            dist_to_player;
  3685.  
  3686.         dist_to_player = vm_vec_dist(&objp->pos, &ConsoleObject->pos);
  3687.  
  3688.         if (objp->control_type == CT_AI) {
  3689.             fprintf(Ai_dump_file, "%3i: %3i %8.3f %8s %8s [%3i %4i]\n",
  3690.                 objnum, objp->segnum, f2fl(dist_to_player), mode_text[ailp->mode], behavior_text[aip->behavior-0x80], aip->hide_index, aip->path_length);
  3691.             if (aip->path_length)
  3692.                 total += aip->path_length;
  3693.         }
  3694.     }
  3695.  
  3696.     fprintf(Ai_dump_file, "Total path length = %4i\n", total);
  3697. #endif
  3698.  
  3699. }
  3700.  
  3701. // ----------------------------------------------------------------------------------
  3702. void force_dump_ai_objects_all(char *msg)
  3703. {
  3704.     int    tsave;
  3705.  
  3706.     tsave = Ai_dump_enable;
  3707.  
  3708.     Ai_dump_enable = 1;
  3709.  
  3710.     sprintf(Ai_error_message, "%s\n", msg);
  3711.     dump_ai_objects_all();
  3712.     Ai_error_message[0] = 0;
  3713.  
  3714.     Ai_dump_enable = tsave;
  3715. }
  3716.  
  3717. // ----------------------------------------------------------------------------------
  3718. void turn_off_ai_dump(void)
  3719. {
  3720.     if (Ai_dump_file != NULL)
  3721.         fclose(Ai_dump_file);
  3722.  
  3723.     Ai_dump_file = NULL;
  3724. }
  3725.  
  3726. #endif
  3727.  
  3728. // ----------------------------------------------------------------------------------
  3729. //    Do things which need to get done for all AI objects each frame.
  3730. //    This includes:
  3731. //        Setting player_awareness (a fix, time in seconds which object is aware of player)
  3732. void do_ai_frame_all(void)
  3733. {
  3734. #ifndef NDEBUG
  3735.     dump_ai_objects_all();
  3736. #endif
  3737.  
  3738.     set_player_awareness_all();
  3739.  
  3740. //    if ((FrameCount & 0x07) == 0)
  3741. //        mprintf((0, "[%i] ", Overall_agitation));
  3742. }
  3743.  
  3744. //--unused-- //    ----------------------------------------------------------------------------------
  3745. //--unused-- //    Reset various state information for a new life.
  3746. //--unused-- //    Probably not going to be called for a new game because for that an entire object
  3747. //--unused-- //    gets reloaded.
  3748. //--unused-- void reset_ai_states(object *objp)
  3749. //--unused-- {
  3750. //--unused--     int        i;
  3751. //--unused--     //ai_static    *aip = &objp->ctype.ai_info;
  3752. //--unused--     ai_local        *ailp = &Ai_local_info[objp-Objects];
  3753. //--unused-- 
  3754. //--unused--     ailp->wait_time = 0;
  3755. //--unused--     ailp->next_fire = 0;
  3756. //--unused--     ailp->player_awareness_type = 0;
  3757. //--unused--     for (i=0; i<MAX_SUBMODELS; i++) {
  3758. //--unused--         ailp->goal_angles[i].p = 0;        ailp->goal_angles[i].b = 0;        ailp->goal_angles[i].h = 0;
  3759. //--unused--         ailp->delta_angles[i].p = 0;    ailp->delta_angles[i].b = 0;    ailp->delta_angles[i].h = 0;
  3760. //--unused--         ailp->goal_state[i] = 0;
  3761. //--unused--         ailp->achieved_state[i] = 0;
  3762. //--unused--     }
  3763. //--unused-- }
  3764.  
  3765. //    Initializations to be performed for all robots for a new level.
  3766. void init_robots_for_level(void)
  3767. {
  3768.     Overall_agitation = 0;
  3769. }
  3770.  
  3771. int ai_save_state( FILE * fp )
  3772. {
  3773.     fwrite( &Ai_initialized, sizeof(int), 1, fp );
  3774.     fwrite( &Overall_agitation, sizeof(int), 1, fp );
  3775.     fwrite( Ai_local_info, sizeof(ai_local)*MAX_OBJECTS, 1, fp );
  3776.     fwrite( Point_segs, sizeof(point_seg)*MAX_POINT_SEGS, 1, fp );
  3777.     fwrite( Ai_cloak_info, sizeof(ai_cloak_info)*MAX_AI_CLOAK_INFO, 1, fp );
  3778.     fwrite( &Boss_cloak_start_time, sizeof(fix), 1, fp );
  3779.     fwrite( &Boss_cloak_end_time , sizeof(fix), 1, fp );
  3780.     fwrite( &Last_teleport_time , sizeof(fix), 1, fp );
  3781.     fwrite( &Boss_teleport_interval, sizeof(fix), 1, fp );
  3782.     fwrite( &Boss_cloak_interval, sizeof(fix), 1, fp );
  3783.     fwrite( &Boss_cloak_duration, sizeof(fix), 1, fp );
  3784.     fwrite( &Last_gate_time, sizeof(fix), 1, fp );
  3785.     fwrite( &Gate_interval, sizeof(fix), 1, fp );
  3786.     fwrite( &Boss_dying_start_time, sizeof(fix), 1, fp );
  3787.     fwrite( &Boss_dying, sizeof(int), 1, fp );
  3788.     fwrite( &Boss_dying_sound_playing, sizeof(int), 1, fp );
  3789.     fwrite( &Boss_hit_this_frame, sizeof(int), 1, fp );
  3790.     fwrite( &Boss_been_hit, sizeof(int), 1, fp );
  3791.     return 1;
  3792. }
  3793.  
  3794. int ai_restore_state( FILE * fp )
  3795. {
  3796.     fread( &Ai_initialized, sizeof(int), 1, fp );
  3797.     fread( &Overall_agitation, sizeof(int), 1, fp );
  3798.     fread( Ai_local_info, sizeof(ai_local)*MAX_OBJECTS, 1, fp );
  3799.     fread( Point_segs, sizeof(point_seg)*MAX_POINT_SEGS, 1, fp );
  3800.     fread( Ai_cloak_info, sizeof(ai_cloak_info)*MAX_AI_CLOAK_INFO, 1, fp );
  3801.     fread( &Boss_cloak_start_time, sizeof(fix), 1, fp );
  3802.     fread( &Boss_cloak_end_time , sizeof(fix), 1, fp );
  3803.     fread( &Last_teleport_time , sizeof(fix), 1, fp );
  3804.     fread( &Boss_teleport_interval, sizeof(fix), 1, fp );
  3805.     fread( &Boss_cloak_interval, sizeof(fix), 1, fp );
  3806.     fread( &Boss_cloak_duration, sizeof(fix), 1, fp );
  3807.     fread( &Last_gate_time, sizeof(fix), 1, fp );
  3808.     fread( &Gate_interval, sizeof(fix), 1, fp );
  3809.     fread( &Boss_dying_start_time, sizeof(fix), 1, fp );
  3810.     fread( &Boss_dying, sizeof(int), 1, fp );
  3811.     fread( &Boss_dying_sound_playing, sizeof(int), 1, fp );
  3812.     fread( &Boss_hit_this_frame, sizeof(int), 1, fp );
  3813.     fread( &Boss_been_hit, sizeof(int), 1, fp );
  3814.     return 1;
  3815. }
  3816.  
  3817. // -- void show_path_and_other(object *objp )
  3818. // -- {
  3819. // --     int            i;
  3820. // --     ai_static    *aip = &objp->ctype.ai_info;
  3821.  
  3822. // --     for (i=0; i<aip->path_length; i++)
  3823. // --         mprintf((0, "%2i ", Point_segs[aip->hide_index+i].segnum));
  3824. // --     mprintf((0, "[pl: %i cur: st: %i]\n", ConsoleObject->segnum, objp->segnum, aip->hide_segment));
  3825.  
  3826. // -- }
  3827.  
  3828. 
  3829.