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