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