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