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