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