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