]> icculus.org git repositories - btb/d2x.git/blob - main/wall.c
configuration fixes
[btb/d2x.git] / main / wall.c
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 #ifdef RCS
16 static char rcsid[] = "$Id: wall.c,v 1.2 2001-01-20 13:49:18 bradleyb Exp $";
17 #endif
18
19 #include <conf.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <math.h>
24 #include <string.h>
25
26 #include "pstypes.h"
27 #include "mono.h"
28 #include "gr.h"
29 #include "wall.h"
30 #include "switch.h"
31 #include "inferno.h"
32 #include "segment.h"
33 #include "error.h"
34 #include "gameseg.h"
35 #include "game.h"
36 #include "bm.h"
37 #include "vclip.h"
38 #include "player.h"
39 #include "gauges.h"
40 #include "text.h"
41 #include "fireball.h"
42 #include "textures.h"
43 #include "sounds.h"
44 #include "newdemo.h"
45 #include "multi.h"
46 #include "gameseq.h"
47 #include "laser.h"              //      For seeing if a flare is stuck in a wall.
48 #include "collide.h"
49 #include "effects.h"
50
51 #ifdef EDITOR
52 #include "editor\editor.h"
53 #endif
54
55 //      Special door on boss level which is locked if not in multiplayer...sorry for this awful solution --MK.
56 #define BOSS_LOCKED_DOOR_LEVEL  7
57 #define BOSS_LOCKED_DOOR_SEG            595
58 #define BOSS_LOCKED_DOOR_SIDE   5
59
60 wall Walls[MAX_WALLS];                                  // Master walls array
61 int Num_walls=0;                                                        // Number of walls
62
63 wclip WallAnims[MAX_WALL_ANIMS];                // Wall animations
64 int Num_wall_anims;
65 //--unused-- int walls_bm_num[MAX_WALL_ANIMS];
66
67 //door Doors[MAX_DOORS];                                        //      Master doors array
68
69 active_door ActiveDoors[MAX_DOORS];
70 int Num_open_doors;                                             // Number of open doors
71
72 #define CLOAKING_WALL_TIME f1_0
73
74 #define MAX_CLOAKING_WALLS 10
75 cloaking_wall CloakingWalls[MAX_CLOAKING_WALLS];
76 int Num_cloaking_walls;
77
78 //--unused-- grs_bitmap *wall_title_bms[MAX_WALL_ANIMS];
79
80 //#define BM_FLAG_TRANSPARENT                   1
81 //#define BM_FLAG_SUPER_TRANSPARENT     2
82
83 #ifdef EDITOR
84 char    Wall_names[7][10] = {
85         "NORMAL   ",
86         "BLASTABLE",
87         "DOOR     ",
88         "ILLUSION ",
89         "OPEN     ",
90         "CLOSED   ",
91         "EXTERNAL "
92 };
93 #endif
94
95 // Function prototypes
96 void kill_stuck_objects(int wallnum);
97
98
99
100 // This function determines whether the current segment/side is transparent
101 //              1 = YES
102 //              0 = NO
103 int check_transparency( segment * seg, int side )
104 {         
105         if ( (seg->sides[side].tmap_num2 & 0x3FFF) == 0) {
106                 if (GameBitmaps[Textures[seg->sides[side].tmap_num].index].bm_flags & BM_FLAG_TRANSPARENT )
107                         return 1;
108                 else 
109                         return 0;
110                 }
111
112         if (GameBitmaps[Textures[seg->sides[side].tmap_num2 & 0x3FFF ].index].bm_flags & BM_FLAG_SUPER_TRANSPARENT )
113                 return 1;
114         else
115                 return 0;
116 }
117
118 //define these here so I don't have to change wall_is_doorway and run
119 //the risk of screwing it up.
120 #define WID_WALL                                                2       // 0/1/0                wall    
121 #define WID_TRANSPARENT_WALL            6       //      0/1/1           transparent wall
122 #define WID_ILLUSORY_WALL                       3       //      1/1/0           illusory wall
123 #define WID_TRANSILLUSORY_WALL  7       //      1/1/1           transparent illusory wall
124 #define WID_NO_WALL                                     5       //      1/0/1           no wall, can fly through
125 #define WID_EXTERNAL                                    8       // 0/0/0/1      don't see it, dont fly through it
126
127 //-----------------------------------------------------------------
128 // This function checks whether we can fly through the given side.
129 //      In other words, whether or not we have a 'doorway'
130 //       Flags:
131 //              WID_FLY_FLAG                            1
132 //              WID_RENDER_FLAG                 2
133 //              WID_RENDPAST_FLAG                       4
134 //       Return values:
135 //              WID_WALL                                                2       // 0/1/0                wall    
136 //              WID_TRANSPARENT_WALL            6       //      0/1/1           transparent wall
137 //              WID_ILLUSORY_WALL                       3       //      1/1/0           illusory wall
138 //              WID_TRANSILLUSORY_WALL  7       //      1/1/1           transparent illusory wall
139 //              WID_NO_WALL                                     5       //      1/0/1           no wall, can fly through
140 int wall_is_doorway ( segment * seg, int side )
141 {
142         int flags, type;
143         int state;
144 //--Covered by macro    // No child.
145 //--Covered by macro    if (seg->children[side] == -1)
146 //--Covered by macro            return WID_WALL;
147
148 //--Covered by macro    if (seg->children[side] == -2)
149 //--Covered by macro            return WID_EXTERNAL_FLAG;
150
151 //--Covered by macro // No wall present.
152 //--Covered by macro    if (seg->sides[side].wall_num == -1)
153 //--Covered by macro            return WID_NO_WALL;
154
155         Assert(seg-Segments>=0 && seg-Segments<=Highest_segment_index);
156         Assert(side>=0 && side<6);
157
158         type = Walls[seg->sides[side].wall_num].type;
159         flags = Walls[seg->sides[side].wall_num].flags;
160
161         if (type == WALL_OPEN)
162                 return WID_NO_WALL;
163
164         if (type == WALL_ILLUSION) {
165                 if (Walls[seg->sides[side].wall_num].flags & WALL_ILLUSION_OFF)
166                         return WID_NO_WALL;
167                 else {
168                         if (check_transparency( seg, side))
169                                 return WID_TRANSILLUSORY_WALL;
170                         else
171                                 return WID_ILLUSORY_WALL;
172                 }
173         }
174
175         if (type == WALL_BLASTABLE) {
176                 if (flags & WALL_BLASTED)
177                         return WID_TRANSILLUSORY_WALL;
178
179                 if (check_transparency( seg, side))
180                         return WID_TRANSPARENT_WALL;
181                 else
182                         return WID_WALL;
183         }       
184         
185         if (flags & WALL_DOOR_OPENED)
186                 return WID_TRANSILLUSORY_WALL;
187         
188         if (type == WALL_CLOAKED)
189                 return WID_RENDER_FLAG | WID_RENDPAST_FLAG | WID_CLOAKED_FLAG;
190
191         state = Walls[seg->sides[side].wall_num].state;
192         if ((type == WALL_DOOR) && (state == WALL_DOOR_OPENING))
193                 return WID_TRANSPARENT_WALL;
194         
195 // If none of the above flags are set, there is no doorway.
196         if (check_transparency( seg, side))
197                 return WID_TRANSPARENT_WALL;
198         else
199                 return WID_WALL; // There are children behind the door.
200 }
201
202 #ifdef EDITOR
203 //-----------------------------------------------------------------
204 // Initializes all the walls (in other words, no special walls)
205 void wall_init()
206 {
207         int i;
208         
209         Num_walls = 0;
210         for (i=0;i<MAX_WALLS;i++) {
211                 Walls[i].segnum = Walls[i].sidenum = -1;
212                 Walls[i].type = WALL_NORMAL;
213                 Walls[i].flags = 0;
214                 Walls[i].hps = 0;
215                 Walls[i].trigger = -1;
216                 Walls[i].clip_num = -1;
217                 Walls[i].linked_wall = -1;
218                 }
219         Num_open_doors = 0;
220         Num_cloaking_walls = 0;
221
222 }
223
224 //-----------------------------------------------------------------
225 // Initializes one wall.
226 void wall_reset(segment *seg, int side)
227 {
228         int i;
229         
230         i = seg->sides[side].wall_num;
231
232         if (i==-1) {
233                 mprintf((0, "Resetting Illegal Wall\n"));
234                 return;
235         }
236
237         Walls[i].segnum = seg-Segments;
238         Walls[i].sidenum = side;
239         Walls[i].type = WALL_NORMAL;
240         Walls[i].flags = 0;
241         Walls[i].hps = 0;
242         Walls[i].trigger = -1;
243         Walls[i].clip_num = -1;
244         Walls[i].linked_wall = -1;
245 }
246 #endif
247
248 //set the tmap_num or tmap_num2 field for a wall/door
249 void wall_set_tmap_num(segment *seg,int side,segment *csegp,int cside,int anim_num,int frame_num)
250 {
251         wclip *anim = &WallAnims[anim_num];
252         int tmap = anim->frames[frame_num];
253
254         if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
255
256         if (anim->flags & WCF_TMAP1)    {
257                 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = tmap;
258                 if ( Newdemo_state == ND_STATE_RECORDING )
259                         newdemo_record_wall_set_tmap_num1(seg-Segments,side,csegp-Segments,cside,tmap);
260         } else  {
261                 Assert(tmap!=0 && seg->sides[side].tmap_num2!=0);
262                 seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = tmap;
263                 if ( Newdemo_state == ND_STATE_RECORDING )
264                         newdemo_record_wall_set_tmap_num2(seg-Segments,side,csegp-Segments,cside,tmap);
265         }
266 }
267
268
269 // -------------------------------------------------------------------------------
270 //when the wall has used all its hitpoints, this will destroy it
271 void blast_blastable_wall(segment *seg, int side)
272 {
273         int Connectside;
274         segment *csegp;
275         int a, n;
276
277         Assert(seg->sides[side].wall_num != -1);
278
279         Walls[seg->sides[side].wall_num].hps = -1;      //say it's blasted
280
281         csegp = &Segments[seg->children[side]];
282         Connectside = find_connect_side(seg, csegp);
283         Assert(Connectside != -1);
284
285         kill_stuck_objects(seg->sides[side].wall_num);
286         kill_stuck_objects(csegp->sides[Connectside].wall_num);
287
288         //if this is an exploding wall, explode it
289         if (WallAnims[Walls[seg->sides[side].wall_num].clip_num].flags & WCF_EXPLODES)
290                 explode_wall(seg-Segments,side);
291         else {
292                 //if not exploding, set final frame, and make door passable
293                 a = Walls[seg->sides[side].wall_num].clip_num;
294                 n = WallAnims[a].num_frames;
295                 wall_set_tmap_num(seg,side,csegp,Connectside,a,n-1);
296                 Walls[seg->sides[side].wall_num].flags |= WALL_BLASTED;
297                 Walls[csegp->sides[Connectside].wall_num].flags |= WALL_BLASTED;
298         }
299
300 }
301
302
303 //-----------------------------------------------------------------
304 // Destroys a blastable wall.
305 void wall_destroy(segment *seg, int side)
306 {
307         Assert(seg->sides[side].wall_num != -1);
308         Assert(seg-Segments != 0);
309
310         if (Walls[seg->sides[side].wall_num].type == WALL_BLASTABLE)
311                 blast_blastable_wall( seg, side );
312         else
313                 Error("Hey bub, you are trying to destroy an indestructable wall.");
314 }
315
316 //-----------------------------------------------------------------
317 // Deteriorate appearance of wall. (Changes bitmap (paste-ons)) 
318 void wall_damage(segment *seg, int side, fix damage)
319 {
320         int a, i, n;
321
322         if (seg->sides[side].wall_num == -1) {
323                 mprintf((0, "Damaging illegal wall\n"));
324                 return;
325         }
326
327         if (Walls[seg->sides[side].wall_num].type != WALL_BLASTABLE)
328                 return;
329         
330         if (!(Walls[seg->sides[side].wall_num].flags & WALL_BLASTED) && Walls[seg->sides[side].wall_num].hps >= 0)
331                 {
332                 int Connectside;
333                 segment *csegp;
334
335                 csegp = &Segments[seg->children[side]];
336                 Connectside = find_connect_side(seg, csegp);
337                 Assert(Connectside != -1);
338                 
339                 Walls[seg->sides[side].wall_num].hps -= damage;
340                 Walls[csegp->sides[Connectside].wall_num].hps -= damage;
341                         
342                 a = Walls[seg->sides[side].wall_num].clip_num;
343                 n = WallAnims[a].num_frames;
344                 
345                 if (Walls[seg->sides[side].wall_num].hps < WALL_HPS*1/n) {
346                         blast_blastable_wall( seg, side );                      
347                         #ifdef NETWORK
348                         if (Game_mode & GM_MULTI)
349                                 multi_send_door_open(seg-Segments, side,Walls[seg->sides[side].wall_num].flags);
350                         #endif
351                 }
352                 else
353                         for (i=0;i<n;i++)
354                                 if (Walls[seg->sides[side].wall_num].hps < WALL_HPS*(n-i)/n) {
355                                         wall_set_tmap_num(seg,side,csegp,Connectside,a,i);
356                                 }
357                 }
358 }
359
360
361 //-----------------------------------------------------------------
362 // Opens a door 
363 void wall_open_door(segment *seg, int side)
364 {
365         wall *w;
366         active_door *d;
367         int Connectside;
368         segment *csegp;
369
370         Assert(seg->sides[side].wall_num != -1);        //Opening door on illegal wall
371
372         w = &Walls[seg->sides[side].wall_num];
373
374         //kill_stuck_objects(seg->sides[side].wall_num);
375
376         if ((w->state == WALL_DOOR_OPENING) ||          //already opening
377                  (w->state == WALL_DOOR_WAITING)        ||              //open, waiting to close
378                  (w->state == WALL_DOOR_OPEN))                  //open, & staying open
379                 return;
380
381         if (w->state == WALL_DOOR_CLOSING) {            //closing, so reuse door
382
383                 int i;
384         
385                 d = NULL;
386
387                 for (i=0;i<Num_open_doors;i++) {                //find door
388
389                         d = &ActiveDoors[i];
390         
391                         if (d->front_wallnum[0]==w-Walls || d->back_wallnum[0]==w-Walls || (d->n_parts==2 && (d->front_wallnum[1]==w-Walls || d->back_wallnum[1]==w-Walls)))
392                                 break;
393                 } 
394
395                 if (i>=Num_open_doors && (Game_mode & GM_MULTI))        
396                                 goto FastFix; 
397                 
398                 Assert(i<Num_open_doors);                               //didn't find door!
399                 Assert( d!=NULL ); // Get John!
400
401                 d->time = WallAnims[w->clip_num].play_time - d->time;
402
403                 if (d->time < 0)
404                         d->time = 0;
405         
406         }
407         else {                                                                                  //create new door
408                 Assert(w->state == WALL_DOOR_CLOSED);
409                 FastFix:
410                 d = &ActiveDoors[Num_open_doors];
411                 d->time = 0;
412                 Num_open_doors++;
413                 Assert( Num_open_doors < MAX_DOORS );
414         }
415
416
417         w->state = WALL_DOOR_OPENING;
418
419         // So that door can't be shot while opening
420         csegp = &Segments[seg->children[side]];
421         Connectside = find_connect_side(seg, csegp);
422         Assert(Connectside != -1);
423
424         Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_OPENING;
425
426         //kill_stuck_objects(csegp->sides[Connectside].wall_num);
427
428         d->front_wallnum[0] = seg->sides[side].wall_num;
429         d->back_wallnum[0] = csegp->sides[Connectside].wall_num;
430
431         Assert( seg-Segments != -1);
432
433         if (Newdemo_state == ND_STATE_RECORDING) {
434                 newdemo_record_door_opening(seg-Segments, side);
435         }
436
437         if (w->linked_wall != -1) {
438                 wall *w2;
439                 segment *seg2;
440
441                 w2              = &Walls[w->linked_wall];
442                 seg2    = &Segments[w2->segnum];
443
444                 Assert(w2->linked_wall == seg->sides[side].wall_num);
445                 //Assert(!(w2->flags & WALL_DOOR_OPENING  ||  w2->flags & WALL_DOOR_OPENED));
446
447                 w2->state = WALL_DOOR_OPENING;
448
449                 csegp = &Segments[seg2->children[w2->sidenum]];
450                 Connectside = find_connect_side(seg2, csegp);
451                 Assert(Connectside != -1);
452                 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_OPENING;
453
454                 d->n_parts = 2;
455                 d->front_wallnum[1] = w->linked_wall;
456                 d->back_wallnum[1] = csegp->sides[Connectside].wall_num;
457         }
458         else
459                 d->n_parts = 1;
460
461
462         if ( Newdemo_state != ND_STATE_PLAYBACK )
463         {
464                 // NOTE THE LINK TO ABOVE!!!!
465                 vms_vector cp;
466                 compute_center_point_on_side(&cp, seg, side );
467                 if (WallAnims[w->clip_num].open_sound > -1 )
468                         digi_link_sound_to_pos( WallAnims[w->clip_num].open_sound, seg-Segments, side, &cp, 0, F1_0 );
469
470         }
471 }
472
473 //-----------------------------------------------------------------
474 // start the transition from closed -> open wall
475 void start_wall_cloak(segment *seg, int side)
476 {
477         wall *w;
478         cloaking_wall *d;
479         int Connectside;
480         segment *csegp;
481         int i;
482
483         if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
484
485         Assert(seg->sides[side].wall_num != -1);        //Opening door on illegal wall
486
487         w = &Walls[seg->sides[side].wall_num];
488
489         if (w->type == WALL_OPEN || w->state == WALL_DOOR_CLOAKING)             //already open or cloaking
490                 return;
491
492         csegp = &Segments[seg->children[side]];
493         Connectside = find_connect_side(seg, csegp);
494         Assert(Connectside != -1);
495
496         if (w->state == WALL_DOOR_DECLOAKING) { //decloaking, so reuse door
497
498                 int i;
499
500                 d = NULL;
501
502                 for (i=0;i<Num_cloaking_walls;i++) {            //find door
503
504                         d = &CloakingWalls[i];
505         
506                         if (d->front_wallnum==w-Walls || d->back_wallnum==w-Walls )
507                                 break;
508                 } 
509
510                 Assert(i<Num_cloaking_walls);                           //didn't find door!
511                 Assert( d!=NULL ); // Get John!
512
513                 d->time = CLOAKING_WALL_TIME - d->time;
514
515         }
516         else if (w->state == WALL_DOOR_CLOSED) {        //create new door
517                 d = &CloakingWalls[Num_cloaking_walls];
518                 d->time = 0;
519                 if (Num_cloaking_walls >= MAX_CLOAKING_WALLS) {         //no more!
520                         Int3();         //ran out of cloaking wall slots
521                         w->type = WALL_OPEN;
522                         Walls[csegp->sides[Connectside].wall_num].type = WALL_OPEN;
523                         return;
524                 }
525                 Num_cloaking_walls++;
526         }
527         else {
528                 Int3();         //unexpected wall state
529                 return;
530         }
531
532         w->state = WALL_DOOR_CLOAKING;
533         Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOAKING;
534
535         d->front_wallnum = seg->sides[side].wall_num;
536         d->back_wallnum = csegp->sides[Connectside].wall_num;
537
538         Assert( seg-Segments != -1);
539
540         Assert(w->linked_wall == -1);
541
542         if ( Newdemo_state != ND_STATE_PLAYBACK ) {
543                 vms_vector cp;
544                 compute_center_point_on_side(&cp, seg, side );
545                 digi_link_sound_to_pos( SOUND_WALL_CLOAK_ON, seg-Segments, side, &cp, 0, F1_0 );
546         }
547
548         for (i=0;i<4;i++) {
549                 d->front_ls[i] = seg->sides[side].uvls[i].l;
550                 d->back_ls[i] = csegp->sides[Connectside].uvls[i].l;
551         }
552 }
553
554 //-----------------------------------------------------------------
555 // start the transition from open -> closed wall
556 void start_wall_decloak(segment *seg, int side)
557 {
558         wall *w;
559         cloaking_wall *d;
560         int Connectside;
561         segment *csegp;
562         int i;
563
564         if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
565
566         Assert(seg->sides[side].wall_num != -1);        //Opening door on illegal wall
567
568         w = &Walls[seg->sides[side].wall_num];
569
570         if (w->type == WALL_CLOSED || w->state == WALL_DOOR_DECLOAKING)         //already closed or decloaking
571                 return;
572
573         if (w->state == WALL_DOOR_CLOAKING) {   //cloaking, so reuse door
574
575                 int i;
576
577                 d = NULL;
578
579                 for (i=0;i<Num_cloaking_walls;i++) {            //find door
580
581                         d = &CloakingWalls[i];
582         
583                         if (d->front_wallnum==w-Walls || d->back_wallnum==w-Walls )
584                                 break;
585                 } 
586
587                 Assert(i<Num_cloaking_walls);                           //didn't find door!
588                 Assert( d!=NULL ); // Get John!
589
590                 d->time = CLOAKING_WALL_TIME - d->time;
591
592         }
593         else if (w->state == WALL_DOOR_CLOSED) {        //create new door
594                 d = &CloakingWalls[Num_cloaking_walls];
595                 d->time = 0;
596                 if (Num_cloaking_walls >= MAX_CLOAKING_WALLS) {         //no more!
597                         Int3();         //ran out of cloaking wall slots
598                         /* what is this _doing_ here?
599                         w->type = WALL_CLOSED;
600                         Walls[csegp->sides[Connectside].wall_num].type = WALL_CLOSED;
601                         */
602                         return;
603                 }
604                 Num_cloaking_walls++;
605         }
606         else {
607                 Int3();         //unexpected wall state
608                 return;
609         }
610
611         w->state = WALL_DOOR_DECLOAKING;
612
613         // So that door can't be shot while opening
614         csegp = &Segments[seg->children[side]];
615         Connectside = find_connect_side(seg, csegp);
616         Assert(Connectside != -1);
617
618         Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_DECLOAKING;
619
620         d->front_wallnum = seg->sides[side].wall_num;
621         d->back_wallnum = csegp->sides[Connectside].wall_num;
622
623         Assert( seg-Segments != -1);
624
625         Assert(w->linked_wall == -1);
626
627         if ( Newdemo_state != ND_STATE_PLAYBACK ) {
628                 vms_vector cp;
629                 compute_center_point_on_side(&cp, seg, side );
630                 digi_link_sound_to_pos( SOUND_WALL_CLOAK_OFF, seg-Segments, side, &cp, 0, F1_0 );
631         }
632
633         for (i=0;i<4;i++) {
634                 d->front_ls[i] = seg->sides[side].uvls[i].l;
635                 d->back_ls[i] = csegp->sides[Connectside].uvls[i].l;
636         }
637 }
638
639 //-----------------------------------------------------------------
640 // This function closes the specified door and restores the closed
641 //  door texture.  This is called when the animation is done
642 void wall_close_door_num(int door_num)
643 {
644         int p;
645         active_door *d;
646         int i;
647
648         d = &ActiveDoors[door_num];
649
650         for (p=0;p<d->n_parts;p++) {
651                 wall *w;
652                 int Connectside, side;
653                 segment *csegp, *seg;
654         
655                 w = &Walls[d->front_wallnum[p]];
656
657                 seg = &Segments[w->segnum];
658                 side = w->sidenum;
659         
660                 Assert(seg->sides[side].wall_num != -1);                //Closing door on illegal wall
661                 
662                 csegp = &Segments[seg->children[side]];
663                 Connectside = find_connect_side(seg, csegp);
664                 Assert(Connectside != -1);
665         
666                 Walls[seg->sides[side].wall_num].state = WALL_DOOR_CLOSED;
667                 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOSED;
668         
669                 wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,0);
670
671         }
672         
673         for (i=door_num;i<Num_open_doors;i++)
674                 ActiveDoors[i] = ActiveDoors[i+1];
675
676         Num_open_doors--;
677
678 }
679
680 int check_poke(int objnum,int segnum,int side)
681 {
682         object *obj = &Objects[objnum];
683
684         //note: don't let objects with zero size block door
685
686         if (obj->size && get_seg_masks(&obj->pos,segnum,obj->size).sidemask & (1<<side))
687                 return 1;               //pokes through side!
688         else
689                 return 0;               //does not!
690
691 }
692
693 //returns true of door in unobjstructed (& thus can close)
694 int is_door_free(segment *seg,int side)
695 {
696         int Connectside;
697         segment *csegp;
698         int objnum;
699         
700         csegp = &Segments[seg->children[side]];
701         Connectside = find_connect_side(seg, csegp);
702         Assert(Connectside != -1);
703
704         //go through each object in each of two segments, and see if
705         //it pokes into the connecting seg
706
707         for (objnum=seg->objects;objnum!=-1;objnum=Objects[objnum].next)
708                 if (Objects[objnum].type!=OBJ_WEAPON && Objects[objnum].type!=OBJ_FIREBALL && check_poke(objnum,seg-Segments,side))
709                         return 0;       //not free
710
711         for (objnum=csegp->objects;objnum!=-1;objnum=Objects[objnum].next)
712                 if (Objects[objnum].type!=OBJ_WEAPON && Objects[objnum].type!=OBJ_FIREBALL && check_poke(objnum,csegp-Segments,Connectside))
713                         return 0;       //not free
714
715         return 1;       //doorway is free!
716 }
717
718
719
720 //-----------------------------------------------------------------
721 // Closes a door 
722 void wall_close_door(segment *seg, int side)
723 {
724         wall *w;
725         active_door *d;
726         int Connectside;
727         segment *csegp;
728
729         Assert(seg->sides[side].wall_num != -1);        //Opening door on illegal wall
730
731         w = &Walls[seg->sides[side].wall_num];
732
733         if ((w->state == WALL_DOOR_CLOSING) ||          //already closing
734                  (w->state == WALL_DOOR_WAITING)        ||              //open, waiting to close
735                  (w->state == WALL_DOOR_CLOSED))                        //closed
736                 return;
737
738         if (!is_door_free(seg,side))
739                 return;
740
741         if (w->state == WALL_DOOR_OPENING) {    //reuse door
742
743                 int i;
744         
745                 d = NULL;
746
747                 for (i=0;i<Num_open_doors;i++) {                //find door
748
749                         d = &ActiveDoors[i];
750         
751                         if (d->front_wallnum[0]==w-Walls || d->back_wallnum[0]==w-Walls || (d->n_parts==2 && (d->front_wallnum[1]==w-Walls || d->back_wallnum[1]==w-Walls)))
752                                 break;
753                 } 
754
755                 Assert(i<Num_open_doors);                               //didn't find door!
756                 Assert( d!=NULL ); // Get John!
757
758                 d->time = WallAnims[w->clip_num].play_time - d->time;
759
760                 if (d->time < 0)
761                         d->time = 0;
762         
763         }
764         else {                                                                                  //create new door
765                 Assert(w->state == WALL_DOOR_OPEN);
766                 d = &ActiveDoors[Num_open_doors];
767                 d->time = 0;
768                 Num_open_doors++;
769                 Assert( Num_open_doors < MAX_DOORS );
770         }
771
772         w->state = WALL_DOOR_CLOSING;
773
774         // So that door can't be shot while opening
775         csegp = &Segments[seg->children[side]];
776         Connectside = find_connect_side(seg, csegp);
777         Assert(Connectside != -1);
778
779         Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOSING;
780
781         d->front_wallnum[0] = seg->sides[side].wall_num;
782         d->back_wallnum[0] = csegp->sides[Connectside].wall_num;
783
784         Assert( seg-Segments != -1);
785
786         if (Newdemo_state == ND_STATE_RECORDING) {
787                 newdemo_record_door_opening(seg-Segments, side);
788         }
789
790         if (w->linked_wall != -1) {
791                 Int3();         //don't think we ever used linked walls
792         }
793         else
794                 d->n_parts = 1;
795
796
797         if ( Newdemo_state != ND_STATE_PLAYBACK )
798         {
799                 // NOTE THE LINK TO ABOVE!!!!
800                 vms_vector cp;
801                 compute_center_point_on_side(&cp, seg, side );
802                 if (WallAnims[w->clip_num].open_sound > -1 )
803                         digi_link_sound_to_pos( WallAnims[w->clip_num].open_sound, seg-Segments, side, &cp, 0, F1_0 );
804
805         }
806 }
807
808 //-----------------------------------------------------------------
809 // Animates opening of a door.
810 // Called in the game loop.
811 void do_door_open(int door_num)
812 {
813         int p;
814         active_door *d;
815
816         Assert(door_num != -1);         //Trying to do_door_open on illegal door
817         
818         d = &ActiveDoors[door_num];
819
820         for (p=0;p<d->n_parts;p++) {
821                 wall *w;
822                 int Connectside, side;
823                 segment *csegp, *seg;
824                 fix time_elapsed, time_total, one_frame;
825                 int i, n;
826         
827                 w = &Walls[d->front_wallnum[p]];
828                 kill_stuck_objects(d->front_wallnum[p]);
829                 kill_stuck_objects(d->back_wallnum[p]);
830
831                 seg = &Segments[w->segnum];
832                 side = w->sidenum;
833         
834                 Assert(seg->sides[side].wall_num != -1);                //Trying to do_door_open on illegal wall
835         
836                 csegp = &Segments[seg->children[side]];
837                 Connectside = find_connect_side(seg, csegp);
838                 Assert(Connectside != -1);
839
840                 d->time += FrameTime;
841         
842                 time_elapsed = d->time;
843                 n = WallAnims[w->clip_num].num_frames;
844                 time_total = WallAnims[w->clip_num].play_time;
845         
846                 one_frame = time_total/n;       
847         
848                 i = time_elapsed/one_frame;
849         
850                 if (i < n)
851                         wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,i);
852         
853                 if (i> n/2) {
854                         Walls[seg->sides[side].wall_num].flags |= WALL_DOOR_OPENED;
855                         Walls[csegp->sides[Connectside].wall_num].flags |= WALL_DOOR_OPENED;
856                 }
857         
858                 if (i >= n-1) {
859                         wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,n-1);
860
861                         // If our door is not automatic just remove it from the list.
862                         if (!(Walls[seg->sides[side].wall_num].flags & WALL_DOOR_AUTO)) {
863                                 for (i=door_num;i<Num_open_doors;i++)
864                                         ActiveDoors[i] = ActiveDoors[i+1];
865                                 Num_open_doors--;
866                                 Walls[seg->sides[side].wall_num].state = WALL_DOOR_OPEN;
867                                 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_OPEN;
868                         }
869                         else {
870
871                                 Walls[seg->sides[side].wall_num].state = WALL_DOOR_WAITING;
872                                 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_WAITING;
873
874                                 ActiveDoors[Num_open_doors].time = 0;   //counts up
875                         }
876                 }
877
878         }
879
880 }
881
882 //-----------------------------------------------------------------
883 // Animates and processes the closing of a door.
884 // Called from the game loop.
885 void do_door_close(int door_num)
886 {
887         int p;
888         active_door *d;
889         wall *w;
890
891         Assert(door_num != -1);         //Trying to do_door_open on illegal door
892         
893         d = &ActiveDoors[door_num];
894
895         w = &Walls[d->front_wallnum[0]];
896
897         //check for objects in doorway before closing
898         if (w->flags & WALL_DOOR_AUTO)
899                 if (!is_door_free(&Segments[w->segnum],w->sidenum)) {
900                         digi_kill_sound_linked_to_segment(w->segnum,w->sidenum,-1);
901                         wall_open_door(&Segments[w->segnum],w->sidenum);                //re-open door
902                         return;
903                 }
904
905         for (p=0;p<d->n_parts;p++) {
906                 wall *w;
907                 int Connectside, side;
908                 segment *csegp, *seg;
909                 fix time_elapsed, time_total, one_frame;
910                 int i, n;
911         
912                 w = &Walls[d->front_wallnum[p]];
913
914                 seg = &Segments[w->segnum];
915                 side = w->sidenum;
916         
917                 if (seg->sides[side].wall_num == -1) {
918                         mprintf((0, "Trying to do_door_close on Illegal wall\n"));
919                         return;
920                 }
921         
922                 //if here, must be auto door
923 //              Assert(Walls[seg->sides[side].wall_num].flags & WALL_DOOR_AUTO);                
924 //don't assert here, because now we have triggers to close non-auto doors
925         
926                 // Otherwise, close it.
927                 csegp = &Segments[seg->children[side]];
928                 Connectside = find_connect_side(seg, csegp);
929                 Assert(Connectside != -1);
930         
931
932                 if ( Newdemo_state != ND_STATE_PLAYBACK )
933                         // NOTE THE LINK TO ABOVE!!
934                         if (p==0)       //only play one sound for linked doors
935                                 if ( d->time==0 )       {               //first time
936                                         vms_vector cp;
937                                         compute_center_point_on_side(&cp, seg, side );
938                                         if (WallAnims[w->clip_num].close_sound  > -1 )
939                                                 digi_link_sound_to_pos( WallAnims[Walls[seg->sides[side].wall_num].clip_num].close_sound, seg-Segments, side, &cp, 0, F1_0 );
940                                 }
941         
942                 d->time += FrameTime;
943
944                 time_elapsed = d->time;
945                 n = WallAnims[w->clip_num].num_frames;
946                 time_total = WallAnims[w->clip_num].play_time;
947         
948                 one_frame = time_total/n;       
949         
950                 i = n-time_elapsed/one_frame-1;
951         
952                 if (i < n/2) {
953                         Walls[seg->sides[side].wall_num].flags &= ~WALL_DOOR_OPENED;
954                         Walls[csegp->sides[Connectside].wall_num].flags &= ~WALL_DOOR_OPENED;
955                 }
956         
957                 // Animate door.
958                 if (i > 0) {
959                         wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,i);
960
961                         Walls[seg->sides[side].wall_num].state = WALL_DOOR_CLOSING;
962                         Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOSING;
963
964                         ActiveDoors[Num_open_doors].time = 0;           //counts up
965
966                 } else 
967                         wall_close_door_num(door_num);
968         }
969 }
970
971
972 //-----------------------------------------------------------------
973 // Turns off an illusionary wall (This will be used primarily for
974 //  wall switches or triggers that can turn on/off illusionary walls.)
975 void wall_illusion_off(segment *seg, int side)
976 {
977         segment *csegp;
978         int cside;
979
980         csegp = &Segments[seg->children[side]];
981         cside = find_connect_side(seg, csegp);
982         Assert(cside != -1);
983
984         if (seg->sides[side].wall_num == -1) {
985                 mprintf((0, "Trying to shut off illusion illegal wall\n"));
986                 return;
987         }
988
989         Walls[seg->sides[side].wall_num].flags |= WALL_ILLUSION_OFF;
990         Walls[csegp->sides[cside].wall_num].flags |= WALL_ILLUSION_OFF;
991
992         kill_stuck_objects(seg->sides[side].wall_num);
993         kill_stuck_objects(csegp->sides[cside].wall_num);
994 }
995
996 //-----------------------------------------------------------------
997 // Turns on an illusionary wall (This will be used primarily for
998 //  wall switches or triggers that can turn on/off illusionary walls.)
999 void wall_illusion_on(segment *seg, int side)
1000 {
1001         segment *csegp;
1002         int cside;
1003
1004         csegp = &Segments[seg->children[side]];
1005         cside = find_connect_side(seg, csegp);
1006         Assert(cside != -1);
1007
1008         if (seg->sides[side].wall_num == -1) {
1009                 mprintf((0, "Trying to turn on illusion illegal wall\n"));
1010                 return;
1011         }
1012
1013         Walls[seg->sides[side].wall_num].flags &= ~WALL_ILLUSION_OFF;
1014         Walls[csegp->sides[cside].wall_num].flags &= ~WALL_ILLUSION_OFF;
1015 }
1016
1017 //      -----------------------------------------------------------------------------
1018 //      Allowed to open the normally locked special boss door if in multiplayer mode.
1019 int special_boss_opening_allowed(int segnum, int sidenum)
1020 {
1021         if (Game_mode & GM_MULTI)
1022                 return (Current_level_num == BOSS_LOCKED_DOOR_LEVEL) && (segnum == BOSS_LOCKED_DOOR_SEG) && (sidenum == BOSS_LOCKED_DOOR_SIDE);
1023         else
1024                 return 0;
1025 }
1026
1027 //-----------------------------------------------------------------
1028 // Determines what happens when a wall is shot
1029 //returns info about wall.  see wall.h for codes
1030 //obj is the object that hit...either a weapon or the player himself
1031 //playernum is the number the player who hit the wall or fired the weapon,
1032 //or -1 if a robot fired the weapon
1033 int wall_hit_process(segment *seg, int side, fix damage, int playernum, object *obj )
1034 {
1035         wall    *w;
1036         fix     show_message;
1037
1038         Assert (seg-Segments != -1);
1039
1040         // If it is not a "wall" then just return.
1041         if ( seg->sides[side].wall_num < 0 )
1042                 return WHP_NOT_SPECIAL;
1043
1044         w = &Walls[seg->sides[side].wall_num];
1045
1046         if ( Newdemo_state == ND_STATE_RECORDING )
1047                 newdemo_record_wall_hit_process( seg-Segments, side, damage, playernum );
1048
1049         if (w->type == WALL_BLASTABLE) {
1050                 if (obj->ctype.laser_info.parent_type == OBJ_PLAYER)
1051                         wall_damage(seg, side, damage);
1052                 return WHP_BLASTABLE;
1053         }
1054
1055         if (playernum != Player_num)    //return if was robot fire
1056                 return WHP_NOT_SPECIAL;
1057
1058         Assert( playernum > -1 );
1059         
1060         //      Determine whether player is moving forward.  If not, don't say negative
1061         //      messages because he probably didn't intentionally hit the door.
1062         if (obj->type == OBJ_PLAYER)
1063                 show_message = (vm_vec_dot(&obj->orient.fvec, &obj->mtype.phys_info.velocity) > 0);
1064         else if (obj->type == OBJ_ROBOT)
1065                 show_message = 0;
1066         else if ((obj->type == OBJ_WEAPON) && (obj->ctype.laser_info.parent_type == OBJ_ROBOT))
1067                 show_message = 0;
1068         else
1069                 show_message = 1;
1070
1071         if (w->keys == KEY_BLUE)
1072                 if (!(Players[playernum].flags & PLAYER_FLAGS_BLUE_KEY)) {
1073                         if ( playernum==Player_num )
1074                                 if (show_message)
1075                                         HUD_init_message("%s %s",TXT_BLUE,TXT_ACCESS_DENIED);
1076                         return WHP_NO_KEY;
1077                 }
1078
1079         if (w->keys == KEY_RED)
1080                 if (!(Players[playernum].flags & PLAYER_FLAGS_RED_KEY)) {
1081                         if ( playernum==Player_num )
1082                                 if (show_message)
1083                                         HUD_init_message("%s %s",TXT_RED,TXT_ACCESS_DENIED);
1084                         return WHP_NO_KEY;
1085                 }
1086         
1087         if (w->keys == KEY_GOLD)
1088                 if (!(Players[playernum].flags & PLAYER_FLAGS_GOLD_KEY)) {
1089                         if ( playernum==Player_num )
1090                                 if (show_message)
1091                                         HUD_init_message("%s %s",TXT_YELLOW,TXT_ACCESS_DENIED);
1092                         return WHP_NO_KEY;
1093                 }
1094
1095         if (w->type == WALL_DOOR)
1096         {
1097                 if ((w->flags & WALL_DOOR_LOCKED ) && !(special_boss_opening_allowed(seg-Segments, side)) ) {
1098                         if ( playernum==Player_num )
1099                                 if (show_message)
1100                                         HUD_init_message(TXT_CANT_OPEN_DOOR);
1101                         return WHP_NO_KEY;
1102                 }
1103                 else {
1104                         if (w->state != WALL_DOOR_OPENING)
1105                         {
1106                                 wall_open_door(seg, side);
1107                         #ifdef NETWORK
1108                                 if (Game_mode & GM_MULTI)
1109                                         multi_send_door_open(seg-Segments, side,w->flags);
1110                         #endif
1111                         }
1112                         return WHP_DOOR;
1113                         
1114                 }
1115         }
1116
1117         return WHP_NOT_SPECIAL;         //default is treat like normal wall
1118 }
1119
1120 //-----------------------------------------------------------------
1121 // Opens doors/destroys wall/shuts off triggers.
1122 void wall_toggle(segment *seg, int side)
1123 {
1124         int wall_num; 
1125
1126         Assert( seg-Segments <= Highest_segment_index);
1127         Assert( side < MAX_SIDES_PER_SEGMENT );
1128
1129         wall_num = seg->sides[side].wall_num;
1130
1131         if (wall_num == -1) {
1132                 mprintf((0, "Illegal wall_toggle\n"));
1133                 return;
1134         }
1135
1136         if ( Newdemo_state == ND_STATE_RECORDING )
1137                 newdemo_record_wall_toggle(seg-Segments, side );
1138
1139         if (Walls[wall_num].type == WALL_BLASTABLE)
1140                 wall_destroy(seg, side);
1141
1142         if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].state == WALL_DOOR_CLOSED))
1143                 wall_open_door(seg, side);
1144
1145 }
1146
1147
1148 //-----------------------------------------------------------------
1149 // Tidy up Walls array for load/save purposes.
1150 void reset_walls()
1151 {
1152         int i;
1153
1154         if (Num_walls < 0) {
1155                 mprintf((0, "Illegal Num_walls\n"));
1156                 return;
1157         }
1158
1159         for (i=Num_walls;i<MAX_WALLS;i++) {
1160                 Walls[i].type = WALL_NORMAL;
1161                 Walls[i].flags = 0;
1162                 Walls[i].hps = 0;
1163                 Walls[i].trigger = -1;
1164                 Walls[i].clip_num = -1;
1165                 }
1166 }
1167
1168 void do_cloaking_wall_frame(int cloaking_wall_num)
1169 {
1170         cloaking_wall *d;
1171         wall *wfront,*wback;
1172
1173         if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
1174
1175         d = &CloakingWalls[cloaking_wall_num];
1176         wfront = &Walls[d->front_wallnum];
1177         wback = &Walls[d->back_wallnum];
1178
1179         d->time += FrameTime;
1180
1181         if (d->time > CLOAKING_WALL_TIME) {
1182                 int i;
1183
1184                 wfront->type = wback->type = WALL_OPEN;
1185                 wfront->state = wback->state = WALL_DOOR_CLOSED;                //why closed? why not?
1186
1187                 for (i=cloaking_wall_num;i<Num_cloaking_walls;i++)
1188                         CloakingWalls[i] = CloakingWalls[i+1];
1189                 Num_cloaking_walls--;
1190
1191         }
1192         else if (d->time > CLOAKING_WALL_TIME/2) {
1193                 int old_type=wfront->type;
1194
1195                 wfront->cloak_value = wback->cloak_value = ((d->time - CLOAKING_WALL_TIME/2) * (GR_FADE_LEVELS-2)) / (CLOAKING_WALL_TIME/2);
1196
1197                 if (old_type != WALL_CLOAKED) {         //just switched
1198                         int i;
1199
1200                         wfront->type = wback->type = WALL_CLOAKED;
1201
1202                         for (i=0;i<4;i++) {
1203                                 Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = d->front_ls[i];
1204                                 Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = d->back_ls[i];
1205                         }
1206                 }
1207         }
1208         else {          //fading out
1209                 fix light_scale;
1210                 int i;
1211
1212                 light_scale = fixdiv(CLOAKING_WALL_TIME/2-d->time,CLOAKING_WALL_TIME/2);
1213
1214                 for (i=0;i<4;i++) {
1215                         Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = fixmul(d->front_ls[i],light_scale);
1216                         Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = fixmul(d->back_ls[i],light_scale);
1217                 }
1218         }
1219
1220         if ( Newdemo_state == ND_STATE_RECORDING )
1221                 newdemo_record_cloaking_wall(d->front_wallnum, d->back_wallnum, wfront->type, wfront->state, wfront->cloak_value, Segments[wfront->segnum].sides[wfront->sidenum].uvls[0].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[1].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[2].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[3].l);
1222
1223 }
1224
1225 void do_decloaking_wall_frame(int cloaking_wall_num)
1226 {
1227         cloaking_wall *d;
1228         wall *wfront,*wback;
1229
1230         if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
1231
1232         d = &CloakingWalls[cloaking_wall_num];
1233         wfront = &Walls[d->front_wallnum];
1234         wback = &Walls[d->back_wallnum];
1235
1236         d->time += FrameTime;
1237
1238         if (d->time > CLOAKING_WALL_TIME) {
1239                 int i;
1240
1241                 wfront->state = wback->state = WALL_DOOR_CLOSED;
1242
1243                 for (i=0;i<4;i++) {
1244                         Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = d->front_ls[i];
1245                         Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = d->back_ls[i];
1246                 }
1247
1248                 for (i=cloaking_wall_num;i<Num_cloaking_walls;i++)
1249                         CloakingWalls[i] = CloakingWalls[i+1];
1250                 Num_cloaking_walls--;
1251
1252         }
1253         else if (d->time > CLOAKING_WALL_TIME/2) {              //fading in
1254                 fix light_scale;
1255                 int i;
1256
1257                 wfront->type = wback->type = WALL_CLOSED;
1258
1259                 light_scale = fixdiv(d->time-CLOAKING_WALL_TIME/2,CLOAKING_WALL_TIME/2);
1260
1261                 for (i=0;i<4;i++) {
1262                         Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = fixmul(d->front_ls[i],light_scale);
1263                         Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = fixmul(d->back_ls[i],light_scale);
1264                 }
1265         }
1266         else {          //cloaking in
1267                 wfront->cloak_value = wback->cloak_value = ((CLOAKING_WALL_TIME/2 - d->time) * (GR_FADE_LEVELS-2)) / (CLOAKING_WALL_TIME/2);
1268                 wfront->type = wback->type = WALL_CLOAKED;
1269         }
1270
1271         if ( Newdemo_state == ND_STATE_RECORDING )
1272                 newdemo_record_cloaking_wall(d->front_wallnum, d->back_wallnum, wfront->type, wfront->state, wfront->cloak_value, Segments[wfront->segnum].sides[wfront->sidenum].uvls[0].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[1].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[2].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[3].l);
1273
1274 }
1275
1276 void wall_frame_process()
1277 {
1278         int i;
1279
1280         for (i=0;i<Num_open_doors;i++) {
1281                 active_door *d;
1282                 wall *w;
1283
1284                 d = &ActiveDoors[i];
1285                 w = &Walls[d->front_wallnum[0]];
1286
1287                 if (w->state == WALL_DOOR_OPENING)
1288                         do_door_open(i);
1289                 else if (w->state == WALL_DOOR_CLOSING)
1290                         do_door_close(i);
1291                 else if (w->state == WALL_DOOR_WAITING) {
1292                         d->time += FrameTime;
1293
1294                         //set flags to fix occatsional netgame problem where door is
1295                         //waiting to close but open flag isn't set
1296                         Assert(d->n_parts == 1);
1297                         w->flags |= WALL_DOOR_OPENED;
1298                         Walls[d->back_wallnum[0]].flags |= WALL_DOOR_OPENED;
1299
1300                         if (d->time > DOOR_WAIT_TIME && is_door_free(&Segments[w->segnum],w->sidenum)) {
1301                                 w->state = WALL_DOOR_CLOSING;
1302                                 d->time = 0;
1303                         }
1304                 } 
1305                 else if (w->state == WALL_DOOR_CLOSED || w->state == WALL_DOOR_OPEN) {
1306                         //this shouldn't happen.  if the wall is in one of these states, 
1307                         //there shouldn't be an activedoor entry for it.  So we'll kill
1308                         //the activedoor entry.  Tres simple.
1309                         int t;
1310                         Int3();         //a bad thing has happened, but I'll try to fix it up
1311                         for (t=i;t<Num_open_doors;t++)
1312                                 ActiveDoors[t] = ActiveDoors[t+1];
1313                         Num_open_doors--;
1314                 } 
1315         } 
1316
1317         for (i=0;i<Num_cloaking_walls;i++) {
1318                 cloaking_wall *d;
1319                 wall *w;
1320
1321                 d = &CloakingWalls[i];
1322                 w = &Walls[d->front_wallnum];
1323
1324                 if (w->state == WALL_DOOR_CLOAKING)
1325                         do_cloaking_wall_frame(i);
1326                 else if (w->state == WALL_DOOR_DECLOAKING)
1327                         do_decloaking_wall_frame(i);
1328                 else
1329                         Int3(); //unexpected wall state
1330         } 
1331 }
1332
1333 int     Num_stuck_objects=0;
1334
1335 stuckobj        Stuck_objects[MAX_STUCK_OBJECTS];
1336
1337 //      An object got stuck in a door (like a flare).
1338 //      Add global entry.
1339 void add_stuck_object(object *objp, int segnum, int sidenum)
1340 {
1341         int     i;
1342         int     wallnum;
1343
1344         wallnum = Segments[segnum].sides[sidenum].wall_num;
1345
1346         if (wallnum != -1) {
1347                 if (Walls[wallnum].flags & WALL_BLASTED)
1348                         objp->flags |= OF_SHOULD_BE_DEAD;  
1349
1350                 for (i=0; i<MAX_STUCK_OBJECTS; i++) {
1351                         if (Stuck_objects[i].wallnum == -1) {
1352                                 Stuck_objects[i].wallnum = wallnum;
1353                                 Stuck_objects[i].objnum = objp-Objects;
1354                                 Stuck_objects[i].signature = objp->signature;
1355                                 // mprintf((0, "Added wall %i at index %i\n", wallnum, i));
1356                                 Num_stuck_objects++;
1357                                 break;
1358                         }
1359                 }
1360                 if (i == MAX_STUCK_OBJECTS)
1361                         mprintf((1, "Warning: Unable to add object %i which got stuck in wall %i to Stuck_objects\n", objp-Objects, wallnum));
1362         }
1363
1364
1365
1366 }
1367
1368 //      --------------------------------------------------------------------------------------------------
1369 //      Look at the list of stuck objects, clean up in case an object has gone away, but not been removed here.
1370 //      Removes up to one/frame.
1371 void remove_obsolete_stuck_objects(void)
1372 {
1373         int     objnum;
1374
1375         //      Safety and efficiency code.  If no stuck objects, should never get inside the IF, but this is faster.
1376         if (!Num_stuck_objects)
1377                 return;
1378
1379         objnum = FrameCount % MAX_STUCK_OBJECTS;
1380
1381         if (Stuck_objects[objnum].wallnum != -1)
1382                 if ((Walls[Stuck_objects[objnum].wallnum].state != WALL_DOOR_CLOSED) || (Objects[Stuck_objects[objnum].objnum].signature != Stuck_objects[objnum].signature)) {
1383                         Num_stuck_objects--;
1384                         Objects[Stuck_objects[objnum].objnum].lifeleft = F1_0/8;
1385                         Stuck_objects[objnum].wallnum = -1;
1386                 }
1387
1388 }
1389
1390 extern void flush_fcd_cache(void);
1391
1392 //      ----------------------------------------------------------------------------------------------------
1393 //      Door with wall index wallnum is opening, kill all objects stuck in it.
1394 void kill_stuck_objects(int wallnum)
1395 {
1396         int     i;
1397
1398         if (Num_stuck_objects == 0) {
1399                 return;
1400         }
1401
1402         Num_stuck_objects=0;
1403
1404         for (i=0; i<MAX_STUCK_OBJECTS; i++)
1405                 if (Stuck_objects[i].wallnum == wallnum) {
1406                         if (Objects[Stuck_objects[i].objnum].type == OBJ_WEAPON) {
1407                                 Objects[Stuck_objects[i].objnum].lifeleft = F1_0/8;
1408                         } else
1409                                 mprintf((1, "Warning: Stuck object of type %i, expected to be of type %i, see wall.c\n", Objects[Stuck_objects[i].objnum].type, OBJ_WEAPON));
1410                                 // Int3();      //      What?  This looks bad.  Object is not a weapon and it is stuck in a wall!
1411                         Stuck_objects[i].wallnum = -1;
1412                 } else if (Stuck_objects[i].wallnum != -1) {
1413                         Num_stuck_objects++;
1414                 }
1415         //      Ok, this is awful, but we need to do things whenever a door opens/closes/disappears, etc.
1416         flush_fcd_cache();
1417
1418 }
1419
1420
1421 // -- unused -- // -----------------------------------------------------------------------------------
1422 // -- unused -- //      Return object id of first flare found embedded in segp:sidenum.
1423 // -- unused -- //      If no flare, return -1.
1424 // -- unused -- int contains_flare(segment *segp, int sidenum)
1425 // -- unused -- {
1426 // -- unused --         int     i;
1427 // -- unused -- 
1428 // -- unused --         for (i=0; i<Num_stuck_objects; i++) {
1429 // -- unused --                 object  *objp = &Objects[Stuck_objects[i].objnum];
1430 // -- unused -- 
1431 // -- unused --                 if ((objp->type == OBJ_WEAPON) && (objp->id == FLARE_ID)) {
1432 // -- unused --                         if (Walls[Stuck_objects[i].wallnum].segnum == segp-Segments)
1433 // -- unused --                                 if (Walls[Stuck_objects[i].wallnum].sidenum == sidenum)
1434 // -- unused --                                         return objp-Objects;
1435 // -- unused --                 }
1436 // -- unused --         }
1437 // -- unused -- 
1438 // -- unused --         return -1;
1439 // -- unused -- }
1440
1441 // -----------------------------------------------------------------------------------
1442 // Initialize stuck objects array.  Called at start of level
1443 void init_stuck_objects(void)
1444 {
1445         int     i;
1446
1447         for (i=0; i<MAX_STUCK_OBJECTS; i++)
1448                 Stuck_objects[i].wallnum = -1;
1449
1450         Num_stuck_objects = 0;
1451 }
1452
1453 // -----------------------------------------------------------------------------------
1454 // Clear out all stuck objects.  Called for a new ship
1455 void clear_stuck_objects(void)
1456 {
1457         int     i;
1458
1459         for (i=0; i<MAX_STUCK_OBJECTS; i++) {
1460                 if (Stuck_objects[i].wallnum != -1) {
1461                         int     objnum;
1462
1463                         objnum = Stuck_objects[i].objnum;
1464
1465                         if ((Objects[objnum].type == OBJ_WEAPON) && (Objects[objnum].id == FLARE_ID))
1466                                 Objects[objnum].lifeleft = F1_0/8;
1467
1468                         Stuck_objects[i].wallnum = -1;
1469
1470                         Num_stuck_objects--;
1471                 }
1472         }
1473
1474         Assert(Num_stuck_objects == 0);
1475
1476 }
1477
1478 // -----------------------------------------------------------------------------------
1479 #define MAX_BLAST_GLASS_DEPTH   5
1480
1481 void bng_process_segment(object *objp, fix damage, segment *segp, int depth, byte *visited)
1482 {
1483         int     i, sidenum;
1484
1485         if (depth > MAX_BLAST_GLASS_DEPTH)
1486                 return;
1487
1488         depth++;
1489
1490         for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
1491                 int                     tm;
1492                 fix                     dist;
1493                 vms_vector      pnt;
1494
1495                 //      Process only walls which have glass.
1496                 if ((tm=segp->sides[sidenum].tmap_num2) != 0) {
1497                         int     ec, db;
1498
1499                         tm &= 0x3fff;                   //tm without flags
1500
1501                         if ((((ec=TmapInfo[tm].eclip_num)!=-1) && ((db=Effects[ec].dest_bm_num)!=-1 && !(Effects[ec].flags&EF_ONE_SHOT))) ||    (ec==-1 && (TmapInfo[tm].destroyed!=-1))) {
1502                                 compute_center_point_on_side(&pnt, segp, sidenum);
1503                                 dist = vm_vec_dist_quick(&pnt, &objp->pos);
1504                                 if (dist < damage/2) {
1505                                         dist = find_connected_distance(&pnt, segp-Segments, &objp->pos, objp->segnum, MAX_BLAST_GLASS_DEPTH, WID_RENDPAST_FLAG);
1506                                         if ((dist > 0) && (dist < damage/2))
1507                                                 check_effect_blowup(segp, sidenum, &pnt, &Objects[objp->ctype.laser_info.parent_num], 1);
1508                                 }
1509                         }
1510                 }
1511         }
1512
1513         for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
1514                 int     segnum = segp->children[i];
1515
1516                 if (segnum != -1) {
1517                         if (!visited[segnum]) {
1518                                 if (WALL_IS_DOORWAY(segp, i) & WID_FLY_FLAG) {
1519                                         visited[segnum] = 1;
1520                                         bng_process_segment(objp, damage, &Segments[segnum], depth, visited);
1521                                 }
1522                         }
1523                 }
1524         }
1525 }
1526
1527 // -----------------------------------------------------------------------------------
1528 //      objp is going to detonate
1529 //      blast nearby monitors, lights, maybe other things
1530 void blast_nearby_glass(object *objp, fix damage)
1531 {
1532         int             i;
1533         byte            visited[MAX_SEGMENTS];
1534         segment *cursegp;
1535
1536         cursegp = &Segments[objp->segnum];
1537         for (i=0; i<=Highest_segment_index; i++)
1538                 visited[i] = 0;
1539
1540         visited[objp->segnum] = 1;
1541         bng_process_segment(objp, damage, cursegp, 0, visited);
1542
1543
1544 }
1545