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