home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / object.c < prev    next >
Text File  |  1998-06-08  |  68KB  |  2,310 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/object.c $
  15.  * $Revision: 2.3 $
  16.  * $Author: john $
  17.  * $Date: 1995/06/15 12:30:51 $
  18.  * 
  19.  * object rendering
  20.  * 
  21.  * $Log: object.c $
  22.  * Revision 2.3  1995/06/15  12:30:51  john
  23.  * Fixed bug with multiplayer ships cloaking out wrongly.
  24.  * 
  25.  * Revision 2.2  1995/05/15  11:34:53  john
  26.  * Fixed bug as Matt directed that fixed problems with the exit
  27.  * triggers being missed on slow frame rate computer.
  28.  * 
  29.  * Revision 2.1  1995/03/21  14:38:51  john
  30.  * Ifdef'd out the NETWORK code.
  31.  * 
  32.  * Revision 2.0  1995/02/27  11:28:14  john
  33.  * New version 2.0, which has no anonymous unions, builds with
  34.  * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
  35.  * 
  36.  * Revision 1.335  1995/02/22  12:57:30  allender
  37.  * remove anonymous unions from object structure
  38.  * 
  39.  * Revision 1.334  1995/02/09  22:04:40  mike
  40.  * fix lifeleft on badass weapons.
  41.  * 
  42.  * Revision 1.333  1995/02/08  12:54:00  matt
  43.  * Fixed object freeing code which was deleting some explosions it shouldn't
  44.  * 
  45.  * Revision 1.332  1995/02/08  11:37:26  mike
  46.  * Check for failures in call to obj_create.
  47.  * 
  48.  * Revision 1.331  1995/02/05  17:48:52  rob
  49.  * Changed assert in obj_relink, more robust.
  50.  * 
  51.  * Revision 1.330  1995/02/05  13:39:48  mike
  52.  * remove invulnerability effect code (actually, comment out).
  53.  * 
  54.  * Revision 1.329  1995/02/04  12:29:52  rob
  55.  * Get rid of potential assert error for explosion detachment.
  56.  * 
  57.  * Revision 1.328  1995/02/01  18:15:57  rob
  58.  * Removed debugging output from engine glow change.
  59.  * 
  60.  * Revision 1.327  1995/02/01  16:20:12  matt
  61.  * Made engine glow vary over a wider range, and made the glow be based
  62.  * on thrust/speed, without regard to direction.
  63.  * 
  64.  * Revision 1.326  1995/01/29  14:46:24  rob
  65.  * Fixed invul. vclip to only appear on player who is invul.
  66.  * 
  67.  * Revision 1.325  1995/01/29  13:48:16  mike
  68.  * Add invulnerability graphical effect viewable by other players.
  69.  * 
  70.  * Revision 1.324  1995/01/29  11:39:25  mike
  71.  * Add invulnerability effect.
  72.  * 
  73.  * Revision 1.323  1995/01/27  17:02:41  mike
  74.  * add more information to an Error call.
  75.  * 
  76.  * Revision 1.322  1995/01/26  22:11:30  mike
  77.  * Purple chromo-blaster (ie, fusion cannon) spruce up (chromification)
  78.  * 
  79.  * Revision 1.321  1995/01/25  20:04:10  matt
  80.  * Moved matrix check to avoid orthogonalizing an uninitialize matrix
  81.  * 
  82.  * Revision 1.320  1995/01/25  12:11:35  matt
  83.  * Make sure orient matrix is orthogonal when resetting player object
  84.  * 
  85.  * Revision 1.319  1995/01/21  21:46:22  mike
  86.  * Optimize code in Assert (and prevent warning message).
  87.  * 
  88.  * Revision 1.318  1995/01/21  21:22:16  rob
  89.  * Removed HUD clear messages.
  90.  * Added more Asserts to try and find illegal control type bug.
  91.  * 
  92.  * Revision 1.317  1995/01/15  15:34:30  matt
  93.  * When freeing object slots, don't free fireballs that will be deleting
  94.  * other objects.
  95.  * 
  96.  * Revision 1.316  1995/01/14  19:16:48  john
  97.  * First version of new bitmap paging code.
  98.  * 
  99.  * Revision 1.315  1995/01/12  18:53:37  john
  100.  * Fixed parameter passing error.
  101.  * 
  102.  * Revision 1.314  1995/01/12  12:09:47  yuan
  103.  * Added coop object capability.
  104.  * 
  105.  * Revision 1.313  1994/12/15  16:45:44  matt
  106.  * Took out slew stuff for release version
  107.  * 
  108.  * Revision 1.312  1994/12/15  13:04:25  mike
  109.  * Replace Players[Player_num].time_total references with GameTime.
  110.  * 
  111.  * Revision 1.311  1994/12/15  11:01:04  mike
  112.  * add Object_minus_one for debugging.
  113.  * 
  114.  * Revision 1.310  1994/12/15  03:03:33  matt
  115.  * Added error checking for NULL return from object_create_explosion()
  116.  * 
  117.  * Revision 1.309  1994/12/14  17:25:31  matt
  118.  * Made next viewer func based on release not ndebug
  119.  * 
  120.  * Revision 1.308  1994/12/13  12:55:42  mike
  121.  * hostages on board messages for when you die.
  122.  * 
  123.  * Revision 1.307  1994/12/12  17:18:11  mike
  124.  * make boss cloak/teleport when get hit, make quad laser 3/4 as powerful.
  125.  * 
  126.  * Revision 1.306  1994/12/12  00:27:11  matt
  127.  * Added support for no-levelling option
  128.  * 
  129.  * Revision 1.305  1994/12/11  22:41:14  matt
  130.  * Added command-line option, -nolevel, which turns off player ship levelling
  131.  * 
  132.  * Revision 1.304  1994/12/11  22:03:23  mike
  133.  * free up object slots as necessary.
  134.  * 
  135.  * Revision 1.303  1994/12/11  14:09:31  mike
  136.  * make boss explosion sounds softer.
  137.  * 
  138.  * Revision 1.302  1994/12/11  13:25:11  matt
  139.  * Restored calls to fix_object_segs() when debugging is turned off, since
  140.  * it's not a big routine, and could fix some possibly bad problems.
  141.  * 
  142.  * Revision 1.301  1994/12/11  12:38:25  mike
  143.  * make boss explosion sounds louder in create_small_fireball.
  144.  * 
  145.  * Revision 1.300  1994/12/10  15:28:37  matt
  146.  * Added asserts for debugging
  147.  * 
  148.  * Revision 1.299  1994/12/09  16:18:51  matt
  149.  * Fixed init_player_object, for editor
  150.  * 
  151.  * Revision 1.298  1994/12/09  15:03:10  matt
  152.  * Two changes for Mike:
  153.  *   1.  Do better placement of camera during death sequence (prevents hang)
  154.  *   2.  Only record dodging information if the player fired in a frame
  155.  * 
  156.  * Revision 1.297  1994/12/09  14:59:12  matt
  157.  * Added system to attach a fireball to another object for rendering purposes,
  158.  * so the fireball always renders on top of (after) the object.
  159.  * 
  160.  * Revision 1.296  1994/12/08  20:05:07  matt
  161.  * Removed unneeded debug message
  162.  * 
  163.  * Revision 1.295  1994/12/08  12:36:02  matt
  164.  * Added new object allocation & deallocation functions so other code
  165.  * could stop messing around with internal object data structures.
  166.  * 
  167.  * Revision 1.294  1994/12/07  20:13:37  matt
  168.  * Added debris object limiter
  169.  * 
  170.  * Revision 1.293  1994/12/06  16:58:38  matt
  171.  * Killed warnings
  172.  * 
  173.  * Revision 1.292  1994/12/05  22:34:35  matt
  174.  * Make tmap_override objects use override texture as alt texture.  This 
  175.  * should have the effect of making simpler models use the override texture.
  176.  * 
  177.  * Revision 1.291  1994/12/05  12:23:53  mike
  178.  * make camera start closer, but move away from player in death sequence.
  179.  * 
  180.  * Revision 1.290  1994/12/02  11:11:18  mike
  181.  * hook sound effect to player small explosions (ctrlcen, too).
  182.  * 
  183.  * Revision 1.289  1994/11/28  21:50:52  mike
  184.  * optimizations.
  185.  * 
  186.  * Revision 1.288  1994/11/27  23:12:28  matt
  187.  * Made changes for new mprintf calling convention
  188.  * 
  189.  * Revision 1.287  1994/11/27  20:35:50  matt
  190.  * Fixed dumb mistake
  191.  * 
  192.  * Revision 1.286  1994/11/27  20:30:52  matt
  193.  * Got rid of warning
  194.  * 
  195.  * Revision 1.285  1994/11/21  11:43:21  mike
  196.  * ndebug stuff.
  197.  * 
  198.  * Revision 1.284  1994/11/19  15:19:37  mike
  199.  * rip out unused code and data.
  200.  * 
  201.  * Revision 1.283  1994/11/18  23:41:59  john
  202.  * Changed some shorts to ints.
  203.  * 
  204.  * Revision 1.282  1994/11/18  16:16:17  mike
  205.  * Separate depth on objects vs. walls.
  206.  * 
  207.  * Revision 1.281  1994/11/18  12:05:35  rob
  208.  * Removed unnecessary invulnerability flag set in player death.
  209.  * (I hope its unnecessary.. its commented out if it proves crucial)
  210.  * fixes powerup dropping bug for net play.
  211.  * 
  212.  * Revision 1.280  1994/11/16  20:36:34  rob
  213.  * Changed player explosion (small) code.
  214.  * 
  215.  * Revision 1.279  1994/11/16  18:26:04  matt
  216.  * Clear tmap override on player, to fix "rock ship" bug
  217.  * 
  218.  * Revision 1.278  1994/11/16  14:54:12  rob
  219.  * Moved hook for network explosions.
  220.  * 
  221.  * Revision 1.277  1994/11/14  11:40:42  mike
  222.  * plot inner polygon on laser based on detail level.
  223.  * 
  224.  * Revision 1.276  1994/11/10  14:02:59  matt
  225.  * Hacked in support for player ships with different textures
  226.  * 
  227.  * Revision 1.275  1994/11/08  12:19:08  mike
  228.  * Make a generally useful function for putting small explosions on any object.
  229.  * 
  230.  * Revision 1.274  1994/11/04  19:55:54  rob
  231.  * Changed calls to player_explode to accomodate new parameter.
  232.  * 
  233.  * Revision 1.273  1994/11/02  21:54:27  matt
  234.  * Delete the camera when the death sequence is done
  235.  * 
  236.  * Revision 1.272  1994/11/02  11:36:35  rob
  237.  * Added player-in-process-of-dying explosions to network play.
  238.  * 
  239.  * Revision 1.271  1994/10/31  17:25:33  matt
  240.  * Fixed cloaked bug
  241.  * 
  242.  * Revision 1.270  1994/10/31  16:11:19  allender
  243.  * on demo recording, store letterbox mode in demo.
  244.  * 
  245.  * Revision 1.269  1994/10/31  10:36:18  mike
  246.  * Make cloak effect fadein/fadeout different for robots versus player.
  247.  * 
  248.  * Revision 1.268  1994/10/30  14:11:44  mike
  249.  * rip out repair center stuff.
  250.  * 
  251.  * Revision 1.267  1994/10/28  19:43:52  mike
  252.  * Boss cloaking effect.
  253.  * 
  254.  * Revision 1.266  1994/10/27  11:33:42  mike
  255.  * Add Highest_ever_object_index -- high water mark in object creation.
  256.  * 
  257.  * Revision 1.265  1994/10/25  10:51:12  matt
  258.  * Vulcan cannon powerups now contain ammo count
  259.  * 
  260.  * Revision 1.264  1994/10/24  20:49:24  matt
  261.  * Made cloaked objects pulse
  262.  * 
  263.  * Revision 1.263  1994/10/21  12:19:45  matt
  264.  * Clear transient objects when saving (& loading) games
  265.  * 
  266.  * Revision 1.262  1994/10/21  11:25:23  mike
  267.  * Use new constant IMMORTAL_TIME.
  268.  * 
  269.  * Revision 1.261  1994/10/19  16:50:35  matt
  270.  * If out of segment, put player in center of segment when checking objects
  271.  * 
  272.  * 
  273.  * Revision 1.260  1994/10/17  23:21:55  mike
  274.  * Clean up robot cloaking, move more to ai.c
  275.  * 
  276.  * Revision 1.259  1994/10/17  21:34:49  matt
  277.  * Added support for new Control Center/Main Reactor
  278.  * 
  279.  * Revision 1.258  1994/10/17  21:18:04  mike
  280.  * robot cloaking.
  281.  * 
  282.  * Revision 1.257  1994/10/17  14:12:23  matt
  283.  * Cleaned up problems with player dying from mine explosion
  284.  * 
  285.  * Revision 1.256  1994/10/15  19:04:31  mike
  286.  * Don't remove proximity bombs after you die.
  287.  * 
  288.  * Revision 1.255  1994/10/14  15:57:00  mike
  289.  * Don't show ids in network mode.
  290.  * Fix, I hope, but in death sequence.
  291.  * 
  292.  * Revision 1.254  1994/10/12  08:04:29  mike
  293.  * Don't decloak player on death.
  294.  * 
  295.  * Revision 1.253  1994/10/11  20:36:16  matt
  296.  * Clear "transient" objects (weapons,explosions,etc.) when starting a level
  297.  * 
  298.  * Revision 1.252  1994/10/11  12:24:09  matt
  299.  * Cleaned up/change badass explosion calls
  300.  * 
  301.  * Revision 1.251  1994/10/08  19:30:20  matt
  302.  * Fixed (I hope) a bug in cloaking of multiplayer objects
  303.  * 
  304.  * Revision 1.250  1994/10/08  14:03:15  rob
  305.  * Changed cloaking routine.
  306.  * 
  307.  * Revision 1.249  1994/10/07  22:17:27  mike
  308.  * Asserts on valid segnum.
  309.  * 
  310.  * Revision 1.248  1994/10/07  19:11:14  matt
  311.  * Added cool cloak transition effect
  312.  * 
  313.  */
  314.  
  315.  
  316. #pragma off (unreferenced)
  317. static char rcsid[] = "$Id: object.c 2.3 1995/06/15 12:30:51 john Exp $";
  318. #pragma on (unreferenced)
  319.  
  320. #include <string.h>    // for memset
  321. #include <stdio.h>
  322.  
  323. #include "inferno.h"
  324. #include "game.h"
  325. #include "gr.h"
  326. #include "stdlib.h"
  327. #include "bm.h"
  328. //#include "error.h"
  329. #include "mono.h"
  330. #include "3d.h"
  331. #include "segment.h"
  332. #include "texmap.h"
  333. #include "laser.h"
  334. #include "key.h"
  335. #include "gameseg.h"
  336. #include "textures.h"
  337.  
  338. #include "object.h"
  339. #include "physics.h"
  340. #include "slew.h"        
  341. #include "render.h"
  342. #include "wall.h"
  343. #include "vclip.h"
  344. #include "polyobj.h"
  345. #include "fireball.h"
  346. #include "laser.h"
  347. #include "error.h"
  348. #include "ai.h"
  349. #include "hostage.h"
  350. #include "morph.h"
  351. #include "cntrlcen.h"
  352. #include "powerup.h"
  353. #include "fuelcen.h"
  354.  
  355. #include "sounds.h"
  356. #include "collide.h"
  357.  
  358. #include "lighting.h"
  359. #include "newdemo.h"
  360. #include "player.h"
  361. #include "weapon.h"
  362. #include "network.h"
  363. #include "newmenu.h"
  364. #include "gauges.h"
  365. #include "arcade.h"
  366. #include "multi.h"
  367. #include "menu.h"
  368. #include "args.h"
  369. #include "text.h"
  370. #include "piggy.h"
  371.  
  372. #ifdef EDITOR
  373. #include "editor\editor.h"
  374. #endif
  375.  
  376. /*
  377.  *  Global variables
  378.  */
  379.  
  380. ubyte CollisionResult[MAX_OBJECT_TYPES][MAX_OBJECT_TYPES];
  381.  
  382. object *ConsoleObject;                    //the object that is the player
  383.  
  384. static short free_obj_list[MAX_OBJECTS];
  385.  
  386. //Data for objects
  387.  
  388. // -- Object stuff
  389.  
  390. //info on the various types of objects
  391. #ifndef NDEBUG
  392. object    Object_minus_one;
  393. #endif
  394.  
  395. object Objects[MAX_OBJECTS];
  396. int num_objects=0;
  397. int Highest_object_index=0;
  398. int Highest_ever_object_index=0;
  399.  
  400. // grs_bitmap *robot_bms[MAX_ROBOT_BITMAPS];    //all bitmaps for all robots
  401.  
  402. // int robot_bm_nums[MAX_ROBOT_TYPES];        //starting bitmap num for each robot
  403. // int robot_n_bitmaps[MAX_ROBOT_TYPES];        //how many bitmaps for each robot
  404.  
  405. // char *robot_names[MAX_ROBOT_TYPES];        //name of each robot
  406.  
  407. //--unused-- int Num_robot_types=0;
  408.  
  409. int print_object_info = 0;
  410. //@@int Object_viewer = 0;
  411.  
  412. //object * Slew_object = NULL;    // Object containing slew object info.
  413.  
  414. //--unused-- int Player_controller_type = 0;
  415.  
  416. //    List of objects rendered last frame in order.  Created at render time, used by homing missiles in laser.c
  417. short ordered_rendered_object_list[MAX_RENDERED_OBJECTS];
  418. int    Num_rendered_objects = 0;
  419.  
  420. #ifndef NDEBUG
  421. char    Object_type_names[MAX_OBJECT_TYPES][9] = {
  422.     "WALL    ",
  423.     "FIREBALL",
  424.     "ROBOT   ",
  425.     "HOSTAGE ",
  426.     "PLAYER  ",
  427.     "WEAPON  ",
  428.     "CAMERA  ",
  429.     "POWERUP ",
  430.     "DEBRIS  ",
  431.     "CNTRLCEN",
  432.     "FLARE   ",
  433.     "CLUTTER ",
  434.     "GHOST   ",
  435.     "LIGHT   ",
  436.     "COOP    ",
  437. };
  438. #endif
  439.  
  440. #ifndef RELEASE
  441. //set viewer object to next object in array
  442. void object_goto_next_viewer()
  443. {
  444.     int i, start_obj = 0;
  445.  
  446.     start_obj = Viewer - Objects;        //get viewer object number
  447.     
  448.     for (i=0;i<=Highest_object_index;i++) {
  449.  
  450.         start_obj++;
  451.         if (start_obj > Highest_object_index ) start_obj = 0;
  452.  
  453.         if (Objects[start_obj].type != OBJ_NONE )    {
  454.             Viewer = &Objects[start_obj];
  455.             return;
  456.         }
  457.     }
  458.  
  459.     Error( "Couldn't find a viewer object!" );
  460.  
  461. }
  462. #endif
  463.  
  464. //draw an object that has one bitmap & doesn't rotate
  465. void draw_object_blob(object *obj,bitmap_index bmi)
  466. {
  467.     grs_bitmap * bm = &GameBitmaps[bmi.index];
  468.     PIGGY_PAGE_IN( bmi );
  469.  
  470.     if (bm->bm_w > bm->bm_h)
  471.  
  472.         g3_draw_bitmap(&obj->pos,obj->size,fixmuldiv(obj->size,bm->bm_h,bm->bm_w),bm);
  473.  
  474.     else 
  475.  
  476.         g3_draw_bitmap(&obj->pos,fixmuldiv(obj->size,bm->bm_w,bm->bm_h),obj->size,bm);
  477.  
  478. }
  479.  
  480. //draw an object that is a texture-mapped rod
  481. void draw_object_tmap_rod(object *obj,bitmap_index bitmapi,int lighted)
  482. {
  483.     grs_bitmap * bitmap = &GameBitmaps[bitmapi.index];
  484.     fix light;
  485.  
  486.     vms_vector delta,top_v,bot_v;
  487.     g3s_point top_p,bot_p;
  488.  
  489.     PIGGY_PAGE_IN(bitmapi);
  490.  
  491.     vm_vec_copy_scale(&delta,&obj->orient.uvec,obj->size);
  492.  
  493.     vm_vec_add(&top_v,&obj->pos,&delta);
  494.     vm_vec_sub(&bot_v,&obj->pos,&delta);
  495.  
  496.     g3_rotate_point(&top_p,&top_v);
  497.     g3_rotate_point(&bot_p,&bot_v);
  498.  
  499.     if (lighted)
  500.         light = compute_object_light(obj,&top_p.p3_vec);
  501.     else
  502.         light = f1_0;
  503.  
  504.     g3_draw_rod_tmap(bitmap,&bot_p,obj->size,&top_p,obj->size,light);
  505.  
  506. }
  507.  
  508. int    Linear_tmap_polygon_objects = 1;
  509.  
  510. extern fix Max_thrust;
  511.  
  512. //used for robot engine glow
  513. #define MAX_VELOCITY i2f(50)
  514.  
  515. //function that takes the same parms as draw_tmap, but renders as flat poly
  516. //we need this to do the cloaked effect
  517. extern void draw_tmap_flat();
  518.  
  519. //what darkening level to use when cloaked
  520. #define CLOAKED_FADE_LEVEL        28
  521.  
  522. #define    CLOAK_FADEIN_DURATION_PLAYER    F2_0
  523. #define    CLOAK_FADEOUT_DURATION_PLAYER    F2_0
  524.  
  525. #define    CLOAK_FADEIN_DURATION_ROBOT    F1_0
  526. #define    CLOAK_FADEOUT_DURATION_ROBOT    F1_0
  527.  
  528. fix    Cloak_fadein_duration;
  529. fix    Cloak_fadeout_duration;
  530.  
  531. //do special cloaked render
  532. draw_cloaked_object(object *obj,fix light,fix *glow,fix cloak_start_time,fix cloak_end_time,bitmap_index * alt_textures)
  533. {
  534.     fix cloak_delta_time,total_cloaked_time;
  535.     fix light_scale;
  536.     int cloak_value;
  537.     int fading=0;        //if true, fading, else cloaking
  538.  
  539.     total_cloaked_time = cloak_end_time-cloak_start_time;
  540.  
  541.     switch (obj->type) {
  542.         case OBJ_PLAYER:
  543.             Cloak_fadein_duration = CLOAK_FADEIN_DURATION_PLAYER;
  544.             Cloak_fadeout_duration = CLOAK_FADEOUT_DURATION_PLAYER;
  545.             break;
  546.         case OBJ_ROBOT:
  547.             Cloak_fadein_duration = CLOAK_FADEIN_DURATION_ROBOT;
  548.             Cloak_fadeout_duration = CLOAK_FADEOUT_DURATION_ROBOT;
  549.             break;
  550.         default:
  551.             Int3();        //    Contact Mike: Unexpected object type in draw_cloaked_object.
  552.     }
  553.  
  554.     cloak_delta_time = GameTime - cloak_start_time;
  555.  
  556.     if (cloak_delta_time < Cloak_fadein_duration/2) {
  557.  
  558.         light_scale = Cloak_fadein_duration/2 - cloak_delta_time;
  559.         fading = 1;
  560.     }
  561.     else if (cloak_delta_time < Cloak_fadein_duration) {
  562.  
  563.         cloak_value = f2i((cloak_delta_time - Cloak_fadein_duration/2) * CLOAKED_FADE_LEVEL);
  564.  
  565.     } else if (GameTime < cloak_end_time-Cloak_fadeout_duration) {
  566.         static int cloak_delta=0,cloak_dir=1;
  567.         static fix cloak_timer=0;
  568.  
  569.         //note, if more than one cloaked object is visible at once, the
  570.         //pulse rate will change!
  571.  
  572.         cloak_timer -= FrameTime;
  573.         while (cloak_timer < 0) {
  574.  
  575.             cloak_timer += Cloak_fadeout_duration/12;
  576.  
  577.             cloak_delta += cloak_dir;
  578.  
  579.             if (cloak_delta==0 || cloak_delta==4)
  580.                 cloak_dir = -cloak_dir;
  581.         }
  582.  
  583.         cloak_value = CLOAKED_FADE_LEVEL - cloak_delta;
  584.     
  585.     } else if (GameTime < cloak_end_time-Cloak_fadeout_duration/2) {
  586.  
  587.         cloak_value = f2i((total_cloaked_time - Cloak_fadeout_duration/2 - cloak_delta_time) * CLOAKED_FADE_LEVEL);
  588.  
  589.     } else {
  590.  
  591.         light_scale = Cloak_fadeout_duration/2 - (total_cloaked_time - cloak_delta_time);
  592.         fading = 1;
  593.     }
  594.  
  595.  
  596.     if (fading) {
  597.         fix new_light,new_glow;
  598.  
  599.         new_light = fixmul(light,light_scale);
  600.         new_glow = fixmul(*glow,light_scale);
  601.         draw_polygon_model(&obj->pos,&obj->orient,&obj->rtype.pobj_info.anim_angles,obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,new_light,&new_glow, alt_textures );
  602.     }
  603.     else {
  604.         Gr_scanline_darkening_level = cloak_value;
  605.         g3_set_special_render(draw_tmap_flat,NULL,NULL);        //use special flat drawer
  606.         draw_polygon_model(&obj->pos,&obj->orient,&obj->rtype.pobj_info.anim_angles,obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,light,glow, alt_textures );
  607.         g3_set_special_render(NULL,NULL,NULL);
  608.         Gr_scanline_darkening_level = GR_FADE_LEVELS;
  609.     }
  610.  
  611. }
  612.  
  613. //draw an object which renders as a polygon model
  614. void draw_polygon_object(object *obj)
  615. {
  616.     fix light;
  617.     int    imsave;
  618.     fix engine_glow_value;
  619.  
  620.     light = compute_object_light(obj,NULL);
  621.  
  622.     imsave = Interpolation_method;
  623.     if (Linear_tmap_polygon_objects)
  624.         Interpolation_method = 1;
  625.  
  626.     //set engine glow value
  627.     engine_glow_value = f1_0/5;
  628.     if (obj->movement_type == MT_PHYSICS) {
  629.  
  630.         if (obj->mtype.phys_info.flags & PF_USES_THRUST && obj->type==OBJ_PLAYER && obj->id==Player_num) {
  631.             fix thrust_mag = vm_vec_mag_quick(&obj->mtype.phys_info.thrust);
  632.             engine_glow_value += (fixdiv(thrust_mag,Player_ship->max_thrust)*4)/5;
  633.         }
  634.         else {
  635.             fix speed = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
  636.             engine_glow_value += (fixdiv(speed,MAX_VELOCITY)*4)/5;
  637.         }
  638.     }
  639.  
  640.     if (obj->rtype.pobj_info.tmap_override != -1) {
  641.         polymodel *pm = &Polygon_models[obj->rtype.pobj_info.model_num];
  642.         bitmap_index bm_ptrs[10];
  643.  
  644.         int i;
  645.  
  646.         Assert(pm->n_textures<=10);
  647.  
  648.         for (i=0;i<pm->n_textures;i++)
  649.             bm_ptrs[i] = Textures[obj->rtype.pobj_info.tmap_override];
  650.  
  651.         draw_polygon_model(&obj->pos,&obj->orient,&obj->rtype.pobj_info.anim_angles,obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,light,&engine_glow_value,bm_ptrs);
  652.     }
  653.     else {
  654.         bitmap_index * alt_textures = NULL;
  655.     
  656.         #ifdef NETWORK
  657.         if ( obj->rtype.pobj_info.alt_textures > 0 )
  658.             alt_textures = multi_player_textures[obj->rtype.pobj_info.alt_textures-1];
  659.         #endif
  660.  
  661.         if (obj->type==OBJ_PLAYER && (Players[obj->id].flags&PLAYER_FLAGS_CLOAKED))
  662.             draw_cloaked_object(obj,light,&engine_glow_value,Players[obj->id].cloak_time,Players[obj->id].cloak_time+CLOAK_TIME_MAX,alt_textures);
  663.         else if ((obj->type == OBJ_ROBOT) && (obj->ctype.ai_info.CLOAKED)) {
  664.             if (Robot_info[obj->id].boss_flag)
  665.                 draw_cloaked_object(obj,light,&engine_glow_value, Boss_cloak_start_time, Boss_cloak_end_time,alt_textures);
  666.             else
  667.                 draw_cloaked_object(obj,light,&engine_glow_value, GameTime-F1_0*10, GameTime+F1_0*10,alt_textures);
  668.         } else {
  669.             draw_polygon_model(&obj->pos,&obj->orient,&obj->rtype.pobj_info.anim_angles,obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,light,&engine_glow_value,alt_textures);
  670.             if (obj->type == OBJ_WEAPON && (Weapon_info[obj->id].model_num_inner > -1 )) {
  671.                 fix dist_to_eye = vm_vec_dist_quick(&Viewer->pos, &obj->pos);
  672.                 if (dist_to_eye < Simple_model_threshhold_scale * F1_0*2)
  673.                     draw_polygon_model(&obj->pos,&obj->orient,&obj->rtype.pobj_info.anim_angles,Weapon_info[obj->id].model_num_inner,obj->rtype.pobj_info.subobj_flags,light,&engine_glow_value,alt_textures);
  674.             }
  675.         }
  676.     }
  677.  
  678.     Interpolation_method = imsave;
  679.  
  680. }
  681.  
  682. //------------------------------------------------------------------------------
  683. // These variables are used to keep a list of the 3 closest robots to the viewer.
  684. // The code works like this: Every time render object is called with a polygon model,
  685. // it finds the distance of that robot to the viewer.  If this distance if within 10
  686. // segments of the viewer, it does the following: If there aren't already 3 robots in
  687. // the closet-robots list, it just sticks that object into the list along with its distance.
  688. // If the list already contains 3 robots, then it finds the robot in that list that is
  689. // farthest from the viewer. If that object is farther than the object currently being
  690. // rendered, then the new object takes over that far object's slot.  *Then* after all 
  691. // objects are rendered, object_render_targets is called an it draws a target on top
  692. // of all the objects.
  693.  
  694. //091494: #define MAX_CLOSE_ROBOTS 3
  695. //--unused-- static int Object_draw_lock_boxes = 0;
  696. //091494: static int Object_num_close = 0;
  697. //091494: static object * Object_close_ones[MAX_CLOSE_ROBOTS];
  698. //091494: static fix Object_close_distance[MAX_CLOSE_ROBOTS];
  699.  
  700. //091494: set_close_objects(object *obj)
  701. //091494: {
  702. //091494:     fix dist;
  703. //091494: 
  704. //091494:     if ( (obj->type != OBJ_ROBOT) || (Object_draw_lock_boxes==0) )    
  705. //091494:         return;
  706. //091494: 
  707. //091494:     // The following code keeps a list of the 10 closest robots to the 
  708. //091494:     // viewer.  See comments in front of this function for how this works.
  709. //091494:     dist = vm_vec_dist( &obj->pos, &Viewer->pos );
  710. //091494:     if ( dist < i2f(20*10) )    {                
  711. //091494:         if ( Object_num_close < MAX_CLOSE_ROBOTS )    {
  712. //091494:             Object_close_ones[Object_num_close] = obj;
  713. //091494:             Object_close_distance[Object_num_close] = dist;
  714. //091494:             Object_num_close++;
  715. //091494:         } else {
  716. //091494:             int i, farthest_robot;
  717. //091494:             fix farthest_distance;
  718. //091494:             // Find the farthest robot in the list
  719. //091494:             farthest_robot = 0;
  720. //091494:             farthest_distance = Object_close_distance[0];
  721. //091494:             for (i=1; i<Object_num_close; i++ )    {
  722. //091494:                 if ( Object_close_distance[i] > farthest_distance )    {
  723. //091494:                     farthest_distance = Object_close_distance[i];
  724. //091494:                     farthest_robot = i;
  725. //091494:                 }
  726. //091494:             }
  727. //091494:             // If this object is closer to the viewer than 
  728. //091494:             // the farthest in the list, replace the farthest with this object.
  729. //091494:             if ( farthest_distance > dist )    {
  730. //091494:                 Object_close_ones[farthest_robot] = obj;
  731. //091494:                 Object_close_distance[farthest_robot] = dist;
  732. //091494:             }
  733. //091494:         }
  734. //091494:     }
  735. //091494: }
  736.  
  737. int    Player_fired_laser_this_frame=-1;
  738.  
  739. // -----------------------------------------------------------------------------
  740. //this routine checks to see if an robot rendered near the middle of
  741. //the screen, and if so and the player had fired, "warns" the robot
  742. void set_robot_location_info(object *objp)
  743. {
  744.     if (Player_fired_laser_this_frame != -1) {
  745.         g3s_point temp;
  746.  
  747.         g3_rotate_point(&temp,&objp->pos);
  748.  
  749.         if (temp.p3_codes & CC_BEHIND)        //robot behind the screen
  750.             return;
  751.  
  752.         //the code below to check for object near the center of the screen
  753.         //completely ignores z, which may not be good
  754.  
  755.         if ((abs(temp.x) < F1_0*4) && (abs(temp.y) < F1_0*4)) {
  756.             objp->ctype.ai_info.danger_laser_num = Player_fired_laser_this_frame;
  757.             objp->ctype.ai_info.danger_laser_signature = Objects[Player_fired_laser_this_frame].signature;
  758.         }
  759.     }
  760.  
  761.  
  762. }
  763.  
  764. //    ------------------------------------------------------------------------------------------------------------------
  765. void create_small_fireball_on_object(object *objp, fix size_scale, int sound_flag)
  766. {
  767.     fix            size;
  768.     vms_vector    pos, rand_vec;
  769.     int            segnum;
  770.  
  771.     pos = objp->pos;
  772.     make_random_vector(&rand_vec);
  773.  
  774.     vm_vec_scale(&rand_vec, objp->size/2);
  775.  
  776.     vm_vec_add2(&pos, &rand_vec);
  777.  
  778.     size = fixmul(size_scale, F1_0 + rand()*4);
  779.  
  780.     segnum = find_point_seg(&pos, objp->segnum);
  781.     if (segnum != -1) {
  782.         object *expl_obj;
  783.         expl_obj = object_create_explosion(segnum, &pos, size, VCLIP_SMALL_EXPLOSION);
  784.         if (!expl_obj)
  785.             return;
  786.         obj_attach(objp,expl_obj);
  787.         if (rand() < 8192) {
  788.             fix    vol = F1_0/2;
  789.             if (objp->type == OBJ_ROBOT)
  790.                 vol *= 2;
  791.             else if (sound_flag)
  792.                 digi_link_sound_to_object(SOUND_EXPLODING_WALL, objp-Objects, 0, vol);
  793.         }
  794.     }
  795. }
  796.  
  797. //    ------------------------------------------------------------------------------------------------------------------
  798. void create_vclip_on_object(object *objp, fix size_scale, int vclip_num)
  799. {
  800.     fix            size;
  801.     vms_vector    pos, rand_vec;
  802.     int            segnum;
  803.  
  804.     pos = objp->pos;
  805.     make_random_vector(&rand_vec);
  806.  
  807.     vm_vec_scale(&rand_vec, objp->size/2);
  808.  
  809.     vm_vec_add2(&pos, &rand_vec);
  810.  
  811.     size = fixmul(size_scale, F1_0 + rand()*4);
  812.  
  813.     segnum = find_point_seg(&pos, objp->segnum);
  814.     if (segnum != -1) {
  815.         object *expl_obj;
  816.         expl_obj = object_create_explosion(segnum, &pos, size, vclip_num);
  817.         if (!expl_obj)
  818.             return;
  819.  
  820.         expl_obj->movement_type = MT_PHYSICS;
  821.         expl_obj->mtype.phys_info.velocity.x = objp->mtype.phys_info.velocity.x/2;
  822.         expl_obj->mtype.phys_info.velocity.y = objp->mtype.phys_info.velocity.y/2;
  823.         expl_obj->mtype.phys_info.velocity.z = objp->mtype.phys_info.velocity.z/2;
  824.     }
  825. }
  826.  
  827. // -- mk, 02/05/95 -- #define    VCLIP_INVULNERABILITY_EFFECT    VCLIP_SMALL_EXPLOSION
  828. // -- mk, 02/05/95 -- 
  829. // -- mk, 02/05/95 -- // -----------------------------------------------------------------------------
  830. // -- mk, 02/05/95 -- void do_player_invulnerability_effect(object *objp)
  831. // -- mk, 02/05/95 -- {
  832. // -- mk, 02/05/95 --     if (rand() < FrameTime*8) {
  833. // -- mk, 02/05/95 --         create_vclip_on_object(objp, F1_0, VCLIP_INVULNERABILITY_EFFECT);
  834. // -- mk, 02/05/95 --     }
  835. // -- mk, 02/05/95 -- }
  836.  
  837. // -----------------------------------------------------------------------------
  838. //    Render an object.  Calls one of several routines based on type
  839. void render_object(object *obj)
  840. {
  841.     int    mld_save;
  842.  
  843.     if ( obj == Viewer ) return;        
  844.  
  845.     if ( obj->type==OBJ_NONE )    {
  846.         #ifndef NDEBUG
  847.         mprintf( (1, "ERROR!!!! Bogus obj %d in seg %d is rendering!\n", obj-Objects, obj->segnum ));
  848.         Int3();
  849.         #endif
  850.         return;
  851.     }
  852.  
  853.     mld_save = Max_linear_depth;
  854.     Max_linear_depth = Max_linear_depth_objects;
  855.  
  856.     switch (obj->render_type) {
  857.  
  858.         case RT_NONE:    break;        //doesn't render, like the player
  859.  
  860.         case RT_POLYOBJ:
  861.  
  862.             draw_polygon_object(obj); 
  863.  
  864.             //"warn" robot if being shot at
  865.             if (obj->type == OBJ_ROBOT)
  866.                 set_robot_location_info(obj);
  867.  
  868. //JOHN SAID TO:            if ( (obj->type==OBJ_PLAYER) && ((keyd_pressed[KEY_W]) || (keyd_pressed[KEY_I])))
  869. //JOHN SAID TO:                object_render_id(obj);
  870.  
  871. // -- mk, 02/05/95 --             if (obj->type == OBJ_PLAYER)
  872. // -- mk, 02/05/95 --                 if (Players[obj->id].flags & PLAYER_FLAGS_INVULNERABLE)
  873. // -- mk, 02/05/95 --                     do_player_invulnerability_effect(obj);
  874.  
  875.             break;
  876.  
  877.         case RT_MORPH:    draw_morph_object(obj); break;
  878.  
  879.         case RT_FIREBALL: draw_fireball(obj); break;
  880.  
  881.         case RT_WEAPON_VCLIP: draw_weapon_vclip(obj); break;
  882.  
  883.         case RT_HOSTAGE: draw_hostage(obj); break;
  884.  
  885.         case RT_POWERUP: draw_powerup(obj); break;
  886.  
  887.         case RT_LASER: Laser_render(obj); break;
  888.  
  889.         default: Error("Unknown render_type <%d>",obj->render_type);
  890.      }
  891.  
  892.     #ifdef NEWDEMO
  893.     if ( obj->render_type != RT_NONE )
  894.         if ( Newdemo_state == ND_STATE_RECORDING )
  895.             newdemo_record_render_object(obj);
  896.     #endif
  897.  
  898.     Max_linear_depth = mld_save;
  899.  
  900. }
  901.  
  902. //--unused-- void object_toggle_lock_targets()    {
  903. //--unused--     Object_draw_lock_boxes ^= 1;
  904. //--unused-- }
  905.  
  906. //091494: //draw target boxes for nearby robots
  907. //091494: void object_render_targets()
  908. //091494: {
  909. //091494:     g3s_point pt;
  910. //091494:     ubyte codes;
  911. //091494:     int i;
  912. //091494:     int radius,x,y;
  913. //091494: 
  914. //091494:     if (Object_draw_lock_boxes==0) 
  915. //091494:         return;
  916. //091494: 
  917. //091494:     for (i=0; i<Object_num_close; i++ )    {
  918. //091494:             
  919. //091494:         codes = g3_rotate_point(&pt, &Object_close_ones[i]->pos );
  920. //091494:         if ( !(codes & CC_BEHIND) )    {
  921. //091494:             g3_project_point(&pt);
  922. //091494:             if (pt.p3_flags & PF_PROJECTED)    {
  923. //091494:                 x = f2i(pt.p3_sx);
  924. //091494:                 y = f2i(pt.p3_sy);
  925. //091494:                 radius = f2i(fixdiv((grd_curcanv->cv_bitmap.bm_w*Object_close_ones[i]->size)/8,pt.z));
  926. //091494:                 gr_setcolor( BM_XRGB(0,31,0) );
  927. //091494:                 gr_box(x-radius,y-radius,x+radius,y+radius);
  928. //091494:             }
  929. //091494:         }
  930. //091494:     }
  931. //091494:     Object_num_close=0;
  932. //091494: }
  933.  
  934.  
  935. //--unused-- //draw target boxes for nearby robots
  936. //--unused-- void object_render_id(object * obj)
  937. //--unused-- {
  938. //--unused--     g3s_point pt;
  939. //--unused--     ubyte codes;
  940. //--unused--     int x,y;
  941. //--unused--     int w, h, aw;
  942. //--unused--     char s[20], *s1;
  943. //--unused-- 
  944. //--unused--     s1 = network_get_player_name( obj-Objects );
  945. //--unused-- 
  946. //--unused--     if (s1)
  947. //--unused--         sprintf( s, "%s", s1 );
  948. //--unused--     else
  949. //--unused--         sprintf( s, "<%d>", obj->id );
  950. //--unused-- 
  951. //--unused--     codes = g3_rotate_point(&pt, &obj->pos );
  952. //--unused--     if ( !(codes & CC_BEHIND) )    {
  953. //--unused--         g3_project_point(&pt);
  954. //--unused--         if (pt.p3_flags & PF_PROJECTED)    {
  955. //--unused--             gr_get_string_size( s, &w, &h, &aw );
  956. //--unused--             x = f2i(pt.p3_sx) - w/2;
  957. //--unused--             y = f2i(pt.p3_sy) - h/2;
  958. //--unused--             if ( x>= 0 && y>=0 && (x+w)<=grd_curcanv->cv_bitmap.bm_w && (y+h)<grd_curcanv->cv_bitmap.bm_h )    {
  959. //--unused--                 gr_set_fontcolor( BM_XRGB(0,31,0), -1 );
  960. //--unused--                 gr_string( x, y, s );
  961. //--unused--             }
  962. //--unused--         }
  963. //--unused--     }
  964. //--unused-- }
  965.  
  966.  
  967. check_and_fix_matrix(vms_matrix *m);
  968.  
  969. #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0
  970.  
  971. void reset_player_object()
  972. {
  973.     int i;
  974.  
  975.     //Init physics
  976.  
  977.     vm_vec_zero(&ConsoleObject->mtype.phys_info.velocity);
  978.     vm_vec_zero(&ConsoleObject->mtype.phys_info.thrust);
  979.     vm_vec_zero(&ConsoleObject->mtype.phys_info.rotvel);
  980.     vm_vec_zero(&ConsoleObject->mtype.phys_info.rotthrust);
  981.     ConsoleObject->mtype.phys_info.brakes = ConsoleObject->mtype.phys_info.turnroll = 0;
  982.     ConsoleObject->mtype.phys_info.mass = Player_ship->mass;
  983.     ConsoleObject->mtype.phys_info.drag = Player_ship->drag;
  984.     ConsoleObject->mtype.phys_info.flags |= PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST;
  985.  
  986.     //Init render info
  987.  
  988.     ConsoleObject->render_type = RT_POLYOBJ;
  989.     ConsoleObject->rtype.pobj_info.model_num = Player_ship->model_num;        //what model is this?
  990.     ConsoleObject->rtype.pobj_info.subobj_flags = 0;        //zero the flags
  991.     ConsoleObject->rtype.pobj_info.tmap_override = -1;        //no tmap override!
  992.  
  993.     for (i=0;i<MAX_SUBMODELS;i++)
  994.         vm_angvec_zero(&ConsoleObject->rtype.pobj_info.anim_angles[i]);
  995.  
  996.     // Clear misc
  997.  
  998.     ConsoleObject->flags = 0;
  999.  
  1000. }
  1001.  
  1002.  
  1003. //make object0 the player, setting all relevant fields
  1004. void init_player_object()
  1005. {
  1006.     ConsoleObject->type = OBJ_PLAYER;
  1007.     ConsoleObject->id = 0;                    //no sub-types for player
  1008.  
  1009.     ConsoleObject->size = Polygon_models[Player_ship->model_num].rad;
  1010.  
  1011.     ConsoleObject->control_type = CT_SLEW;            //default is player slewing
  1012.     ConsoleObject->movement_type = MT_PHYSICS;        //change this sometime
  1013.  
  1014.     ConsoleObject->lifeleft = IMMORTAL_TIME;
  1015.  
  1016.     ConsoleObject->attached_obj = -1;
  1017.  
  1018.     reset_player_object();
  1019.  
  1020. }
  1021.  
  1022. //sets up the free list & init player & whatever else
  1023. void init_objects()
  1024. {
  1025.     int i;
  1026.  
  1027.     collide_init();
  1028.  
  1029.     for (i=0;i<MAX_OBJECTS;i++) {
  1030.         free_obj_list[i] = i;
  1031.         Objects[i].type = OBJ_NONE;
  1032.         Objects[i].segnum = -1;
  1033.     }
  1034.  
  1035.     for (i=0;i<MAX_SEGMENTS;i++)
  1036.         Segments[i].objects = -1;
  1037.  
  1038.     ConsoleObject = Viewer = &Objects[0];
  1039.  
  1040.     init_player_object();
  1041.     obj_link(ConsoleObject-Objects,0);    //put in the world in segment 0
  1042.  
  1043.     num_objects = 1;                        //just the player
  1044.     Highest_object_index = 0;
  1045.  
  1046.     
  1047. }
  1048.  
  1049. //after calling init_object(), the network code has grabbed specific
  1050. //object slots without allocating them.  Go though the objects & build
  1051. //the free list, then set the apporpriate globals
  1052. void special_reset_objects(void)
  1053. {
  1054.     int i;
  1055.  
  1056.     num_objects=MAX_OBJECTS;
  1057.  
  1058.     Highest_object_index = 0;
  1059.     Assert(Objects[0].type != OBJ_NONE);        //0 should be used
  1060.  
  1061.     for (i=MAX_OBJECTS;i--;)
  1062.         if (Objects[i].type == OBJ_NONE)
  1063.             free_obj_list[--num_objects] = i;
  1064.         else
  1065.             if (i > Highest_object_index)
  1066.                 Highest_object_index = i;
  1067. }
  1068.  
  1069. #ifndef NDEBUG
  1070. int is_object_in_seg( int segnum, int objn )
  1071. {
  1072.     int objnum, count = 0;
  1073.  
  1074.     for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next)    {
  1075.         if ( count > MAX_OBJECTS )     {
  1076.             Int3();
  1077.             return count;
  1078.         }
  1079.         if ( objnum==objn ) count++;
  1080.     }
  1081.      return count;
  1082. }
  1083.  
  1084. int search_all_segments_for_object( int objnum )
  1085. {
  1086.     int i;
  1087.     int count = 0;
  1088.  
  1089.     for (i=0; i<=Highest_segment_index; i++) {
  1090.         count += is_object_in_seg( i, objnum );
  1091.     }
  1092.     return count;
  1093. }
  1094.  
  1095. void johns_obj_unlink(int segnum, int objnum)
  1096. {
  1097.     object  *obj = &Objects[objnum];
  1098.     segment *seg = &Segments[segnum];
  1099.  
  1100.     Assert(objnum != -1);
  1101.  
  1102.     if (obj->prev == -1)
  1103.         seg->objects = obj->next;
  1104.     else
  1105.         Objects[obj->prev].next = obj->next;
  1106.  
  1107.     if (obj->next != -1) Objects[obj->next].prev = obj->prev;
  1108. }
  1109.  
  1110. void remove_incorrect_objects()
  1111. {
  1112.     int segnum, objnum, count;
  1113.  
  1114.     for (segnum=0; segnum <= Highest_segment_index; segnum++) {
  1115.         count = 0;
  1116.         for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next)    {
  1117.             count++;
  1118.             #ifndef NDEBUG
  1119.             if ( count > MAX_OBJECTS )    {
  1120.                 mprintf((1, "Object list in segment %d is circular.\n", segnum ));
  1121.                 Int3();
  1122.             }
  1123.             #endif
  1124.             if (Objects[objnum].segnum != segnum )    {
  1125.                 #ifndef NDEBUG
  1126.                 mprintf((0, "Removing object %d from segment %d.\n", objnum, segnum ));
  1127.                 Int3();
  1128.                 #endif
  1129.                 johns_obj_unlink(segnum,objnum);
  1130.             }
  1131.         }
  1132.     }
  1133. }
  1134.  
  1135. void remove_all_objects_but( int segnum, int objnum )
  1136. {
  1137.     int i;
  1138.  
  1139.     for (i=0; i<=Highest_segment_index; i++) {
  1140.         if (segnum != i )    {
  1141.             if (is_object_in_seg( i, objnum ))    {
  1142.                 johns_obj_unlink( i, objnum );
  1143.             }
  1144.         }
  1145.     }
  1146. }
  1147.  
  1148. int check_duplicate_objects()
  1149. {
  1150.     int i, count=0;
  1151.     
  1152.     for (i=0;i<=Highest_object_index;i++) {
  1153.         if ( Objects[i].type != OBJ_NONE )    {
  1154.             count = search_all_segments_for_object( i );
  1155.             if ( count > 1 )    {
  1156.                 #ifndef NDEBUG
  1157.                 mprintf((1, "Object %d is in %d segments!\n", i, count ));
  1158.                 Int3();
  1159.                 #endif
  1160.                 remove_all_objects_but( Objects[i].segnum,  i );
  1161.                 return count;
  1162.             }
  1163.         }
  1164.     }
  1165.     return count;
  1166. }
  1167.  
  1168. void list_seg_objects( int segnum )
  1169. {
  1170.     int objnum, count = 0;
  1171.  
  1172.     for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next)    {
  1173.         count++;
  1174.         if ( count > MAX_OBJECTS )     {
  1175.             Int3();
  1176.             return;
  1177.         }
  1178.     }
  1179.     return;
  1180.  
  1181. }
  1182. #endif
  1183.  
  1184. //link the object into the list for its segment
  1185. void obj_link(int objnum,int segnum)
  1186. {
  1187.     object *obj = &Objects[objnum];
  1188.  
  1189.     Assert(objnum != -1);
  1190.  
  1191.     Assert(obj->segnum == -1);
  1192.  
  1193.     Assert(segnum>=0 && segnum<=Highest_segment_index);
  1194.  
  1195.     obj->segnum = segnum;
  1196.     
  1197.     obj->next = Segments[segnum].objects;
  1198.     obj->prev = -1;
  1199.  
  1200.     Segments[segnum].objects = objnum;
  1201.  
  1202.     if (obj->next != -1) Objects[obj->next].prev = objnum;
  1203.     
  1204.     //list_seg_objects( segnum );
  1205.     //check_duplicate_objects();
  1206.  
  1207.     Assert(Objects[0].next != 0);
  1208.     if (Objects[0].next == 0)
  1209.         Objects[0].next = -1;
  1210.  
  1211.     Assert(Objects[0].prev != 0);
  1212.     if (Objects[0].prev == 0)
  1213.         Objects[0].prev = -1;
  1214. }
  1215.  
  1216. void obj_unlink(int objnum)
  1217. {
  1218.     object  *obj = &Objects[objnum];
  1219.     segment *seg = &Segments[obj->segnum];
  1220.  
  1221.     Assert(objnum != -1);
  1222.  
  1223.     if (obj->prev == -1)
  1224.         seg->objects = obj->next;
  1225.     else
  1226.         Objects[obj->prev].next = obj->next;
  1227.  
  1228.     if (obj->next != -1) Objects[obj->next].prev = obj->prev;
  1229.  
  1230.     obj->segnum = -1;
  1231.  
  1232.     Assert(Objects[0].next != 0);
  1233.     Assert(Objects[0].prev != 0);
  1234. }
  1235.  
  1236. int Object_next_signature = 0;
  1237.  
  1238. int Debris_object_count=0;
  1239.  
  1240. int    Unused_object_slots;
  1241.  
  1242. //returns the number of a free object, updating Highest_object_index.
  1243. //Generally, obj_create() should be called to get an object, since it
  1244. //fills in important fields and does the linking.
  1245. //returns -1 if no free objects
  1246. int obj_allocate(void)
  1247. {
  1248.     int objnum;
  1249.  
  1250.     if ( num_objects >= MAX_OBJECTS ) {
  1251.         #ifndef NDEBUG
  1252.         mprintf((1, "Object creation failed - too many objects!\n" ));
  1253.         #endif
  1254.         return -1;
  1255.     }
  1256.  
  1257.     objnum = free_obj_list[num_objects++];
  1258.  
  1259.     if (objnum > Highest_object_index) {
  1260.         Highest_object_index = objnum;
  1261.         if (Highest_object_index > Highest_ever_object_index)
  1262.             Highest_ever_object_index = Highest_object_index;
  1263.     }
  1264.  
  1265. {
  1266. int    i;
  1267. Unused_object_slots=0;
  1268. for (i=0; i<=Highest_object_index; i++)
  1269.     if (Objects[i].type == OBJ_NONE)
  1270.         Unused_object_slots++;
  1271. }
  1272.     return objnum;
  1273. }
  1274.  
  1275. //frees up an object.  Generally, obj_delete() should be called to get
  1276. //rid of an object.  This function deallocates the object entry after
  1277. //the object has been unlinked
  1278. void obj_free(int objnum)
  1279. {
  1280.     free_obj_list[--num_objects] = objnum;
  1281.     Assert(num_objects >= 0);
  1282.  
  1283.     if (objnum == Highest_object_index)
  1284.         while (Objects[--Highest_object_index].type == OBJ_NONE);
  1285. }
  1286.  
  1287. //-----------------------------------------------------------------------------
  1288. //    Scan the object list, freeing down to num_used objects
  1289. void free_object_slots(int num_used)
  1290. {
  1291.     int    i, olind;
  1292.     int    obj_list[MAX_OBJECTS];
  1293.     int    num_already_free, num_to_free;
  1294.  
  1295.     olind = 0;
  1296.     num_already_free = MAX_OBJECTS - Highest_object_index - 1;
  1297.  
  1298.     if (MAX_OBJECTS - num_already_free < num_used)
  1299.         return;
  1300.  
  1301.     for (i=0; i<=Highest_object_index; i++) {
  1302.         if (Objects[i].flags & OF_SHOULD_BE_DEAD)
  1303.             num_already_free++;
  1304.         else
  1305.             switch (Objects[i].type) {
  1306.                 case OBJ_NONE:
  1307.                     num_already_free++;
  1308.                     if (MAX_OBJECTS - num_already_free < num_used)
  1309.                         return;
  1310.                     break;
  1311.                 case OBJ_WALL:
  1312.                 case OBJ_FLARE:
  1313.                     Int3();        //    This is curious.  What is an object that is a wall?
  1314.                     break;
  1315.                 case OBJ_FIREBALL:
  1316.                 case OBJ_WEAPON:
  1317.                 case OBJ_DEBRIS:
  1318.                     obj_list[olind++] = i;
  1319.                     break;
  1320.                 case OBJ_ROBOT:
  1321.                 case OBJ_HOSTAGE:
  1322.                 case OBJ_PLAYER:
  1323.                 case OBJ_CNTRLCEN:
  1324.                 case OBJ_CLUTTER:
  1325.                 case OBJ_GHOST:
  1326.                 case OBJ_LIGHT:
  1327.                 case OBJ_CAMERA:
  1328.                 case OBJ_POWERUP:
  1329.                     break;
  1330.             }
  1331.  
  1332.     }
  1333.  
  1334.     num_to_free = MAX_OBJECTS - num_used - num_already_free;
  1335.  
  1336.     if (num_to_free > olind) {
  1337.         mprintf((1, "Warning: Asked to free %i objects, but can only free %i.\n", num_to_free, olind));
  1338.         num_to_free = olind;
  1339.     }
  1340.  
  1341.     for (i=0; i<num_to_free; i++)
  1342.         if (Objects[obj_list[i]].type == OBJ_DEBRIS) {
  1343.             num_to_free--;
  1344.             mprintf((0, "Freeing   DEBRIS object %3i\n", obj_list[i]));
  1345.             Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
  1346.         }
  1347.  
  1348.     if (!num_to_free)
  1349.         return;
  1350.  
  1351.     for (i=0; i<num_to_free; i++)
  1352.         if (Objects[obj_list[i]].type == OBJ_FIREBALL  &&  Objects[obj_list[i]].ctype.expl_info.delete_objnum==-1) {
  1353.             num_to_free--;
  1354.             mprintf((0, "Freeing FIREBALL object %3i\n", obj_list[i]));
  1355.             Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
  1356.         }
  1357.  
  1358.     if (!num_to_free)
  1359.         return;
  1360.  
  1361.     for (i=0; i<num_to_free; i++)
  1362.         if ((Objects[obj_list[i]].type == OBJ_WEAPON) && (Objects[obj_list[i]].id == FLARE_ID)) {
  1363.             num_to_free--;
  1364.             mprintf((0, "Freeing    FLARE object %3i\n", obj_list[i]));
  1365.             Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
  1366.         }
  1367.  
  1368.     if (!num_to_free)
  1369.         return;
  1370.  
  1371.     for (i=0; i<num_to_free; i++)
  1372.         if ((Objects[obj_list[i]].type == OBJ_WEAPON) && (Objects[obj_list[i]].id != FLARE_ID)) {
  1373.             num_to_free--;
  1374.             mprintf((0, "Freeing   WEAPON object %3i\n", obj_list[i]));
  1375.             Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
  1376.         }
  1377.  
  1378. }
  1379.  
  1380. //-----------------------------------------------------------------------------
  1381. //initialize a new object.  adds to the list for the given segment
  1382. //note that segnum is really just a suggestion, since this routine actually
  1383. //searches for the correct segment
  1384. //returns the object number
  1385. int obj_create(ubyte type,ubyte id,int segnum,vms_vector *pos,
  1386.                 vms_matrix *orient,fix size,ubyte ctype,ubyte mtype,ubyte rtype)
  1387. {
  1388.     int objnum;
  1389.     object *obj;
  1390.  
  1391.     Assert((segnum <= Highest_segment_index) && (segnum >= 0));
  1392.     Assert(ctype <= CT_CNTRLCEN);
  1393.  
  1394.     if (type==OBJ_DEBRIS && Debris_object_count>=Max_debris_objects)
  1395.         return -1;
  1396.  
  1397.     if (get_seg_masks(pos,segnum,0).centermask!=0)
  1398.         if ((segnum=find_point_seg(pos,segnum))==-1) {
  1399.             #ifndef NDEBUG
  1400.             mprintf((0,"Bad segnum in obj_create (type=%d)\n",type));
  1401.             #endif
  1402.             return -1;        //don't create this object
  1403.         }
  1404.  
  1405.     // Find next free object
  1406.     objnum = obj_allocate();
  1407.  
  1408.     if (objnum == -1)        //no free objects
  1409.         return -1;
  1410.  
  1411.     Assert(Objects[objnum].type == OBJ_NONE);        //make sure unused 
  1412.  
  1413.     obj = &Objects[objnum];
  1414.  
  1415.     Assert(obj->segnum == -1);
  1416.  
  1417.     // Zero out object structure to keep weird bugs from happening
  1418.     // in uninitialized fields.
  1419.     memset( obj, 0, sizeof(object) );
  1420.  
  1421.     obj->signature                = Object_next_signature++;
  1422.     obj->type                     = type;
  1423.     obj->id                         = id;
  1424.     obj->last_pos                = *pos;
  1425.     obj->pos                     = *pos;
  1426.     obj->size                     = size;
  1427.     obj->flags                     = 0;
  1428.     if (orient != NULL) 
  1429.         obj->orient             = *orient;
  1430.  
  1431.     obj->control_type         = ctype;
  1432.     obj->movement_type         = mtype;
  1433.     obj->render_type             = rtype;
  1434.     obj->contains_type        = -1;
  1435.  
  1436.     obj->lifeleft                 = IMMORTAL_TIME;        //assume immortal
  1437.     obj->attached_obj            = -1;
  1438.  
  1439.     if (obj->control_type == CT_POWERUP)
  1440.         obj->ctype.powerup_info.count = 1;
  1441.  
  1442.     // Init physics info for this object
  1443.     if (obj->movement_type == MT_PHYSICS) {
  1444.  
  1445.         vm_vec_zero(&obj->mtype.phys_info.velocity);
  1446.         vm_vec_zero(&obj->mtype.phys_info.thrust);
  1447.         vm_vec_zero(&obj->mtype.phys_info.rotvel);
  1448.         vm_vec_zero(&obj->mtype.phys_info.rotthrust);
  1449.  
  1450.         obj->mtype.phys_info.mass        = 0;
  1451.         obj->mtype.phys_info.drag         = 0;
  1452.         obj->mtype.phys_info.brakes    = 0;
  1453.         obj->mtype.phys_info.turnroll    = 0;
  1454.         obj->mtype.phys_info.flags        = 0;
  1455.     }
  1456.  
  1457.     if (obj->render_type == RT_POLYOBJ)
  1458.         obj->rtype.pobj_info.tmap_override = -1;
  1459.  
  1460.     obj->shields                 = 20*F1_0;
  1461.  
  1462.     segnum = find_point_seg(pos,segnum);        //find correct segment
  1463.  
  1464.     Assert(segnum!=-1);
  1465.  
  1466.     obj->segnum = -1;                    //set to zero by memset, above
  1467.     obj_link(objnum,segnum);
  1468.  
  1469.     //    Set (or not) persistent bit in phys_info.
  1470.     if (obj->type == OBJ_WEAPON) {
  1471.         obj->mtype.phys_info.flags |= (Weapon_info[obj->id].persistent*PF_PERSISTENT);
  1472.         obj->ctype.laser_info.creation_time = GameTime;
  1473.         obj->ctype.laser_info.last_hitobj = -1;
  1474.         obj->ctype.laser_info.multiplier = F1_0;
  1475.     }
  1476.  
  1477.     if (obj->control_type == CT_EXPLOSION)
  1478.         obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
  1479.  
  1480.     #ifndef NDEBUG
  1481.     if (print_object_info)    
  1482.         mprintf( (0, "Created object %d of type %d\n", objnum, obj->type ));
  1483.     #endif
  1484.  
  1485.     if (obj->type == OBJ_DEBRIS)
  1486.         Debris_object_count++;
  1487.  
  1488.     return objnum;
  1489. }
  1490.  
  1491. #ifdef EDITOR
  1492. //create a copy of an object. returns new object number
  1493. int obj_create_copy(int objnum, vms_vector *new_pos, int newsegnum)
  1494. {
  1495.     int newobjnum;
  1496.     object *obj;
  1497.  
  1498.     // Find next free object
  1499.     newobjnum = obj_allocate();
  1500.  
  1501.     if (newobjnum == -1)
  1502.         return -1;
  1503.  
  1504.     obj = &Objects[newobjnum];
  1505.  
  1506.     *obj = Objects[objnum];
  1507.  
  1508.     obj->pos = obj->last_pos = *new_pos;
  1509.  
  1510.     obj->next = obj->prev = obj->segnum = -1;
  1511.  
  1512.     obj_link(newobjnum,newsegnum);
  1513.  
  1514.     obj->signature                = Object_next_signature++;
  1515.  
  1516.     //we probably should initialize sub-structures here
  1517.  
  1518.     return newobjnum;
  1519.  
  1520. }
  1521. #endif
  1522.  
  1523. //remove object from the world
  1524. void obj_delete(int objnum)
  1525. {
  1526.     object *obj = &Objects[objnum];
  1527.  
  1528.     Assert(objnum != -1);
  1529.     Assert(objnum != 0 );
  1530.     Assert(obj->type != OBJ_NONE);
  1531.     Assert(obj != ConsoleObject);
  1532.  
  1533.     if (obj == Viewer)        //deleting the viewer?
  1534.         Viewer = ConsoleObject;                        //..make the player the viewer
  1535.  
  1536.     if (obj->flags & OF_ATTACHED)        //detach this from object
  1537.         obj_detach_one(obj);
  1538.  
  1539.     if (obj->attached_obj != -1)        //detach all objects from this
  1540.         obj_detach_all(obj);
  1541.  
  1542.     #if !defined(NDEBUG) && !defined(NMONO)
  1543.     if (print_object_info) mprintf( (0, "Deleting object %d of type %d\n", objnum, Objects[objnum].type ));
  1544.     #endif
  1545.  
  1546.     if (obj->type == OBJ_DEBRIS)
  1547.         Debris_object_count--;
  1548.  
  1549.     obj_unlink(objnum);
  1550.  
  1551.     Assert(Objects[0].next != 0);
  1552.  
  1553.     obj->type = OBJ_NONE;        //unused!
  1554.     obj->signature = -1;
  1555.  
  1556.     obj_free(objnum);
  1557. }
  1558.  
  1559. #define    DEATH_SEQUENCE_LENGTH            (F1_0*5)
  1560. #define    DEATH_SEQUENCE_EXPLODE_TIME    (F1_0*2)
  1561.  
  1562. int        Player_is_dead = 0;            //    If !0, then player is dead, but game continues so he can watch.
  1563. object    *Dead_player_camera = NULL;    //    Object index of object watching deader.
  1564. fix        Player_time_of_death;        //    Time at which player died.
  1565. object    *Viewer_save;
  1566. int        Player_flags_save;
  1567. int        Player_exploded = 0;
  1568. int        Death_sequence_aborted=0;
  1569. int        Player_eggs_dropped=0;
  1570. fix        Camera_to_player_dist_goal=F1_0*4;
  1571.  
  1572. ubyte        Control_type_save, Render_type_save, cockpit_mode_save;
  1573.  
  1574. //    ------------------------------------------------------------------------------------------------------------------
  1575. void dead_player_end(void)
  1576. {
  1577.     if (!Player_is_dead)
  1578.         return;
  1579.  
  1580.     if (Newdemo_state == ND_STATE_RECORDING)
  1581.         newdemo_record_restore_cockpit();
  1582.  
  1583.     Player_is_dead = 0;
  1584.     Player_exploded = 0;
  1585.     obj_delete(Dead_player_camera-Objects);
  1586.     Dead_player_camera = NULL;
  1587.     select_cockpit(cockpit_mode_save);
  1588.     Viewer = Viewer_save;
  1589.     ConsoleObject->type = OBJ_PLAYER;
  1590.     ConsoleObject->flags = Player_flags_save;
  1591.  
  1592.     Assert((Control_type_save == CT_FLYING) || (Control_type_save == CT_SLEW));
  1593.  
  1594.     ConsoleObject->control_type = Control_type_save;
  1595.     ConsoleObject->render_type = Render_type_save;
  1596.     Players[Player_num].flags &= ~PLAYER_FLAGS_INVULNERABLE;
  1597.     Player_eggs_dropped = 0;
  1598.  
  1599. }
  1600.  
  1601. //    ------------------------------------------------------------------------------------------------------------------
  1602. //    Camera is less than size of player away from 
  1603. void set_camera_pos(vms_vector *camera_pos, object *objp)
  1604. {
  1605.     int    count = 0;
  1606.     fix    camera_player_dist;
  1607.     fix    far_scale;
  1608.  
  1609.     camera_player_dist = vm_vec_dist_quick(camera_pos, &objp->pos);
  1610.  
  1611.     if (camera_player_dist < Camera_to_player_dist_goal) { //2*objp->size) {
  1612.         //    Camera is too close to player object, so move it away.
  1613.         vms_vector    player_camera_vec;
  1614.         fvi_query    fq;
  1615.         fvi_info        hit_data;
  1616.         vms_vector    local_p1;
  1617.  
  1618.         vm_vec_sub(&player_camera_vec, camera_pos, &objp->pos);
  1619.         if ((player_camera_vec.x == 0) && (player_camera_vec.y == 0) && (player_camera_vec.z == 0))
  1620.             player_camera_vec.x += F1_0/16;
  1621.  
  1622.         hit_data.hit_type = HIT_WALL;
  1623.         far_scale = F1_0;
  1624.  
  1625.         while ((hit_data.hit_type != HIT_NONE) && (count++ < 6)) {
  1626.             vms_vector    closer_p1;
  1627.             vm_vec_normalize_quick(&player_camera_vec);
  1628.             vm_vec_scale(&player_camera_vec, Camera_to_player_dist_goal);
  1629.  
  1630.             fq.p0 = &objp->pos;
  1631.             vm_vec_add(&closer_p1, &objp->pos, &player_camera_vec);        //    This is the actual point we want to put the camera at.
  1632.             vm_vec_scale(&player_camera_vec, far_scale);                        //    ...but find a point 50% further away...
  1633.             vm_vec_add(&local_p1, &objp->pos, &player_camera_vec);        //    ...so we won't have to do as many cuts.
  1634.  
  1635.             fq.p1 = &local_p1;
  1636.             fq.startseg = objp->segnum;
  1637.             fq.rad = 0;
  1638.             fq.thisobjnum = objp-Objects;
  1639.             fq.ignore_obj_list = NULL;
  1640.             fq.flags = 0;
  1641.             find_vector_intersection( &fq, &hit_data);
  1642.  
  1643.             if (hit_data.hit_type == HIT_NONE) {
  1644.                 *camera_pos = closer_p1;
  1645.             } else {
  1646.                 make_random_vector(&player_camera_vec);
  1647.                 far_scale = 3*F1_0/2;
  1648.             }
  1649.         }
  1650.     }
  1651. }
  1652.  
  1653. extern void drop_player_eggs(object *objp);
  1654. extern int get_explosion_vclip(object *obj,int stage);
  1655.  
  1656. //    ------------------------------------------------------------------------------------------------------------------
  1657. void dead_player_frame(void)
  1658. {
  1659.     fix    time_dead;
  1660.     vms_vector    fvec;
  1661.  
  1662.     if (Player_is_dead) {
  1663.         time_dead = GameTime - Player_time_of_death;
  1664.  
  1665.         //    If unable to create camera at time of death, create now.
  1666.         if (Dead_player_camera == Viewer_save) {
  1667.             int        objnum;
  1668.             object    *player = &Objects[Players[Player_num].objnum];
  1669.  
  1670.             objnum = obj_create(OBJ_CAMERA, 0, player->segnum, &player->pos, &player->orient, 0, CT_NONE, MT_NONE, RT_NONE);
  1671.  
  1672.             mprintf((0, "Creating new dead player camera.\n"));
  1673.             if (objnum != -1)
  1674.                 Viewer = Dead_player_camera = &Objects[objnum];
  1675.             else {
  1676.                 mprintf((1, "Can't create dead player camera.\n"));
  1677.                 Int3();
  1678.             }
  1679.         }        
  1680.  
  1681.         ConsoleObject->mtype.phys_info.rotvel.x = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/4;
  1682.         ConsoleObject->mtype.phys_info.rotvel.y = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/2;
  1683.         ConsoleObject->mtype.phys_info.rotvel.z = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/3;
  1684.  
  1685.         Camera_to_player_dist_goal = min(time_dead*8, F1_0*20) + ConsoleObject->size;
  1686.  
  1687.         set_camera_pos(&Dead_player_camera->pos, ConsoleObject);
  1688.  
  1689. //        if (time_dead < DEATH_SEQUENCE_EXPLODE_TIME+F1_0*2) {
  1690.             vm_vec_sub(&fvec, &ConsoleObject->pos, &Dead_player_camera->pos);
  1691.             vm_vector_2_matrix(&Dead_player_camera->orient, &fvec, NULL, NULL);
  1692. //        } else {
  1693. //            Dead_player_camera->movement_type = MT_PHYSICS;
  1694. //            Dead_player_camera->mtype.phys_info.rotvel.y = F1_0/8;
  1695. //        }
  1696.  
  1697.         if (time_dead > DEATH_SEQUENCE_EXPLODE_TIME) {
  1698.             if (!Player_exploded) {
  1699.  
  1700.             if (Players[Player_num].hostages_on_board > 1)
  1701.                 HUD_init_message(TXT_SHIP_DESTROYED_2, Players[Player_num].hostages_on_board);
  1702.             else if (Players[Player_num].hostages_on_board == 1)
  1703.                 HUD_init_message(TXT_SHIP_DESTROYED_1);
  1704.             else
  1705.                 HUD_init_message(TXT_SHIP_DESTROYED_0);
  1706.  
  1707.                 Player_exploded = 1;
  1708.                 if (!Arcade_mode) {
  1709.                     drop_player_eggs(ConsoleObject);
  1710.                     Player_eggs_dropped = 1;
  1711.                     #ifdef NETWORK
  1712.                     if (Game_mode & GM_MULTI)
  1713.                     {
  1714.                         multi_send_position(Players[Player_num].objnum);
  1715.                         multi_send_player_explode(MULTI_PLAYER_EXPLODE);
  1716.                     }
  1717.                     #endif
  1718.                 }
  1719.  
  1720.                 explode_badass_player(ConsoleObject);
  1721.  
  1722.                 //is this next line needed, given the badass call above?
  1723.                 explode_object(ConsoleObject,0);
  1724.                 ConsoleObject->flags &= ~OF_SHOULD_BE_DEAD;        //don't really kill player
  1725.                 ConsoleObject->render_type = RT_NONE;                //..just make him disappear
  1726.                 ConsoleObject->type = OBJ_GHOST;                        //..and kill intersections
  1727.             }
  1728.         } else {
  1729.             if (rand() < FrameTime*4) {
  1730.                 #ifdef NETWORK
  1731.                 if (Game_mode & GM_MULTI)
  1732.                     multi_send_create_explosion(Player_num);
  1733.                 #endif
  1734.                 create_small_fireball_on_object(ConsoleObject, F1_0, 1);
  1735.             }
  1736.         }
  1737.  
  1738.  
  1739.         if (!Arcade_mode) {
  1740.             if (Death_sequence_aborted) { //time_dead > DEATH_SEQUENCE_LENGTH) {
  1741.                 if (!Player_eggs_dropped) {
  1742.                     drop_player_eggs(ConsoleObject);
  1743.                     Player_eggs_dropped = 1;
  1744.                     #ifdef NETWORK
  1745.                     if (Game_mode & GM_MULTI)
  1746.                     {
  1747.                         multi_send_position(Players[Player_num].objnum);
  1748.                         multi_send_player_explode(MULTI_PLAYER_EXPLODE);
  1749.                     }
  1750.                     #endif
  1751.                 }
  1752.  
  1753.                 DoPlayerDead();        //kill_player();
  1754.             }
  1755.         } else {
  1756.             if (Death_sequence_aborted || (time_dead > DEATH_SEQUENCE_LENGTH)) {
  1757.                 DoPlayerDead();        //kill_player();
  1758.             }
  1759.         }
  1760.     }
  1761. }
  1762.  
  1763. Killed_in_frame = -1;
  1764. Killed_objnum = -1;
  1765.  
  1766. //    ------------------------------------------------------------------------------------------------------------------
  1767. void start_player_death_sequence(object *player)
  1768. {
  1769.     int    objnum;
  1770.  
  1771.     Assert(player == ConsoleObject);
  1772.     if ((Player_is_dead != 0) || (Dead_player_camera != NULL))
  1773.         return;
  1774.  
  1775.     //Assert(Player_is_dead == 0);
  1776.     //Assert(Dead_player_camera == NULL);
  1777.  
  1778.     reset_rear_view();
  1779.  
  1780.     if (!(Game_mode & GM_MULTI))
  1781.         HUD_clear_messages();
  1782.  
  1783.     Killed_in_frame = FrameCount;
  1784.     Killed_objnum = player-Objects;
  1785.     Death_sequence_aborted = 0;
  1786.  
  1787.     #ifdef NETWORK
  1788.     if (Game_mode & GM_MULTI) 
  1789.     {
  1790.         multi_send_kill(Players[Player_num].objnum);
  1791.     }
  1792.     #endif
  1793.     
  1794.     PaletteRedAdd = 40;
  1795.     Player_is_dead = 1;
  1796.     Players[Player_num].flags &= ~(PLAYER_FLAGS_AFTERBURNER);
  1797.  
  1798.     vm_vec_zero(&player->mtype.phys_info.rotthrust);
  1799.     vm_vec_zero(&player->mtype.phys_info.thrust);
  1800.  
  1801.     Player_time_of_death = GameTime;
  1802.  
  1803.     objnum = obj_create(OBJ_CAMERA, 0, player->segnum, &player->pos, &player->orient, 0, CT_NONE, MT_NONE, RT_NONE);
  1804.     Viewer_save = Viewer;
  1805.     if (objnum != -1)
  1806.         Viewer = Dead_player_camera = &Objects[objnum];
  1807.     else {
  1808.         mprintf((1, "Can't create dead player camera.\n"));
  1809.         Int3();
  1810.         Dead_player_camera = Viewer;
  1811.     }
  1812.  
  1813.     cockpit_mode_save = Cockpit_mode;
  1814.     select_cockpit(CM_LETTERBOX);
  1815.     if (Newdemo_state == ND_STATE_RECORDING)
  1816.         newdemo_record_letterbox();
  1817.  
  1818.     Player_flags_save = player->flags;
  1819.     Control_type_save = player->control_type;
  1820.     Render_type_save = player->render_type;
  1821.  
  1822.     player->flags &= ~OF_SHOULD_BE_DEAD;
  1823. //    Players[Player_num].flags |= PLAYER_FLAGS_INVULNERABLE;
  1824.     player->control_type = CT_NONE;
  1825.     player->shields = F1_0*1000;
  1826.  
  1827.     PALETTE_FLASH_SET(0,0,0);
  1828. }
  1829.  
  1830. //    ------------------------------------------------------------------------------------------------------------------
  1831. void obj_delete_all_that_should_be_dead()
  1832. {
  1833.     int i;
  1834.     object *objp;
  1835.     int        local_dead_player_object=-1;
  1836.  
  1837.     // Move all objects
  1838.     objp = Objects;
  1839.  
  1840.     for (i=0;i<=Highest_object_index;i++) {
  1841.         if ((objp->type!=OBJ_NONE) && (objp->flags&OF_SHOULD_BE_DEAD) )    {
  1842.             Assert(!(objp->type==OBJ_FIREBALL && objp->ctype.expl_info.delete_time!=-1));
  1843.             if (objp->type==OBJ_PLAYER) {
  1844.                 if ( objp->id == Player_num ) {
  1845.                     if (local_dead_player_object == -1) {
  1846.                         start_player_death_sequence(objp);
  1847.                         local_dead_player_object = objp-Objects;
  1848.                     } else
  1849.                         Int3();    //    Contact Mike: Illegal, killed player twice in this frame!
  1850.                                     // Ok to continue, won't start death sequence again!
  1851.                     // kill_player();
  1852.                 }
  1853.             } else {                    
  1854.                 obj_delete(i);
  1855.             }
  1856.         }
  1857.         objp++;
  1858.     }
  1859. }
  1860.  
  1861. //when an object has moved into a new segment, this function unlinks it
  1862. //from its old segment, and links it into the new segment
  1863. void obj_relink(int objnum,int newsegnum)
  1864. {
  1865.  
  1866.     Assert((objnum >= 0) && (objnum <= Highest_object_index));
  1867.     Assert((newsegnum <= Highest_segment_index) && (newsegnum >= 0));
  1868.  
  1869.     obj_unlink(objnum);
  1870.  
  1871.     obj_link(objnum,newsegnum);
  1872.     
  1873. #ifndef NDEBUG
  1874.     if (get_seg_masks(&Objects[objnum].pos,Objects[objnum].segnum,0).centermask!=0)
  1875.         mprintf((1, "obj_relink violates seg masks.\n"));
  1876. #endif
  1877. }
  1878.  
  1879. //process a continuously-spinning object
  1880. spin_object(object *obj)
  1881. {
  1882.     vms_angvec rotangs;
  1883.     vms_matrix rotmat, new_pm;
  1884.  
  1885.     Assert(obj->movement_type == MT_SPINNING);
  1886.  
  1887.     rotangs.p = fixmul(obj->mtype.spin_rate.x,FrameTime);
  1888.     rotangs.h = fixmul(obj->mtype.spin_rate.y,FrameTime);
  1889.     rotangs.b = fixmul(obj->mtype.spin_rate.z,FrameTime);
  1890.  
  1891.     vm_angles_2_matrix(&rotmat,&rotangs);
  1892.  
  1893.     vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
  1894.     obj->orient = new_pm;
  1895.  
  1896.     check_and_fix_matrix(&obj->orient);
  1897. }
  1898.  
  1899. //--------------------------------------------------------------------
  1900. //move an object for the current frame
  1901. void object_move_one( object * obj )
  1902. {
  1903.  
  1904.     #ifndef DEMO_ONLY
  1905.  
  1906.     int    previous_segment = obj->segnum;
  1907.  
  1908.     obj->last_pos = obj->pos;            // Save the current position
  1909.  
  1910.     if ((obj->type==OBJ_PLAYER) && (Player_num==obj->id))    {
  1911.         fix fuel;
  1912.         fuel=fuelcen_give_fuel( &Segments[obj->segnum], i2f(100)-Players[Player_num].energy );
  1913.         if (fuel > 0 )    {
  1914.             Players[Player_num].energy += fuel;
  1915.         }
  1916.     }
  1917.  
  1918.     if (obj->lifeleft != IMMORTAL_TIME)    //if not immortal...
  1919.         obj->lifeleft -= FrameTime;        //...inevitable countdown towards death
  1920.  
  1921.     switch (obj->control_type) {
  1922.  
  1923.         case CT_NONE: break;
  1924.  
  1925.         case CT_FLYING:
  1926.  
  1927.             #if !defined(NDEBUG) && !defined(NMONO)
  1928.             if (print_object_info>1) mprintf( (0, "Moving player object #%d\n", obj-Objects ));
  1929.             #endif
  1930.  
  1931.             read_flying_controls( obj );
  1932.  
  1933.             break;
  1934.  
  1935.         case CT_REPAIRCEN: Int3();    // -- hey! these are no longer supported!! -- do_repair_sequence(obj); break;
  1936.  
  1937.         case CT_POWERUP: do_powerup_frame(obj); break;
  1938.     
  1939.         case CT_MORPH:            //morph implies AI
  1940.             do_morph_frame(obj);
  1941.             //NOTE: FALLS INTO AI HERE!!!!
  1942.  
  1943.         case CT_AI:
  1944.             //NOTE LINK TO CT_MORPH ABOVE!!!
  1945.             if (Game_suspended & SUSP_ROBOTS) return;
  1946.             #if !defined(NDEBUG) && !defined(NMONO)
  1947.             if (print_object_info>1) mprintf( (0, "AI: Moving robot object #%d\n",obj-Objects ));
  1948.             #endif
  1949.             do_ai_frame(obj);
  1950.             break;
  1951.  
  1952.         case CT_WEAPON:        Laser_do_weapon_sequence(obj); break;
  1953.         case CT_EXPLOSION:    do_explosion_sequence(obj); break;
  1954.  
  1955.         #ifndef RELEASE
  1956.         case CT_SLEW:
  1957.             if ( keyd_pressed[KEY_PAD5] ) slew_stop( obj );
  1958.             if ( keyd_pressed[KEY_NUMLOCK] )         {
  1959.                 slew_reset_orient( obj ); 
  1960.                 * (ubyte *) 0x417 &= ~0x20;        //kill numlock
  1961.             }
  1962.             slew_frame(0 );        // Does velocity addition for us.
  1963.             break;
  1964.         #endif
  1965.  
  1966. //        case CT_FLYTHROUGH:
  1967. //            do_flythrough(obj,0);            // HACK:do_flythrough should operate on an object!!!!
  1968. //            //check_object_seg(obj);
  1969. //            return;    // DON'T DO THE REST OF OBJECT STUFF SINCE THIS IS A SPECIAL CASE!!!
  1970. //            break;
  1971.  
  1972.         case CT_DEBRIS: do_debris_frame(obj); break;
  1973.  
  1974.         case CT_LIGHT: break;        //doesn't do anything
  1975.  
  1976.         case CT_REMOTE: break;     //movement is handled in com_process_input
  1977.  
  1978.         case CT_CNTRLCEN: do_controlcen_frame(obj); break;
  1979.  
  1980.         default:
  1981.  
  1982.             Error("Unknown control type %d in object %i, sig/type/id = %i/%i/%i",obj->control_type, obj-Objects, obj->signature, obj->type, obj->id);
  1983.  
  1984.             break;
  1985.  
  1986.     }
  1987.  
  1988.     if (obj->lifeleft < 0 ) {        // We died of old age
  1989.         obj->flags |= OF_SHOULD_BE_DEAD;
  1990.         if ( Weapon_info[obj->id].damage_radius )
  1991.             explode_badass_weapon(obj);
  1992.     }
  1993.  
  1994.     if (obj->type == OBJ_NONE || obj->flags&OF_SHOULD_BE_DEAD)
  1995.         return;            //object has been deleted
  1996.  
  1997.     switch (obj->movement_type) {
  1998.  
  1999.         case MT_NONE:            break;                                //this doesn't move
  2000.  
  2001.         case MT_PHYSICS:        do_physics_sim(obj);    break;    //move by physics
  2002.  
  2003.         case MT_SPINNING:        spin_object(obj); break;
  2004.  
  2005.     }
  2006.  
  2007.     //    If player and moved to another segment, see if hit any triggers.
  2008.     if (obj->type == OBJ_PLAYER && obj->movement_type==MT_PHYSICS)    {
  2009.         if (previous_segment != obj->segnum) {
  2010.             int    connect_side,i;
  2011.             for (i=0;i<n_phys_segs-1;i++) {
  2012.                 connect_side = find_connect_side(&Segments[phys_seglist[i+1]], &Segments[phys_seglist[i]]);
  2013.                 if (connect_side != -1)
  2014.                     check_trigger(&Segments[phys_seglist[i]], connect_side, obj-Objects);
  2015.                     //check_trigger(&Segments[previous_segment], connect_side, obj-Objects);
  2016.                 #ifndef NDEBUG
  2017.                 else {    // segments are not directly connected, so do binary subdivision until you find connected segments.
  2018.                     mprintf((1, "UNCONNECTED SEGMENTS %d,%d\n",phys_seglist[i+1],phys_seglist[i]));
  2019.                 }
  2020.                 #endif
  2021.             }
  2022.         }
  2023.     }
  2024.  
  2025.     #else
  2026.         obj++;        //kill warning
  2027.     #endif
  2028. }
  2029.  
  2030. int    Max_used_objects = MAX_OBJECTS - 20;
  2031.  
  2032. //--------------------------------------------------------------------
  2033. //move all objects for the current frame
  2034. void object_move_all()
  2035. {
  2036.     int i;
  2037.     object *objp;
  2038.  
  2039. //    check_duplicate_objects();
  2040. //    remove_incorrect_objects();
  2041.  
  2042.     if (Highest_object_index > Max_used_objects)
  2043.         free_object_slots(Max_used_objects);        //    Free all possible object slots.
  2044.  
  2045.     obj_delete_all_that_should_be_dead();
  2046.  
  2047.     if (Auto_leveling_on)
  2048.         ConsoleObject->mtype.phys_info.flags |= PF_LEVELLING;
  2049.     else
  2050.         ConsoleObject->mtype.phys_info.flags &= ~PF_LEVELLING;
  2051.  
  2052.     // Move all objects
  2053.     objp = Objects;
  2054.  
  2055.     #ifndef DEMO_ONLY
  2056.     for (i=0;i<=Highest_object_index;i++) {
  2057.         if ( (objp->type != OBJ_NONE) && (!(objp->flags&OF_SHOULD_BE_DEAD)) )    {
  2058.             object_move_one( objp );
  2059.         }
  2060.         objp++;
  2061.     }
  2062.     #else
  2063.         i=0;    //kill warning
  2064.     #endif
  2065.  
  2066. //    check_duplicate_objects();
  2067. //    remove_incorrect_objects();
  2068.  
  2069. }
  2070.  
  2071.  
  2072. //--unused-- // -----------------------------------------------------------
  2073. //--unused-- //    Moved here from eobject.c on 02/09/94 by MK.
  2074. //--unused-- int find_last_obj(int i)
  2075. //--unused-- {
  2076. //--unused--     for (i=MAX_OBJECTS;--i>=0;)
  2077. //--unused--         if (Objects[i].type != OBJ_NONE) break;
  2078. //--unused-- 
  2079. //--unused--     return i;
  2080. //--unused-- 
  2081. //--unused-- }
  2082.  
  2083.  
  2084. //make object array non-sparse
  2085. void compress_objects(void)
  2086. {
  2087.     int start_i;    //,last_i;
  2088.  
  2089.     //last_i = find_last_obj(MAX_OBJECTS);
  2090.  
  2091.     //    Note: It's proper to do < (rather than <=) Highest_object_index here because we
  2092.     //    are just removing gaps, and the last object can't be a gap.
  2093.     for (start_i=0;start_i<Highest_object_index;start_i++)
  2094.  
  2095.         if (Objects[start_i].type == OBJ_NONE) {
  2096.  
  2097.             int    segnum_copy;
  2098.  
  2099.             segnum_copy = Objects[Highest_object_index].segnum;
  2100.  
  2101.             obj_unlink(Highest_object_index);
  2102.  
  2103.             Objects[start_i] = Objects[Highest_object_index];
  2104.  
  2105.             #ifdef EDITOR
  2106.             if (Cur_object_index == Highest_object_index)
  2107.                 Cur_object_index = start_i;
  2108.             #endif
  2109.  
  2110.             Objects[Highest_object_index].type = OBJ_NONE;
  2111.  
  2112.             obj_link(start_i,segnum_copy);
  2113.  
  2114.             while (Objects[--Highest_object_index].type == OBJ_NONE);
  2115.  
  2116.             //last_i = find_last_obj(last_i);
  2117.             
  2118.         }
  2119.  
  2120.     reset_objects(num_objects);
  2121.  
  2122. }
  2123.  
  2124. //called after load.  Takes number of objects,  and objects should be 
  2125. //compressed.  resets free list, marks unused objects as unused
  2126. void reset_objects(int n_objs)
  2127. {
  2128.     int i;
  2129.  
  2130.     num_objects = n_objs;
  2131.  
  2132.     Assert(num_objects>0);
  2133.  
  2134.     for (i=num_objects;i<MAX_OBJECTS;i++) {
  2135.         free_obj_list[i] = i;
  2136.         Objects[i].type = OBJ_NONE;
  2137.         Objects[i].segnum = -1;
  2138.     }
  2139.  
  2140.     Highest_object_index = num_objects-1;
  2141.  
  2142.     Debris_object_count = 0;
  2143. }
  2144.  
  2145. //Tries to find a segment for an object, using find_point_seg()
  2146. int find_object_seg(object * obj )
  2147. {
  2148.     return find_point_seg(&obj->pos,obj->segnum);
  2149. }
  2150.  
  2151.  
  2152. //If an object is in a segment, set its segnum field and make sure it's
  2153. //properly linked.  If not in any segment, returns 0, else 1.
  2154. //callers should generally use find_vector_intersection()  
  2155. int update_object_seg(object * obj )
  2156. {
  2157.     int newseg;
  2158.  
  2159.     newseg = find_object_seg(obj);
  2160.  
  2161.     if (newseg == -1)
  2162.         return 0;
  2163.  
  2164.     if ( newseg != obj->segnum )
  2165.         obj_relink(obj-Objects, newseg );
  2166.  
  2167.     return 1;
  2168. }
  2169.  
  2170.  
  2171. //go through all objects and make sure they have the correct segment numbers
  2172. fix_object_segs()
  2173. {
  2174.     int i;
  2175.  
  2176.     for (i=0;i<=Highest_object_index;i++)
  2177.         if (Objects[i].type != OBJ_NONE)
  2178.             if (update_object_seg(&Objects[i]) == 0) {
  2179.                 mprintf((1,"Cannot find segment for object %d in fix_object_segs()\n"));
  2180.                 Int3();
  2181.                 compute_segment_center(&Objects[i].pos,&Segments[Objects[i].segnum]);
  2182.             }
  2183. }
  2184.  
  2185.  
  2186. //--unused-- void object_use_new_object_list( object * new_list )
  2187. //--unused-- {
  2188. //--unused--     int i, segnum;
  2189. //--unused--     object *obj;
  2190. //--unused-- 
  2191. //--unused--     // First, unlink all the old objects for the segments array
  2192. //--unused--     for (segnum=0; segnum <= Highest_segment_index; segnum++) {
  2193. //--unused--         Segments[segnum].objects = -1;
  2194. //--unused--     }
  2195. //--unused--     // Then, erase all the objects
  2196. //--unused--     reset_objects(1);
  2197. //--unused-- 
  2198. //--unused--     // Fill in the object array
  2199. //--unused--     memcpy( Objects, new_list, sizeof(object)*MAX_OBJECTS );
  2200. //--unused-- 
  2201. //--unused--     Highest_object_index=-1;
  2202. //--unused-- 
  2203. //--unused--     // Relink 'em
  2204. //--unused--     for (i=0; i<MAX_OBJECTS; i++ )    {
  2205. //--unused--         obj = &Objects[i];
  2206. //--unused--         if ( obj->type != OBJ_NONE )    {
  2207. //--unused--             num_objects++;
  2208. //--unused--             Highest_object_index = i;
  2209. //--unused--             segnum = obj->segnum;
  2210. //--unused--             obj->next = obj->prev = obj->segnum = -1;
  2211. //--unused--             obj_link(i,segnum);
  2212. //--unused--         } else {
  2213. //--unused--             obj->next = obj->prev = obj->segnum = -1;
  2214. //--unused--         }
  2215. //--unused--     }
  2216. //--unused--     
  2217. //--unused-- }
  2218.  
  2219. //delete objects, such as weapons & explosions, that shouldn't stay between levels
  2220. //    Changed by MK on 10/15/94, don't remove proximity bombs.
  2221. //if clear_all is set, clear even proximity bombs
  2222. void clear_transient_objects(int clear_all)
  2223. {
  2224.     int objnum;
  2225.     object *obj; 
  2226.  
  2227.     for (objnum=0,obj=&Objects[0];objnum<=Highest_object_index;objnum++,obj++)
  2228.         if (((obj->type == OBJ_WEAPON) && (clear_all || obj->id != PROXIMITY_ID)) ||
  2229.              obj->type == OBJ_FIREBALL ||
  2230.              obj->type == OBJ_DEBRIS ||
  2231.              obj->type == OBJ_DEBRIS ||
  2232.              (obj->type!=OBJ_NONE && obj->flags & OF_EXPLODING)) {
  2233.  
  2234.             #ifndef NDEBUG
  2235.             if (Objects[objnum].lifeleft > i2f(2))
  2236.                 mprintf((0,"Note: Clearing object %d (type=%d, id=%d) with lifeleft=%x\n",objnum,Objects[objnum].type,Objects[objnum].id,Objects[objnum].lifeleft));
  2237.             #endif
  2238.             obj_delete(objnum);
  2239.         }
  2240.         #ifndef NDEBUG
  2241.          else if (Objects[objnum].type!=OBJ_NONE && Objects[objnum].lifeleft < i2f(2))
  2242.             mprintf((0,"Note: NOT clearing object %d (type=%d, id=%d) with lifeleft=%x\n",objnum,Objects[objnum].type,Objects[objnum].id,Objects[objnum].lifeleft));
  2243.         #endif
  2244. }
  2245.  
  2246. //attaches an object, such as a fireball, to another object, such as a robot
  2247. void obj_attach(object *parent,object *sub)
  2248. {
  2249.     Assert(sub->type == OBJ_FIREBALL);
  2250.     Assert(sub->control_type == CT_EXPLOSION);
  2251.  
  2252.     Assert(sub->ctype.expl_info.next_attach==-1);
  2253.     Assert(sub->ctype.expl_info.prev_attach==-1);
  2254.  
  2255.     Assert(parent->attached_obj==-1 || Objects[parent->attached_obj].ctype.expl_info.prev_attach==-1);
  2256.  
  2257.     sub->ctype.expl_info.next_attach = parent->attached_obj;
  2258.  
  2259.     if (sub->ctype.expl_info.next_attach != -1)
  2260.         Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = sub-Objects;
  2261.  
  2262.     parent->attached_obj = sub-Objects;
  2263.  
  2264.     sub->ctype.expl_info.attach_parent = parent-Objects;
  2265.     sub->flags |= OF_ATTACHED;
  2266.  
  2267.     Assert(sub->ctype.expl_info.next_attach != sub-Objects);
  2268.     Assert(sub->ctype.expl_info.prev_attach != sub-Objects);
  2269. }
  2270.  
  2271. //dettaches one object
  2272. void obj_detach_one(object *sub)
  2273. {
  2274.     Assert(sub->flags & OF_ATTACHED);
  2275.     Assert(sub->ctype.expl_info.attach_parent != -1);
  2276.  
  2277.     if ((Objects[sub->ctype.expl_info.attach_parent].type == OBJ_NONE) || (Objects[sub->ctype.expl_info.attach_parent].attached_obj == -1))
  2278.     {
  2279.         sub->flags &= ~OF_ATTACHED;
  2280.         return;
  2281.     }
  2282.  
  2283.     if (sub->ctype.expl_info.next_attach != -1) {
  2284.         Assert(Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach=sub-Objects);
  2285.         Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = sub->ctype.expl_info.prev_attach;
  2286.     }
  2287.  
  2288.     if (sub->ctype.expl_info.prev_attach != -1) {
  2289.         Assert(Objects[sub->ctype.expl_info.prev_attach].ctype.expl_info.next_attach=sub-Objects);
  2290.         Objects[sub->ctype.expl_info.prev_attach].ctype.expl_info.next_attach = sub->ctype.expl_info.next_attach;
  2291.     }
  2292.     else {
  2293.         Assert(Objects[sub->ctype.expl_info.attach_parent].attached_obj=sub-Objects);
  2294.         Objects[sub->ctype.expl_info.attach_parent].attached_obj = sub->ctype.expl_info.next_attach;
  2295.     }
  2296.  
  2297.     sub->ctype.expl_info.next_attach = sub->ctype.expl_info.prev_attach = -1;
  2298.     sub->flags &= ~OF_ATTACHED;
  2299.  
  2300. }
  2301.  
  2302. //dettaches all objects from this object
  2303. void obj_detach_all(object *parent)
  2304. {
  2305.     while (parent->attached_obj != -1)
  2306.         obj_detach_one(&Objects[parent->attached_obj]);
  2307. }
  2308.  
  2309. 
  2310.