]> icculus.org git repositories - btb/d2x.git/blob - main/wall.c
Rename include/error.h to include/dxxerror.h
[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 "dxxerror.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, 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         //kill_stuck_objects(seg->sides[side].wall_num);
351
352         if ((w->state == WALL_DOOR_OPENING) ||          //already opening
353                  (w->state == WALL_DOOR_WAITING)        ||              //open, waiting to close
354                  (w->state == WALL_DOOR_OPEN))                  //open, & staying open
355                 return;
356
357         if (w->state == WALL_DOOR_CLOSING) {            //closing, so reuse door
358
359                 int i;
360         
361                 d = NULL;
362
363                 for (i=0;i<Num_open_doors;i++) {                //find door
364
365                         d = &ActiveDoors[i];
366         
367                         if (d->front_wallnum[0] == WALL_NUMBER(w) ||
368                                 d->back_wallnum[0] == WALL_NUMBER(w) ||
369                                 (d->n_parts == 2 && (d->front_wallnum[1] == WALL_NUMBER(w) ||
370                                                      d->back_wallnum[1] == WALL_NUMBER(w))))
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 == WALL_NUMBER(w) || d->back_wallnum == WALL_NUMBER(w))
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 == WALL_NUMBER(w) || d->back_wallnum == WALL_NUMBER(w))
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, 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
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_NUMBER(w) ||
740                                 d->back_wallnum[0] == WALL_NUMBER(w) ||
741                                 (d->n_parts == 2 && (d->front_wallnum[1] == WALL_NUMBER(w) ||
742                                                      d->back_wallnum[1] == WALL_NUMBER(w))))
743                                 break;
744                 }
745
746                 Assert(i<Num_open_doors);                               //didn't find door!
747                 Assert( d!=NULL ); // Get John!
748
749                 d->time = WallAnims[w->clip_num].play_time - d->time;
750
751                 if (d->time < 0)
752                         d->time = 0;
753         
754         }
755         else {                                                                                  //create new door
756                 Assert(w->state == WALL_DOOR_OPEN);
757                 d = &ActiveDoors[Num_open_doors];
758                 d->time = 0;
759                 Num_open_doors++;
760                 Assert( Num_open_doors < MAX_DOORS );
761         }
762
763         w->state = WALL_DOOR_CLOSING;
764
765         // So that door can't be shot while opening
766         csegp = &Segments[seg->children[side]];
767         Connectside = find_connect_side(seg, csegp);
768         Assert(Connectside != -1);
769         cwall_num = csegp->sides[Connectside].wall_num;
770         if (cwall_num > -1)
771                 Walls[cwall_num].state = WALL_DOOR_CLOSING;
772
773         d->front_wallnum[0] = seg->sides[side].wall_num;
774         d->back_wallnum[0] = cwall_num;
775
776         Assert( SEGMENT_NUMBER(seg) != -1 );
777
778         if (Newdemo_state == ND_STATE_RECORDING) {
779                 newdemo_record_door_opening(SEGMENT_NUMBER(seg), side);
780         }
781
782         if (w->linked_wall != -1) {
783                 Int3();         //don't think we ever used linked walls
784         }
785         else
786                 d->n_parts = 1;
787
788
789         if ( Newdemo_state != ND_STATE_PLAYBACK )
790         {
791                 // NOTE THE LINK TO ABOVE!!!!
792                 vms_vector cp;
793                 compute_center_point_on_side(&cp, seg, side );
794                 if (WallAnims[w->clip_num].open_sound > -1 )
795                         digi_link_sound_to_pos( WallAnims[w->clip_num].open_sound, SEGMENT_NUMBER(seg), side, &cp, 0, F1_0 );
796
797         }
798 }
799
800 //-----------------------------------------------------------------
801 // Animates opening of a door.
802 // Called in the game loop.
803 void do_door_open(int door_num)
804 {
805         int p;
806         active_door *d;
807
808         Assert(door_num != -1);         //Trying to do_door_open on illegal door
809         
810         d = &ActiveDoors[door_num];
811
812         for (p=0;p<d->n_parts;p++) {
813                 wall *w;
814                 int Connectside, side;
815                 segment *csegp, *seg;
816                 fix time_elapsed, time_total, one_frame;
817                 int i, n;
818         
819                 w = &Walls[d->front_wallnum[p]];
820                 kill_stuck_objects(d->front_wallnum[p]);
821                 kill_stuck_objects(d->back_wallnum[p]);
822
823                 seg = &Segments[w->segnum];
824                 side = w->sidenum;
825         
826                 Assert(seg->sides[side].wall_num != -1);                //Trying to do_door_open on illegal wall
827         
828                 csegp = &Segments[seg->children[side]];
829                 Connectside = find_connect_side(seg, csegp);
830                 Assert(Connectside != -1);
831
832                 d->time += FrameTime;
833         
834                 time_elapsed = d->time;
835                 n = WallAnims[w->clip_num].num_frames;
836                 time_total = WallAnims[w->clip_num].play_time;
837         
838                 one_frame = time_total/n;       
839         
840                 i = time_elapsed/one_frame;
841         
842                 if (i < n)
843                         wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,i);
844         
845                 if (i> n/2) {
846                         Walls[seg->sides[side].wall_num].flags |= WALL_DOOR_OPENED;
847                         Walls[csegp->sides[Connectside].wall_num].flags |= WALL_DOOR_OPENED;
848                 }
849         
850                 if (i >= n-1) {
851                         wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,n-1);
852
853                         // If our door is not automatic just remove it from the list.
854                         if (!(Walls[seg->sides[side].wall_num].flags & WALL_DOOR_AUTO)) {
855                                 for (i=door_num;i<Num_open_doors;i++)
856                                         ActiveDoors[i] = ActiveDoors[i+1];
857                                 Num_open_doors--;
858                                 Walls[seg->sides[side].wall_num].state = WALL_DOOR_OPEN;
859                                 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_OPEN;
860                         }
861                         else {
862
863                                 Walls[seg->sides[side].wall_num].state = WALL_DOOR_WAITING;
864                                 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_WAITING;
865
866                                 ActiveDoors[Num_open_doors].time = 0;   //counts up
867                         }
868                 }
869
870         }
871
872 }
873
874 //-----------------------------------------------------------------
875 // Animates and processes the closing of a door.
876 // Called from the game loop.
877 void do_door_close(int door_num)
878 {
879         int p;
880         active_door *d;
881         wall *w;
882
883         Assert(door_num != -1);         //Trying to do_door_open on illegal door
884         
885         d = &ActiveDoors[door_num];
886
887         w = &Walls[d->front_wallnum[0]];
888
889         //check for objects in doorway before closing
890         if (w->flags & WALL_DOOR_AUTO)
891                 if (!is_door_free(&Segments[w->segnum],w->sidenum)) {
892                         digi_kill_sound_linked_to_segment(w->segnum,w->sidenum,-1);
893                         wall_open_door(&Segments[w->segnum],w->sidenum);                //re-open door
894                         return;
895                 }
896
897         for (p=0;p<d->n_parts;p++) {
898                 wall *w;
899                 int Connectside, side;
900                 segment *csegp, *seg;
901                 fix time_elapsed, time_total, one_frame;
902                 int i, n;
903         
904                 w = &Walls[d->front_wallnum[p]];
905
906                 seg = &Segments[w->segnum];
907                 side = w->sidenum;
908         
909                 if (seg->sides[side].wall_num == -1) {
910                         mprintf((0, "Trying to do_door_close on Illegal wall\n"));
911                         return;
912                 }
913         
914                 //if here, must be auto door
915 //              Assert(Walls[seg->sides[side].wall_num].flags & WALL_DOOR_AUTO);                
916 //don't assert here, because now we have triggers to close non-auto doors
917         
918                 // Otherwise, close it.
919                 csegp = &Segments[seg->children[side]];
920                 Connectside = find_connect_side(seg, csegp);
921                 Assert(Connectside != -1);
922         
923
924                 if ( Newdemo_state != ND_STATE_PLAYBACK )
925                         // NOTE THE LINK TO ABOVE!!
926                         if (p==0)       //only play one sound for linked doors
927                                 if ( d->time==0 )       {               //first time
928                                         vms_vector cp;
929                                         compute_center_point_on_side(&cp, seg, side );
930                                         if (WallAnims[w->clip_num].close_sound  > -1 )
931                                                 digi_link_sound_to_pos( WallAnims[Walls[seg->sides[side].wall_num].clip_num].close_sound, SEGMENT_NUMBER(seg), side, &cp, 0, F1_0 );
932                                 }
933         
934                 d->time += FrameTime;
935
936                 time_elapsed = d->time;
937                 n = WallAnims[w->clip_num].num_frames;
938                 time_total = WallAnims[w->clip_num].play_time;
939         
940                 one_frame = time_total/n;       
941         
942                 i = n-time_elapsed/one_frame-1;
943         
944                 if (i < n/2) {
945                         Walls[seg->sides[side].wall_num].flags &= ~WALL_DOOR_OPENED;
946                         Walls[csegp->sides[Connectside].wall_num].flags &= ~WALL_DOOR_OPENED;
947                 }
948         
949                 // Animate door.
950                 if (i > 0) {
951                         wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,i);
952
953                         Walls[seg->sides[side].wall_num].state = WALL_DOOR_CLOSING;
954                         Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOSING;
955
956                         ActiveDoors[Num_open_doors].time = 0;           //counts up
957
958                 } else
959                         wall_close_door_num(door_num);
960         }
961 }
962
963
964 //-----------------------------------------------------------------
965 // Turns off an illusionary wall (This will be used primarily for
966 //  wall switches or triggers that can turn on/off illusionary walls.)
967 void wall_illusion_off(segment *seg, int side)
968 {
969         segment *csegp;
970         int cside;
971
972         csegp = &Segments[seg->children[side]];
973         cside = find_connect_side(seg, csegp);
974         Assert(cside != -1);
975
976         if (seg->sides[side].wall_num == -1) {
977                 mprintf((0, "Trying to shut off illusion illegal wall\n"));
978                 return;
979         }
980
981         Walls[seg->sides[side].wall_num].flags |= WALL_ILLUSION_OFF;
982         Walls[csegp->sides[cside].wall_num].flags |= WALL_ILLUSION_OFF;
983
984         kill_stuck_objects(seg->sides[side].wall_num);
985         kill_stuck_objects(csegp->sides[cside].wall_num);
986 }
987
988 //-----------------------------------------------------------------
989 // Turns on an illusionary wall (This will be used primarily for
990 //  wall switches or triggers that can turn on/off illusionary walls.)
991 void wall_illusion_on(segment *seg, int side)
992 {
993         segment *csegp;
994         int cside;
995
996         csegp = &Segments[seg->children[side]];
997         cside = find_connect_side(seg, csegp);
998         Assert(cside != -1);
999
1000         if (seg->sides[side].wall_num == -1) {
1001                 mprintf((0, "Trying to turn on illusion illegal wall\n"));
1002                 return;
1003         }
1004
1005         Walls[seg->sides[side].wall_num].flags &= ~WALL_ILLUSION_OFF;
1006         Walls[csegp->sides[cside].wall_num].flags &= ~WALL_ILLUSION_OFF;
1007 }
1008
1009 //      -----------------------------------------------------------------------------
1010 //      Allowed to open the normally locked special boss door if in multiplayer mode.
1011 int special_boss_opening_allowed(int segnum, int sidenum)
1012 {
1013         if (Game_mode & GM_MULTI)
1014                 return (Current_level_num == BOSS_LOCKED_DOOR_LEVEL) && (segnum == BOSS_LOCKED_DOOR_SEG) && (sidenum == BOSS_LOCKED_DOOR_SIDE);
1015         else
1016                 return 0;
1017 }
1018
1019 //-----------------------------------------------------------------
1020 // Determines what happens when a wall is shot
1021 //returns info about wall.  see wall.h for codes
1022 //obj is the object that hit...either a weapon or the player himself
1023 //playernum is the number the player who hit the wall or fired the weapon,
1024 //or -1 if a robot fired the weapon
1025 int wall_hit_process(segment *seg, int side, fix damage, int playernum, object *obj )
1026 {
1027         wall    *w;
1028         fix     show_message;
1029
1030         Assert( SEGMENT_NUMBER(seg) != -1 );
1031
1032         // If it is not a "wall" then just return.
1033         if ( seg->sides[side].wall_num < 0 )
1034                 return WHP_NOT_SPECIAL;
1035
1036         w = &Walls[seg->sides[side].wall_num];
1037
1038         if ( Newdemo_state == ND_STATE_RECORDING )
1039                 newdemo_record_wall_hit_process( SEGMENT_NUMBER(seg), side, damage, playernum );
1040
1041         if (w->type == WALL_BLASTABLE) {
1042                 if (obj->ctype.laser_info.parent_type == OBJ_PLAYER)
1043                         wall_damage(seg, side, damage);
1044                 return WHP_BLASTABLE;
1045         }
1046
1047         if (playernum != Player_num)    //return if was robot fire
1048                 return WHP_NOT_SPECIAL;
1049
1050         Assert( playernum > -1 );
1051         
1052         //      Determine whether player is moving forward.  If not, don't say negative
1053         //      messages because he probably didn't intentionally hit the door.
1054         if (obj->type == OBJ_PLAYER)
1055                 show_message = (vm_vec_dot(&obj->orient.fvec, &obj->mtype.phys_info.velocity) > 0);
1056         else if (obj->type == OBJ_ROBOT)
1057                 show_message = 0;
1058         else if ((obj->type == OBJ_WEAPON) && (obj->ctype.laser_info.parent_type == OBJ_ROBOT))
1059                 show_message = 0;
1060         else
1061                 show_message = 1;
1062
1063         if (w->keys == KEY_BLUE)
1064                 if (!(Players[playernum].flags & PLAYER_FLAGS_BLUE_KEY)) {
1065                         if ( playernum==Player_num )
1066                                 if (show_message)
1067                                         HUD_init_message("%s %s",TXT_BLUE,TXT_ACCESS_DENIED);
1068                         return WHP_NO_KEY;
1069                 }
1070
1071         if (w->keys == KEY_RED)
1072                 if (!(Players[playernum].flags & PLAYER_FLAGS_RED_KEY)) {
1073                         if ( playernum==Player_num )
1074                                 if (show_message)
1075                                         HUD_init_message("%s %s",TXT_RED,TXT_ACCESS_DENIED);
1076                         return WHP_NO_KEY;
1077                 }
1078         
1079         if (w->keys == KEY_GOLD)
1080                 if (!(Players[playernum].flags & PLAYER_FLAGS_GOLD_KEY)) {
1081                         if ( playernum==Player_num )
1082                                 if (show_message)
1083                                         HUD_init_message("%s %s",TXT_YELLOW,TXT_ACCESS_DENIED);
1084                         return WHP_NO_KEY;
1085                 }
1086
1087         if (w->type == WALL_DOOR)
1088         {
1089                 if ( (w->flags & WALL_DOOR_LOCKED ) && !(special_boss_opening_allowed(SEGMENT_NUMBER(seg), side)) ) {
1090                         if ( playernum==Player_num )
1091                                 if (show_message)
1092                                         HUD_init_message(TXT_CANT_OPEN_DOOR);
1093                         return WHP_NO_KEY;
1094                 }
1095                 else {
1096                         if (w->state != WALL_DOOR_OPENING)
1097                         {
1098                                 wall_open_door(seg, side);
1099                         #ifdef NETWORK
1100                                 if (Game_mode & GM_MULTI)
1101                                         multi_send_door_open(SEGMENT_NUMBER(seg), side, w->flags);
1102                         #endif
1103                         }
1104                         return WHP_DOOR;
1105                         
1106                 }
1107         }
1108
1109         return WHP_NOT_SPECIAL;         //default is treat like normal wall
1110 }
1111
1112 //-----------------------------------------------------------------
1113 // Opens doors/destroys wall/shuts off triggers.
1114 void wall_toggle(segment *seg, int side)
1115 {
1116         int wall_num;
1117
1118         if (SEGMENT_NUMBER(seg) > Highest_segment_index)
1119         {
1120                 Warning("Can't toggle side %d of segment %d - nonexistent segment!\n", side, SEGMENT_NUMBER(seg));
1121                 return;
1122         }
1123         Assert( side < MAX_SIDES_PER_SEGMENT );
1124
1125         wall_num = seg->sides[side].wall_num;
1126
1127         if (wall_num == -1) {
1128                 mprintf((0, "Illegal wall_toggle\n"));
1129                 return;
1130         }
1131
1132         if ( Newdemo_state == ND_STATE_RECORDING )
1133                 newdemo_record_wall_toggle( SEGMENT_NUMBER(seg), side );
1134
1135         if (Walls[wall_num].type == WALL_BLASTABLE)
1136                 wall_destroy(seg, side);
1137
1138         if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].state == WALL_DOOR_CLOSED))
1139                 wall_open_door(seg, side);
1140
1141 }
1142
1143
1144 //-----------------------------------------------------------------
1145 // Tidy up Walls array for load/save purposes.
1146 void reset_walls()
1147 {
1148         int i;
1149
1150         if (Num_walls < 0) {
1151                 mprintf((0, "Illegal Num_walls\n"));
1152                 return;
1153         }
1154
1155         for (i=Num_walls;i<MAX_WALLS;i++) {
1156                 Walls[i].type = WALL_NORMAL;
1157                 Walls[i].flags = 0;
1158                 Walls[i].hps = 0;
1159                 Walls[i].trigger = -1;
1160                 Walls[i].clip_num = -1;
1161                 }
1162 }
1163
1164 void do_cloaking_wall_frame(int cloaking_wall_num)
1165 {
1166         cloaking_wall *d;
1167         wall *wfront,*wback;
1168
1169         if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
1170
1171         d = &CloakingWalls[cloaking_wall_num];
1172         wfront = &Walls[d->front_wallnum];
1173         wback = (d->back_wallnum > -1) ? Walls + d->back_wallnum : NULL;
1174
1175         d->time += FrameTime;
1176
1177         if (d->time > CLOAKING_WALL_TIME) {
1178                 int i;
1179
1180                 wfront->type = WALL_OPEN;
1181                 wfront->state = WALL_DOOR_CLOSED;               //why closed? why not?
1182                 if (wback) {
1183                         wback->type = WALL_OPEN;
1184                         wback->state = WALL_DOOR_CLOSED;                //why closed? why not?
1185                 }
1186
1187                 for (i=cloaking_wall_num;i<Num_cloaking_walls;i++)
1188                         CloakingWalls[i] = CloakingWalls[i+1];
1189                 Num_cloaking_walls--;
1190
1191         }
1192         else if (d->time > CLOAKING_WALL_TIME/2) {
1193                 int old_type=wfront->type;
1194
1195                 wfront->cloak_value = ((d->time - CLOAKING_WALL_TIME/2) * (GR_FADE_LEVELS-2)) / (CLOAKING_WALL_TIME/2);
1196                 if (wback)
1197                         wback->cloak_value = wfront->cloak_value;
1198
1199                 if (old_type != WALL_CLOAKED) {         //just switched
1200                         int i;
1201
1202                         wfront->type = WALL_CLOAKED;
1203                         if (wback)
1204                                 wback->type = WALL_CLOAKED;
1205
1206                         for (i=0;i<4;i++) {
1207                                 Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = d->front_ls[i];
1208                                 if (wback)
1209                                         Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = d->back_ls[i];
1210                         }
1211                 }
1212         }
1213         else {          //fading out
1214                 fix light_scale;
1215                 int i;
1216
1217                 light_scale = fixdiv(CLOAKING_WALL_TIME/2-d->time,CLOAKING_WALL_TIME/2);
1218
1219                 for (i=0;i<4;i++) {
1220                         Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = fixmul(d->front_ls[i],light_scale);
1221                         if (wback)
1222                                 Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = fixmul(d->back_ls[i],light_scale);
1223                 }
1224         }
1225
1226         if ( Newdemo_state == ND_STATE_RECORDING )
1227                 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);
1228
1229 }
1230
1231 void do_decloaking_wall_frame(int cloaking_wall_num)
1232 {
1233         cloaking_wall *d;
1234         wall *wfront,*wback;
1235
1236         if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
1237
1238         d = &CloakingWalls[cloaking_wall_num];
1239         wfront = &Walls[d->front_wallnum];
1240         wback = (d->back_wallnum > -1) ? Walls + d->back_wallnum : NULL;
1241
1242         d->time += FrameTime;
1243
1244         if (d->time > CLOAKING_WALL_TIME) {
1245                 int i;
1246
1247                 wfront->state = WALL_DOOR_CLOSED;
1248                 if (wback)
1249                         wback->state = WALL_DOOR_CLOSED;
1250
1251                 for (i=0;i<4;i++) {
1252                         Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = d->front_ls[i];
1253                         if (wback)
1254                                 Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = d->back_ls[i];
1255                 }
1256
1257                 for (i=cloaking_wall_num;i<Num_cloaking_walls;i++)
1258                         CloakingWalls[i] = CloakingWalls[i+1];
1259                 Num_cloaking_walls--;
1260
1261         }
1262         else if (d->time > CLOAKING_WALL_TIME/2) {              //fading in
1263                 fix light_scale;
1264                 int i;
1265
1266                 wfront->type = wback->type = WALL_CLOSED;
1267
1268                 light_scale = fixdiv(d->time-CLOAKING_WALL_TIME/2,CLOAKING_WALL_TIME/2);
1269
1270                 for (i=0;i<4;i++) {
1271                         Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = fixmul(d->front_ls[i],light_scale);
1272                         if (wback)
1273                         Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = fixmul(d->back_ls[i],light_scale);
1274                 }
1275         }
1276         else {          //cloaking in
1277                 wfront->cloak_value = ((CLOAKING_WALL_TIME/2 - d->time) * (GR_FADE_LEVELS-2)) / (CLOAKING_WALL_TIME/2);
1278                 wfront->type = WALL_CLOAKED;
1279                 if (wback) {
1280                         wback->cloak_value = wfront->cloak_value;
1281                         wback->type = WALL_CLOAKED;
1282                 }
1283         }
1284
1285         if ( Newdemo_state == ND_STATE_RECORDING )
1286                 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);
1287
1288 }
1289
1290 void wall_frame_process()
1291 {
1292         int i;
1293
1294         for (i=0;i<Num_open_doors;i++) {
1295                 active_door *d;
1296                 wall *w;
1297
1298                 d = &ActiveDoors[i];
1299                 w = &Walls[d->front_wallnum[0]];
1300
1301                 if (w->state == WALL_DOOR_OPENING)
1302                         do_door_open(i);
1303                 else if (w->state == WALL_DOOR_CLOSING)
1304                         do_door_close(i);
1305                 else if (w->state == WALL_DOOR_WAITING) {
1306                         d->time += FrameTime;
1307
1308                         //set flags to fix occatsional netgame problem where door is
1309                         //waiting to close but open flag isn't set
1310                         Assert(d->n_parts == 1);
1311                         w->flags |= WALL_DOOR_OPENED;
1312                         if (d->back_wallnum[0] > -1)
1313                                 Walls[d->back_wallnum[0]].flags |= WALL_DOOR_OPENED;
1314
1315                         if (d->time > DOOR_WAIT_TIME && is_door_free(&Segments[w->segnum],w->sidenum)) {
1316                                 w->state = WALL_DOOR_CLOSING;
1317                                 d->time = 0;
1318                         }
1319                 }
1320                 else if (w->state == WALL_DOOR_CLOSED || w->state == WALL_DOOR_OPEN) {
1321                         //this shouldn't happen.  if the wall is in one of these states,
1322                         //there shouldn't be an activedoor entry for it.  So we'll kill
1323                         //the activedoor entry.  Tres simple.
1324                         int t;
1325                         Int3();         //a bad thing has happened, but I'll try to fix it up
1326                         for (t=i;t<Num_open_doors;t++)
1327                                 ActiveDoors[t] = ActiveDoors[t+1];
1328                         Num_open_doors--;
1329                 }
1330         }
1331
1332         for (i=0;i<Num_cloaking_walls;i++) {
1333                 cloaking_wall *d;
1334                 wall *w;
1335
1336                 d = &CloakingWalls[i];
1337                 w = &Walls[d->front_wallnum];
1338
1339                 if (w->state == WALL_DOOR_CLOAKING)
1340                         do_cloaking_wall_frame(i);
1341                 else if (w->state == WALL_DOOR_DECLOAKING)
1342                         do_decloaking_wall_frame(i);
1343 #ifdef _DEBUG
1344                 else
1345                         Int3(); //unexpected wall state
1346 #endif
1347         }
1348 }
1349
1350 int     Num_stuck_objects=0;
1351
1352 stuckobj        Stuck_objects[MAX_STUCK_OBJECTS];
1353
1354 //      An object got stuck in a door (like a flare).
1355 //      Add global entry.
1356 void add_stuck_object(object *objp, int segnum, int sidenum)
1357 {
1358         int     i;
1359         int     wallnum;
1360
1361         wallnum = Segments[segnum].sides[sidenum].wall_num;
1362
1363         if (wallnum != -1) {
1364                 if (Walls[wallnum].flags & WALL_BLASTED)
1365                         objp->flags |= OF_SHOULD_BE_DEAD;
1366
1367                 for (i=0; i<MAX_STUCK_OBJECTS; i++) {
1368                         if (Stuck_objects[i].wallnum == -1) {
1369                                 Stuck_objects[i].wallnum = wallnum;
1370                                 Stuck_objects[i].objnum = OBJECT_NUMBER(objp);
1371                                 Stuck_objects[i].signature = objp->signature;
1372                                 // mprintf((0, "Added wall %i at index %i\n", wallnum, i));
1373                                 Num_stuck_objects++;
1374                                 break;
1375                         }
1376                 }
1377                 if (i == MAX_STUCK_OBJECTS)
1378                         mprintf((1, "Warning: Unable to add object %i which got stuck in wall %i to Stuck_objects\n", OBJECT_NUMBER(objp), wallnum));
1379         }
1380
1381
1382
1383 }
1384
1385 //      --------------------------------------------------------------------------------------------------
1386 //      Look at the list of stuck objects, clean up in case an object has gone away, but not been removed here.
1387 //      Removes up to one/frame.
1388 void remove_obsolete_stuck_objects(void)
1389 {
1390         int     objnum;
1391
1392         //      Safety and efficiency code.  If no stuck objects, should never get inside the IF, but this is faster.
1393         if (!Num_stuck_objects)
1394                 return;
1395
1396         objnum = FrameCount % MAX_STUCK_OBJECTS;
1397
1398         if (Stuck_objects[objnum].wallnum != -1)
1399                 if ((Walls[Stuck_objects[objnum].wallnum].state != WALL_DOOR_CLOSED) || (Objects[Stuck_objects[objnum].objnum].signature != Stuck_objects[objnum].signature)) {
1400                         Num_stuck_objects--;
1401                         Objects[Stuck_objects[objnum].objnum].lifeleft = F1_0/8;
1402                         Stuck_objects[objnum].wallnum = -1;
1403                 }
1404
1405 }
1406
1407 extern void flush_fcd_cache(void);
1408
1409 //      ----------------------------------------------------------------------------------------------------
1410 //      Door with wall index wallnum is opening, kill all objects stuck in it.
1411 void kill_stuck_objects(int wallnum)
1412 {
1413         int     i;
1414
1415         if (wallnum < 0 || Num_stuck_objects == 0) {
1416                 return;
1417         }
1418
1419         Num_stuck_objects=0;
1420
1421         for (i=0; i<MAX_STUCK_OBJECTS; i++)
1422                 if (Stuck_objects[i].wallnum == wallnum) {
1423                         if (Objects[Stuck_objects[i].objnum].type == OBJ_WEAPON) {
1424                                 Objects[Stuck_objects[i].objnum].lifeleft = F1_0/8;
1425                         } else
1426                                 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));
1427                                 // Int3();      //      What?  This looks bad.  Object is not a weapon and it is stuck in a wall!
1428                         Stuck_objects[i].wallnum = -1;
1429                 } else if (Stuck_objects[i].wallnum != -1) {
1430                         Num_stuck_objects++;
1431                 }
1432         //      Ok, this is awful, but we need to do things whenever a door opens/closes/disappears, etc.
1433         flush_fcd_cache();
1434
1435 }
1436
1437
1438 // -- unused -- // -----------------------------------------------------------------------------------
1439 // -- unused -- //      Return object id of first flare found embedded in segp:sidenum.
1440 // -- unused -- //      If no flare, return -1.
1441 // -- unused -- int contains_flare(segment *segp, int sidenum)
1442 // -- unused -- {
1443 // -- unused --         int     i;
1444 // -- unused --
1445 // -- unused --         for (i=0; i<Num_stuck_objects; i++) {
1446 // -- unused --                 object  *objp = &Objects[Stuck_objects[i].objnum];
1447 // -- unused --
1448 // -- unused --                 if ((objp->type == OBJ_WEAPON) && (objp->id == FLARE_ID)) {
1449 // -- unused --                         if (Walls[Stuck_objects[i].wallnum].segnum == SEGMENT_NUMBER(segp))
1450 // -- unused --                                 if (Walls[Stuck_objects[i].wallnum].sidenum == sidenum)
1451 // -- unused --                                         return OBJECT_NUMBER(objp);
1452 // -- unused --                 }
1453 // -- unused --         }
1454 // -- unused --
1455 // -- unused --         return -1;
1456 // -- unused -- }
1457
1458 // -----------------------------------------------------------------------------------
1459 // Initialize stuck objects array.  Called at start of level
1460 void init_stuck_objects(void)
1461 {
1462         int     i;
1463
1464         for (i=0; i<MAX_STUCK_OBJECTS; i++)
1465                 Stuck_objects[i].wallnum = -1;
1466
1467         Num_stuck_objects = 0;
1468 }
1469
1470 // -----------------------------------------------------------------------------------
1471 // Clear out all stuck objects.  Called for a new ship
1472 void clear_stuck_objects(void)
1473 {
1474         int     i;
1475
1476         for (i=0; i<MAX_STUCK_OBJECTS; i++) {
1477                 if (Stuck_objects[i].wallnum != -1) {
1478                         int     objnum;
1479
1480                         objnum = Stuck_objects[i].objnum;
1481
1482                         if ((Objects[objnum].type == OBJ_WEAPON) && (Objects[objnum].id == FLARE_ID))
1483                                 Objects[objnum].lifeleft = F1_0/8;
1484
1485                         Stuck_objects[i].wallnum = -1;
1486
1487                         Num_stuck_objects--;
1488                 }
1489         }
1490
1491         Assert(Num_stuck_objects == 0);
1492
1493 }
1494
1495 // -----------------------------------------------------------------------------------
1496 #define MAX_BLAST_GLASS_DEPTH   5
1497
1498 void bng_process_segment(object *objp, fix damage, segment *segp, int depth, sbyte *visited)
1499 {
1500         int     i, sidenum;
1501
1502         if (depth > MAX_BLAST_GLASS_DEPTH)
1503                 return;
1504
1505         depth++;
1506
1507         for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
1508                 int                     tm;
1509                 fix                     dist;
1510                 vms_vector      pnt;
1511
1512                 //      Process only walls which have glass.
1513                 if ((tm=segp->sides[sidenum].tmap_num2) != 0) {
1514                         int     ec, db;
1515
1516                         tm &= 0x3fff;                   //tm without flags
1517
1518                         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))) {
1519                                 compute_center_point_on_side(&pnt, segp, sidenum);
1520                                 dist = vm_vec_dist_quick(&pnt, &objp->pos);
1521                                 if (dist < damage/2) {
1522                                         dist = find_connected_distance(&pnt, SEGMENT_NUMBER(segp), &objp->pos, objp->segnum, MAX_BLAST_GLASS_DEPTH, WID_RENDPAST_FLAG);
1523                                         if ((dist > 0) && (dist < damage/2))
1524                                                 check_effect_blowup(segp, sidenum, &pnt, &Objects[objp->ctype.laser_info.parent_num], 1);
1525                                 }
1526                         }
1527                 }
1528         }
1529
1530         for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
1531                 int     segnum = segp->children[i];
1532
1533                 if (segnum != -1) {
1534                         if (!visited[segnum]) {
1535                                 if (WALL_IS_DOORWAY(segp, i) & WID_FLY_FLAG) {
1536                                         visited[segnum] = 1;
1537                                         bng_process_segment(objp, damage, &Segments[segnum], depth, visited);
1538                                 }
1539                         }
1540                 }
1541         }
1542 }
1543
1544 // -----------------------------------------------------------------------------------
1545 //      objp is going to detonate
1546 //      blast nearby monitors, lights, maybe other things
1547 void blast_nearby_glass(object *objp, fix damage)
1548 {
1549         int             i;
1550         sbyte   visited[MAX_SEGMENTS];
1551         segment *cursegp;
1552
1553         cursegp = &Segments[objp->segnum];
1554         for (i=0; i<=Highest_segment_index; i++)
1555                 visited[i] = 0;
1556
1557         visited[objp->segnum] = 1;
1558         bng_process_segment(objp, damage, cursegp, 0, visited);
1559
1560
1561 }
1562
1563 #define MAX_CLIP_FRAMES_D1 20
1564
1565 /*
1566  * reads a wclip structure from a CFILE
1567  */
1568 int wclip_read_n_d1(wclip *wc, int n, CFILE *fp)
1569 {
1570         int i, j;
1571
1572         for (i = 0; i < n; i++) {
1573                 wc[i].play_time = cfile_read_fix(fp);
1574                 wc[i].num_frames = cfile_read_short(fp);
1575                 for (j = 0; j < MAX_CLIP_FRAMES_D1; j++)
1576                         wc[i].frames[j] = cfile_read_short(fp);
1577                 wc[i].open_sound = cfile_read_short(fp);
1578                 wc[i].close_sound = cfile_read_short(fp);
1579                 wc[i].flags = cfile_read_short(fp);
1580                 cfread(wc[i].filename, 13, 1, fp);
1581                 wc[i].pad = cfile_read_byte(fp);
1582         }
1583         return i;
1584 }
1585
1586 #ifndef FAST_FILE_IO
1587 /*
1588  * reads a wclip structure from a CFILE
1589  */
1590 int wclip_read_n(wclip *wc, int n, CFILE *fp)
1591 {
1592         int i, j;
1593
1594         for (i = 0; i < n; i++) {
1595                 wc[i].play_time = cfile_read_fix(fp);
1596                 wc[i].num_frames = cfile_read_short(fp);
1597                 for (j = 0; j < MAX_CLIP_FRAMES; j++)
1598                         wc[i].frames[j] = cfile_read_short(fp);
1599                 wc[i].open_sound = cfile_read_short(fp);
1600                 wc[i].close_sound = cfile_read_short(fp);
1601                 wc[i].flags = cfile_read_short(fp);
1602                 cfread(wc[i].filename, 13, 1, fp);
1603                 wc[i].pad = cfile_read_byte(fp);
1604         }
1605         return i;
1606 }
1607
1608 /*
1609  * reads a v16_wall structure from a CFILE
1610  */
1611 extern void v16_wall_read(v16_wall *w, CFILE *fp)
1612 {
1613         w->type = cfile_read_byte(fp);
1614         w->flags = cfile_read_byte(fp);
1615         w->hps = cfile_read_fix(fp);
1616         w->trigger = cfile_read_byte(fp);
1617         w->clip_num = cfile_read_byte(fp);
1618         w->keys = cfile_read_byte(fp);
1619 }
1620
1621 /*
1622  * reads a v19_wall structure from a CFILE
1623  */
1624 extern void v19_wall_read(v19_wall *w, CFILE *fp)
1625 {
1626         w->segnum = cfile_read_int(fp);
1627         w->sidenum = cfile_read_int(fp);
1628         w->type = cfile_read_byte(fp);
1629         w->flags = cfile_read_byte(fp);
1630         w->hps = cfile_read_fix(fp);
1631         w->trigger = cfile_read_byte(fp);
1632         w->clip_num = cfile_read_byte(fp);
1633         w->keys = cfile_read_byte(fp);
1634         w->linked_wall = cfile_read_int(fp);
1635 }
1636
1637 /*
1638  * reads a wall structure from a CFILE
1639  */
1640 extern void wall_read(wall *w, CFILE *fp)
1641 {
1642         w->segnum = cfile_read_int(fp);
1643         w->sidenum = cfile_read_int(fp);
1644         w->hps = cfile_read_fix(fp);
1645         w->linked_wall = cfile_read_int(fp);
1646         w->type = cfile_read_byte(fp);
1647         w->flags = cfile_read_byte(fp);
1648         w->state = cfile_read_byte(fp);
1649         w->trigger = cfile_read_byte(fp);
1650         w->clip_num = cfile_read_byte(fp);
1651         w->keys = cfile_read_byte(fp);
1652         w->controlling_trigger = cfile_read_byte(fp);
1653         w->cloak_value = cfile_read_byte(fp);
1654 }
1655
1656 /*
1657  * reads a v19_door structure from a CFILE
1658  */
1659 extern void v19_door_read(v19_door *d, CFILE *fp)
1660 {
1661         d->n_parts = cfile_read_int(fp);
1662         d->seg[0] = cfile_read_short(fp);
1663         d->seg[1] = cfile_read_short(fp);
1664         d->side[0] = cfile_read_short(fp);
1665         d->side[1] = cfile_read_short(fp);
1666         d->type[0] = cfile_read_short(fp);
1667         d->type[1] = cfile_read_short(fp);
1668         d->open = cfile_read_fix(fp);
1669 }
1670
1671 /*
1672  * reads an active_door structure from a CFILE
1673  */
1674 extern void active_door_read(active_door *ad, CFILE *fp)
1675 {
1676         ad->n_parts = cfile_read_int(fp);
1677         ad->front_wallnum[0] = cfile_read_short(fp);
1678         ad->front_wallnum[1] = cfile_read_short(fp);
1679         ad->back_wallnum[0] = cfile_read_short(fp);
1680         ad->back_wallnum[1] = cfile_read_short(fp);
1681         ad->time = cfile_read_fix(fp);
1682 }
1683 #endif
1684
1685 void wall_write(wall *w, short version, CFILE *fp)
1686 {
1687         if (version >= 17)
1688         {
1689                 PHYSFS_writeSLE32(fp, w->segnum);
1690                 PHYSFS_writeSLE32(fp, w->sidenum);
1691         }
1692
1693         if (version >= 20)
1694         {
1695                 PHYSFSX_writeFix(fp, w->hps);
1696                 PHYSFS_writeSLE32(fp, w->linked_wall);
1697         }
1698         
1699         PHYSFSX_writeU8(fp, w->type);
1700         PHYSFSX_writeU8(fp, w->flags);
1701         
1702         if (version < 20)
1703                 PHYSFSX_writeFix(fp, w->hps);
1704         else
1705                 PHYSFSX_writeU8(fp, w->state);
1706         
1707         PHYSFSX_writeU8(fp, w->trigger);
1708         PHYSFSX_writeU8(fp, w->clip_num);
1709         PHYSFSX_writeU8(fp, w->keys);
1710         
1711         if (version >= 20)
1712         {
1713                 PHYSFSX_writeU8(fp, w->controlling_trigger);
1714                 PHYSFSX_writeU8(fp, w->cloak_value);
1715         }
1716         else if (version >= 17)
1717                 PHYSFS_writeSLE32(fp, w->linked_wall);
1718 }