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