//find which side to fly out of find_exit_side(object *obj) { int i; vms_vector prefvec,segcenter,sidevec; fix best_val=-f2_0; int best_side; segment *pseg = &Segments[obj->segnum]; //find exit side vm_vec_normalized_dir_quick(&prefvec,&obj->pos,&obj->last_pos); compute_segment_center(&segcenter,pseg); best_side=-1; for (i=MAX_SIDES_PER_SEGMENT;--i >= 0;) { fix d; if (pseg->children[i]!=-1) { compute_center_point_on_side(&sidevec,pseg,i); vm_vec_normalized_dir_quick(&sidevec,&sidevec,&segcenter); d = vm_vec_dotprod(&sidevec,&prefvec); if (labs(d) < MIN_D) d=0; if (d > best_val) {best_val=d; best_side=i;} } } Assert(best_side!=-1); return best_side; }
//sets the player facing curseg/curside, normal to face0 of curside, and //far enough away to see all of curside int SetPlayerFromCursegMinusOne() { vms_vector view_vec,view_vec2,side_center; vms_vector corner_v[4]; vms_vector upvec; g3s_point corner_p[4]; int i; fix max,view_dist=f1_0*10; static int edgenum=0; int newseg; view_vec = Cursegp->sides[Curside].normals[0]; vm_vec_negate(&view_vec); compute_center_point_on_side(&side_center,Cursegp,Curside); vm_vec_copy_scale(&view_vec2,&view_vec,view_dist); vm_vec_sub(&ConsoleObject->pos,&side_center,&view_vec2); vm_vec_sub(&upvec, &Vertices[Cursegp->verts[Side_to_verts[Curside][edgenum%4]]], &Vertices[Cursegp->verts[Side_to_verts[Curside][(edgenum+3)%4]]]); edgenum++; vm_vector_2_matrix(&ConsoleObject->orient,&view_vec,&upvec,NULL); gr_set_current_canvas(Canv_editor_game); g3_start_frame(); g3_set_view_matrix(&ConsoleObject->pos,&ConsoleObject->orient,Render_zoom); for (i=max=0;i<4;i++) { corner_v[i] = Vertices[Cursegp->verts[Side_to_verts[Curside][i]]]; g3_rotate_point(&corner_p[i],&corner_v[i]); if (labs(corner_p[i].p3_x) > max) max = labs(corner_p[i].p3_x); if (labs(corner_p[i].p3_y) > max) max = labs(corner_p[i].p3_y); } view_dist = fixmul(view_dist,fixdiv(fixdiv(max,SIDE_VIEW_FRAC),corner_p[0].p3_z)); vm_vec_copy_scale(&view_vec2,&view_vec,view_dist); vm_vec_sub(&ConsoleObject->pos,&side_center,&view_vec2); //obj_relink(ConsoleObject-Objects, SEG_PTR_2_NUM(Cursegp) ); //update_object_seg(ConsoleObject); //might have backed right out of curseg newseg = find_point_seg(&ConsoleObject->pos,SEG_PTR_2_NUM(Cursegp) ); if (newseg != -1) obj_relink(ConsoleObject-Objects,newseg); Update_flags |= UF_ED_STATE_CHANGED | UF_GAME_VIEW_CHANGED; return 1; }
void bng_process_segment(object *objp, fix damage, segment *segp, int depth, byte *visited) { int i, sidenum; if (depth > MAX_BLAST_GLASS_DEPTH) return; depth++; for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) { int tm; fix dist; vms_vector pnt; // Process only walls which have glass. if ((tm=segp->sides[sidenum].tmap_num2) != 0) { int ec, db; tm &= 0x3fff; //tm without flags 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))) { compute_center_point_on_side(&pnt, segp, sidenum); dist = vm_vec_dist_quick(&pnt, &objp->pos); if (dist < damage/2) { dist = find_connected_distance(&pnt, segp-Segments, &objp->pos, objp->segnum, MAX_BLAST_GLASS_DEPTH, WID_RENDPAST_FLAG); if ((dist > 0) && (dist < damage/2)) check_effect_blowup(segp, sidenum, &pnt, &Objects[objp->ctype.laser_info.parent_num], 1); } } } } for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) { int segnum = segp->children[i]; if (segnum != -1) { if (!visited[segnum]) { if (WALL_IS_DOORWAY(segp, i) & WID_FLY_FLAG) { visited[segnum] = 1; bng_process_segment(objp, damage, &Segments[segnum], depth, visited); } } } } }
void move_player_2_segment_and_rotate(segment *seg,int side) { vms_vector vp; vms_vector upvec; static int edgenum=0; compute_segment_center(&ConsoleObject->pos,seg); compute_center_point_on_side(&vp,seg,side); vm_vec_sub2(&vp,&ConsoleObject->pos); vm_vec_sub(&upvec, &Vertices[Cursegp->verts[Side_to_verts[Curside][edgenum%4]]], &Vertices[Cursegp->verts[Side_to_verts[Curside][(edgenum+3)%4]]]); edgenum++; vm_vector_2_matrix(&ConsoleObject->orient,&vp,&upvec,NULL); // vm_vector_2_matrix(&ConsoleObject->orient,&vp,NULL,NULL); obj_relink( ConsoleObject-Objects, SEG_PTR_2_NUM(seg) ); }
//go through this level and start any eclip sounds void set_sound_sources() { int segnum,sidenum; segment *seg; digi_init_sounds(); //clear old sounds for (seg=&Segments[0],segnum=0;segnum<=Highest_segment_index;seg++,segnum++) for (sidenum=0;sidenum<MAX_SIDES_PER_SEGMENT;sidenum++) { int tm,ec,sn; if ((tm=seg->sides[sidenum].tmap_num2) != 0) if ((ec=TmapInfo[tm&0x3fff].eclip_num)!=-1) if ((sn=Effects[ec].sound_num)!=-1) { vms_vector pnt; compute_center_point_on_side(&pnt,seg,sidenum); digi_link_sound_to_pos(sn,segnum,sidenum,&pnt,1, F1_0/2); } } }
//called for each level to load & setup the exit sequence load_endlevel_data(int level_num) { char filename[13]; char line[LINE_LEN],*p; CFILE *ifile; int var,segnum,sidenum; int exit_side, i; int have_binary = 0; endlevel_data_loaded = 0; //not loaded yet try_again: ; if (level_num<0) //secret level strcpy(filename,Secret_level_names[-level_num-1]); else //normal level strcpy(filename,Level_names[level_num-1]); if (!convert_ext(filename,"END")) return; ifile = cfopen(filename,"rb"); if (!ifile) { convert_ext(filename,"TXB"); ifile = cfopen(filename,"rb"); if (!ifile) if (level_num==1) { return; //abort //Error("Cannot load file text of binary version of <%s>",filename); } else { level_num = 1; goto try_again; } have_binary = 1; } //ok...this parser is pretty simple. It ignores comments, but //everything else must be in the right place var = 0; while (cfgets(line,LINE_LEN,ifile)) { if (have_binary) { for (i = 0; i < strlen(line) - 1; i++) { encode_rotate_left(&(line[i])); line[i] = line[i] ^ BITMAP_TBL_XOR; encode_rotate_left(&(line[i])); } p = line; } if ((p=strchr(line,';'))!=NULL) *p = 0; //cut off comment for (p=line+strlen(line)-1;p>line && isspace(*p);*p--=0); for (p=line;isspace(*p);p++); if (!*p) //empty line continue; switch (var) { case 0: { //ground terrain int iff_error; ubyte pal[768]; if (terrain_bm_instance.bm_data) free(terrain_bm_instance.bm_data); iff_error = iff_read_bitmap(p,&terrain_bm_instance,BM_LINEAR,pal); if (iff_error != IFF_NO_ERROR) { mprintf((1, "File %s - IFF error: %s",p,iff_errormsg(iff_error))); Error("File %s - IFF error: %s",p,iff_errormsg(iff_error)); } terrain_bitmap = &terrain_bm_instance; gr_remap_bitmap_good( terrain_bitmap, pal, iff_transparent_color, -1); break; } case 1: //height map load_terrain(p); break; case 2: sscanf(p,"%d,%d",&exit_point_bmx,&exit_point_bmy); break; case 3: //exit heading exit_angles.h = i2f(atoi(p))/360; break; case 4: { //planet bitmap int iff_error; ubyte pal[768]; if (satellite_bm_instance.bm_data) free(satellite_bm_instance.bm_data); iff_error = iff_read_bitmap(p,&satellite_bm_instance,BM_LINEAR,pal); if (iff_error != IFF_NO_ERROR) { mprintf((1, "File %s - IFF error: %s",p,iff_errormsg(iff_error))); Error("File %s - IFF error: %s",p,iff_errormsg(iff_error)); } satellite_bitmap = &satellite_bm_instance; gr_remap_bitmap_good( satellite_bitmap, pal, iff_transparent_color, -1); break; } case 5: //earth pos case 7: { //station pos vms_matrix tm; vms_angvec ta; int pitch,head; sscanf(p,"%d,%d",&head,&pitch); ta.h = i2f(head)/360; ta.p = -i2f(pitch)/360; ta.b = 0; vm_angles_2_matrix(&tm,&ta); if (var==5) satellite_pos = tm.fvec; //vm_vec_copy_scale(&satellite_pos,&tm.fvec,SATELLITE_DIST); else station_pos = tm.fvec; break; } case 6: //planet size satellite_size = i2f(atoi(p)); break; } var++; } Assert(var == NUM_VARS); // OK, now the data is loaded. Initialize everything //find the exit sequence by searching all segments for a side with //children == -2 for (segnum=0,exit_segnum=-1;exit_segnum==-1 && segnum<=Highest_segment_index;segnum++) for (sidenum=0;sidenum<6;sidenum++) if (Segments[segnum].children[sidenum] == -2) { exit_segnum = segnum; exit_side = sidenum; break; } Assert(exit_segnum!=-1); compute_segment_center(&mine_exit_point,&Segments[exit_segnum]); extract_orient_from_segment(&mine_exit_orient,&Segments[exit_segnum]); compute_center_point_on_side(&mine_side_exit_point,&Segments[exit_segnum],exit_side); vm_vec_scale_add(&mine_ground_exit_point,&mine_exit_point,&mine_exit_orient.uvec,-i2f(20)); //compute orientation of surface { vms_vector tv; vms_matrix exit_orient,tm; vm_angles_2_matrix(&exit_orient,&exit_angles); vm_transpose_matrix(&exit_orient); vm_matrix_x_matrix(&surface_orient,&mine_exit_orient,&exit_orient); vm_copy_transpose_matrix(&tm,&surface_orient); vm_vec_rotate(&tv,&station_pos,&tm); vm_vec_scale_add(&station_pos,&mine_exit_point,&tv,STATION_DIST); vm_vec_rotate(&tv,&satellite_pos,&tm); vm_vec_scale_add(&satellite_pos,&mine_exit_point,&tv,SATELLITE_DIST); vm_vector_2_matrix(&tm,&tv,&surface_orient.uvec,NULL); vm_vec_copy_scale(&satellite_upvec,&tm.uvec,SATELLITE_HEIGHT); } cfclose(ifile); endlevel_data_loaded = 1; }
do_endlevel_flythrough(int n) { object *obj; segment *pseg; int old_player_seg; flydata = &fly_objects[n]; obj = flydata->obj; old_player_seg = obj->segnum; //move the player for this frame if (!flydata->first_time) { vm_vec_scale_add2(&obj->pos,&flydata->step,FrameTime); angvec_add2_scale(&flydata->angles,&flydata->angstep,FrameTime); vm_angles_2_matrix(&obj->orient,&flydata->angles); } //check new player seg update_object_seg(obj); pseg = &Segments[obj->segnum]; if (flydata->first_time || obj->segnum != old_player_seg) { //moved into new seg vms_vector curcenter,nextcenter; fix step_size,seg_time; short entry_side,exit_side; //what sides we entry and leave through vms_vector dest_point; //where we are heading (center of exit_side) vms_angvec dest_angles; //where we want to be pointing vms_matrix dest_orient; int up_side; //find new exit side if (!flydata->first_time) { entry_side = matt_find_connect_side(obj->segnum,old_player_seg); exit_side = Side_opposite[entry_side]; } if (flydata->first_time || entry_side==-1 || pseg->children[exit_side]==-1) exit_side = find_exit_side(obj); { //find closest side to align to fix d,largest_d=-f1_0; int i; for (i=0;i<6;i++) { #ifdef COMPACT_SEGS vms_vector v1; get_side_normal(pseg, i, 0, &v1 ); d = vm_vec_dot(&v1,&flydata->obj->orient.uvec); #else d = vm_vec_dot(&pseg->sides[i].normals[0],&flydata->obj->orient.uvec); #endif if (d > largest_d) {largest_d = d; up_side=i;} } } //update target point & angles compute_center_point_on_side(&dest_point,pseg,exit_side); //update target point and movement points //offset object sideways if (flydata->offset_frac) { int s0=-1,s1,i; vms_vector s0p,s1p; fix dist; for (i=0;i<6;i++) if (i!=entry_side && i!=exit_side && i!=up_side && i!=Side_opposite[up_side]) if (s0==-1) s0 = i; else s1 = i; compute_center_point_on_side(&s0p,pseg,s0); compute_center_point_on_side(&s1p,pseg,s1); dist = fixmul(vm_vec_dist(&s0p,&s1p),flydata->offset_frac); if (dist-flydata->offset_dist > MAX_SLIDE_PER_SEGMENT) dist = flydata->offset_dist + MAX_SLIDE_PER_SEGMENT; flydata->offset_dist = dist; vm_vec_scale_add2(&dest_point,&obj->orient.rvec,dist); } vm_vec_sub(&flydata->step,&dest_point,&obj->pos); step_size = vm_vec_normalize_quick(&flydata->step); vm_vec_scale(&flydata->step,flydata->speed); compute_segment_center(&curcenter,pseg); compute_segment_center(&nextcenter,&Segments[pseg->children[exit_side]]); vm_vec_sub(&flydata->headvec,&nextcenter,&curcenter); #ifdef COMPACT_SEGS { vms_vector _v1; get_side_normal(pseg, up_side, 0, &_v1 ); vm_vector_2_matrix(&dest_orient,&flydata->headvec,&_v1,NULL); } #else vm_vector_2_matrix(&dest_orient,&flydata->headvec,&pseg->sides[up_side].normals[0],NULL); #endif vm_extract_angles_matrix(&dest_angles,&dest_orient); if (flydata->first_time) vm_extract_angles_matrix(&flydata->angles,&obj->orient); seg_time = fixdiv(step_size,flydata->speed); //how long through seg if (seg_time) { flydata->angstep.x = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.p,dest_angles.p),seg_time))); flydata->angstep.z = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.b,dest_angles.b),seg_time))); flydata->angstep.y = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.h,dest_angles.h),seg_time))); } else { flydata->angles = dest_angles; flydata->angstep.x = flydata->angstep.y = flydata->angstep.z = 0; } } flydata->first_time=0; }
//----------------------------------------------------------------- // Animates and processes the closing of a door. // Called from the game loop. void do_door_close(int door_num) { int p; active_door *d; wall *w; Assert(door_num != -1); //Trying to do_door_open on illegal door d = &ActiveDoors[door_num]; w = &Walls[d->front_wallnum[0]]; //check for objects in doorway before closing if (w->flags & WALL_DOOR_AUTO) if (!is_door_free(&Segments[w->segnum],w->sidenum)) { digi_kill_sound_linked_to_segment(w->segnum,w->sidenum,-1); wall_open_door(&Segments[w->segnum],w->sidenum); //re-open door return; } for (p=0;p<d->n_parts;p++) { wall *w; int Connectside, side; segment *csegp, *seg; fix time_elapsed, time_total, one_frame; int i, n; w = &Walls[d->front_wallnum[p]]; seg = &Segments[w->segnum]; side = w->sidenum; if (seg->sides[side].wall_num == -1) { mprintf((0, "Trying to do_door_close on Illegal wall\n")); return; } //if here, must be auto door // Assert(Walls[seg->sides[side].wall_num].flags & WALL_DOOR_AUTO); //don't assert here, because now we have triggers to close non-auto doors // Otherwise, close it. csegp = &Segments[seg->children[side]]; Connectside = find_connect_side(seg, csegp); Assert(Connectside != -1); if ( Newdemo_state != ND_STATE_PLAYBACK ) // NOTE THE LINK TO ABOVE!! if (p==0) //only play one sound for linked doors if ( d->time==0 ) { //first time vms_vector cp; compute_center_point_on_side(&cp, seg, side ); if (WallAnims[w->clip_num].close_sound > -1 ) digi_link_sound_to_pos( WallAnims[Walls[seg->sides[side].wall_num].clip_num].close_sound, seg-Segments, side, &cp, 0, F1_0 ); } d->time += FrameTime; time_elapsed = d->time; n = WallAnims[w->clip_num].num_frames; time_total = WallAnims[w->clip_num].play_time; one_frame = time_total/n; i = n-time_elapsed/one_frame-1; if (i < n/2) { Walls[seg->sides[side].wall_num].flags &= ~WALL_DOOR_OPENED; Walls[csegp->sides[Connectside].wall_num].flags &= ~WALL_DOOR_OPENED; } // Animate door. if (i > 0) { wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,i); Walls[seg->sides[side].wall_num].state = WALL_DOOR_CLOSING; Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOSING; ActiveDoors[Num_open_doors].time = 0; //counts up } else wall_close_door_num(door_num); } }
//----------------------------------------------------------------- // Closes a door void wall_close_door(segment *seg, int side) { wall *w; active_door *d; int Connectside; segment *csegp; Assert(seg->sides[side].wall_num != -1); //Opening door on illegal wall w = &Walls[seg->sides[side].wall_num]; if ((w->state == WALL_DOOR_CLOSING) || //already closing (w->state == WALL_DOOR_WAITING) || //open, waiting to close (w->state == WALL_DOOR_CLOSED)) //closed return; if (!is_door_free(seg,side)) return; if (w->state == WALL_DOOR_OPENING) { //reuse door int i; d = NULL; for (i=0;i<Num_open_doors;i++) { //find door d = &ActiveDoors[i]; 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))) break; } Assert(i<Num_open_doors); //didn't find door! Assert( d!=NULL ); // Get John! d->time = WallAnims[w->clip_num].play_time - d->time; if (d->time < 0) d->time = 0; } else { //create new door Assert(w->state == WALL_DOOR_OPEN); d = &ActiveDoors[Num_open_doors]; d->time = 0; Num_open_doors++; Assert( Num_open_doors < MAX_DOORS ); } w->state = WALL_DOOR_CLOSING; // So that door can't be shot while opening csegp = &Segments[seg->children[side]]; Connectside = find_connect_side(seg, csegp); Assert(Connectside != -1); Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOSING; d->front_wallnum[0] = seg->sides[side].wall_num; d->back_wallnum[0] = csegp->sides[Connectside].wall_num; Assert( seg-Segments != -1); if (Newdemo_state == ND_STATE_RECORDING) { newdemo_record_door_opening(seg-Segments, side); } if (w->linked_wall != -1) { Int3(); //don't think we ever used linked walls } else d->n_parts = 1; if ( Newdemo_state != ND_STATE_PLAYBACK ) { // NOTE THE LINK TO ABOVE!!!! vms_vector cp; compute_center_point_on_side(&cp, seg, side ); if (WallAnims[w->clip_num].open_sound > -1 ) digi_link_sound_to_pos( WallAnims[w->clip_num].open_sound, seg-Segments, side, &cp, 0, F1_0 ); } }
//----------------------------------------------------------------- // start the transition from open -> closed wall void start_wall_decloak(segment *seg, int side) { wall *w; cloaking_wall *d; int Connectside; segment *csegp; int i; if ( Newdemo_state==ND_STATE_PLAYBACK ) return; Assert(seg->sides[side].wall_num != -1); //Opening door on illegal wall w = &Walls[seg->sides[side].wall_num]; if (w->type == WALL_CLOSED || w->state == WALL_DOOR_DECLOAKING) //already closed or decloaking return; if (w->state == WALL_DOOR_CLOAKING) { //cloaking, so reuse door int i; d = NULL; for (i=0;i<Num_cloaking_walls;i++) { //find door d = &CloakingWalls[i]; if (d->front_wallnum==w-Walls || d->back_wallnum==w-Walls ) break; } Assert(i<Num_cloaking_walls); //didn't find door! Assert( d!=NULL ); // Get John! d->time = CLOAKING_WALL_TIME - d->time; } else if (w->state == WALL_DOOR_CLOSED) { //create new door d = &CloakingWalls[Num_cloaking_walls]; d->time = 0; if (Num_cloaking_walls >= MAX_CLOAKING_WALLS) { //no more! Int3(); //ran out of cloaking wall slots /* what is this _doing_ here? w->type = WALL_CLOSED; Walls[csegp->sides[Connectside].wall_num].type = WALL_CLOSED; */ return; } Num_cloaking_walls++; } else { Int3(); //unexpected wall state return; } w->state = WALL_DOOR_DECLOAKING; // So that door can't be shot while opening csegp = &Segments[seg->children[side]]; Connectside = find_connect_side(seg, csegp); Assert(Connectside != -1); Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_DECLOAKING; d->front_wallnum = seg->sides[side].wall_num; d->back_wallnum = csegp->sides[Connectside].wall_num; Assert( seg-Segments != -1); Assert(w->linked_wall == -1); if ( Newdemo_state != ND_STATE_PLAYBACK ) { vms_vector cp; compute_center_point_on_side(&cp, seg, side ); digi_link_sound_to_pos( SOUND_WALL_CLOAK_OFF, seg-Segments, side, &cp, 0, F1_0 ); } for (i=0;i<4;i++) { d->front_ls[i] = seg->sides[side].uvls[i].l; d->back_ls[i] = csegp->sides[Connectside].uvls[i].l; } }
//----------------------------------------------------------------- // Opens a door void wall_open_door(segment *seg, int side) { wall *w; active_door *d; int Connectside; segment *csegp; Assert(seg->sides[side].wall_num != -1); //Opening door on illegal wall w = &Walls[seg->sides[side].wall_num]; //kill_stuck_objects(seg->sides[side].wall_num); if ((w->state == WALL_DOOR_OPENING) || //already opening (w->state == WALL_DOOR_WAITING) || //open, waiting to close (w->state == WALL_DOOR_OPEN)) //open, & staying open return; if (w->state == WALL_DOOR_CLOSING) { //closing, so reuse door int i; d = NULL; for (i=0;i<Num_open_doors;i++) { //find door d = &ActiveDoors[i]; 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))) break; } if (i>=Num_open_doors && (Game_mode & GM_MULTI)) goto FastFix; Assert(i<Num_open_doors); //didn't find door! Assert( d!=NULL ); // Get John! d->time = WallAnims[w->clip_num].play_time - d->time; if (d->time < 0) d->time = 0; } else { //create new door Assert(w->state == WALL_DOOR_CLOSED); FastFix: d = &ActiveDoors[Num_open_doors]; d->time = 0; Num_open_doors++; Assert( Num_open_doors < MAX_DOORS ); } w->state = WALL_DOOR_OPENING; // So that door can't be shot while opening csegp = &Segments[seg->children[side]]; Connectside = find_connect_side(seg, csegp); Assert(Connectside != -1); Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_OPENING; //kill_stuck_objects(csegp->sides[Connectside].wall_num); d->front_wallnum[0] = seg->sides[side].wall_num; d->back_wallnum[0] = csegp->sides[Connectside].wall_num; Assert( seg-Segments != -1); if (Newdemo_state == ND_STATE_RECORDING) { newdemo_record_door_opening(seg-Segments, side); } if (w->linked_wall != -1) { wall *w2; segment *seg2; w2 = &Walls[w->linked_wall]; seg2 = &Segments[w2->segnum]; Assert(w2->linked_wall == seg->sides[side].wall_num); //Assert(!(w2->flags & WALL_DOOR_OPENING || w2->flags & WALL_DOOR_OPENED)); w2->state = WALL_DOOR_OPENING; csegp = &Segments[seg2->children[w2->sidenum]]; Connectside = find_connect_side(seg2, csegp); Assert(Connectside != -1); Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_OPENING; d->n_parts = 2; d->front_wallnum[1] = w->linked_wall; d->back_wallnum[1] = csegp->sides[Connectside].wall_num; } else d->n_parts = 1; if ( Newdemo_state != ND_STATE_PLAYBACK ) { // NOTE THE LINK TO ABOVE!!!! vms_vector cp; compute_center_point_on_side(&cp, seg, side ); if (WallAnims[w->clip_num].open_sound > -1 ) digi_link_sound_to_pos( WallAnims[w->clip_num].open_sound, seg-Segments, side, &cp, 0, F1_0 ); } }
//----------------------------------------------------------------- // Opens a door void wall_open_door(segment *seg, int side) { wall *w; active_door *d; int Connectside, wall_num, cwall_num = -1; segment *csegp; Assert(seg->sides[side].wall_num != -1); //Opening door on illegal wall w = &Walls[seg->sides[side].wall_num]; wall_num = w - Walls; //kill_stuck_objects(seg->sides[side].wall_num); if ((w->state == WALL_DOOR_OPENING) || //already opening (w->state == WALL_DOOR_WAITING) || //open, waiting to close (w->state == WALL_DOOR_OPEN)) //open, & staying open return; if (w->state == WALL_DOOR_CLOSING) { //closing, so reuse door int i; d = NULL; for (i=0;i<Num_open_doors;i++) { //find door d = &ActiveDoors[i]; if (d->front_wallnum[0]==w-Walls || d->back_wallnum[0]==wall_num || (d->n_parts==2 && (d->front_wallnum[1]==wall_num || d->back_wallnum[1]==wall_num))) break; } if (i>=Num_open_doors) // likely in demo playback or multiplayer { d = &ActiveDoors[Num_open_doors]; d->time = 0; Num_open_doors++; Assert( Num_open_doors < MAX_DOORS ); } else { Assert( d!=NULL ); // Get John! d->time = WallAnims[w->clip_num].play_time - d->time; if (d->time < 0) d->time = 0; } } else { //create new door Assert(w->state == WALL_DOOR_CLOSED); d = &ActiveDoors[Num_open_doors]; d->time = 0; Num_open_doors++; Assert( Num_open_doors < MAX_DOORS ); } w->state = WALL_DOOR_OPENING; // So that door can't be shot while opening csegp = &Segments[seg->children[side]]; Connectside = find_connect_side(seg, csegp); if (Connectside >= 0) { cwall_num = csegp->sides[Connectside].wall_num; if (cwall_num > -1) { Walls[cwall_num].state = WALL_DOOR_OPENING; d->back_wallnum[0] = cwall_num; } d->front_wallnum[0] = seg->sides[side].wall_num; } else con_printf(CON_URGENT, "Illegal Connectside %i in wall_open_door. Trying to hop over. Please check your level!\n", side); Assert( seg-Segments != -1); if (Newdemo_state == ND_STATE_RECORDING) { newdemo_record_door_opening(seg-Segments, side); } if (w->linked_wall != -1) { wall *w2; segment *seg2; w2 = &Walls[w->linked_wall]; seg2 = &Segments[w2->segnum]; Assert(w2->linked_wall == seg->sides[side].wall_num); //Assert(!(w2->flags & WALL_DOOR_OPENING || w2->flags & WALL_DOOR_OPENED)); w2->state = WALL_DOOR_OPENING; csegp = &Segments[seg2->children[w2->sidenum]]; Connectside = find_connect_side(seg2, csegp); Assert(Connectside != -1); if (cwall_num > -1) Walls[cwall_num].state = WALL_DOOR_OPENING; d->n_parts = 2; d->front_wallnum[1] = w->linked_wall; d->back_wallnum[1] = cwall_num; } else d->n_parts = 1; if ( Newdemo_state != ND_STATE_PLAYBACK ) { // NOTE THE LINK TO ABOVE!!!! vms_vector cp; compute_center_point_on_side(&cp, seg, side ); if (WallAnims[w->clip_num].open_sound > -1 ) digi_link_sound_to_pos( WallAnims[w->clip_num].open_sound, seg-Segments, side, &cp, 0, F1_0 ); } }
//----------------------------------------------------------------- // Animates and processes the closing of a door. // Called from the game loop. void do_door_close(int door_num) { int p; active_door *d; wall *w; Assert(door_num != -1); //Trying to do_door_open on illegal door d = &ActiveDoors[door_num]; w = &Walls[d->front_wallnum[0]]; //check for objects in doorway before closing if (w->flags & WALL_DOOR_AUTO) for (p=0;p<d->n_parts;p++) { int Connectside, side; segment *csegp, *seg; int objnum; seg = &Segments[w->segnum]; side = w->sidenum; csegp = &Segments[seg->children[side]]; Connectside = find_connect_side(seg, csegp); Assert(Connectside != -1); //go through each object in each of two segments, and see if //it pokes into the connecting seg for (objnum=seg->objects;objnum!=-1;objnum=Objects[objnum].next) if (check_poke(objnum,seg-Segments,side)) return; //abort! for (objnum=csegp->objects;objnum!=-1;objnum=Objects[objnum].next) if (check_poke(objnum,csegp-Segments,Connectside)) return; //abort! } for (p=0;p<d->n_parts;p++) { wall *w; int Connectside, side; segment *csegp, *seg; fix time_elapsed, time_total, one_frame; int i, n; w = &Walls[d->front_wallnum[p]]; seg = &Segments[w->segnum]; side = w->sidenum; if (seg->sides[side].wall_num == -1) { return; } //if here, must be auto door Assert(Walls[seg->sides[side].wall_num].flags & WALL_DOOR_AUTO); // Otherwise, close it. csegp = &Segments[seg->children[side]]; Connectside = find_connect_side(seg, csegp); Assert(Connectside != -1); if ( Newdemo_state != ND_STATE_PLAYBACK ) // NOTE THE LINK TO ABOVE!! if (p==0) //only play one sound for linked doors if ( d->time==0 ) { //first time vms_vector cp; compute_center_point_on_side(&cp, seg, side ); if (WallAnims[w->clip_num].close_sound > -1 ) digi_link_sound_to_pos( WallAnims[Walls[seg->sides[side].wall_num].clip_num].close_sound, seg-Segments, side, &cp, 0, F1_0 ); } d->time += FrameTime; time_elapsed = d->time; n = WallAnims[w->clip_num].num_frames; time_total = WallAnims[w->clip_num].play_time; one_frame = time_total/n; i = n-time_elapsed/one_frame-1; if (i < n/2) { Walls[seg->sides[side].wall_num].flags &= ~WALL_DOOR_OPENED; Walls[csegp->sides[Connectside].wall_num].flags &= ~WALL_DOOR_OPENED; } // Animate door. if (i > 0) { wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,i); Walls[seg->sides[side].wall_num].state = WALL_DOOR_CLOSING; Walls[csegp->sides[Connectside].wall_num].state = WALL_DOOR_CLOSING; ActiveDoors[Num_open_doors].time = 0; //counts up } else wall_close_door(door_num); } }
void objfly_move_to_new_segment( object * obj, short newseg, int first_time ) { segment *pseg; int old_object_seg = obj->segnum; if ( newseg != obj->segnum ) obj_relink(obj-Objects, newseg ); pseg = &Segments[obj->segnum]; if ( first_time || obj->segnum != old_object_seg) { //moved into new seg vms_vector curcenter,nextcenter; fix step_size,seg_time; short entry_side,exit_side; //what sides we entry and leave through vms_vector dest_point; //where we are heading (center of exit_side) vms_angvec dest_angles; //where we want to be pointing //find new exit side if ( !first_time ) { entry_side = matt_find_connect_side(obj->segnum,old_object_seg); exit_side = Side_opposite[entry_side]; } //if (first_time) obj->fly_info.ft_mode = FP_FORWARD; if (first_time || entry_side==-1 || (pseg->children[exit_side]==-1) || (obj->fly_info.ft_mode!=FP_FORWARD) ) { int i; vms_vector prefvec,segcenter,sidevec; fix best_val=-f2_0; int best_side; //find exit side if (obj->fly_info.ft_mode == FP_FORWARD) { if (first_time) prefvec = obj->orient.fvec; else prefvec = obj->fly_info.heading; vm_vec_normalize(&prefvec); } else prefvec = obj->orient.vecs[obj->fly_info.ft_mode%3]; if (obj->fly_info.ft_mode >= 3) {prefvec.x = -prefvec.x; prefvec.y = -prefvec.y; prefvec.z = -prefvec.z;} compute_segment_center(&segcenter,pseg); best_side=-1; for (i=MAX_SIDES_PER_SEGMENT;--i >= 0;) { fix d; if (pseg->children[i]!=-1) { compute_center_point_on_side(&sidevec,pseg,i); vm_vec_sub2(&sidevec,&segcenter); vm_vec_normalize(&sidevec); d = vm_vec_dotprod(&sidevec,&prefvec); if (labs(d) < MIN_D) d=0; if (d > best_val || (d==best_val && i==exit_side)) {best_val=d; best_side=i;} } } if (best_val > 0) obj->fly_info.ft_mode = FP_FORWARD; Assert(best_side!=-1); exit_side = best_side; } //update target point & angles compute_center_point_on_side(&dest_point,pseg,exit_side); //update target point and movement points vm_vec_sub(&obj->phys_info.velocity,&dest_point,&obj->pos); step_size = vm_vec_normalize(&obj->phys_info.velocity); vm_vec_scale(&obj->phys_info.velocity, obj->phys_info.speed); compute_segment_center(&curcenter,pseg); compute_segment_center(&nextcenter,&Segments[pseg->children[exit_side]]); vm_vec_sub(&obj->fly_info.heading,&nextcenter,&curcenter); angles_from_vector(&dest_angles,&obj->fly_info.heading); //extract angles if (first_time) angles_from_vector(&obj->phys_info.rotvel,&obj->orient.fvec); seg_time = fixdiv(step_size,obj->phys_info.speed); //how long through seg if (seg_time) { obj->fly_info.angle_step.p = fixdiv(delta_ang(obj->phys_info.rotvel.p,dest_angles.p),seg_time); obj->fly_info.angle_step.b = fixdiv(delta_ang(obj->phys_info.rotvel.b,dest_angles.b),seg_time); obj->fly_info.angle_step.h = fixdiv(delta_ang(obj->phys_info.rotvel.h,dest_angles.h),seg_time); } else { obj->phys_info.rotvel = dest_angles; obj->fly_info.angle_step.p = obj->fly_info.angle_step.b = obj->fly_info.angle_step.h = 0; } } }
int generate_curve( fix r1scale, fix r4scale ) { vms_vector vec_dir, tvec; vms_vector coord,prev_point; vms_equation coeffs; fix enddist, nextdist; int firstsegflag; fix t, maxscale; fixang rangle, uangle; const vcsegptr_t cursegp = Cursegp; compute_center_point_on_side(p1, cursegp, Curside); switch( Curside ) { case WLEFT: extract_right_vector_from_segment(cursegp, r1); vm_vec_scale(r1, -F1_0 ); break; case WTOP: extract_up_vector_from_segment(cursegp, r1); break; case WRIGHT: extract_right_vector_from_segment(cursegp, r1); break; case WBOTTOM: extract_up_vector_from_segment(cursegp, r1); vm_vec_scale(r1, -F1_0 ); break; case WBACK: extract_forward_vector_from_segment(cursegp, r1); break; case WFRONT: extract_forward_vector_from_segment(cursegp, r1); vm_vec_scale(r1, -F1_0 ); break; } const vcsegptr_t markedsegp = Markedsegp; compute_center_point_on_side(p4, markedsegp, Markedside); switch( Markedside ) { case WLEFT: extract_right_vector_from_segment(markedsegp, r4); extract_up_vector_from_segment(markedsegp, r4t); break; case WTOP: extract_up_vector_from_segment(markedsegp, r4); vm_vec_scale(r4, -F1_0 ); extract_forward_vector_from_segment(markedsegp, r4t); vm_vec_scale(r4t, -F1_0 ); break; case WRIGHT: extract_right_vector_from_segment(markedsegp, r4); vm_vec_scale(r4, -F1_0 ); extract_up_vector_from_segment(markedsegp, r4t); break; case WBOTTOM: extract_up_vector_from_segment(markedsegp, r4); extract_forward_vector_from_segment(markedsegp, r4t); break; case WBACK: extract_forward_vector_from_segment(markedsegp, r4); vm_vec_scale(r4, -F1_0 ); extract_up_vector_from_segment(markedsegp, r4t); break; case WFRONT: extract_forward_vector_from_segment(markedsegp, r4); extract_up_vector_from_segment(markedsegp, r4t); break; } r1save = r1; tvec = r1; vm_vec_scale(r1,r1scale); vm_vec_scale(r4,r4scale); create_curve( p1, p4, r1, r4, coeffs ); OriginalSeg = Cursegp; OriginalMarkedSeg = Markedsegp; OriginalSide = Curside; OriginalMarkedSide = Markedside; CurveNumSegs = 0; coord = prev_point = p1; t=0; firstsegflag = 1; enddist = F1_0; nextdist = 0; while ( enddist > fixmul( nextdist, 1.5*F1_0 )) { vms_matrix rotmat; if (firstsegflag==1) firstsegflag=0; else extract_forward_vector_from_segment(cursegp, tvec); nextdist = vm_vec_mag(tvec); // nextdist := distance to next point t = curve_dist(&coeffs, 3, t, prev_point, nextdist); // t = argument at which function is forward vector magnitude units away from prev_point (in 3-space, not along curve) coord = evaluate_curve(&coeffs, 3, t); // coord := point about forward vector magnitude units away from prev_point enddist = vm_vec_dist(coord, p4); // enddist := distance from current to end point, vec_dir used as a temporary variable //vm_vec_normalize(vm_vec_sub(&vec_dir, &coord, &prev_point)); vm_vec_normalized_dir(vec_dir, coord, prev_point); if (!med_attach_segment(Cursegp, vmsegptr(&New_segment), Curside, AttachSide)) { med_extract_matrix_from_segment(cursegp, &rotmat); // rotmat := matrix describing orientation of Cursegp const auto tdest = vm_vec_rotate(vec_dir,rotmat); // tdest := vec_dir in reference frame of Cursegp vec_dir = tdest; const auto rotmat2 = vm_vector_2_matrix(vec_dir,nullptr,nullptr); med_rotate_segment( Cursegp, rotmat2 ); prev_point = coord; Curside = Side_opposite[AttachSide]; CurveSegs[CurveNumSegs]=Cursegp; CurveNumSegs++; } else return 0; } extract_up_vector_from_segment(cursegp, tvec); uangle = vm_vec_delta_ang( tvec, r4t, r4 ); if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4; if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4; if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4; if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4; extract_right_vector_from_segment(cursegp, tvec); rangle = vm_vec_delta_ang( tvec, r4t, r4 ); if (rangle >= F1_0/8) rangle -= F1_0/4; if (rangle >= F1_0/8) rangle -= F1_0/4; if (rangle <= -F1_0/8) rangle += F1_0/4; if (rangle <= -F1_0/8) rangle += F1_0/4; if ((uangle != 0) && (rangle != 0)) { maxscale = CurveNumSegs*F1_0; generate_banked_curve(maxscale, coeffs); } if (CurveNumSegs) { med_form_bridge_segment( Cursegp, Side_opposite[AttachSide], Markedsegp, Markedside ); CurveSegs[CurveNumSegs] = vmsegptr(Markedsegp->children[Markedside]); CurveNumSegs++; } Cursegp = OriginalSeg; Curside = OriginalSide; med_create_new_segment_from_cursegp(); //warn_if_concave_segments(); if (CurveNumSegs) return 1; else return 0; }
do_flythrough(object *obj,int first_time) //set true if init { segment *pseg; int old_player_seg = obj->segnum; if (first_time) { //vms_vector zero_vector = {0,0,0}; obj->control_type = CT_FLYTHROUGH; //obj->fly_info.angle_step.p = 0; //obj->fly_info.angle_step.b = 0; //obj->fly_info.angle_step.h = 0; //obj->fly_info.heading = zero_vector; } //move the player for this frame if (!first_time) { //vms_vector tempv; //fix rot_step; vm_vec_scale_add2(&obj->pos,&player_step,FrameTime); angvec_add2_scale(&player_angles,&player_angstep,FrameTime); vm_angles_2_matrix(&obj->orient,&player_angles); } //check new player seg update_object_seg(obj); pseg = &Segments[obj->segnum]; if (first_time || obj->segnum != old_player_seg) { //moved into new seg vms_vector curcenter,nextcenter; fix step_size,seg_time; short entry_side,exit_side; //what sides we entry and leave through vms_vector dest_point; //where we are heading (center of exit_side) vms_angvec dest_angles; //where we want to be pointing //find new exit side if (!first_time) { entry_side = matt_find_connect_side(obj->segnum,old_player_seg); exit_side = Side_opposite[entry_side]; } if (first_time) ft_preference = FP_FORWARD; if (first_time || entry_side==-1 || pseg->children[exit_side]==-1 || ft_preference!=FP_FORWARD) { int i; vms_vector prefvec,segcenter,sidevec; fix best_val=-f2_0; int best_side; //find exit side if (ft_preference == FP_FORWARD) { if (first_time) prefvec = obj->orient.fvec; else prefvec = headvec; vm_vec_normalize(&prefvec); } else prefvec = obj->orient.vecs[ft_preference%3]; if (ft_preference >= 3) {prefvec.x = -prefvec.x; prefvec.y = -prefvec.y; prefvec.z = -prefvec.z;} compute_segment_center(&segcenter,pseg); best_side=-1; for (i=MAX_SIDES_PER_SEGMENT;--i >= 0;) { fix d; if (pseg->children[i]!=-1) { compute_center_point_on_side(&sidevec,pseg,i); //vm_vec_sub2(&sidevec,&segcenter); //vm_vec_normalize(&sidevec); vm_vec_normalized_dir(&sidevec,&sidevec,&segcenter); d = vm_vec_dotprod(&sidevec,&prefvec); if (labs(d) < MIN_D) d=0; if (d > best_val || (d==best_val && i==exit_side)) {best_val=d; best_side=i;} } } if (best_val > 0) ft_preference = FP_FORWARD; Assert(best_side!=-1); exit_side = best_side; } //update target point & angles compute_center_point_on_side(&dest_point,pseg,exit_side); //update target point and movement points vm_vec_sub(&player_step,&dest_point,&obj->pos); step_size = vm_vec_normalize(&player_step); vm_vec_scale(&player_step,player_speed); compute_segment_center(&curcenter,pseg); compute_segment_center(&nextcenter,&Segments[pseg->children[exit_side]]); vm_vec_sub(&headvec,&nextcenter,&curcenter); //angles_from_vector(&dest_angles,&headvec); //extract angles vm_extract_angles_vector(&dest_angles,&headvec); //extract angles if (first_time) //angles_from_vector(&player_angles,&obj->orient.fvec); vm_extract_angles_vector(&player_angles,&obj->orient.fvec); seg_time = fixdiv(step_size,player_speed); //how long through seg if (seg_time) { player_angstep.p = fixdiv(delta_ang(player_angles.p,dest_angles.p),seg_time); player_angstep.b = fixdiv(delta_ang(player_angles.b,dest_angles.b),seg_time); player_angstep.h = fixdiv(delta_ang(player_angles.h,dest_angles.h),seg_time); } else { player_angles = dest_angles; player_angstep.p = player_angstep.b = player_angstep.h = 0; } } }