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