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.
19 static char rcsid[] = "$Id: wall.c,v 1.4 2001-10-25 02:15:57 bradleyb Exp $";
48 #include "laser.h" // For seeing if a flare is stuck in a wall.
53 #include "editor/editor.h"
56 // Special door on boss level which is locked if not in multiplayer...sorry for this awful solution --MK.
57 #define BOSS_LOCKED_DOOR_LEVEL 7
58 #define BOSS_LOCKED_DOOR_SEG 595
59 #define BOSS_LOCKED_DOOR_SIDE 5
61 wall Walls[MAX_WALLS]; // Master walls array
62 int Num_walls=0; // Number of walls
64 wclip WallAnims[MAX_WALL_ANIMS]; // Wall animations
66 //--unused-- int walls_bm_num[MAX_WALL_ANIMS];
68 //door Doors[MAX_DOORS]; // Master doors array
70 active_door ActiveDoors[MAX_DOORS];
71 int Num_open_doors; // Number of open doors
73 #define CLOAKING_WALL_TIME f1_0
75 #define MAX_CLOAKING_WALLS 10
76 cloaking_wall CloakingWalls[MAX_CLOAKING_WALLS];
77 int Num_cloaking_walls;
79 //--unused-- grs_bitmap *wall_title_bms[MAX_WALL_ANIMS];
81 //#define BM_FLAG_TRANSPARENT 1
82 //#define BM_FLAG_SUPER_TRANSPARENT 2
85 char Wall_names[7][10] = {
96 // Function prototypes
97 void kill_stuck_objects(int wallnum);
101 // This function determines whether the current segment/side is transparent
104 int check_transparency( segment * seg, int side )
106 if ( (seg->sides[side].tmap_num2 & 0x3FFF) == 0) {
107 if (GameBitmaps[Textures[seg->sides[side].tmap_num].index].bm_flags & BM_FLAG_TRANSPARENT )
113 if (GameBitmaps[Textures[seg->sides[side].tmap_num2 & 0x3FFF ].index].bm_flags & BM_FLAG_SUPER_TRANSPARENT )
119 //define these here so I don't have to change wall_is_doorway and run
120 //the risk of screwing it up.
121 #define WID_WALL 2 // 0/1/0 wall
122 #define WID_TRANSPARENT_WALL 6 // 0/1/1 transparent wall
123 #define WID_ILLUSORY_WALL 3 // 1/1/0 illusory wall
124 #define WID_TRANSILLUSORY_WALL 7 // 1/1/1 transparent illusory wall
125 #define WID_NO_WALL 5 // 1/0/1 no wall, can fly through
126 #define WID_EXTERNAL 8 // 0/0/0/1 don't see it, dont fly through it
128 //-----------------------------------------------------------------
129 // This function checks whether we can fly through the given side.
130 // In other words, whether or not we have a 'doorway'
134 // WID_RENDPAST_FLAG 4
136 // WID_WALL 2 // 0/1/0 wall
137 // WID_TRANSPARENT_WALL 6 // 0/1/1 transparent wall
138 // WID_ILLUSORY_WALL 3 // 1/1/0 illusory wall
139 // WID_TRANSILLUSORY_WALL 7 // 1/1/1 transparent illusory wall
140 // WID_NO_WALL 5 // 1/0/1 no wall, can fly through
141 int wall_is_doorway ( segment * seg, int side )
145 //--Covered by macro // No child.
146 //--Covered by macro if (seg->children[side] == -1)
147 //--Covered by macro return WID_WALL;
149 //--Covered by macro if (seg->children[side] == -2)
150 //--Covered by macro return WID_EXTERNAL_FLAG;
152 //--Covered by macro // No wall present.
153 //--Covered by macro if (seg->sides[side].wall_num == -1)
154 //--Covered by macro return WID_NO_WALL;
156 Assert(seg-Segments>=0 && seg-Segments<=Highest_segment_index);
157 Assert(side>=0 && side<6);
159 type = Walls[seg->sides[side].wall_num].type;
160 flags = Walls[seg->sides[side].wall_num].flags;
162 if (type == WALL_OPEN)
165 if (type == WALL_ILLUSION) {
166 if (Walls[seg->sides[side].wall_num].flags & WALL_ILLUSION_OFF)
169 if (check_transparency( seg, side))
170 return WID_TRANSILLUSORY_WALL;
172 return WID_ILLUSORY_WALL;
176 if (type == WALL_BLASTABLE) {
177 if (flags & WALL_BLASTED)
178 return WID_TRANSILLUSORY_WALL;
180 if (check_transparency( seg, side))
181 return WID_TRANSPARENT_WALL;
186 if (flags & WALL_DOOR_OPENED)
187 return WID_TRANSILLUSORY_WALL;
189 if (type == WALL_CLOAKED)
190 return WID_RENDER_FLAG | WID_RENDPAST_FLAG | WID_CLOAKED_FLAG;
192 state = Walls[seg->sides[side].wall_num].state;
193 if ((type == WALL_DOOR) && (state == WALL_DOOR_OPENING))
194 return WID_TRANSPARENT_WALL;
196 // If none of the above flags are set, there is no doorway.
197 if (check_transparency( seg, side))
198 return WID_TRANSPARENT_WALL;
200 return WID_WALL; // There are children behind the door.
204 //-----------------------------------------------------------------
205 // Initializes all the walls (in other words, no special walls)
211 for (i=0;i<MAX_WALLS;i++) {
212 Walls[i].segnum = Walls[i].sidenum = -1;
213 Walls[i].type = WALL_NORMAL;
216 Walls[i].trigger = -1;
217 Walls[i].clip_num = -1;
218 Walls[i].linked_wall = -1;
221 Num_cloaking_walls = 0;
225 //-----------------------------------------------------------------
226 // Initializes one wall.
227 void wall_reset(segment *seg, int side)
231 i = seg->sides[side].wall_num;
234 mprintf((0, "Resetting Illegal Wall\n"));
238 Walls[i].segnum = seg-Segments;
239 Walls[i].sidenum = side;
240 Walls[i].type = WALL_NORMAL;
243 Walls[i].trigger = -1;
244 Walls[i].clip_num = -1;
245 Walls[i].linked_wall = -1;
249 //set the tmap_num or tmap_num2 field for a wall/door
250 void wall_set_tmap_num(segment *seg,int side,segment *csegp,int cside,int anim_num,int frame_num)
252 wclip *anim = &WallAnims[anim_num];
253 int tmap = anim->frames[frame_num];
255 if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
257 if (anim->flags & WCF_TMAP1) {
258 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = tmap;
259 if ( Newdemo_state == ND_STATE_RECORDING )
260 newdemo_record_wall_set_tmap_num1(seg-Segments,side,csegp-Segments,cside,tmap);
262 Assert(tmap!=0 && seg->sides[side].tmap_num2!=0);
263 seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = tmap;
264 if ( Newdemo_state == ND_STATE_RECORDING )
265 newdemo_record_wall_set_tmap_num2(seg-Segments,side,csegp-Segments,cside,tmap);
270 // -------------------------------------------------------------------------------
271 //when the wall has used all its hitpoints, this will destroy it
272 void blast_blastable_wall(segment *seg, int side)
278 Assert(seg->sides[side].wall_num != -1);
280 Walls[seg->sides[side].wall_num].hps = -1; //say it's blasted
282 csegp = &Segments[seg->children[side]];
283 Connectside = find_connect_side(seg, csegp);
284 Assert(Connectside != -1);
286 kill_stuck_objects(seg->sides[side].wall_num);
287 kill_stuck_objects(csegp->sides[Connectside].wall_num);
289 //if this is an exploding wall, explode it
290 if (WallAnims[Walls[seg->sides[side].wall_num].clip_num].flags & WCF_EXPLODES)
291 explode_wall(seg-Segments,side);
293 //if not exploding, set final frame, and make door passable
294 a = Walls[seg->sides[side].wall_num].clip_num;
295 n = WallAnims[a].num_frames;
296 wall_set_tmap_num(seg,side,csegp,Connectside,a,n-1);
297 Walls[seg->sides[side].wall_num].flags |= WALL_BLASTED;
298 Walls[csegp->sides[Connectside].wall_num].flags |= WALL_BLASTED;
304 //-----------------------------------------------------------------
305 // Destroys a blastable wall.
306 void wall_destroy(segment *seg, int side)
308 Assert(seg->sides[side].wall_num != -1);
309 Assert(seg-Segments != 0);
311 if (Walls[seg->sides[side].wall_num].type == WALL_BLASTABLE)
312 blast_blastable_wall( seg, side );
314 Error("Hey bub, you are trying to destroy an indestructable wall.");
317 //-----------------------------------------------------------------
318 // Deteriorate appearance of wall. (Changes bitmap (paste-ons))
319 void wall_damage(segment *seg, int side, fix damage)
323 if (seg->sides[side].wall_num == -1) {
324 mprintf((0, "Damaging illegal wall\n"));
328 if (Walls[seg->sides[side].wall_num].type != WALL_BLASTABLE)
331 if (!(Walls[seg->sides[side].wall_num].flags & WALL_BLASTED) && Walls[seg->sides[side].wall_num].hps >= 0)
336 csegp = &Segments[seg->children[side]];
337 Connectside = find_connect_side(seg, csegp);
338 Assert(Connectside != -1);
340 Walls[seg->sides[side].wall_num].hps -= damage;
341 Walls[csegp->sides[Connectside].wall_num].hps -= damage;
343 a = Walls[seg->sides[side].wall_num].clip_num;
344 n = WallAnims[a].num_frames;
346 if (Walls[seg->sides[side].wall_num].hps < WALL_HPS*1/n) {
347 blast_blastable_wall( seg, side );
349 if (Game_mode & GM_MULTI)
350 multi_send_door_open(seg-Segments, side,Walls[seg->sides[side].wall_num].flags);
355 if (Walls[seg->sides[side].wall_num].hps < WALL_HPS*(n-i)/n) {
356 wall_set_tmap_num(seg,side,csegp,Connectside,a,i);
362 //-----------------------------------------------------------------
364 void wall_open_door(segment *seg, int side)
371 Assert(seg->sides[side].wall_num != -1); //Opening door on illegal wall
373 w = &Walls[seg->sides[side].wall_num];
375 //kill_stuck_objects(seg->sides[side].wall_num);
377 if ((w->state == WALL_DOOR_OPENING) || //already opening
378 (w->state == WALL_DOOR_WAITING) || //open, waiting to close
379 (w->state == WALL_DOOR_OPEN)) //open, & staying open
382 if (w->state == WALL_DOOR_CLOSING) { //closing, so reuse door
388 for (i=0;i<Num_open_doors;i++) { //find door
392 if (d->front_wallnum[0]==w-Walls || d->back_wallnum[0]==w-Walls || (d->n_parts==2 && (d->front_wallnum[1]==w-Walls || d->back_wallnum[1]==w-Walls)))
396 if (i>=Num_open_doors && (Game_mode & GM_MULTI))
399 Assert(i<Num_open_doors); //didn't find door!
400 Assert( d!=NULL ); // Get John!
402 d->time = WallAnims[w->clip_num].play_time - d->time;
408 else { //create new door
409 Assert(w->state == WALL_DOOR_CLOSED);
411 d = &ActiveDoors[Num_open_doors];
414 Assert( Num_open_doors < MAX_DOORS );
418 w->state = WALL_DOOR_OPENING;
420 // So that door can't be shot while opening
421 csegp = &Segments[seg->children[side]];
422 Connectside = find_connect_side(seg, csegp);
423 Assert(Connectside != -1);
425 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_OPENING;
427 //kill_stuck_objects(csegp->sides[Connectside].wall_num);
429 d->front_wallnum[0] = seg->sides[side].wall_num;
430 d->back_wallnum[0] = csegp->sides[Connectside].wall_num;
432 Assert( seg-Segments != -1);
434 if (Newdemo_state == ND_STATE_RECORDING) {
435 newdemo_record_door_opening(seg-Segments, side);
438 if (w->linked_wall != -1) {
442 w2 = &Walls[w->linked_wall];
443 seg2 = &Segments[w2->segnum];
445 Assert(w2->linked_wall == seg->sides[side].wall_num);
446 //Assert(!(w2->flags & WALL_DOOR_OPENING || w2->flags & WALL_DOOR_OPENED));
448 w2->state = WALL_DOOR_OPENING;
450 csegp = &Segments[seg2->children[w2->sidenum]];
451 Connectside = find_connect_side(seg2, csegp);
452 Assert(Connectside != -1);
453 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_OPENING;
456 d->front_wallnum[1] = w->linked_wall;
457 d->back_wallnum[1] = csegp->sides[Connectside].wall_num;
463 if ( Newdemo_state != ND_STATE_PLAYBACK )
465 // NOTE THE LINK TO ABOVE!!!!
467 compute_center_point_on_side(&cp, seg, side );
468 if (WallAnims[w->clip_num].open_sound > -1 )
469 digi_link_sound_to_pos( WallAnims[w->clip_num].open_sound, seg-Segments, side, &cp, 0, F1_0 );
474 //-----------------------------------------------------------------
475 // start the transition from closed -> open wall
476 void start_wall_cloak(segment *seg, int side)
484 if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
486 Assert(seg->sides[side].wall_num != -1); //Opening door on illegal wall
488 w = &Walls[seg->sides[side].wall_num];
490 if (w->type == WALL_OPEN || w->state == WALL_DOOR_CLOAKING) //already open or cloaking
493 csegp = &Segments[seg->children[side]];
494 Connectside = find_connect_side(seg, csegp);
495 Assert(Connectside != -1);
497 if (w->state == WALL_DOOR_DECLOAKING) { //decloaking, so reuse door
503 for (i=0;i<Num_cloaking_walls;i++) { //find door
505 d = &CloakingWalls[i];
507 if (d->front_wallnum==w-Walls || d->back_wallnum==w-Walls )
511 Assert(i<Num_cloaking_walls); //didn't find door!
512 Assert( d!=NULL ); // Get John!
514 d->time = CLOAKING_WALL_TIME - d->time;
517 else if (w->state == WALL_DOOR_CLOSED) { //create new door
518 d = &CloakingWalls[Num_cloaking_walls];
520 if (Num_cloaking_walls >= MAX_CLOAKING_WALLS) { //no more!
521 Int3(); //ran out of cloaking wall slots
523 Walls[csegp->sides[Connectside].wall_num].type = WALL_OPEN;
526 Num_cloaking_walls++;
529 Int3(); //unexpected wall state
533 w->state = WALL_DOOR_CLOAKING;
534 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOAKING;
536 d->front_wallnum = seg->sides[side].wall_num;
537 d->back_wallnum = csegp->sides[Connectside].wall_num;
539 Assert( seg-Segments != -1);
541 Assert(w->linked_wall == -1);
543 if ( Newdemo_state != ND_STATE_PLAYBACK ) {
545 compute_center_point_on_side(&cp, seg, side );
546 digi_link_sound_to_pos( SOUND_WALL_CLOAK_ON, seg-Segments, side, &cp, 0, F1_0 );
550 d->front_ls[i] = seg->sides[side].uvls[i].l;
551 d->back_ls[i] = csegp->sides[Connectside].uvls[i].l;
555 //-----------------------------------------------------------------
556 // start the transition from open -> closed wall
557 void start_wall_decloak(segment *seg, int side)
565 if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
567 Assert(seg->sides[side].wall_num != -1); //Opening door on illegal wall
569 w = &Walls[seg->sides[side].wall_num];
571 if (w->type == WALL_CLOSED || w->state == WALL_DOOR_DECLOAKING) //already closed or decloaking
574 if (w->state == WALL_DOOR_CLOAKING) { //cloaking, so reuse door
580 for (i=0;i<Num_cloaking_walls;i++) { //find door
582 d = &CloakingWalls[i];
584 if (d->front_wallnum==w-Walls || d->back_wallnum==w-Walls )
588 Assert(i<Num_cloaking_walls); //didn't find door!
589 Assert( d!=NULL ); // Get John!
591 d->time = CLOAKING_WALL_TIME - d->time;
594 else if (w->state == WALL_DOOR_CLOSED) { //create new door
595 d = &CloakingWalls[Num_cloaking_walls];
597 if (Num_cloaking_walls >= MAX_CLOAKING_WALLS) { //no more!
598 Int3(); //ran out of cloaking wall slots
599 /* what is this _doing_ here?
600 w->type = WALL_CLOSED;
601 Walls[csegp->sides[Connectside].wall_num].type = WALL_CLOSED;
605 Num_cloaking_walls++;
608 Int3(); //unexpected wall state
612 w->state = WALL_DOOR_DECLOAKING;
614 // So that door can't be shot while opening
615 csegp = &Segments[seg->children[side]];
616 Connectside = find_connect_side(seg, csegp);
617 Assert(Connectside != -1);
619 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_DECLOAKING;
621 d->front_wallnum = seg->sides[side].wall_num;
622 d->back_wallnum = csegp->sides[Connectside].wall_num;
624 Assert( seg-Segments != -1);
626 Assert(w->linked_wall == -1);
628 if ( Newdemo_state != ND_STATE_PLAYBACK ) {
630 compute_center_point_on_side(&cp, seg, side );
631 digi_link_sound_to_pos( SOUND_WALL_CLOAK_OFF, seg-Segments, side, &cp, 0, F1_0 );
635 d->front_ls[i] = seg->sides[side].uvls[i].l;
636 d->back_ls[i] = csegp->sides[Connectside].uvls[i].l;
640 //-----------------------------------------------------------------
641 // This function closes the specified door and restores the closed
642 // door texture. This is called when the animation is done
643 void wall_close_door_num(int door_num)
649 d = &ActiveDoors[door_num];
651 for (p=0;p<d->n_parts;p++) {
653 int Connectside, side;
654 segment *csegp, *seg;
656 w = &Walls[d->front_wallnum[p]];
658 seg = &Segments[w->segnum];
661 Assert(seg->sides[side].wall_num != -1); //Closing door on illegal wall
663 csegp = &Segments[seg->children[side]];
664 Connectside = find_connect_side(seg, csegp);
665 Assert(Connectside != -1);
667 Walls[seg->sides[side].wall_num].state = WALL_DOOR_CLOSED;
668 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOSED;
670 wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,0);
674 for (i=door_num;i<Num_open_doors;i++)
675 ActiveDoors[i] = ActiveDoors[i+1];
681 int check_poke(int objnum,int segnum,int side)
683 object *obj = &Objects[objnum];
685 //note: don't let objects with zero size block door
687 if (obj->size && get_seg_masks(&obj->pos,segnum,obj->size).sidemask & (1<<side))
688 return 1; //pokes through side!
690 return 0; //does not!
694 //returns true of door in unobjstructed (& thus can close)
695 int is_door_free(segment *seg,int side)
701 csegp = &Segments[seg->children[side]];
702 Connectside = find_connect_side(seg, csegp);
703 Assert(Connectside != -1);
705 //go through each object in each of two segments, and see if
706 //it pokes into the connecting seg
708 for (objnum=seg->objects;objnum!=-1;objnum=Objects[objnum].next)
709 if (Objects[objnum].type!=OBJ_WEAPON && Objects[objnum].type!=OBJ_FIREBALL && check_poke(objnum,seg-Segments,side))
712 for (objnum=csegp->objects;objnum!=-1;objnum=Objects[objnum].next)
713 if (Objects[objnum].type!=OBJ_WEAPON && Objects[objnum].type!=OBJ_FIREBALL && check_poke(objnum,csegp-Segments,Connectside))
716 return 1; //doorway is free!
721 //-----------------------------------------------------------------
723 void wall_close_door(segment *seg, int side)
730 Assert(seg->sides[side].wall_num != -1); //Opening door on illegal wall
732 w = &Walls[seg->sides[side].wall_num];
734 if ((w->state == WALL_DOOR_CLOSING) || //already closing
735 (w->state == WALL_DOOR_WAITING) || //open, waiting to close
736 (w->state == WALL_DOOR_CLOSED)) //closed
739 if (!is_door_free(seg,side))
742 if (w->state == WALL_DOOR_OPENING) { //reuse door
748 for (i=0;i<Num_open_doors;i++) { //find door
752 if (d->front_wallnum[0]==w-Walls || d->back_wallnum[0]==w-Walls || (d->n_parts==2 && (d->front_wallnum[1]==w-Walls || d->back_wallnum[1]==w-Walls)))
756 Assert(i<Num_open_doors); //didn't find door!
757 Assert( d!=NULL ); // Get John!
759 d->time = WallAnims[w->clip_num].play_time - d->time;
765 else { //create new door
766 Assert(w->state == WALL_DOOR_OPEN);
767 d = &ActiveDoors[Num_open_doors];
770 Assert( Num_open_doors < MAX_DOORS );
773 w->state = WALL_DOOR_CLOSING;
775 // So that door can't be shot while opening
776 csegp = &Segments[seg->children[side]];
777 Connectside = find_connect_side(seg, csegp);
778 Assert(Connectside != -1);
780 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOSING;
782 d->front_wallnum[0] = seg->sides[side].wall_num;
783 d->back_wallnum[0] = csegp->sides[Connectside].wall_num;
785 Assert( seg-Segments != -1);
787 if (Newdemo_state == ND_STATE_RECORDING) {
788 newdemo_record_door_opening(seg-Segments, side);
791 if (w->linked_wall != -1) {
792 Int3(); //don't think we ever used linked walls
798 if ( Newdemo_state != ND_STATE_PLAYBACK )
800 // NOTE THE LINK TO ABOVE!!!!
802 compute_center_point_on_side(&cp, seg, side );
803 if (WallAnims[w->clip_num].open_sound > -1 )
804 digi_link_sound_to_pos( WallAnims[w->clip_num].open_sound, seg-Segments, side, &cp, 0, F1_0 );
809 //-----------------------------------------------------------------
810 // Animates opening of a door.
811 // Called in the game loop.
812 void do_door_open(int door_num)
817 Assert(door_num != -1); //Trying to do_door_open on illegal door
819 d = &ActiveDoors[door_num];
821 for (p=0;p<d->n_parts;p++) {
823 int Connectside, side;
824 segment *csegp, *seg;
825 fix time_elapsed, time_total, one_frame;
828 w = &Walls[d->front_wallnum[p]];
829 kill_stuck_objects(d->front_wallnum[p]);
830 kill_stuck_objects(d->back_wallnum[p]);
832 seg = &Segments[w->segnum];
835 Assert(seg->sides[side].wall_num != -1); //Trying to do_door_open on illegal wall
837 csegp = &Segments[seg->children[side]];
838 Connectside = find_connect_side(seg, csegp);
839 Assert(Connectside != -1);
841 d->time += FrameTime;
843 time_elapsed = d->time;
844 n = WallAnims[w->clip_num].num_frames;
845 time_total = WallAnims[w->clip_num].play_time;
847 one_frame = time_total/n;
849 i = time_elapsed/one_frame;
852 wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,i);
855 Walls[seg->sides[side].wall_num].flags |= WALL_DOOR_OPENED;
856 Walls[csegp->sides[Connectside].wall_num].flags |= WALL_DOOR_OPENED;
860 wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,n-1);
862 // If our door is not automatic just remove it from the list.
863 if (!(Walls[seg->sides[side].wall_num].flags & WALL_DOOR_AUTO)) {
864 for (i=door_num;i<Num_open_doors;i++)
865 ActiveDoors[i] = ActiveDoors[i+1];
867 Walls[seg->sides[side].wall_num].state = WALL_DOOR_OPEN;
868 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_OPEN;
872 Walls[seg->sides[side].wall_num].state = WALL_DOOR_WAITING;
873 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_WAITING;
875 ActiveDoors[Num_open_doors].time = 0; //counts up
883 //-----------------------------------------------------------------
884 // Animates and processes the closing of a door.
885 // Called from the game loop.
886 void do_door_close(int door_num)
892 Assert(door_num != -1); //Trying to do_door_open on illegal door
894 d = &ActiveDoors[door_num];
896 w = &Walls[d->front_wallnum[0]];
898 //check for objects in doorway before closing
899 if (w->flags & WALL_DOOR_AUTO)
900 if (!is_door_free(&Segments[w->segnum],w->sidenum)) {
901 digi_kill_sound_linked_to_segment(w->segnum,w->sidenum,-1);
902 wall_open_door(&Segments[w->segnum],w->sidenum); //re-open door
906 for (p=0;p<d->n_parts;p++) {
908 int Connectside, side;
909 segment *csegp, *seg;
910 fix time_elapsed, time_total, one_frame;
913 w = &Walls[d->front_wallnum[p]];
915 seg = &Segments[w->segnum];
918 if (seg->sides[side].wall_num == -1) {
919 mprintf((0, "Trying to do_door_close on Illegal wall\n"));
923 //if here, must be auto door
924 // Assert(Walls[seg->sides[side].wall_num].flags & WALL_DOOR_AUTO);
925 //don't assert here, because now we have triggers to close non-auto doors
927 // Otherwise, close it.
928 csegp = &Segments[seg->children[side]];
929 Connectside = find_connect_side(seg, csegp);
930 Assert(Connectside != -1);
933 if ( Newdemo_state != ND_STATE_PLAYBACK )
934 // NOTE THE LINK TO ABOVE!!
935 if (p==0) //only play one sound for linked doors
936 if ( d->time==0 ) { //first time
938 compute_center_point_on_side(&cp, seg, side );
939 if (WallAnims[w->clip_num].close_sound > -1 )
940 digi_link_sound_to_pos( WallAnims[Walls[seg->sides[side].wall_num].clip_num].close_sound, seg-Segments, side, &cp, 0, F1_0 );
943 d->time += FrameTime;
945 time_elapsed = d->time;
946 n = WallAnims[w->clip_num].num_frames;
947 time_total = WallAnims[w->clip_num].play_time;
949 one_frame = time_total/n;
951 i = n-time_elapsed/one_frame-1;
954 Walls[seg->sides[side].wall_num].flags &= ~WALL_DOOR_OPENED;
955 Walls[csegp->sides[Connectside].wall_num].flags &= ~WALL_DOOR_OPENED;
960 wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,i);
962 Walls[seg->sides[side].wall_num].state = WALL_DOOR_CLOSING;
963 Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOSING;
965 ActiveDoors[Num_open_doors].time = 0; //counts up
968 wall_close_door_num(door_num);
973 //-----------------------------------------------------------------
974 // Turns off an illusionary wall (This will be used primarily for
975 // wall switches or triggers that can turn on/off illusionary walls.)
976 void wall_illusion_off(segment *seg, int side)
981 csegp = &Segments[seg->children[side]];
982 cside = find_connect_side(seg, csegp);
985 if (seg->sides[side].wall_num == -1) {
986 mprintf((0, "Trying to shut off illusion illegal wall\n"));
990 Walls[seg->sides[side].wall_num].flags |= WALL_ILLUSION_OFF;
991 Walls[csegp->sides[cside].wall_num].flags |= WALL_ILLUSION_OFF;
993 kill_stuck_objects(seg->sides[side].wall_num);
994 kill_stuck_objects(csegp->sides[cside].wall_num);
997 //-----------------------------------------------------------------
998 // Turns on an illusionary wall (This will be used primarily for
999 // wall switches or triggers that can turn on/off illusionary walls.)
1000 void wall_illusion_on(segment *seg, int side)
1005 csegp = &Segments[seg->children[side]];
1006 cside = find_connect_side(seg, csegp);
1007 Assert(cside != -1);
1009 if (seg->sides[side].wall_num == -1) {
1010 mprintf((0, "Trying to turn on illusion illegal wall\n"));
1014 Walls[seg->sides[side].wall_num].flags &= ~WALL_ILLUSION_OFF;
1015 Walls[csegp->sides[cside].wall_num].flags &= ~WALL_ILLUSION_OFF;
1018 // -----------------------------------------------------------------------------
1019 // Allowed to open the normally locked special boss door if in multiplayer mode.
1020 int special_boss_opening_allowed(int segnum, int sidenum)
1022 if (Game_mode & GM_MULTI)
1023 return (Current_level_num == BOSS_LOCKED_DOOR_LEVEL) && (segnum == BOSS_LOCKED_DOOR_SEG) && (sidenum == BOSS_LOCKED_DOOR_SIDE);
1028 //-----------------------------------------------------------------
1029 // Determines what happens when a wall is shot
1030 //returns info about wall. see wall.h for codes
1031 //obj is the object that hit...either a weapon or the player himself
1032 //playernum is the number the player who hit the wall or fired the weapon,
1033 //or -1 if a robot fired the weapon
1034 int wall_hit_process(segment *seg, int side, fix damage, int playernum, object *obj )
1039 Assert (seg-Segments != -1);
1041 // If it is not a "wall" then just return.
1042 if ( seg->sides[side].wall_num < 0 )
1043 return WHP_NOT_SPECIAL;
1045 w = &Walls[seg->sides[side].wall_num];
1047 if ( Newdemo_state == ND_STATE_RECORDING )
1048 newdemo_record_wall_hit_process( seg-Segments, side, damage, playernum );
1050 if (w->type == WALL_BLASTABLE) {
1051 if (obj->ctype.laser_info.parent_type == OBJ_PLAYER)
1052 wall_damage(seg, side, damage);
1053 return WHP_BLASTABLE;
1056 if (playernum != Player_num) //return if was robot fire
1057 return WHP_NOT_SPECIAL;
1059 Assert( playernum > -1 );
1061 // Determine whether player is moving forward. If not, don't say negative
1062 // messages because he probably didn't intentionally hit the door.
1063 if (obj->type == OBJ_PLAYER)
1064 show_message = (vm_vec_dot(&obj->orient.fvec, &obj->mtype.phys_info.velocity) > 0);
1065 else if (obj->type == OBJ_ROBOT)
1067 else if ((obj->type == OBJ_WEAPON) && (obj->ctype.laser_info.parent_type == OBJ_ROBOT))
1072 if (w->keys == KEY_BLUE)
1073 if (!(Players[playernum].flags & PLAYER_FLAGS_BLUE_KEY)) {
1074 if ( playernum==Player_num )
1076 HUD_init_message("%s %s",TXT_BLUE,TXT_ACCESS_DENIED);
1080 if (w->keys == KEY_RED)
1081 if (!(Players[playernum].flags & PLAYER_FLAGS_RED_KEY)) {
1082 if ( playernum==Player_num )
1084 HUD_init_message("%s %s",TXT_RED,TXT_ACCESS_DENIED);
1088 if (w->keys == KEY_GOLD)
1089 if (!(Players[playernum].flags & PLAYER_FLAGS_GOLD_KEY)) {
1090 if ( playernum==Player_num )
1092 HUD_init_message("%s %s",TXT_YELLOW,TXT_ACCESS_DENIED);
1096 if (w->type == WALL_DOOR)
1098 if ((w->flags & WALL_DOOR_LOCKED ) && !(special_boss_opening_allowed(seg-Segments, side)) ) {
1099 if ( playernum==Player_num )
1101 HUD_init_message(TXT_CANT_OPEN_DOOR);
1105 if (w->state != WALL_DOOR_OPENING)
1107 wall_open_door(seg, side);
1109 if (Game_mode & GM_MULTI)
1110 multi_send_door_open(seg-Segments, side,w->flags);
1118 return WHP_NOT_SPECIAL; //default is treat like normal wall
1121 //-----------------------------------------------------------------
1122 // Opens doors/destroys wall/shuts off triggers.
1123 void wall_toggle(segment *seg, int side)
1127 Assert( seg-Segments <= Highest_segment_index);
1128 Assert( side < MAX_SIDES_PER_SEGMENT );
1130 wall_num = seg->sides[side].wall_num;
1132 if (wall_num == -1) {
1133 mprintf((0, "Illegal wall_toggle\n"));
1137 if ( Newdemo_state == ND_STATE_RECORDING )
1138 newdemo_record_wall_toggle(seg-Segments, side );
1140 if (Walls[wall_num].type == WALL_BLASTABLE)
1141 wall_destroy(seg, side);
1143 if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].state == WALL_DOOR_CLOSED))
1144 wall_open_door(seg, side);
1149 //-----------------------------------------------------------------
1150 // Tidy up Walls array for load/save purposes.
1155 if (Num_walls < 0) {
1156 mprintf((0, "Illegal Num_walls\n"));
1160 for (i=Num_walls;i<MAX_WALLS;i++) {
1161 Walls[i].type = WALL_NORMAL;
1164 Walls[i].trigger = -1;
1165 Walls[i].clip_num = -1;
1169 void do_cloaking_wall_frame(int cloaking_wall_num)
1172 wall *wfront,*wback;
1174 if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
1176 d = &CloakingWalls[cloaking_wall_num];
1177 wfront = &Walls[d->front_wallnum];
1178 wback = &Walls[d->back_wallnum];
1180 d->time += FrameTime;
1182 if (d->time > CLOAKING_WALL_TIME) {
1185 wfront->type = wback->type = WALL_OPEN;
1186 wfront->state = wback->state = WALL_DOOR_CLOSED; //why closed? why not?
1188 for (i=cloaking_wall_num;i<Num_cloaking_walls;i++)
1189 CloakingWalls[i] = CloakingWalls[i+1];
1190 Num_cloaking_walls--;
1193 else if (d->time > CLOAKING_WALL_TIME/2) {
1194 int old_type=wfront->type;
1196 wfront->cloak_value = wback->cloak_value = ((d->time - CLOAKING_WALL_TIME/2) * (GR_FADE_LEVELS-2)) / (CLOAKING_WALL_TIME/2);
1198 if (old_type != WALL_CLOAKED) { //just switched
1201 wfront->type = wback->type = WALL_CLOAKED;
1204 Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = d->front_ls[i];
1205 Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = d->back_ls[i];
1213 light_scale = fixdiv(CLOAKING_WALL_TIME/2-d->time,CLOAKING_WALL_TIME/2);
1216 Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = fixmul(d->front_ls[i],light_scale);
1217 Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = fixmul(d->back_ls[i],light_scale);
1221 if ( Newdemo_state == ND_STATE_RECORDING )
1222 newdemo_record_cloaking_wall(d->front_wallnum, d->back_wallnum, wfront->type, wfront->state, wfront->cloak_value, Segments[wfront->segnum].sides[wfront->sidenum].uvls[0].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[1].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[2].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[3].l);
1226 void do_decloaking_wall_frame(int cloaking_wall_num)
1229 wall *wfront,*wback;
1231 if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
1233 d = &CloakingWalls[cloaking_wall_num];
1234 wfront = &Walls[d->front_wallnum];
1235 wback = &Walls[d->back_wallnum];
1237 d->time += FrameTime;
1239 if (d->time > CLOAKING_WALL_TIME) {
1242 wfront->state = wback->state = WALL_DOOR_CLOSED;
1245 Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = d->front_ls[i];
1246 Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = d->back_ls[i];
1249 for (i=cloaking_wall_num;i<Num_cloaking_walls;i++)
1250 CloakingWalls[i] = CloakingWalls[i+1];
1251 Num_cloaking_walls--;
1254 else if (d->time > CLOAKING_WALL_TIME/2) { //fading in
1258 wfront->type = wback->type = WALL_CLOSED;
1260 light_scale = fixdiv(d->time-CLOAKING_WALL_TIME/2,CLOAKING_WALL_TIME/2);
1263 Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = fixmul(d->front_ls[i],light_scale);
1264 Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = fixmul(d->back_ls[i],light_scale);
1267 else { //cloaking in
1268 wfront->cloak_value = wback->cloak_value = ((CLOAKING_WALL_TIME/2 - d->time) * (GR_FADE_LEVELS-2)) / (CLOAKING_WALL_TIME/2);
1269 wfront->type = wback->type = WALL_CLOAKED;
1272 if ( Newdemo_state == ND_STATE_RECORDING )
1273 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);
1277 void wall_frame_process()
1281 for (i=0;i<Num_open_doors;i++) {
1285 d = &ActiveDoors[i];
1286 w = &Walls[d->front_wallnum[0]];
1288 if (w->state == WALL_DOOR_OPENING)
1290 else if (w->state == WALL_DOOR_CLOSING)
1292 else if (w->state == WALL_DOOR_WAITING) {
1293 d->time += FrameTime;
1295 //set flags to fix occatsional netgame problem where door is
1296 //waiting to close but open flag isn't set
1297 Assert(d->n_parts == 1);
1298 w->flags |= WALL_DOOR_OPENED;
1299 Walls[d->back_wallnum[0]].flags |= WALL_DOOR_OPENED;
1301 if (d->time > DOOR_WAIT_TIME && is_door_free(&Segments[w->segnum],w->sidenum)) {
1302 w->state = WALL_DOOR_CLOSING;
1306 else if (w->state == WALL_DOOR_CLOSED || w->state == WALL_DOOR_OPEN) {
1307 //this shouldn't happen. if the wall is in one of these states,
1308 //there shouldn't be an activedoor entry for it. So we'll kill
1309 //the activedoor entry. Tres simple.
1311 Int3(); //a bad thing has happened, but I'll try to fix it up
1312 for (t=i;t<Num_open_doors;t++)
1313 ActiveDoors[t] = ActiveDoors[t+1];
1318 for (i=0;i<Num_cloaking_walls;i++) {
1322 d = &CloakingWalls[i];
1323 w = &Walls[d->front_wallnum];
1325 if (w->state == WALL_DOOR_CLOAKING)
1326 do_cloaking_wall_frame(i);
1327 else if (w->state == WALL_DOOR_DECLOAKING)
1328 do_decloaking_wall_frame(i);
1330 Int3(); //unexpected wall state
1334 int Num_stuck_objects=0;
1336 stuckobj Stuck_objects[MAX_STUCK_OBJECTS];
1338 // An object got stuck in a door (like a flare).
1339 // Add global entry.
1340 void add_stuck_object(object *objp, int segnum, int sidenum)
1345 wallnum = Segments[segnum].sides[sidenum].wall_num;
1347 if (wallnum != -1) {
1348 if (Walls[wallnum].flags & WALL_BLASTED)
1349 objp->flags |= OF_SHOULD_BE_DEAD;
1351 for (i=0; i<MAX_STUCK_OBJECTS; i++) {
1352 if (Stuck_objects[i].wallnum == -1) {
1353 Stuck_objects[i].wallnum = wallnum;
1354 Stuck_objects[i].objnum = objp-Objects;
1355 Stuck_objects[i].signature = objp->signature;
1356 // mprintf((0, "Added wall %i at index %i\n", wallnum, i));
1357 Num_stuck_objects++;
1361 if (i == MAX_STUCK_OBJECTS)
1362 mprintf((1, "Warning: Unable to add object %i which got stuck in wall %i to Stuck_objects\n", objp-Objects, wallnum));
1369 // --------------------------------------------------------------------------------------------------
1370 // Look at the list of stuck objects, clean up in case an object has gone away, but not been removed here.
1371 // Removes up to one/frame.
1372 void remove_obsolete_stuck_objects(void)
1376 // Safety and efficiency code. If no stuck objects, should never get inside the IF, but this is faster.
1377 if (!Num_stuck_objects)
1380 objnum = FrameCount % MAX_STUCK_OBJECTS;
1382 if (Stuck_objects[objnum].wallnum != -1)
1383 if ((Walls[Stuck_objects[objnum].wallnum].state != WALL_DOOR_CLOSED) || (Objects[Stuck_objects[objnum].objnum].signature != Stuck_objects[objnum].signature)) {
1384 Num_stuck_objects--;
1385 Objects[Stuck_objects[objnum].objnum].lifeleft = F1_0/8;
1386 Stuck_objects[objnum].wallnum = -1;
1391 extern void flush_fcd_cache(void);
1393 // ----------------------------------------------------------------------------------------------------
1394 // Door with wall index wallnum is opening, kill all objects stuck in it.
1395 void kill_stuck_objects(int wallnum)
1399 if (Num_stuck_objects == 0) {
1403 Num_stuck_objects=0;
1405 for (i=0; i<MAX_STUCK_OBJECTS; i++)
1406 if (Stuck_objects[i].wallnum == wallnum) {
1407 if (Objects[Stuck_objects[i].objnum].type == OBJ_WEAPON) {
1408 Objects[Stuck_objects[i].objnum].lifeleft = F1_0/8;
1410 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));
1411 // Int3(); // What? This looks bad. Object is not a weapon and it is stuck in a wall!
1412 Stuck_objects[i].wallnum = -1;
1413 } else if (Stuck_objects[i].wallnum != -1) {
1414 Num_stuck_objects++;
1416 // Ok, this is awful, but we need to do things whenever a door opens/closes/disappears, etc.
1422 // -- unused -- // -----------------------------------------------------------------------------------
1423 // -- unused -- // Return object id of first flare found embedded in segp:sidenum.
1424 // -- unused -- // If no flare, return -1.
1425 // -- unused -- int contains_flare(segment *segp, int sidenum)
1427 // -- unused -- int i;
1429 // -- unused -- for (i=0; i<Num_stuck_objects; i++) {
1430 // -- unused -- object *objp = &Objects[Stuck_objects[i].objnum];
1432 // -- unused -- if ((objp->type == OBJ_WEAPON) && (objp->id == FLARE_ID)) {
1433 // -- unused -- if (Walls[Stuck_objects[i].wallnum].segnum == segp-Segments)
1434 // -- unused -- if (Walls[Stuck_objects[i].wallnum].sidenum == sidenum)
1435 // -- unused -- return objp-Objects;
1439 // -- unused -- return -1;
1442 // -----------------------------------------------------------------------------------
1443 // Initialize stuck objects array. Called at start of level
1444 void init_stuck_objects(void)
1448 for (i=0; i<MAX_STUCK_OBJECTS; i++)
1449 Stuck_objects[i].wallnum = -1;
1451 Num_stuck_objects = 0;
1454 // -----------------------------------------------------------------------------------
1455 // Clear out all stuck objects. Called for a new ship
1456 void clear_stuck_objects(void)
1460 for (i=0; i<MAX_STUCK_OBJECTS; i++) {
1461 if (Stuck_objects[i].wallnum != -1) {
1464 objnum = Stuck_objects[i].objnum;
1466 if ((Objects[objnum].type == OBJ_WEAPON) && (Objects[objnum].id == FLARE_ID))
1467 Objects[objnum].lifeleft = F1_0/8;
1469 Stuck_objects[i].wallnum = -1;
1471 Num_stuck_objects--;
1475 Assert(Num_stuck_objects == 0);
1479 // -----------------------------------------------------------------------------------
1480 #define MAX_BLAST_GLASS_DEPTH 5
1482 void bng_process_segment(object *objp, fix damage, segment *segp, int depth, byte *visited)
1486 if (depth > MAX_BLAST_GLASS_DEPTH)
1491 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
1496 // Process only walls which have glass.
1497 if ((tm=segp->sides[sidenum].tmap_num2) != 0) {
1500 tm &= 0x3fff; //tm without flags
1502 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))) {
1503 compute_center_point_on_side(&pnt, segp, sidenum);
1504 dist = vm_vec_dist_quick(&pnt, &objp->pos);
1505 if (dist < damage/2) {
1506 dist = find_connected_distance(&pnt, segp-Segments, &objp->pos, objp->segnum, MAX_BLAST_GLASS_DEPTH, WID_RENDPAST_FLAG);
1507 if ((dist > 0) && (dist < damage/2))
1508 check_effect_blowup(segp, sidenum, &pnt, &Objects[objp->ctype.laser_info.parent_num], 1);
1514 for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
1515 int segnum = segp->children[i];
1518 if (!visited[segnum]) {
1519 if (WALL_IS_DOORWAY(segp, i) & WID_FLY_FLAG) {
1520 visited[segnum] = 1;
1521 bng_process_segment(objp, damage, &Segments[segnum], depth, visited);
1528 // -----------------------------------------------------------------------------------
1529 // objp is going to detonate
1530 // blast nearby monitors, lights, maybe other things
1531 void blast_nearby_glass(object *objp, fix damage)
1534 byte visited[MAX_SEGMENTS];
1537 cursegp = &Segments[objp->segnum];
1538 for (i=0; i<=Highest_segment_index; i++)
1541 visited[objp->segnum] = 1;
1542 bng_process_segment(objp, damage, cursegp, 0, visited);