void supernova_get_eye(vec3d *eye_pos, matrix *eye_orient) { // supernova camera pos vec3d Supernova_camera_pos; static matrix Supernova_camera_orient; vec3d at; vec3d sun_temp, sun_vec; vec3d view; // set the controls for the heart of the sun stars_get_sun_pos(0, &sun_temp); vm_vec_add2(&sun_temp, &Player_obj->pos); vm_vec_sub(&sun_vec, &sun_temp, &Player_obj->pos); vm_vec_normalize(&sun_vec); // always set the camera pos vec3d move; matrix whee; vm_vector_2_matrix(&whee, &move, NULL, NULL); vm_vec_scale_add(&Supernova_camera_pos, &Player_obj->pos, &whee.vec.rvec, sn_cam_distance); vm_vec_scale_add2(&Supernova_camera_pos, &whee.vec.uvec, 30.0f); //cam->set_position(&Supernova_camera_pos); *eye_pos = Supernova_camera_pos; // if we're no longer moving the camera if(Supernova_time < (SUPERNOVA_CUT_TIME - SUPERNOVA_CAMERA_MOVE_TIME)) { // *eye_pos = Supernova_camera_pos; //cam->set_rotation(&Supernova_camera_orient); *eye_orient = Supernova_camera_orient; } // otherwise move it else { // get a vector somewhere between the supernova shockwave and the player ship at = Player_obj->pos; vm_vec_scale_add2(&at, &sun_vec, sn_distance); vm_vec_sub(&move, &Player_obj->pos, &at); vm_vec_normalize(&move); // linearly move towards the player pos float pct = ((SUPERNOVA_CUT_TIME - Supernova_time) / SUPERNOVA_CAMERA_MOVE_TIME); vm_vec_scale_add2(&at, &move, sn_distance * pct); vm_vec_sub(&view, &at, &Supernova_camera_pos); vm_vec_normalize(&view); vm_vector_2_matrix(&Supernova_camera_orient, &view, NULL, NULL); //cam->set_rotation(&Supernova_camera_orient); *eye_orient = Supernova_camera_orient; } //return supernova_camera; }
void orient_editor::update_object(object *ptr) { if (ptr->type != OBJ_WAYPOINT && m_point_to) { vec3d v, loc; matrix m; memset(&v, 0, sizeof(vec3d)); loc.xyz.x = convert(m_location_x); loc.xyz.y = convert(m_location_y); loc.xyz.z = convert(m_location_z); if (((CButton *) GetDlgItem(IDC_POINT_TO_OBJECT))->GetCheck() == 1) { v = Objects[index[m_object_index]].pos; vm_vec_sub2(&v, &ptr->pos); } else if (((CButton *) GetDlgItem(IDC_POINT_TO_LOCATION))->GetCheck() == 1) { vm_vec_sub(&v, &loc, &ptr->pos); } else { Assert(0); // neither radio button is checked. } if (!v.xyz.x && !v.xyz.y && !v.xyz.z){ return; // can't point to itself. } vm_vector_2_matrix(&m, &v, NULL, NULL); ptr->orient = m; } }
/** * This function needs to be called by big ships which have shields. It should be able to be modified to deal with * the large polygons we use for their shield meshes - unknownplayer * * At lower detail levels, shield hit effects are a single texture, applied to one enlarged triangle. */ void create_shield_low_detail(int objnum, int model_num, matrix *orient, vec3d *centerp, vec3d *tcp, int tr0, shield_info *shieldp) { matrix tom; int gi; int shnum; shnum = get_global_shield_tri(); Shield_hits[shnum].type = SH_TYPE_1; gi = get_free_global_shield_index(); Global_tris[gi].used = 1; Global_tris[gi].trinum = -1; // This tells triangle renderer to not render in case detail_level was switched. Global_tris[gi].creation_time = Missiontime; Shield_hits[shnum].tri_list[0] = gi; Shield_hits[shnum].num_tris = 1; Shield_hits[shnum].start_time = Missiontime; Shield_hits[shnum].objnum = objnum; Shield_hits[shnum].rgb[0] = 255; Shield_hits[shnum].rgb[1] = 255; Shield_hits[shnum].rgb[2] = 255; if((objnum >= 0) && (objnum < MAX_OBJECTS) && (Objects[objnum].type == OBJ_SHIP) && (Objects[objnum].instance >= 0) && (Objects[objnum].instance < MAX_SHIPS) && (Ships[Objects[objnum].instance].ship_info_index >= 0) && (Ships[Objects[objnum].instance].ship_info_index < Num_ship_classes)) { ship_info *sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index]; Shield_hits[shnum].rgb[0] = sip->shield_color[0]; Shield_hits[shnum].rgb[1] = sip->shield_color[1]; Shield_hits[shnum].rgb[2] = sip->shield_color[2]; } vm_vector_2_matrix(&tom, &shieldp->tris[tr0].norm, NULL, NULL); create_low_detail_poly(gi, tcp, &tom.vec.rvec, &tom.vec.uvec); }
// add some jitter to a flak gun's aiming direction, take into account range to target so that we're never _too_ far off // assumes dir is normalized void flak_jitter_aim(vec3d* dir, float dist_to_target, float weapon_subsys_strength) { vec3d rand_twist_pre, rand_twist_post; matrix temp; vec3d final_aim; float error_val; // get the matrix needed to rotate the base direction to the actual direction vm_vector_2_matrix(&temp, dir, NULL, NULL); // error value error_val = Flak_error + (Flak_error * 0.65f * (1.0f - weapon_subsys_strength)); // scale the rvec by some random value and make it the "pre-twist" value float rand_dist = frand_range(0.0f, error_val); // no jitter - so do nothing if (rand_dist <= 0.0f) { return; } vm_vec_copy_scale(&rand_twist_pre, &temp.vec.rvec, rand_dist); // now rotate the twist vector around the x axis (the base aim axis) at a random angle vm_rot_point_around_line(&rand_twist_post, &rand_twist_pre, fl_radian(359.0f * frand_range(0.0f, 1.0f)), &vmd_zero_vector, dir); // add the resulting vector to the base aim vector and normalize final_aim = *dir; vm_vec_scale(&final_aim, dist_to_target); vm_vec_add(dir, &final_aim, &rand_twist_post); vm_vec_normalize(dir); }
//make sure matrix is orthogonal void check_and_fix_matrix(vms_matrix *m) { vms_matrix tempm; vm_vector_2_matrix(&tempm,&m->fvec,&m->uvec,NULL); *m = tempm; }
// --------------------------------------------------------------------------------------------------- // Do chase mode. // View current segment (Cursegp) from the previous segment. void set_chase_matrix(segment *sp) { int v; vms_vector forvec = ZERO_VECTOR, upvec; vms_vector tv = ZERO_VECTOR; segment *psp; // move back two segments, if possible, else move back one, if possible, else use current if (IS_CHILD(sp->children[WFRONT])) { psp = &Segments[sp->children[WFRONT]]; if (IS_CHILD(psp->children[WFRONT])) psp = &Segments[psp->children[WFRONT]]; } else psp = sp; for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) vm_vec_add2(&forvec,&Vertices[sp->verts[v]]); vm_vec_scale(&forvec,F1_0/MAX_VERTICES_PER_SEGMENT); for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) vm_vec_add2(&tv,&Vertices[psp->verts[v]]); vm_vec_scale(&tv,F1_0/MAX_VERTICES_PER_SEGMENT); Ed_view_target = forvec; vm_vec_sub2(&forvec,&tv); extract_up_vector_from_segment(psp,&upvec); if (!((forvec.x == 0) && (forvec.y == 0) && (forvec.z == 0))) vm_vector_2_matrix(&LargeView.ev_matrix,&forvec,&upvec,NULL); }
void get_turret_cam_orient(camera *cam, matrix *ori) { object_h obj(cam->get_object_host()); if(!obj.IsValid()) return; vm_vector_2_matrix(ori, &normal_cache, vm_vec_same(&normal_cache, &cam->get_object_host()->orient.vec.uvec)?NULL:&cam->get_object_host()->orient.vec.uvec, NULL); }
//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 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) ); }
void create_shield_explosion(int objnum, int model_num, matrix *orient, vec3d *centerp, vec3d *tcp, int tr0) { matrix tom; // Texture Orientation Matrix shield_info *shieldp; polymodel *pm; int i; if (Objects[objnum].flags & OF_NO_SHIELDS) return; pm = model_get(model_num); Num_tris = pm->shield.ntris; shieldp = &pm->shield; if (Num_tris == 0) return; if ( (Detail.shield_effects == 1) || (Detail.shield_effects == 2) ) { create_shield_low_detail(objnum, model_num, orient, centerp, tcp, tr0, shieldp); return; } for (i=0; i<Num_tris; i++) shieldp->tris[i].used = 0; // Compute orientation matrix from normal of surface hit. // Note, this will cause the shape of the bitmap to change abruptly // as the impact point moves to another triangle. To prevent this, // you could average the normals at the vertices, then interpolate the // normals from the vertices to get a smoothly changing normal across the face. // I had tried using the vector from the impact point to the center, which // changes smoothly, but this looked surprisingly bad. vm_vector_2_matrix(&tom, &shieldp->tris[tr0].norm, NULL, NULL); // Create the shield from the current triangle, as well as its neighbors. create_shield_from_triangle(tr0, orient, shieldp, tcp, centerp, Objects[objnum].radius, &tom.vec.rvec, &tom.vec.uvec); for (i=0; i<3; i++) create_shield_from_triangle(shieldp->tris[tr0].neighbors[i], orient, shieldp, tcp, centerp, Objects[objnum].radius, &tom.vec.rvec, &tom.vec.uvec); copy_shield_to_globals(objnum, shieldp); }
void camera::set_rotation_facing(vec3d *in_target, float in_rotation_time, float in_rotation_acceleration_time, float in_rotation_deceleration_time) { matrix temp_matrix = IDENTITY_MATRIX; if(in_target != NULL) { vec3d position = vmd_zero_vector; this->get_info(&position, NULL); if(in_target->xyz.x == position.xyz.x && in_target->xyz.y == position.xyz.y && in_target->xyz.z == position.xyz.z) { Warning(LOCATION, "Camera tried to point to self"); return; } vec3d targetvec; vm_vec_normalized_dir(&targetvec, in_target, &position); vm_vector_2_matrix(&temp_matrix, &targetvec, NULL, NULL); } set_rotation(&temp_matrix, in_rotation_time, in_rotation_acceleration_time, in_rotation_deceleration_time); }
matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, float fov, float aspect, float veryneardist, float neardist, float middist, float fardist) { if(Static_light.empty()) return vmd_identity_matrix; light *lp = *(Static_light.begin()); if ( lp == NULL ) { return vmd_identity_matrix; } vec3d light_dir; matrix light_matrix; vm_vec_copy_normalize(&light_dir, &lp->vec); vm_vector_2_matrix(&light_matrix, &light_dir, &eye_orient->vec.uvec, NULL); shadows_construct_light_frustum(&Shadow_frustums[0], &light_matrix, eye_orient, eye_pos, fov, aspect, 0.0f, veryneardist); shadows_construct_light_frustum(&Shadow_frustums[1], &light_matrix, eye_orient, eye_pos, fov, aspect, veryneardist - (veryneardist - 0.0f)* 0.2f, neardist); shadows_construct_light_frustum(&Shadow_frustums[2], &light_matrix, eye_orient, eye_pos, fov, aspect, neardist - (neardist - veryneardist) * 0.2f, middist); shadows_construct_light_frustum(&Shadow_frustums[3], &light_matrix, eye_orient, eye_pos, fov, aspect, middist - (middist - neardist) * 0.2f, fardist); Shadow_cascade_distances[0] = veryneardist; Shadow_cascade_distances[1] = neardist; Shadow_cascade_distances[2] = middist; Shadow_cascade_distances[3] = fardist; Shadow_proj_matrix[0] = Shadow_frustums[0].proj_matrix; Shadow_proj_matrix[1] = Shadow_frustums[1].proj_matrix; Shadow_proj_matrix[2] = Shadow_frustums[2].proj_matrix; Shadow_proj_matrix[3] = Shadow_frustums[3].proj_matrix; gr_shadow_map_start(&Shadow_view_matrix, &light_matrix); return light_matrix; }
void do_object_physics( object * obj ) { vms_angvec rotang; vms_vector frame_vec; //movement in this frame vms_vector new_pos,ipos; //position after this frame int iseg; int hit; vms_matrix rotmat,new_pm; int count=0; short joy_x,joy_y,btns; int joyx_moved,joyy_moved; fix speed; vms_vector *desired_upvec; fixang delta_ang,roll_ang; vms_vector forvec = {0,0,f1_0}; vms_matrix temp_matrix; //check keys rotang.pitch = ROT_SPEED * (key_down_time(KEY_UP) - key_down_time(KEY_DOWN)); rotang.head = ROT_SPEED * (key_down_time(KEY_RIGHT) - key_down_time(KEY_LEFT)); rotang.bank = 0; //check for joystick movement joy_get_pos(&joy_x,&joy_y); btns=joy_get_btns(); joyx_moved = (abs(joy_x - _old_joy_x)>JOY_NULL); joyy_moved = (abs(joy_y - _old_joy_y)>JOY_NULL); if (abs(joy_x) < JOY_NULL) joy_x = 0; if (abs(joy_y) < JOY_NULL) joy_y = 0; if (!rotang.pitch) rotang.pitch = fixmul(-joy_y * 128,FrameTime); if (!rotang.head) rotang.head = fixmul(joy_x * 128,FrameTime); if (joyx_moved) _old_joy_x = joy_x; if (joyy_moved) _old_joy_y = joy_y; speed = ((btns&2) || keyd_pressed[KEY_A])?SLOW_SPEED*3:(keyd_pressed[KEY_Z]?SLOW_SPEED/2:SLOW_SPEED); //now build matrices, do rotations, etc., etc. vm_angles_2_matrix(&rotmat,&rotang); vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat); obj->orient = new_pm; //move player vm_vec_copy_scale(&obj->velocity,&obj->orient.fvec,speed); vm_vec_copy_scale(&frame_vec,&obj->velocity,FrameTime); do { fix wall_part; vms_vector tvec; count++; vm_vec_add(&new_pos,&obj->pos,&frame_vec); hit = find_vector_intersection(&ipos,&iseg,&obj->pos,obj->seg_id,&new_pos,obj->size,-1); obj->seg_id = iseg; obj->pos = ipos; //-FIXJOHN-if (hit==HIT_OBJECT) ExplodeObject(hit_objnum); if (hit==HIT_WALL) { vm_vec_sub(&frame_vec,&new_pos,&obj->pos); //part through wall wall_part = vm_vec_dot(wall_norm,&frame_vec); vm_vec_copy_scale(&tvec,wall_norm,wall_part); if ((wall_part == 0) || (vm_vec_mag(&tvec) < 5)) Int3(); vm_vec_sub2(&frame_vec,&tvec); } } while (hit == HIT_WALL); Assert(check_point_in_seg(&obj->pos,obj->seg_id,0).centermask==0); //now bank player according to segment orientation desired_upvec = &Segments[obj->seg_id].sides[3].faces[0].normal; if (labs(vm_vec_dot(desired_upvec,&obj->orient.fvec)) < f1_0/2) { vm_vector_2_matrix(&temp_matrix,&obj->orient.fvec,desired_upvec,NULL); delta_ang = vm_vec_delta_ang(&obj->orient.uvec,&temp_matrix.uvec,&obj->orient.fvec); if (rotang.head) delta_ang += (rotang.head<0)?TURNROLL_ANG:-TURNROLL_ANG; if (abs(delta_ang) > DAMP_ANG) { roll_ang = fixmul(FrameTime,ROLL_RATE); if (abs(delta_ang) < roll_ang) roll_ang = delta_ang; else if (delta_ang<0) roll_ang = -roll_ang; vm_vec_ang_2_matrix(&rotmat,&forvec,roll_ang); vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat); obj->orient = new_pm; } } }
void do_physics_align_object( object * obj ) { vms_vector desired_upvec; fixang delta_ang,roll_ang; //vms_vector forvec = {0,0,f1_0}; vms_matrix temp_matrix; fix d,largest_d=-f1_0; int i,best_side; best_side=0; // bank player according to segment orientation //find side of segment that player is most alligned with for (i=0;i<6;i++) { #ifdef COMPACT_SEGS vms_vector _tv1; get_side_normal( &Segments[obj->segnum], i, 0, &_tv1 ); d = vm_vec_dot(&_tv1,&obj->orient.uvec); #else d = vm_vec_dot(&Segments[obj->segnum].sides[i].normals[0],&obj->orient.uvec); #endif if (d > largest_d) {largest_d = d; best_side=i;} } if (floor_levelling) { // old way: used floor's normal as upvec #ifdef COMPACT_SEGS get_side_normal(&Segments[obj->segnum], 3, 0, &desired_upvec ); #else desired_upvec = Segments[obj->segnum].sides[3].normals[0]; #endif } else // new player leveling code: use normal of side closest to our up vec if (get_num_faces(&Segments[obj->segnum].sides[best_side])==2) { #ifdef COMPACT_SEGS vms_vector normals[2]; get_side_normals(&Segments[obj->segnum], best_side, &normals[0], &normals[1] ); desired_upvec.x = (normals[0].x + normals[1].x) / 2; desired_upvec.y = (normals[0].y + normals[1].y) / 2; desired_upvec.z = (normals[0].z + normals[1].z) / 2; vm_vec_normalize(&desired_upvec); #else side *s = &Segments[obj->segnum].sides[best_side]; desired_upvec.x = (s->normals[0].x + s->normals[1].x) / 2; desired_upvec.y = (s->normals[0].y + s->normals[1].y) / 2; desired_upvec.z = (s->normals[0].z + s->normals[1].z) / 2; vm_vec_normalize(&desired_upvec); #endif } else #ifdef COMPACT_SEGS get_side_normal(&Segments[obj->segnum], best_side, 0, &desired_upvec ); #else desired_upvec = Segments[obj->segnum].sides[best_side].normals[0]; #endif if (labs(vm_vec_dot(&desired_upvec,&obj->orient.fvec)) < f1_0/2) { vms_angvec tangles; vm_vector_2_matrix(&temp_matrix,&obj->orient.fvec,&desired_upvec,NULL); delta_ang = vm_vec_delta_ang(&obj->orient.uvec,&temp_matrix.uvec,&obj->orient.fvec); delta_ang += obj->mtype.phys_info.turnroll; if (abs(delta_ang) > DAMP_ANG) { vms_matrix rotmat, new_pm; roll_ang = fixmul(FrameTime,ROLL_RATE); if (abs(delta_ang) < roll_ang) roll_ang = delta_ang; else if (delta_ang<0) roll_ang = -roll_ang; tangles.p = tangles.h = 0; tangles.b = roll_ang; vm_angles_2_matrix(&rotmat,&tangles); vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat); obj->orient = new_pm; } else floor_levelling=0; } }
// ******************************************************************************************** // Engages autopilot // This does: // * Control switched from player to AI // * Time compression to 32x // * Lock time compression -WMC // * Tell AI to fly to targeted Nav Point (for all nav-status wings/ships) // * Sets max waypoint speed to the best-speed of the slowest ship tagged bool StartAutopilot() { // Check for support ship and dismiss it if it is not doing anything. // If the support ship is doing something then tell the user such. for ( object *objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { if ((objp->type == OBJ_SHIP) && !(objp->flags & OF_SHOULD_BE_DEAD)) { Assertion((objp->instance >= 0) && (objp->instance < MAX_SHIPS), "objp does not have a valid pointer to a ship. Pointer is %d, which is smaller than 0 or bigger than %d", objp->instance, MAX_SHIPS); ship *shipp = &Ships[objp->instance]; if (shipp->team != Player_ship->team) continue; Assertion((shipp->ship_info_index >= 0) && (shipp->ship_info_index < MAX_SHIP_CLASSES), "Ship '%s' does not have a valid pointer to a ship class. Pointer is %d, which is smaller than 0 or bigger than %d", shipp->ship_name, shipp->ship_info_index, MAX_SHIP_CLASSES); ship_info *sip = &Ship_info[shipp->ship_info_index]; if ( !(sip->flags & SIF_SUPPORT) ) continue; // don't deal with dying or departing support ships if ( shipp->flags & (SF_DYING | SF_DEPARTING) ) continue; Assert(shipp->ai_index != -1); ai_info* support_ship_aip = &(Ai_info[Ships[objp->instance].ai_index]); // is support ship trying to rearm-repair if ( ai_find_goal_index( support_ship_aip->goals, AI_GOAL_REARM_REPAIR ) == -1 ) { // no, so tell it to depart ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_WARP, -1, NULL, support_ship_aip ); } else { // yes send_autopilot_msgID(NP_MSG_FAIL_SUPPORT_WORKING); return false; } } } if (!CanAutopilot()) return false; AutoPilotEngaged = true; // find the ship that is "leading" all of the ships when the player starts // autopilot // by default the ship that is leading the autopilot session the player's // wing leader (if the player is the wing leader then it will be the // player). // TODO:implement a way to allow a FREDer to say a different ship is leader Autopilot_flight_leader = get_wing_leader(Player_ship->wingnum); if ( Autopilot_flight_leader == NULL ) { // force player to be the leader if he doesn't have a wing Autopilot_flight_leader = Player_obj; } if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) LockAPConv = timestamp(); // lock convergence instantly else LockAPConv = timestamp(3000); // 3 seconds before we lock convergence Player_use_ai = 1; set_time_compression(1); lock_time_compression(true); // determine speed cap int i,j, wcount=1, tc_factor=1; float speed_cap = 1000000.0; // 1m is a safe starting point float radius = Player_obj->radius, distance = 0.0f, ftemp; bool capshipPresent = false; int capship_counts[3]; // three size classes capship_counts[0] = 0; capship_counts[1] = 0; capship_counts[2] = 0; int capship_placed[3]; // three size classes capship_placed[0] = 0; capship_placed[1] = 0; capship_placed[2] = 0; float capship_spreads[3]; capship_spreads[0] = 0.0f; capship_spreads[1] = 0.0f; capship_spreads[2] = 0.0f; SCP_vector<int> capIndexes; // empty the autopilot wings map autopilot_wings.clear(); // vars for usage w/ cinematic vec3d pos, norm1, perp, tpos, rpos = Player_obj->pos, zero; memset(&zero, 0, sizeof(vec3d)); // instantly turn player toward tpos if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) { vm_vec_sub(&norm1, Navs[CurrentNav].GetPosition(), &Player_obj->pos); vm_vector_2_matrix(&Player_obj->orient, &norm1, NULL, NULL); } for (i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum != -1 && (Ships[i].flags2 & SF2_NAVPOINT_CARRY || (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY) ) ) { if (speed_cap > vm_vec_mag(&Ship_info[Ships[i].ship_info_index].max_vel)) speed_cap = vm_vec_mag(&Ship_info[Ships[i].ship_info_index].max_vel); } } // damp speed_cap to 90% of actual -- to make sure ships stay in formation if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) speed_cap = 0.90f * speed_cap; if ( speed_cap < 1.0f ) { /* We need to deal with this so that incorrectly flagged ships will not cause the engine to fail to limit all the ships speeds correctly. */ Warning(LOCATION, "Ship speed cap is way too small (%f)!\n" "This is normally caused by a ship that has nav-carry-status set, but cannot move itself (like a Cargo container).\n" "Speed cap has been set to 1.0 m/s.", speed_cap); speed_cap = 1.0f; } ramp_bias = speed_cap/50.0f; // assign ship goals // when assigning goals to individual ships only do so if Ships[shipnum].wingnum != -1 // we will assign wing goals below for (i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum != -1 && (Ships[i].flags2 & SF2_NAVPOINT_CARRY || (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY) ) ) { // do we have capital ships in the area? if (Ship_info[Ships[i].ship_info_index].flags & ( SIF_CRUISER | SIF_CAPITAL | SIF_SUPERCAP | SIF_CORVETTE | SIF_AWACS | SIF_GAS_MINER | SIF_FREIGHTER | SIF_TRANSPORT)) { capshipPresent = true; capIndexes.push_back(i); // ok.. what size class if (Ship_info[Ships[i].ship_info_index].flags & (SIF_CAPITAL | SIF_SUPERCAP)) { capship_counts[0]++; if (capship_spreads[0] < Objects[Ships[i].objnum].radius) capship_spreads[0] = Objects[Ships[i].objnum].radius; } else if (Ship_info[Ships[i].ship_info_index].flags & (SIF_CORVETTE)) { capship_counts[1]++; if (capship_spreads[1] < Objects[Ships[i].objnum].radius) capship_spreads[1] = Objects[Ships[i].objnum].radius; } else { capship_counts[2]++; if (capship_spreads[2] < Objects[Ships[i].objnum].radius) capship_spreads[2] = Objects[Ships[i].objnum].radius; } } // check for bigger radius for usage later /*if (!vm_vec_cmp(&rpos, &Player_obj->pos)) // want to make sure rpos isn't player pos - we can worry about it being largest object's later { rpos = Objects[Ships[i].objnum].pos; }*/ if (Objects[Ships[i].objnum].radius > radius) { rpos = Objects[Ships[i].objnum].pos; radius = Objects[Ships[i].objnum].radius; } if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) {// instantly turn the ship to match the direction player is looking //vm_vec_sub(&norm1, Navs[CurrentNav].GetPosition(), &Player_obj->pos); vm_vector_2_matrix(&Objects[Ships[i].objnum].orient, &norm1, NULL, NULL); } // snap wings into formation if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS && // only if using cinematics (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY) // only if in a wing && Autopilot_flight_leader != &Objects[Ships[i].objnum]) //only if not flight leader's object { ai_info *aip = &Ai_info[Ships[i].ai_index]; int wingnum = aip->wing, wing_index = get_wing_index(&Objects[Ships[i].objnum], wingnum); vec3d goal_point; object *leader_objp = get_wing_leader(wingnum); if (leader_objp != &Objects[Ships[i].objnum]) { // not leader.. get our position relative to leader get_absolute_wing_pos_autopilot(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT); } else { ai_clear_wing_goals(wingnum); j = 1+int( (float)floor(double(wcount-1)/2.0) ); switch (wcount % 2) { case 1: // back-left vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); //vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.fvec); vm_vec_normalize(&perp); vm_vec_scale(&perp, -166.0f*j); // 166m is supposedly the optimal range according to tolwyn vm_vec_add(&goal_point, &Autopilot_flight_leader->pos, &perp); break; default: //back-right case 0: vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); //vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.fvec); vm_vec_normalize(&perp); vm_vec_scale(&perp, 166.0f*j); vm_vec_add(&goal_point, &Autopilot_flight_leader->pos, &perp); break; } autopilot_wings[wingnum] = wcount; wcount++; } Objects[Ships[i].objnum].pos = goal_point; if (vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[i].objnum].pos) > distance) { distance = vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[i].objnum].pos); } } // lock primary and secondary weapons if ( LockWeaponsDuringAutopilot ) Ships[i].flags2 |= (SF2_PRIMARIES_LOCKED | SF2_SECONDARIES_LOCKED); // clear the ship goals and cap the waypoint speed ai_clear_ship_goals(&Ai_info[Ships[i].ai_index]); Ai_info[Ships[i].ai_index].waypoint_speed_cap = (int)speed_cap; // if they're not part of a wing set their goal if (Ships[i].wingnum == -1 || The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) { if (Navs[CurrentNav].flags & NP_WAYPOINT) { ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_WAYPOINTS_ONCE, 0, ((waypoint_list*)Navs[CurrentNav].target_obj)->get_name(), &Ai_info[Ships[i].ai_index] ); //fixup has to wait until after wing goals } else { ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_FLY_TO_SHIP, 0, ((ship*)Navs[CurrentNav].target_obj)->ship_name, &Ai_info[Ships[i].ai_index] ); } } } } // assign wing goals if (!(The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)) { for (i = 0; i < MAX_WINGS; i++) { if (Wings[i].flags & WF_NAV_CARRY ) { //ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip ); //ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_STAY_NEAR_SHIP, 0, target_shipname, wingnum ); //ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, target_shipname, wingnum ); //ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) ); ai_clear_wing_goals( i ); if (Navs[CurrentNav].flags & NP_WAYPOINT) { ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, ((waypoint_list*)Navs[CurrentNav].target_obj)->get_name(), i ); // "fix up" the goal for (j = 0; j < MAX_AI_GOALS; j++) { if (Wings[i].ai_goals[j].ai_mode == AI_GOAL_WAYPOINTS_ONCE || Wings[i].ai_goals[j].ai_mode == AIM_WAYPOINTS ) { autopilot_ai_waypoint_goal_fixup(&(Wings[i].ai_goals[j])); } } } else { ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_FLY_TO_SHIP, 0, ((ship*)Navs[CurrentNav].target_obj)->ship_name, i ); } } } } // fixup has to go down here because ships are assigned goals during wing goals as well for (i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum != -1) { if (Ships[i].flags2 & SF2_NAVPOINT_CARRY || (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY)) for (j = 0; j < MAX_AI_GOALS; j++) { if (Ai_info[Ships[i].ai_index].goals[j].ai_mode == AI_GOAL_WAYPOINTS_ONCE || Ai_info[Ships[i].ai_index].goals[j].ai_mode == AIM_WAYPOINTS) { autopilot_ai_waypoint_goal_fixup( &(Ai_info[Ships[i].ai_index].goals[j]) ); // formation fixup //ai_form_on_wing(&Objects[Ships[i].objnum], &Objects[Player_ship->objnum]); } } } } start_dist = DistanceTo(CurrentNav); // ----------------------------- setup cinematic ----------------------------- if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) { if (capshipPresent) { // position capships vec3d right, front, up, offset; for (SCP_vector<int>::iterator idx = capIndexes.begin(); idx != capIndexes.end(); ++idx) { vm_vec_add(&right, &Autopilot_flight_leader->orient.vec.rvec, &zero); vm_vec_add(&front, &Autopilot_flight_leader->orient.vec.fvec, &zero); vm_vec_add(&up, &Autopilot_flight_leader->orient.vec.uvec, &zero); vm_vec_add(&offset, &zero, &zero); if (Ship_info[Ships[*idx].ship_info_index].flags & (SIF_CAPITAL | SIF_SUPERCAP)) { //0 - below - three lines of position // front/back to zero vm_vec_add(&front, &zero, &zero); // position below vm_vec_scale(&up, capship_spreads[0]); // scale the up vector by the radius of the largest ship in this formation part switch (capship_placed[0] % 3) { case 1: // right vm_vec_scale(&right, capship_spreads[0]); break; case 2: // left vm_vec_scale(&right, -capship_spreads[0]); break; default: // straight case 0: vm_vec_add(&right, &zero, &zero); vm_vec_scale(&up, 1.5); // add an extra half-radius break; } // scale by row vm_vec_scale(&right, (1+((float)floor((float)capship_placed[0]/3)))); vm_vec_scale(&up, -(1+((float)floor((float)capship_placed[0]/3)))); capship_placed[0]++; } else if (Ship_info[Ships[*idx].ship_info_index].flags & SIF_CORVETTE) { //1 above - 3 lines of position // front/back to zero vm_vec_add(&front, &zero, &zero); // position below vm_vec_scale(&up, capship_spreads[1]); // scale the up vector by the radius of the largest ship in this formation part switch (capship_placed[1] % 3) { case 1: // right vm_vec_scale(&right, capship_spreads[1]); break; case 2: // left vm_vec_scale(&right, -capship_spreads[1]); break; default: // straight case 0: vm_vec_add(&right, &zero, &zero); vm_vec_scale(&up, 1.5); // add an extra half-radius break; } // scale by row vm_vec_scale(&right, (1+((float)floor((float)capship_placed[1]/3)))); vm_vec_scale(&up, (1+((float)floor((float)capship_placed[1]/3)))); // move ourselves up and out of the way of the smaller ships vm_vec_add(&perp, &Autopilot_flight_leader->orient.vec.uvec, &zero); vm_vec_scale(&perp, capship_spreads[2]); vm_vec_add(&up, &up, &perp); capship_placed[1]++; } else { //2 either side - 6 lines of position (right (dir, front, back), left (dir, front, back) ) // placing pattern: right, left, front right, front left, rear right, rear left // up/down to zero vm_vec_add(&up, &zero, &zero); switch (capship_placed[2] % 6) { case 5: // rear left vm_vec_scale(&right, -capship_spreads[2]); vm_vec_scale(&front, -capship_spreads[2]); break; case 4: // rear right vm_vec_scale(&right, capship_spreads[2]); vm_vec_scale(&front, -capship_spreads[2]); break; case 3: // front left vm_vec_scale(&right, -capship_spreads[2]); vm_vec_scale(&front, capship_spreads[2]); break; case 2: // front right vm_vec_scale(&right, capship_spreads[2]); vm_vec_scale(&front, capship_spreads[2]); break; case 1: // straight left vm_vec_scale(&right, 1.5); vm_vec_scale(&right, -capship_spreads[2]); vm_vec_add(&front, &zero, &zero); break; default: // straight right case 0: vm_vec_scale(&right, 1.5); vm_vec_scale(&right, capship_spreads[2]); vm_vec_add(&front, &zero, &zero); break; } // these ships seem to pack a little too tightly vm_vec_scale(&right, 2*(1+((float)floor((float)capship_placed[2]/3)))); vm_vec_scale(&front, 2*(1+((float)floor((float)capship_placed[2]/3)))); // move "out" by 166*(wcount-1) so we don't bump into fighters vm_vec_add(&perp, &Autopilot_flight_leader->orient.vec.rvec, &zero); vm_vec_scale(&perp, 166.0f*float(wcount-1)); if ( (capship_placed[2] % 2) == 0) vm_vec_add(&right, &right, &perp); else vm_vec_sub(&right, &right, &perp); capship_placed[2]++; } // integrate the up/down componant vm_vec_add(&offset, &offset, &up); //integrate the left/right componant vm_vec_add(&offset, &offset, &right); //integrate the left/right componant vm_vec_add(&offset, &offset, &front); // global scale the position by 50% //vm_vec_scale(&offset, 1.5); vm_vec_add(&Objects[Ships[*idx].objnum].pos, &Autopilot_flight_leader->pos, &offset); if (vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[*idx].objnum].pos) > distance) { distance = vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[*idx].objnum].pos); } } } ftemp = floor(Autopilot_flight_leader->phys_info.max_vel.xyz.z/speed_cap); if (ftemp >= 2.0f && ftemp < 4.0f) tc_factor = 2; else if (ftemp >= 4.0f && ftemp < 8.0f) tc_factor = 4; else if (ftemp >= 8.0f) tc_factor = 8; tpos = *Navs[CurrentNav].GetPosition(); // determine distance toward nav at which camera will be vm_vec_sub(&pos, &tpos, &Autopilot_flight_leader->pos); vm_vec_normalize(&pos); // pos is now a unit vector in the direction we will be moving the camera //norm1 = pos; vm_vec_scale(&pos, 5*speed_cap*tc_factor); // pos is now scaled by 5 times the speed (5 seconds ahead) vm_vec_add(&pos, &pos, &Autopilot_flight_leader->pos); // pos is now 5*speed cap in front of player ship switch (myrand()%24) // 8 different ways of getting perp points // 4 of which will not be used when capships are present (anything below, or straight above) { case 1: // down case 9: case 16: if (capship_placed[0] == 0) vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.uvec); else { // become up-left vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.uvec); vm_vec_sub(&perp, &perp, &Autopilot_flight_leader->orient.vec.rvec); } break; case 2: // up case 10: case 23: vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); if (capshipPresent) // become up-right vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.rvec); break; case 3: // left case 11: case 22: vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); break; case 4: // up-left case 12: case 21: vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; case 5: // up-right case 13: case 20: vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; case 6: // down-left case 14: case 19: vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); if (capship_placed[0] < 2) vm_vec_sub(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); else vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; case 7: // down-right case 15: case 18: vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); if (capship_placed[0] < 1) vm_vec_sub(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); else vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; default: case 0: // right case 8: case 17: perp = Autopilot_flight_leader->orient.vec.rvec; break; } vm_vec_normalize(&perp); //vm_vec_scale(&perp, 2*radius+distance); vm_vec_scale(&perp, Autopilot_flight_leader->radius+radius); // randomly scale up/down by up to 20% j = 20-myrand()%40; // [-20,20] vm_vec_scale(&perp, 1.0f+(float(j)/100.0f)); vm_vec_add(&cameraPos, &pos, &perp); if (capshipPresent) { vm_vec_normalize(&perp); // place it behind //vm_vec_copy_scale(&norm1, &Player_obj->orient.vec.fvec, -2*(Player_obj->radius+radius*(1.0f+(float(j)/100.0f)))); //vm_vec_add(&cameraTarget, &cameraTarget, &norm1); vm_vec_copy_scale(&cameraTarget,&perp, radius/5.0f); //vm_vec_scale(&cameraTarget, Player_obj->radius+radius*(1.0f+(float(j)/100.0f))); //vm_vec_add(&cameraTarget, &pos, &cameraTarget); //CameraSpeed = (radius+distance)/25; //vm_vec_add(&cameraTarget, &zero, &perp); //vm_vec_scale(&CameraVelocity, (radius+distance/100.f)); //vm_vec_scale(&CameraVelocity, 1.0f/float(NPS_TICKRATE*tc_factor)); } else { vm_vec_add(&cameraTarget, &zero, &zero); //CameraSpeed = 0; } //CameraMoving = false; EndAPCinematic = timestamp((10000*tc_factor)+NPS_TICKRATE); // 10 objective seconds before end of cinematic MoveCamera = timestamp((5500*tc_factor)+NPS_TICKRATE); camMovingTime = int(4.5*float(tc_factor)); set_time_compression((float)tc_factor); } return true; }
// ----------------------------------------------------------------------------------------------------------- //Simulate a physics object for this frame void do_physics_sim(dxxobject *obj) { objnum_t ignore_obj_list[MAX_IGNORE_OBJS]; unsigned n_ignore_objs; segnum_t iseg; int try_again; int fate=0; vms_vector frame_vec; //movement in this frame vms_vector new_pos,ipos; //position after this frame int count=0; objnum_t objnum; segnum_t WallHitSeg; int WallHitSide; fvi_info hit_info; fvi_query fq; vms_vector save_pos; segnum_t save_seg; fix drag; fix sim_time,old_sim_time; vms_vector start_pos; int obj_stopped=0; fix moved_time; //how long objected moved before hit something physics_info *pi; segnum_t orig_segnum = obj->segnum; int bounced=0; fix PhysTime = (FrameTime<F1_0/30?F1_0/30:FrameTime); Assert(obj->movement_type == MT_PHYSICS); #ifndef NDEBUG if (Dont_move_ai_objects) if (obj->control_type == CT_AI) return; #endif pi = &obj->mtype.phys_info; do_physics_sim_rot(obj); if (!(pi->velocity.x || pi->velocity.y || pi->velocity.z || pi->thrust.x || pi->thrust.y || pi->thrust.z)) return; objnum = obj-Objects; n_phys_segs = 0; /* As this engine was not designed for that high FPS as we intend, we use F1_0/30 max. for sim_time to ensure scaling and dot products stay accurate and reliable. The object position intended for this frame will be scaled down later, after the main collision-loop is done. This won't make collision results be equal in all FPS settings, but hopefully more accurate, the higher our FPS are. */ sim_time = PhysTime; //FrameTime; //debug_obj = obj; #ifdef EXTRA_DEBUG //check for correct object segment if(!get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask == 0) { if (!update_object_seg(obj)) { if (!(Game_mode & GM_MULTI)) Int3(); compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } } #endif start_pos = obj->pos; n_ignore_objs = 0; Assert(obj->mtype.phys_info.brakes==0); //brakes not used anymore? //if uses thrust, cannot have zero drag Assert(!(obj->mtype.phys_info.flags&PF_USES_THRUST) || obj->mtype.phys_info.drag!=0); //do thrust & drag // NOTE: this always must be dependent on FrameTime, if sim_time differs! if ((drag = obj->mtype.phys_info.drag) != 0) { int count; vms_vector accel; fix r,k,have_accel; count = FrameTime / FT; r = FrameTime % FT; k = fixdiv(r,FT); if (obj->mtype.phys_info.flags & PF_USES_THRUST) { vm_vec_copy_scale(&accel,&obj->mtype.phys_info.thrust,fixdiv(f1_0,obj->mtype.phys_info.mass)); have_accel = (accel.x || accel.y || accel.z); while (count--) { if (have_accel) vm_vec_add2(&obj->mtype.phys_info.velocity,&accel); vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-drag); } //do linear scale on remaining bit of time vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&accel,k); if (drag) vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-fixmul(k,drag)); } else if (drag) { fix total_drag=f1_0; while (count--) total_drag = fixmul(total_drag,f1_0-drag); //do linear scale on remaining bit of time total_drag = fixmul(total_drag,f1_0-fixmul(k,drag)); vm_vec_scale(&obj->mtype.phys_info.velocity,total_drag); } } do { try_again = 0; //Move the object vm_vec_copy_scale(&frame_vec, &obj->mtype.phys_info.velocity, sim_time); if ( (frame_vec.x==0) && (frame_vec.y==0) && (frame_vec.z==0) ) break; count++; // If retry count is getting large, then we are trying to do something stupid. if (count > 8) break; // in original code this was 3 for all non-player objects. still leave us some limit in case fvi goes apeshit. vm_vec_add(&new_pos,&obj->pos,&frame_vec); ignore_obj_list[n_ignore_objs] = object_none; fq.p0 = &obj->pos; fq.startseg = obj->segnum; fq.p1 = &new_pos; fq.rad = obj->size; fq.thisobjnum = objnum; fq.ignore_obj_list = ignore_obj_list; fq.flags = FQ_CHECK_OBJS; if (obj->type == OBJ_WEAPON) fq.flags |= FQ_TRANSPOINT; if (obj->type == OBJ_PLAYER) fq.flags |= FQ_GET_SEGLIST; fate = find_vector_intersection(&fq,&hit_info); // Matt: Mike's hack. if (fate == HIT_OBJECT) { dxxobject *objp = &Objects[hit_info.hit_object]; if (((objp->type == OBJ_WEAPON) && ((objp->id == PROXIMITY_ID) || (objp->id == SUPERPROX_ID))) || objp->type == OBJ_POWERUP) // do not increase count for powerups since they *should* not change our movement count--; } #ifndef NDEBUG if (fate == HIT_BAD_P0) { Int3(); } #endif if (obj->type == OBJ_PLAYER) { int i; if (n_phys_segs && phys_seglist[n_phys_segs-1]==hit_info.seglist[0]) n_phys_segs--; for (i=0;(i<hit_info.n_segs) && (n_phys_segs<MAX_FVI_SEGS-1); ) phys_seglist[n_phys_segs++] = hit_info.seglist[i++]; } ipos = hit_info.hit_pnt; iseg = hit_info.hit_seg; WallHitSide = hit_info.hit_side; WallHitSeg = hit_info.hit_side_seg; if (iseg==segment_none) { //some sort of horrible error if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; break; } Assert(!((fate==HIT_WALL) && ((WallHitSeg == segment_none) || (WallHitSeg > Highest_segment_index)))); //if(!get_seg_masks(&hit_info.hit_pnt, hit_info.hit_seg, 0, __FILE__, __LINE__).centermask == 0) // Int3(); save_pos = obj->pos; //save the object's position save_seg = obj->segnum; // update object's position and segment number obj->pos = ipos; if ( iseg != obj->segnum ) obj_relink(objnum, iseg ); //if start point not in segment, move object to center of segment if (get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask !=0 ) { segnum_t n; if ((n=find_object_seg(obj))==segment_none) { //Int3(); if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=segment_none) { obj->pos = obj->last_pos; obj_relink(objnum, n ); } else { compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; } return; } //calulate new sim time { //vms_vector moved_vec; vms_vector moved_vec_n; fix attempted_dist,actual_dist; old_sim_time = sim_time; actual_dist = vm_vec_normalized_dir(&moved_vec_n,&obj->pos,&save_pos); if (fate==HIT_WALL && vm_vec_dot(&moved_vec_n,&frame_vec) < 0) { //moved backwards //don't change position or sim_time obj->pos = save_pos; //iseg = obj->segnum; //don't change segment obj_relink(objnum, save_seg ); moved_time = 0; } else { attempted_dist = vm_vec_mag(&frame_vec); sim_time = fixmuldiv(sim_time,attempted_dist-actual_dist,attempted_dist); moved_time = old_sim_time - sim_time; if (sim_time < 0 || sim_time>old_sim_time) { sim_time = old_sim_time; //WHY DOES THIS HAPPEN?? moved_time = 0; } } } switch( fate ) { case HIT_WALL: { vms_vector moved_v; //@@fix total_d,moved_d; fix hit_speed=0,wall_part=0; // Find hit speed vm_vec_sub(&moved_v,&obj->pos,&save_pos); wall_part = vm_vec_dot(&moved_v,&hit_info.hit_wallnorm); if ((wall_part != 0 && moved_time>0 && (hit_speed=-fixdiv(wall_part,moved_time))>0) || obj->type == OBJ_WEAPON || obj->type == OBJ_DEBRIS) collide_object_with_wall( obj, hit_speed, WallHitSeg, WallHitSide, &hit_info.hit_pnt ); if (obj->type == OBJ_PLAYER) scrape_player_on_wall(obj, WallHitSeg, WallHitSide, &hit_info.hit_pnt ); Assert( WallHitSeg != segment_none && WallHitSeg <= Highest_segment_index ); Assert( WallHitSide > -1 ); if ( !(obj->flags&OF_SHOULD_BE_DEAD) ) { int forcefield_bounce; //bounce off a forcefield Assert(cheats.bouncyfire || !(obj->mtype.phys_info.flags & PF_STICK && obj->mtype.phys_info.flags & PF_BOUNCE)); //can't be bounce and stick forcefield_bounce = (TmapInfo[Segments[WallHitSeg].sides[WallHitSide].tmap_num].flags & TMI_FORCE_FIELD); if (!forcefield_bounce && (obj->mtype.phys_info.flags & PF_STICK)) { //stop moving add_stuck_object(obj, WallHitSeg, WallHitSide); vm_vec_zero(&obj->mtype.phys_info.velocity); obj_stopped = 1; try_again = 0; } else { // Slide object along wall int check_vel=0; wall_part = vm_vec_dot(&hit_info.hit_wallnorm,&obj->mtype.phys_info.velocity); // if wall_part, make sure the value is sane enough to get usable velocity computed if (wall_part < 0 && wall_part > -f1_0) wall_part = -f1_0; if (wall_part > 0 && wall_part < f1_0) wall_part = f1_0; if (forcefield_bounce || (obj->mtype.phys_info.flags & PF_BOUNCE)) { //bounce off wall wall_part *= 2; //Subtract out wall part twice to achieve bounce if (forcefield_bounce) { check_vel = 1; //check for max velocity if (obj->type == OBJ_PLAYER) wall_part *= 2; //player bounce twice as much } if ( obj->mtype.phys_info.flags & PF_BOUNCES_TWICE) { Assert(obj->mtype.phys_info.flags & PF_BOUNCE); if (obj->mtype.phys_info.flags & PF_BOUNCED_ONCE) obj->mtype.phys_info.flags &= ~(PF_BOUNCE+PF_BOUNCED_ONCE+PF_BOUNCES_TWICE); else obj->mtype.phys_info.flags |= PF_BOUNCED_ONCE; } bounced = 1; //this object bounced } vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&hit_info.hit_wallnorm,-wall_part); if (check_vel) { fix vel = vm_vec_mag_quick(&obj->mtype.phys_info.velocity); if (vel > MAX_OBJECT_VEL) vm_vec_scale(&obj->mtype.phys_info.velocity,fixdiv(MAX_OBJECT_VEL,vel)); } if (bounced && obj->type == OBJ_WEAPON) vm_vector_2_matrix(&obj->orient,&obj->mtype.phys_info.velocity,&obj->orient.uvec,NULL); try_again = 1; } } break; } case HIT_OBJECT: { vms_vector old_vel; // Mark the hit object so that on a retry the fvi code // ignores this object. Assert(hit_info.hit_object != object_none); // Calculcate the hit point between the two objects. { vms_vector *ppos0, *ppos1, pos_hit; fix size0, size1; ppos0 = &Objects[hit_info.hit_object].pos; ppos1 = &obj->pos; size0 = Objects[hit_info.hit_object].size; size1 = obj->size; Assert(size0+size1 != 0); // Error, both sizes are 0, so how did they collide, anyway?!? //vm_vec_scale(vm_vec_sub(&pos_hit, ppos1, ppos0), fixdiv(size0, size0 + size1)); //vm_vec_add2(&pos_hit, ppos0); vm_vec_sub(&pos_hit, ppos1, ppos0); vm_vec_scale_add(&pos_hit,ppos0,&pos_hit,fixdiv(size0, size0 + size1)); old_vel = obj->mtype.phys_info.velocity; collide_two_objects( obj, &Objects[hit_info.hit_object], &pos_hit); } // Let object continue its movement if ( !(obj->flags&OF_SHOULD_BE_DEAD) ) { //obj->pos = save_pos; if (obj->mtype.phys_info.flags&PF_PERSISTENT || (old_vel.x == obj->mtype.phys_info.velocity.x && old_vel.y == obj->mtype.phys_info.velocity.y && old_vel.z == obj->mtype.phys_info.velocity.z)) { //if (Objects[hit_info.hit_object].type == OBJ_POWERUP) ignore_obj_list[n_ignore_objs++] = hit_info.hit_object; try_again = 1; } } break; } case HIT_NONE: break; #ifndef NDEBUG case HIT_BAD_P0: Int3(); // Unexpected collision type: start point not in specified segment. break; default: // Unknown collision type returned from find_vector_intersection!! Int3(); break; #endif } } while ( try_again ); // Pass retry count info to AI. if (obj->control_type == CT_AI) { if (count > 0) { Ai_local_info[objnum].retry_count = count-1; #ifndef NDEBUG Total_retries += count-1; Total_sims++; #endif } } // As sim_time may not base on FrameTime, scale actual object position to get accurate movement if (PhysTime/FrameTime > 0) { vms_vector md; vm_vec_sub(&md, &obj->pos, &start_pos); vm_vec_scale(&md, F1_0/((float)PhysTime/FrameTime)); vm_vec_add(&obj->pos,&start_pos, &md); //check for and update correct object segment if(!get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask == 0) { if (!update_object_seg(obj)) { if (!(Game_mode & GM_MULTI)) Int3(); compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } } } // After collision with objects and walls, set velocity from actual movement if (!obj_stopped && !bounced && ((obj->type == OBJ_PLAYER) || (obj->type == OBJ_ROBOT) || (obj->type == OBJ_DEBRIS)) && ((fate == HIT_WALL) || (fate == HIT_OBJECT) || (fate == HIT_BAD_P0)) ) { vms_vector moved_vec; vm_vec_sub(&moved_vec,&obj->pos,&start_pos); vm_vec_copy_scale(&obj->mtype.phys_info.velocity,&moved_vec,fixdiv(f1_0,FrameTime)); } fix_illegal_wall_intersection(obj, &start_pos); //Assert(check_point_in_seg(&obj->pos,obj->segnum,0).centermask==0); //if (obj->control_type == CT_FLYING) if (obj->mtype.phys_info.flags & PF_LEVELLING) do_physics_align_object( obj ); //hack to keep player from going through closed doors if (obj->type==OBJ_PLAYER && obj->segnum!=orig_segnum && (!cheats.ghostphysics) ) { int sidenum; sidenum = find_connect_side(&Segments[obj->segnum],&Segments[orig_segnum]); if (sidenum != -1) { if (! (WALL_IS_DOORWAY(&Segments[orig_segnum],sidenum) & WID_FLY_FLAG)) { side_t *s; int vertnum,num_faces,i; fix dist; int vertex_list[6]; //bump object back s = &Segments[orig_segnum].sides[sidenum]; if (orig_segnum==segment_none) Error("orig_segnum == -1 in physics"); create_abs_vertex_lists(&num_faces, vertex_list, orig_segnum, sidenum, __FILE__, __LINE__); //let's pretend this wall is not triangulated vertnum = vertex_list[0]; for (i=1;i<4;i++) if (vertex_list[i] < vertnum) vertnum = vertex_list[i]; #ifdef COMPACT_SEGS { vms_vector _vn; get_side_normal(&Segments[orig_segnum], sidenum, 0, &_vn ); dist = vm_dist_to_plane(&start_pos, &_vn, &Vertices[vertnum]); vm_vec_scale_add(&obj->pos,&start_pos,&_vn,obj->size-dist); } #else dist = vm_dist_to_plane(&start_pos, &s->normals[0], &Vertices[vertnum]); vm_vec_scale_add(&obj->pos,&start_pos,&s->normals[0],obj->size-dist); #endif update_object_seg(obj); } } } //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL #ifndef NDEBUG //if end point not in segment, move object to last pos, or segment center if (get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask != 0) { if (find_object_seg(obj)==segment_none) { segnum_t n; //Int3(); if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=segment_none) { obj->pos = obj->last_pos; obj_relink(objnum, n ); } else { compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; } } //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL #endif }
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; }
// ---------------------------------------------------------------------------------------------------------- void robotmaker_proc( FuelCenter * robotcen ) { fix dist_to_player; vms_vector cur_object_loc; //, direction; int matcen_num, segnum, objnum; object *obj; fix top_time; vms_vector direction; if (robotcen->Enabled == 0) return; if (robotcen->Disable_time > 0) { robotcen->Disable_time -= FrameTime; if (robotcen->Disable_time <= 0) { robotcen->Enabled = 0; } } // No robot making in multiplayer mode. #ifdef NETWORK #ifndef SHAREWARE if ((Game_mode & GM_MULTI) && (!(Game_mode & GM_MULTI_ROBOTS) || !multi_i_am_master())) return; #else if (Game_mode & GM_MULTI) return; #endif #endif // Wait until transmorgafier has capacity to make a robot... if ( robotcen->Capacity <= 0 ) { return; } matcen_num = Segment2s[robotcen->segnum].matcen_num; if ( matcen_num == -1 ) { return; } if (RobotCenters[matcen_num].robot_flags[0]==0 && RobotCenters[matcen_num].robot_flags[1]==0) { return; } // Wait until we have a free slot for this puppy... // <<<<<<<<<<<<<<<< Num robots in mine >>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<< Max robots in mine >>>>>>>>>>>>>>> if ( (Players[Player_num].num_robots_level - Players[Player_num].num_kills_level) >= (Gamesave_num_org_robots + Num_extry_robots ) ) { return; } robotcen->Timer += FrameTime; switch( robotcen->Flag ) { case 0: // Wait until next robot can generate if (Game_mode & GM_MULTI) { top_time = ROBOT_GEN_TIME; } else { dist_to_player = vm_vec_dist_quick( &ConsoleObject->pos, &robotcen->Center ); top_time = dist_to_player/64 + d_rand() * 2 + F1_0*2; if ( top_time > ROBOT_GEN_TIME ) top_time = ROBOT_GEN_TIME + d_rand(); if ( top_time < F1_0*2 ) top_time = F1_0*3/2 + d_rand()*2; } if (robotcen->Timer > top_time ) { int count=0; int i, my_station_num = robotcen-Station; object *obj; // Make sure this robotmaker hasn't put out its max without having any of them killed. for (i=0; i<=Highest_object_index; i++) if (Objects[i].type == OBJ_ROBOT) if ((Objects[i].matcen_creator^0x80) == my_station_num) count++; if (count > Difficulty_level + 3) { robotcen->Timer /= 2; return; } // Whack on any robot or player in the matcen segment. count=0; segnum = robotcen->segnum; for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next) { count++; if ( count > MAX_OBJECTS ) { Int3(); return; } if (Objects[objnum].type==OBJ_ROBOT) { collide_robot_and_materialization_center(&Objects[objnum]); robotcen->Timer = top_time/2; return; } else if (Objects[objnum].type==OBJ_PLAYER ) { collide_player_and_materialization_center(&Objects[objnum]); robotcen->Timer = top_time/2; return; } } compute_segment_center(&cur_object_loc, &Segments[robotcen->segnum]); // HACK!!! The 10 under here should be something equal to the 1/2 the size of the segment. obj = object_create_explosion(robotcen->segnum, &cur_object_loc, i2f(10), VCLIP_MORPHING_ROBOT ); if (obj) extract_orient_from_segment(&obj->orient,&Segments[robotcen->segnum]); if ( Vclip[VCLIP_MORPHING_ROBOT].sound_num > -1 ) { digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, robotcen->segnum, 0, &cur_object_loc, 0, F1_0 ); } robotcen->Flag = 1; robotcen->Timer = 0; } break; case 1: // Wait until 1/2 second after VCLIP started. if (robotcen->Timer > (Vclip[VCLIP_MORPHING_ROBOT].play_time/2) ) { robotcen->Capacity -= EnergyToCreateOneRobot; robotcen->Flag = 0; robotcen->Timer = 0; compute_segment_center(&cur_object_loc, &Segments[robotcen->segnum]); // If this is the first materialization, set to valid robot. if (RobotCenters[matcen_num].robot_flags[0] != 0 || RobotCenters[matcen_num].robot_flags[1] != 0) { int type; uint flags; sbyte legal_types[64]; // 64 bits, the width of robot_flags[]. int num_types, robot_index, i; num_types = 0; for (i=0;i<2;i++) { robot_index = i*32; flags = RobotCenters[matcen_num].robot_flags[i]; while (flags) { if (flags & 1) legal_types[num_types++] = robot_index; flags >>= 1; robot_index++; } } if (num_types == 1) type = legal_types[0]; else type = legal_types[(d_rand() * num_types) / 32768]; obj = create_morph_robot(&Segments[robotcen->segnum], &cur_object_loc, type ); if (obj != NULL) { #ifndef SHAREWARE #ifdef NETWORK if (Game_mode & GM_MULTI) multi_send_create_robot(robotcen-Station, obj-Objects, type); #endif #endif obj->matcen_creator = (robotcen-Station) | 0x80; // Make object faces player... vm_vec_sub( &direction, &ConsoleObject->pos,&obj->pos ); vm_vector_2_matrix( &obj->orient, &direction, &obj->orient.uvec, NULL); morph_start( obj ); //robotcen->last_created_obj = obj; //robotcen->last_created_sig = robotcen->last_created_obj->signature; } } }
// ----------------------------------------------------------------------------------------------------------- //Simulate a physics object for this frame void do_physics_sim(object *obj) { int ignore_obj_list[MAX_IGNORE_OBJS],n_ignore_objs; int iseg; int try_again; int fate; vms_vector frame_vec; //movement in this frame vms_vector new_pos,ipos; //position after this frame int count=0; int objnum; int WallHitSeg, WallHitSide; fvi_info hit_info; fvi_query fq; vms_vector save_pos; int save_seg; fix drag; fix sim_time,old_sim_time; vms_vector start_pos; int obj_stopped=0; fix moved_time; //how long objected moved before hit something vms_vector save_p0,save_p1; physics_info *pi; int orig_segnum = obj->segnum; int bounced=0; Assert(obj->type != OBJ_NONE); Assert(obj->movement_type == MT_PHYSICS); #ifndef NDEBUG if (Dont_move_ai_objects) if (obj->control_type == CT_AI) return; #endif pi = &obj->mtype.phys_info; do_physics_sim_rot(obj); if (!(pi->velocity.x || pi->velocity.y || pi->velocity.z || pi->thrust.x || pi->thrust.y || pi->thrust.z)) return; objnum = obj-Objects; n_phys_segs = 0; disable_new_fvi_stuff = (obj->type != OBJ_PLAYER); sim_time = FrameTime; //debug_obj = obj; #ifdef EXTRA_DEBUG if (obj == debug_obj) { printf("object %d:\n start pos = %x %x %x\n",objnum,XYZ(&obj->pos)); printf(" thrust = %x %x %x\n",XYZ(&obj->mtype.phys_info.thrust)); printf(" sim_time = %x\n",sim_time); } //check for correct object segment if(!get_seg_masks(&obj->pos,obj->segnum,0).centermask==0) { #ifndef NDEBUG mprintf((0,"Warning: object %d not in given seg!\n",objnum)); #endif //Int3(); Removed by Rob 10/5/94 if (!update_object_seg(obj)) { #ifndef NDEBUG mprintf((0,"Warning: can't find seg for object %d - moving\n",objnum)); #endif if (!(Game_mode & GM_MULTI)) Int3(); compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } } #endif start_pos = obj->pos; n_ignore_objs = 0; Assert(obj->mtype.phys_info.brakes==0); //brakes not used anymore? //if uses thrust, cannot have zero drag Assert(!(obj->mtype.phys_info.flags&PF_USES_THRUST) || obj->mtype.phys_info.drag!=0); //mprintf((0,"thrust=%x speed=%x\n",vm_vec_mag(&obj->mtype.phys_info.thrust),vm_vec_mag(&obj->mtype.phys_info.velocity))); //do thrust & drag if ((drag = obj->mtype.phys_info.drag) != 0) { int count; vms_vector accel; fix r,k; count = sim_time / FT; r = sim_time % FT; k = fixdiv(r,FT); if (obj->mtype.phys_info.flags & PF_USES_THRUST) { vm_vec_copy_scale(&accel,&obj->mtype.phys_info.thrust,fixdiv(f1_0,obj->mtype.phys_info.mass)); while (count--) { vm_vec_add2(&obj->mtype.phys_info.velocity,&accel); vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-drag); } //do linear scale on remaining bit of time vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&accel,k); vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-fixmul(k,drag)); } else { fix total_drag=f1_0; while (count--) total_drag = fixmul(total_drag,f1_0-drag); //do linear scale on remaining bit of time total_drag = fixmul(total_drag,f1_0-fixmul(k,drag)); vm_vec_scale(&obj->mtype.phys_info.velocity,total_drag); } } #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" velocity = %x %x %x\n",XYZ(&obj->mtype.phys_info.velocity)); #endif do { try_again = 0; //Move the object vm_vec_copy_scale(&frame_vec, &obj->mtype.phys_info.velocity, sim_time); #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" pass %d, frame_vec = %x %x %x\n",count,XYZ(&frame_vec)); #endif if ( (frame_vec.x==0) && (frame_vec.y==0) && (frame_vec.z==0) ) break; count++; // If retry count is getting large, then we are trying to do something stupid. if ( count > 3) { if (obj->type == OBJ_PLAYER) { if (count > 8) break; } else break; } vm_vec_add(&new_pos,&obj->pos,&frame_vec); #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" desired_pos = %x %x %x\n",XYZ(&new_pos)); #endif ignore_obj_list[n_ignore_objs] = -1; #ifdef EXTRA_DEBUG if (obj == debug_obj) { printf(" FVI parms: p0 = %8x %8x %8x, segnum=%x, size=%x\n",XYZ(&obj->pos),obj->segnum,obj->size); printf(" p1 = %8x %8x %8x\n",XYZ(&new_pos)); } #endif fq.p0 = &obj->pos; fq.startseg = obj->segnum; fq.p1 = &new_pos; fq.rad = obj->size; fq.thisobjnum = objnum; fq.ignore_obj_list = ignore_obj_list; fq.flags = FQ_CHECK_OBJS; if (obj->type == OBJ_WEAPON) fq.flags |= FQ_TRANSPOINT; if (obj->type == OBJ_PLAYER) fq.flags |= FQ_GET_SEGLIST; //@@ if (get_seg_masks(&obj->pos,obj->segnum,0).centermask!=0) //@@ Int3(); save_p0 = *fq.p0; save_p1 = *fq.p1; fate = find_vector_intersection(&fq,&hit_info); // Matt: Mike's hack. if (fate == HIT_OBJECT) { object *objp = &Objects[hit_info.hit_object]; if ((objp->type == OBJ_WEAPON) && ((objp->id == PROXIMITY_ID) || (objp->id == SUPERPROX_ID))) count--; } #ifndef NDEBUG if (fate == HIT_BAD_P0) { mprintf((0,"Warning: Bad p0 in physics! Object = %i, type = %i [%s]\n", obj-Objects, obj->type, Object_type_names[obj->type])); Int3(); } #endif if (obj->type == OBJ_PLAYER) { int i; if (n_phys_segs && phys_seglist[n_phys_segs-1]==hit_info.seglist[0]) n_phys_segs--; for (i=0;(i<hit_info.n_segs) && (n_phys_segs<MAX_FVI_SEGS-1); ) phys_seglist[n_phys_segs++] = hit_info.seglist[i++]; } #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" fate = %d, hit_pnt = %8x %8x %8x\n",fate,XYZ(&hit_info.hit_pnt));; #endif ipos = hit_info.hit_pnt; iseg = hit_info.hit_seg; WallHitSide = hit_info.hit_side; WallHitSeg = hit_info.hit_side_seg; if (iseg==-1) { //some sort of horrible error #ifndef NDEBUG mprintf((1,"iseg==-1 in physics! Object = %i, type = %i (%s)\n", obj-Objects, obj->type, Object_type_names[obj->type])); #endif //Int3(); //compute_segment_center(&ipos,&Segments[obj->segnum]); //ipos.x += objnum; //iseg = obj->segnum; //fate = HIT_NONE; if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; break; } Assert(!((fate==HIT_WALL) && ((WallHitSeg == -1) || (WallHitSeg > Highest_segment_index)))); //if(!get_seg_masks(&hit_info.hit_pnt,hit_info.hit_seg,0).centermask==0) // Int3(); save_pos = obj->pos; //save the object's position save_seg = obj->segnum; // update object's position and segment number obj->pos = ipos; #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" new pos = %x %x %x\n",XYZ(&obj->pos)); #endif if ( iseg != obj->segnum ) obj_relink(objnum, iseg ); //if start point not in segment, move object to center of segment if (get_seg_masks(&obj->pos,obj->segnum,0).centermask!=0) { int n; if ((n=find_object_seg(obj))==-1) { //Int3(); if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=-1) { obj->pos = obj->last_pos; obj_relink(objnum, n ); } else { compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; } return; } //calulate new sim time { //vms_vector moved_vec; vms_vector moved_vec_n; fix attempted_dist,actual_dist; old_sim_time = sim_time; actual_dist = vm_vec_normalized_dir(&moved_vec_n,&obj->pos,&save_pos); if (fate==HIT_WALL && vm_vec_dot(&moved_vec_n,&frame_vec) < 0) { //moved backwards //don't change position or sim_time //******* mprintf((0,"Obj %d moved backwards\n",obj-Objects)); #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" Warning: moved backwards!\n"); #endif obj->pos = save_pos; //iseg = obj->segnum; //don't change segment obj_relink(objnum, save_seg ); moved_time = 0; } else { //if (obj == debug_obj) // printf(" moved_vec = %x %x %x\n",XYZ(&moved_vec)); attempted_dist = vm_vec_mag(&frame_vec); sim_time = fixmuldiv(sim_time,attempted_dist-actual_dist,attempted_dist); moved_time = old_sim_time - sim_time; if (sim_time < 0 || sim_time>old_sim_time) { #ifndef NDEBUG mprintf((0,"Bogus sim_time = %x, old = %x\n",sim_time,old_sim_time)); if (obj == debug_obj) printf(" Bogus sim_time = %x, old = %x, attempted_dist = %x, actual_dist = %x\n",sim_time,old_sim_time,attempted_dist,actual_dist); //Int3(); Removed by Rob #endif sim_time = old_sim_time; //WHY DOES THIS HAPPEN?? moved_time = 0; } } #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" new sim_time = %x\n",sim_time); #endif } switch( fate ) { case HIT_WALL: { vms_vector moved_v; //@@fix total_d,moved_d; fix hit_speed,wall_part; // Find hit speed vm_vec_sub(&moved_v,&obj->pos,&save_pos); wall_part = vm_vec_dot(&moved_v,&hit_info.hit_wallnorm); if (wall_part != 0 && moved_time>0 && (hit_speed=-fixdiv(wall_part,moved_time))>0) collide_object_with_wall( obj, hit_speed, WallHitSeg, WallHitSide, &hit_info.hit_pnt ); else scrape_object_on_wall(obj, WallHitSeg, WallHitSide, &hit_info.hit_pnt ); Assert( WallHitSeg > -1 ); Assert( WallHitSide > -1 ); if ( !(obj->flags&OF_SHOULD_BE_DEAD) ) { int forcefield_bounce; //bounce off a forcefield Assert(BounceCheat || !(obj->mtype.phys_info.flags & PF_STICK && obj->mtype.phys_info.flags & PF_BOUNCE)); //can't be bounce and stick forcefield_bounce = (TmapInfo[Segments[WallHitSeg].sides[WallHitSide].tmap_num].flags & TMI_FORCE_FIELD); if (!forcefield_bounce && (obj->mtype.phys_info.flags & PF_STICK)) { //stop moving // mprintf((0, "Object %i stuck at %i:%i\n", obj-Objects, WallHitSeg, WallHitSide)); add_stuck_object(obj, WallHitSeg, WallHitSide); vm_vec_zero(&obj->mtype.phys_info.velocity); obj_stopped = 1; try_again = 0; } else { // Slide object along wall int check_vel=0; //We're constrained by wall, so subtract wall part from //velocity vector wall_part = vm_vec_dot(&hit_info.hit_wallnorm,&obj->mtype.phys_info.velocity); // mprintf((0, "%d", f2i(vm_vec_mag(&hit_info.hit_wallnorm)) )); if (forcefield_bounce || (obj->mtype.phys_info.flags & PF_BOUNCE)) { //bounce off wall wall_part *= 2; //Subtract out wall part twice to achieve bounce if (forcefield_bounce) { check_vel = 1; //check for max velocity if (obj->type == OBJ_PLAYER) wall_part *= 2; //player bounce twice as much } if ( obj->mtype.phys_info.flags & PF_BOUNCES_TWICE) { Assert(obj->mtype.phys_info.flags & PF_BOUNCE); if (obj->mtype.phys_info.flags & PF_BOUNCED_ONCE) obj->mtype.phys_info.flags &= ~(PF_BOUNCE+PF_BOUNCED_ONCE+PF_BOUNCES_TWICE); else obj->mtype.phys_info.flags |= PF_BOUNCED_ONCE; } bounced = 1; //this object bounced } vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&hit_info.hit_wallnorm,-wall_part); // mprintf((0, "Velocity at bounce time = %d\n", f2i(vm_vec_mag(&obj->mtype.phys_info.velocity)))); //if (obj==ConsoleObject) // mprintf((0,"player vel = %x\n",vm_vec_mag_quick(&obj->mtype.phys_info.velocity))); if (check_vel) { fix vel = vm_vec_mag_quick(&obj->mtype.phys_info.velocity); if (vel > MAX_OBJECT_VEL) vm_vec_scale(&obj->mtype.phys_info.velocity,fixdiv(MAX_OBJECT_VEL,vel)); } if (bounced && obj->type == OBJ_WEAPON) vm_vector_2_matrix(&obj->orient,&obj->mtype.phys_info.velocity,&obj->orient.uvec,NULL); #ifdef EXTRA_DEBUG if (obj == debug_obj) { printf(" sliding - wall_norm %x %x %x %x\n",wall_part,XYZ(&hit_info.hit_wallnorm)); printf(" wall_part %x, new velocity = %x %x %x\n",wall_part,XYZ(&obj->mtype.phys_info.velocity)); } #endif try_again = 1; } } break; } case HIT_OBJECT: { vms_vector old_vel; // Mark the hit object so that on a retry the fvi code // ignores this object. Assert(hit_info.hit_object != -1); // Calculcate the hit point between the two objects. { vms_vector *ppos0, *ppos1, pos_hit; fix size0, size1; ppos0 = &Objects[hit_info.hit_object].pos; ppos1 = &obj->pos; size0 = Objects[hit_info.hit_object].size; size1 = obj->size; Assert(size0+size1 != 0); // Error, both sizes are 0, so how did they collide, anyway?!? //vm_vec_scale(vm_vec_sub(&pos_hit, ppos1, ppos0), fixdiv(size0, size0 + size1)); //vm_vec_add2(&pos_hit, ppos0); vm_vec_sub(&pos_hit, ppos1, ppos0); vm_vec_scale_add(&pos_hit,ppos0,&pos_hit,fixdiv(size0, size0 + size1)); old_vel = obj->mtype.phys_info.velocity; collide_two_objects( obj, &Objects[hit_info.hit_object], &pos_hit); } // Let object continue its movement if ( !(obj->flags&OF_SHOULD_BE_DEAD) ) { //obj->pos = save_pos; if (obj->mtype.phys_info.flags&PF_PERSISTENT || (old_vel.x == obj->mtype.phys_info.velocity.x && old_vel.y == obj->mtype.phys_info.velocity.y && old_vel.z == obj->mtype.phys_info.velocity.z)) { //if (Objects[hit_info.hit_object].type == OBJ_POWERUP) ignore_obj_list[n_ignore_objs++] = hit_info.hit_object; try_again = 1; } } break; } case HIT_NONE: #ifdef TACTILE if (TactileStick && obj==ConsoleObject && !(FrameCount & 15)) Tactile_Xvibrate_clear (); #endif break; #ifndef NDEBUG case HIT_BAD_P0: Int3(); // Unexpected collision type: start point not in specified segment. mprintf((0,"Warning: Bad p0 in physics!!!\n")); break; default: // Unknown collision type returned from find_vector_intersection!! Int3(); break; #endif } } while ( try_again ); // Pass retry count info to AI. if (obj->control_type == CT_AI) { if (count > 0) { Ai_local_info[objnum].retry_count = count-1; #ifndef NDEBUG Total_retries += count-1; Total_sims++; #endif } } //I'm not sure why we do this. I wish there were a comment that //explained it. I think maybe it only needs to be done if the object //is sliding, but I don't know if (!obj_stopped && !bounced) { //Set velocity from actual movement vms_vector moved_vec; vm_vec_sub(&moved_vec,&obj->pos,&start_pos); vm_vec_copy_scale(&obj->mtype.phys_info.velocity,&moved_vec,fixdiv(f1_0,FrameTime)); #ifdef BUMP_HACK if (obj==ConsoleObject && (obj->mtype.phys_info.velocity.x==0 && obj->mtype.phys_info.velocity.y==0 && obj->mtype.phys_info.velocity.z==0) && !(obj->mtype.phys_info.thrust.x==0 && obj->mtype.phys_info.thrust.y==0 && obj->mtype.phys_info.thrust.z==0)) { vms_vector center,bump_vec; //bump player a little towards center of segment to unstick compute_segment_center(¢er,&Segments[obj->segnum]); vm_vec_normalized_dir_quick(&bump_vec,¢er,&obj->pos); //don't bump player toward center of reactor segment if (Segment2s[obj->segnum].special == SEGMENT_IS_CONTROLCEN) vm_vec_negate(&bump_vec); vm_vec_scale_add2(&obj->pos,&bump_vec,obj->size/5); //if moving away from seg, might move out of seg, so update if (Segment2s[obj->segnum].special == SEGMENT_IS_CONTROLCEN) update_object_seg(obj); } #endif } //Assert(check_point_in_seg(&obj->pos,obj->segnum,0).centermask==0); //if (obj->control_type == CT_FLYING) if (obj->mtype.phys_info.flags & PF_LEVELLING) do_physics_align_object( obj ); //hack to keep player from going through closed doors if (obj->type==OBJ_PLAYER && obj->segnum!=orig_segnum && (Physics_cheat_flag!=0xBADA55) ) { int sidenum; sidenum = find_connect_side(&Segments[obj->segnum],&Segments[orig_segnum]); if (sidenum != -1) { if (! (WALL_IS_DOORWAY(&Segments[orig_segnum],sidenum) & WID_FLY_FLAG)) { side *s; int vertnum,num_faces,i; fix dist; int vertex_list[6]; //bump object back s = &Segments[orig_segnum].sides[sidenum]; if (orig_segnum==-1) Error("orig_segnum == -1 in physics"); create_abs_vertex_lists( &num_faces, vertex_list, orig_segnum, sidenum); //let's pretend this wall is not triangulated vertnum = vertex_list[0]; for (i=1;i<4;i++) if (vertex_list[i] < vertnum) vertnum = vertex_list[i]; #ifdef COMPACT_SEGS { vms_vector _vn; get_side_normal(&Segments[orig_segnum], sidenum, 0, &_vn ); dist = vm_dist_to_plane(&start_pos, &_vn, &Vertices[vertnum]); vm_vec_scale_add(&obj->pos,&start_pos,&_vn,obj->size-dist); } #else dist = vm_dist_to_plane(&start_pos, &s->normals[0], &Vertices[vertnum]); vm_vec_scale_add(&obj->pos,&start_pos,&s->normals[0],obj->size-dist); #endif update_object_seg(obj); } } } //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL #ifndef NDEBUG //if end point not in segment, move object to last pos, or segment center if (get_seg_masks(&obj->pos,obj->segnum,0).centermask!=0) { if (find_object_seg(obj)==-1) { int n; //Int3(); if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=-1) { obj->pos = obj->last_pos; obj_relink(objnum, n ); } else { compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; } } //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL #endif }
//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; }
void camera::get_info(vec3d *position, matrix *orientation) { if(position == NULL && orientation == NULL) return; eye* eyep = NULL; vec3d host_normal; //POSITION if(!(flags & CAM_STATIONARY_POS) || object_host.IsValid()) { c_pos = vmd_zero_vector; vec3d pt; pos_x.get(&pt.xyz.x, NULL); pos_y.get(&pt.xyz.y, NULL); pos_z.get(&pt.xyz.z, NULL); if(object_host.IsValid()) { object *objp = object_host.objp; int model_num = object_get_model(objp); polymodel *pm = NULL; if(model_num > -1) { pm = model_get(model_num); } if(object_host_submodel < 0 || pm == NULL) { vm_vec_unrotate(&c_pos, &pt, &object_host.objp->orient); vm_vec_add2(&c_pos, &object_host.objp->pos); } else { eyep = get_submodel_eye(pm, object_host_submodel); if(eyep) { vec3d c_pos_in; find_submodel_instance_point_normal( &c_pos_in, &host_normal, objp, eyep->parent, &eyep->pnt, &eyep->norm); vm_vec_unrotate(&c_pos, &c_pos_in, &objp->orient); vm_vec_add2(&c_pos, &objp->pos); } else { model_find_world_point( &c_pos, &pt, pm->id, object_host_submodel, &objp->orient, &objp->pos ); } } } else { c_pos = pt; } //Do custom position stuff, if needed if(func_custom_position != NULL && !eyep) { func_custom_position(this, &c_pos); } } if(position != NULL) *position = c_pos; //ORIENTATION if(orientation != NULL) { bool target_set = false; if(!(flags & CAM_STATIONARY_ORI) || object_target.IsValid() || object_host.IsValid()) { if(object_target.IsValid()) { object *objp = object_target.objp; int model_num = object_get_model(objp); polymodel *pm = NULL; vec3d target_pos = vmd_zero_vector; //See if we can get the model if(model_num > -1) { pm = model_get(model_num); } //If we don't have a submodel or don't have the model use object pos //Otherwise, find the submodel pos as it is rotated if(object_target_submodel < 0 || pm == NULL) { target_pos = objp->pos; } else { model_find_world_point( &target_pos, &vmd_zero_vector, pm->id, object_target_submodel, &objp->orient, &objp->pos ); } vec3d targetvec; vm_vec_normalized_dir(&targetvec, &target_pos, &c_pos); vm_vector_2_matrix(&c_ori, &targetvec, NULL, NULL); target_set = true; } else if(object_host.IsValid()) { if(eyep) { vm_vector_2_matrix(&c_ori, &host_normal, vm_vec_same(&host_normal, &object_host.objp->orient.vec.uvec)?NULL:&object_host.objp->orient.vec.uvec, NULL); target_set = true; } else { c_ori = object_host.objp->orient; } } else { c_ori = vmd_identity_matrix; } matrix mtxA = c_ori; matrix mtxB = IDENTITY_MATRIX; float pos = 0.0f; for(int i = 0; i < 9; i++) { ori[i].get(&pos, NULL); mtxB.a1d[i] = pos; } vm_matrix_x_matrix(&c_ori, &mtxA, &mtxB); vm_orthogonalize_matrix(&c_ori); } //Do custom orientation stuff, if needed if(func_custom_orientation != NULL && !target_set) { func_custom_orientation(this, &c_ori); } *orientation = c_ori; } }
do_endlevel_frame() { static fix timer; vms_vector save_last_pos; static fix explosion_wait1=0; static fix explosion_wait2=0; static fix bank_rate; static fix ext_expl_halflife; save_last_pos = ConsoleObject->last_pos; //don't let move code change this object_move_all(); ConsoleObject->last_pos = save_last_pos; if (ext_expl_playing) { external_explosion.lifeleft -= FrameTime; do_explosion_sequence(&external_explosion); if (external_explosion.lifeleft < ext_expl_halflife) mine_destroyed = 1; if (external_explosion.flags & OF_SHOULD_BE_DEAD) ext_expl_playing = 0; } if (cur_fly_speed != desired_fly_speed) { fix delta = desired_fly_speed - cur_fly_speed; fix frame_accel = fixmul(FrameTime,FLY_ACCEL); if (abs(delta) < frame_accel) cur_fly_speed = desired_fly_speed; else if (delta > 0) cur_fly_speed += frame_accel; else cur_fly_speed -= frame_accel; } //do big explosions if (!outside_mine) { if (Endlevel_sequence==EL_OUTSIDE) { vms_vector tvec; vm_vec_sub(&tvec,&ConsoleObject->pos,&mine_side_exit_point); if (vm_vec_dot(&tvec,&mine_exit_orient.fvec) > 0) { object *tobj; outside_mine = 1; tobj = object_create_explosion(exit_segnum,&mine_side_exit_point,i2f(50),VCLIP_BIG_PLAYER_EXPLOSION); if (tobj) { external_explosion = *tobj; tobj->flags |= OF_SHOULD_BE_DEAD; flash_scale = 0; //kill lights in mine ext_expl_halflife = tobj->lifeleft; ext_expl_playing = 1; } digi_link_sound_to_pos( SOUND_BIG_ENDLEVEL_EXPLOSION, exit_segnum, 0, &mine_side_exit_point, 0, i2f(3)/4 ); } } //do explosions chasing player if ((explosion_wait1-=FrameTime) < 0) { vms_vector tpnt; int segnum; object *expl; static int sound_count; vm_vec_scale_add(&tpnt,&ConsoleObject->pos,&ConsoleObject->orient.fvec,-ConsoleObject->size*5); vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.rvec,(rand()-RAND_MAX/2)*15); vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.uvec,(rand()-RAND_MAX/2)*15); segnum = find_point_seg(&tpnt,ConsoleObject->segnum); if (segnum != -1) { expl = object_create_explosion(segnum,&tpnt,i2f(20),VCLIP_BIG_PLAYER_EXPLOSION); if (rand()<10000 || ++sound_count==7) { //pseudo-random digi_link_sound_to_pos( SOUND_TUNNEL_EXPLOSION, segnum, 0, &tpnt, 0, F1_0 ); sound_count=0; } } explosion_wait1 = 0x2000 + rand()/4; } } //do little explosions on walls if (Endlevel_sequence >= EL_FLYTHROUGH && Endlevel_sequence < EL_OUTSIDE) if ((explosion_wait2-=FrameTime) < 0) { vms_vector tpnt; fvi_query fq; fvi_info hit_data; //create little explosion on wall vm_vec_copy_scale(&tpnt,&ConsoleObject->orient.rvec,(rand()-RAND_MAX/2)*100); vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.uvec,(rand()-RAND_MAX/2)*100); vm_vec_add2(&tpnt,&ConsoleObject->pos); if (Endlevel_sequence == EL_FLYTHROUGH) vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.fvec,rand()*200); else vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.fvec,rand()*60); //find hit point on wall fq.p0 = &ConsoleObject->pos; fq.p1 = &tpnt; fq.startseg = ConsoleObject->segnum; fq.rad = 0; fq.thisobjnum = 0; fq.ignore_obj_list = NULL; fq.flags = 0; find_vector_intersection(&fq,&hit_data); if (hit_data.hit_type==HIT_WALL && hit_data.hit_seg!=-1) object_create_explosion(hit_data.hit_seg,&hit_data.hit_pnt,i2f(3)+rand()*6,VCLIP_SMALL_EXPLOSION); explosion_wait2 = (0xa00 + rand()/8)/2; } switch (Endlevel_sequence) { case EL_OFF: return; case EL_FLYTHROUGH: { do_endlevel_flythrough(0); if (ConsoleObject->segnum == transition_segnum) { int objnum; Endlevel_sequence = EL_LOOKBACK; objnum = obj_create(OBJ_CAMERA, 0, ConsoleObject->segnum,&ConsoleObject->pos,&ConsoleObject->orient,0, CT_NONE,MT_NONE,RT_NONE); if (objnum == -1) { //can't get object, so abort mprintf((1, "Can't get object for endlevel sequence. Aborting endlevel sequence.\n")); stop_endlevel_sequence(); return; } Viewer = endlevel_camera = &Objects[objnum]; select_cockpit(CM_LETTERBOX); fly_objects[1] = fly_objects[0]; fly_objects[1].obj = endlevel_camera; fly_objects[1].speed = (5*cur_fly_speed)/4; fly_objects[1].offset_frac = 0x4000; vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,i2f(7)); timer=0x20000; } break; } case EL_LOOKBACK: { do_endlevel_flythrough(0); do_endlevel_flythrough(1); if (timer>0) { timer -= FrameTime; if (timer < 0) //reduce speed fly_objects[1].speed = fly_objects[0].speed; } if (endlevel_camera->segnum == exit_segnum) { vms_angvec cam_angles,exit_seg_angles; Endlevel_sequence = EL_OUTSIDE; timer = i2f(2); vm_vec_negate(&endlevel_camera->orient.fvec); vm_vec_negate(&endlevel_camera->orient.rvec); vm_extract_angles_matrix(&cam_angles,&endlevel_camera->orient); vm_extract_angles_matrix(&exit_seg_angles,&mine_exit_orient); bank_rate = (-exit_seg_angles.b - cam_angles.b)/2; ConsoleObject->control_type = endlevel_camera->control_type = CT_NONE; //_MARK_("Starting outside");//Commented out by KRB #ifdef SLEW_ON slew_obj = endlevel_camera; #endif } break; } case EL_OUTSIDE: { #ifndef SLEW_ON vms_angvec cam_angles; #endif vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed)); #ifndef SLEW_ON vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,fixmul(FrameTime,-2*cur_fly_speed)); vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.uvec,fixmul(FrameTime,-cur_fly_speed/10)); vm_extract_angles_matrix(&cam_angles,&endlevel_camera->orient); cam_angles.b += fixmul(bank_rate,FrameTime); vm_angles_2_matrix(&endlevel_camera->orient,&cam_angles); #endif timer -= FrameTime; if (timer < 0) { Endlevel_sequence = EL_STOPPED; vm_extract_angles_matrix(&player_angles,&ConsoleObject->orient); timer = i2f(3); } break; } case EL_STOPPED: { get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos); chase_angles(&player_angles,&player_dest_angles); vm_angles_2_matrix(&ConsoleObject->orient,&player_angles); vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed)); timer -= FrameTime; if (timer < 0) { #ifdef SLEW_ON slew_obj = endlevel_camera; _do_slew_movement(endlevel_camera,1,1); timer += FrameTime; //make time stop break; #else #ifdef SHORT_SEQUENCE stop_endlevel_sequence(); #else Endlevel_sequence = EL_PANNING; vm_extract_angles_matrix(&camera_cur_angles,&endlevel_camera->orient); timer = i2f(3); if (Game_mode & GM_MULTI) { // try to skip part of the seq if multiplayer stop_endlevel_sequence(); return; } //mprintf((0,"Switching to pan...\n")); #endif //SHORT_SEQUENCE #endif //SLEW_ON } break; } #ifndef SHORT_SEQUENCE case EL_PANNING: { #ifndef SLEW_ON int mask; #endif get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos); chase_angles(&player_angles,&player_dest_angles); vm_angles_2_matrix(&ConsoleObject->orient,&player_angles); vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed)); #ifdef SLEW_ON _do_slew_movement(endlevel_camera,1,1); #else get_angs_to_object(&camera_desired_angles,&ConsoleObject->pos,&endlevel_camera->pos); mask = chase_angles(&camera_cur_angles,&camera_desired_angles); vm_angles_2_matrix(&endlevel_camera->orient,&camera_cur_angles); if ((mask&5) == 5) { vms_vector tvec; Endlevel_sequence = EL_CHASING; //_MARK_("Done outside");//Commented out -KRB vm_vec_normalized_dir_quick(&tvec,&station_pos,&ConsoleObject->pos); vm_vector_2_matrix(&ConsoleObject->orient,&tvec,&surface_orient.uvec,NULL); desired_fly_speed *= 2; //mprintf((0,"Switching to chase...\n")); } #endif break; } case EL_CHASING: { fix d,speed_scale; #ifdef SLEW_ON _do_slew_movement(endlevel_camera,1,1); #endif get_angs_to_object(&camera_desired_angles,&ConsoleObject->pos,&endlevel_camera->pos); chase_angles(&camera_cur_angles,&camera_desired_angles); #ifndef SLEW_ON vm_angles_2_matrix(&endlevel_camera->orient,&camera_cur_angles); #endif d = vm_vec_dist_quick(&ConsoleObject->pos,&endlevel_camera->pos); speed_scale = fixdiv(d,i2f(0x20)); if (d<f1_0) d=f1_0; get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos); chase_angles(&player_angles,&player_dest_angles); vm_angles_2_matrix(&ConsoleObject->orient,&player_angles); vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed)); #ifndef SLEW_ON vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,fixmul(FrameTime,fixmul(speed_scale,cur_fly_speed))); if (vm_vec_dist(&ConsoleObject->pos,&station_pos) < i2f(10)) stop_endlevel_sequence(); #endif break; } #endif //ifdef SHORT_SEQUENCE } }
//make a series of photographs do_photos() { FILE *vfile,*upvfile; int photo_num=0; char savename[13]; grs_canvas *photo_canvas; vms_vector viewer_pos; vms_matrix viewer_orient; vfile=fopen("vectors.lst","rt"); upvfile=fopen("upvecs.c","wt"); Assert(vfile!=NULL && upvfile!=NULL); fprintf(upvfile,"\n\n#include \"vecmat.h\"\n\nvms_vector up_vecs[] = {\n"); photo_canvas = gr_create_canvas(64,64); gr_set_current_canvas(photo_canvas); while (!feof(vfile)) { vms_vector v; vms_matrix m; float x,y,z; int nf; nf = fscanf(vfile,"%f %f %f",&x,&y,&z); if (nf!=3) break; vm_vec_make(&v,fl2f(x),fl2f(y),fl2f(z)); vm_vector_2_matrix(&m,&v,NULL,NULL); fprintf(upvfile,"\t\t\t{%#x,%#x,%#x},\n",m.uvec.x,m.uvec.y,m.uvec.z); vm_vec_scale(&v,PHOTO_DIST); vm_vec_add(&viewer_pos,&cube_position,&v); viewer_orient = m; vm_vec_negate(&viewer_orient.fvec); vm_vec_negate(&viewer_orient.rvec); gr_clear_canvas(129); g3_start_frame(); g3_set_view_matrix(&viewer_pos,&viewer_orient,0x9000); draw_cube(); g3_end_frame(); gr_set_current_canvas(Canv_game); gr_ubitmap(0,0,&photo_canvas->cv_bitmap); gr_set_current_canvas(photo_canvas); sprintf(savename,"cube_%02d.bbm",photo_num); iff_write_bitmap(savename,&photo_canvas->cv_bitmap,gr_palette); photo_num++; } gr_free_canvas(photo_canvas); fprintf(upvfile,"\t\t};\n"); fclose(vfile); fclose(upvfile); }