int create_downwash_effect_component(downwash_component *this_downwash_component, downwash_types downwash_type, vec3d *position, int *entity_index_list, float main_rotor_radius, float main_rotor_rpm, float min_altitude) { int loop, count, terrain_type, trail_type; short int quadrant_x, quadrant_z; unsigned char alpha_percentage; float lifetime, lifetime_min, lifetime_max, scale_min, scale_max, scale, angle, radius, relative_radius, height, altitude, half_altitude, main_rotor_radius_minus_altitude; vec3d pos, offset, iv; terrain_3d_point_data terrain_info; entity *new_entity; memset (&terrain_info, 0, sizeof (terrain_3d_point_data)); count = this_downwash_component->trail_count; if ( count < 1 ) { return 0; } // Xhit: This is the altitude of the helicopter relative to the ground level. (030328) altitude = position->y - min_altitude; half_altitude = (altitude / 2.0); main_rotor_radius_minus_altitude = main_rotor_radius - altitude; scale_min = this_downwash_component->scale_min; scale_max = this_downwash_component->scale_max; lifetime_min = this_downwash_component->lifetime_min; lifetime_max = this_downwash_component->lifetime_max; // Xhit: initialising quadrant variables (030328) quadrant_x = 1; quadrant_z = 1; // // create smoke trails // for ( loop = 0 ; loop < count ; loop ++ ) { lifetime = lifetime_min + fabs( ( lifetime_max - lifetime_min ) * sfrand1() ); angle = frand1() * PI_OVER_TWO; relative_radius = main_rotor_radius * frand1(); scale = relative_radius + scale_min; if(scale > scale_max) scale = scale_max; switch(downwash_type) { case DOWNWASH_TYPE_LAND: case DOWNWASH_TYPE_LAND_DUAL_ROTORS: { //Xhit: If altitude bigger than main rotor radius then the smoke should be centered beneath the helicopter. (030328) if(altitude >= main_rotor_radius) { radius = relative_radius; height = (half_altitude * (radius / main_rotor_radius) + half_altitude) * frand1(); }else { radius = relative_radius + main_rotor_radius_minus_altitude; height = (half_altitude * ((radius - main_rotor_radius_minus_altitude) / main_rotor_radius) + half_altitude + scale ) * frand1(); } break; } case DOWNWASH_TYPE_WATER: case DOWNWASH_TYPE_WATER_DUAL_ROTORS: { if(altitude >= main_rotor_radius) { radius = relative_radius; }else { radius = relative_radius + main_rotor_radius_minus_altitude; } // Xhit: Changed to 2 instead of main_rotor_radius so smoke is created just over water level (030515) height = 2 * frand1(); break; } default: { debug_fatal("DOWNWASH : trying to create an unrecogniseable downwash effect"); break; } } //Xhit: If main rotor(s) only (not displaced main rotors) then. (030328) if((this_downwash_component->create_in_all_quadrants) && (loop < 4)) { //Xhit: This cryptical thing is to determine in which quadrant this sprite is going to be created. (030328) // ^z // | // 1|0 x // -+---> // 3|2 // // loop = 0 -> x= 1, z= 1; loop = 1 -> x= -1, z= 1; // loop = 2 -> x= 1, z= -1; loop = 3 -> x= -1, z= -1; quadrant_x = 1 | -(loop & 1); quadrant_z = 1 | -(loop & 2); offset.x = quadrant_x * radius * ( cos ( angle ) ); offset.y = height; offset.z = quadrant_z * radius * ( sin ( angle ) ); }else //Xhit: If scattered downwash effect and if the heli got more than one main rotor (on different axis) then // add two more trails at the sides of the heli. (030328) if((this_downwash_component->create_in_all_quadrants) && (loop >= 4) && (count == 6)) { //Xhit: loop = 4 -> x= 1; loop = 5 -> x= -1; (030328) quadrant_x = 1 | -(loop & 1); relative_radius = main_rotor_radius * frand1(); offset.x = quadrant_x * radius; offset.y = height; offset.z = frand1() * (main_rotor_radius / 2); }else { debug_fatal("DOWNWASH : trying to create an unrecogniseable downwash effect"); } pos.x = position->x + offset.x ; pos.z = position->z + offset.z; //Xhit: This is necessary if it's going to work on tilting terrain. (030328) get_3d_terrain_point_data (pos.x, pos.z, &terrain_info); pos.y = get_3d_terrain_point_data_elevation (&terrain_info); pos.y = pos.y + offset.y; bound_position_to_map_volume( &pos ); //Xhit: Decide which trail type is going to be used, this makes mapping to type of downwash effect fast. (030328) terrain_type = get_3d_terrain_point_data_type(&terrain_info); trail_type = get_terrain_surface_type(terrain_type) + SMOKE_LIST_TYPE_DOWNWASH_START; #if DEBUG_MODULE debug_log("DOWNWASH.C: terrain_type: %d, trail_type: %d", terrain_type, trail_type); #endif iv.x = pos.x - position->x; iv.y = relative_radius; iv.z = pos.z - position->z; //Xhit: If heli on ground then let the dust-smoke fade in according to increasing main_rotor_rpm // otherwise set it according to the altitude of the heli (higher = less dust smoke) (030328) if(altitude < 1.0) { alpha_percentage = (unsigned char)(main_rotor_rpm); }else { //Xhit: "+ 1.0" is to guarantee that alpha_percentage > 0. (030328) alpha_percentage = (unsigned char)((1.0 - (altitude / (DOWNWASH_EFFECT_MAX_ALTITUDE + 1.0))) * 100); } new_entity = create_local_entity ( ENTITY_TYPE_SMOKE_LIST, entity_index_list[ loop ], ENTITY_ATTR_INT_VALUE (INT_TYPE_ENTITY_SUB_TYPE, ENTITY_SUB_TYPE_EFFECT_SMOKE_LIST_DOWNWASH), ENTITY_ATTR_INT_VALUE (INT_TYPE_SMOKE_TYPE, trail_type), ENTITY_ATTR_INT_VALUE (INT_TYPE_COLOUR_ALPHA, alpha_percentage), ENTITY_ATTR_FLOAT_VALUE (FLOAT_TYPE_GENERATOR_LIFETIME, this_downwash_component->generator_lifetime), ENTITY_ATTR_FLOAT_VALUE (FLOAT_TYPE_FREQUENCY, this_downwash_component->frequency), ENTITY_ATTR_FLOAT_VALUE (FLOAT_TYPE_SMOKE_LIFETIME, lifetime), ENTITY_ATTR_FLOAT_VALUE (FLOAT_TYPE_SCALE, scale), ENTITY_ATTR_VEC3D (VEC3D_TYPE_INITIAL_VELOCITY, iv.x, iv.y, iv.z), ENTITY_ATTR_VEC3D (VEC3D_TYPE_POSITION, pos.x, pos.y, pos.z), ENTITY_ATTR_END ); entity_index_list[ loop ] = get_local_entity_index( new_entity ); } return count; }
void ship_vehicle_movement (entity *en) { ship_vehicle *raw; entity *guide, *current_waypoint; vec3d wp_pos, wp_vec, new_pos; float roll, pitch, heading, sqr_range, turn_rate, required_heading, delta_heading, current_velocity, new_velocity; raw = get_local_entity_data (en); // // abort if mobile is not moving (i.e. landed, or dead) // if (!get_local_entity_int_value (en, INT_TYPE_MOBILE_MOVING)) { return; } // // abort if the mobile has no PRIMARY guide (also stops ships from moving if just engaging) // guide = get_local_entity_primary_guide (en); if (!guide) { return; } current_waypoint = get_local_entity_parent (guide, LIST_TYPE_CURRENT_WAYPOINT); ASSERT (current_waypoint); current_velocity = raw->vh.mob.velocity; // // GET WAYPOINT POSITION // ship_movement_get_waypoint_position (en, &wp_pos); wp_vec.x = wp_pos.x - raw->vh.mob.position.x; wp_vec.y = 0; wp_vec.z = wp_pos.z - raw->vh.mob.position.z; sqr_range = ((wp_vec.x * wp_vec.x) + (wp_vec.z * wp_vec.z)); #if DEBUG_MODULE create_vectored_debug_3d_object (&wp_pos, (vec3d *) &raw->vh.mob.attitude [1], OBJECT_3D_RED_ARROW, 0, 0.20); #endif // ???? if (fabs (sqr_range) < 1 * CENTIMETRE) { wp_vec.z = 0; wp_vec.y = 0; wp_vec.z = 1; } //////////////////////////////////////// // // angles // //////////////////////////////////////// // heading normalise_3d_vector (&wp_vec); heading = get_heading_from_attitude_matrix (raw->vh.mob.attitude); required_heading = atan2 (wp_vec.x, wp_vec.z); { float angle, range, v; range = sqrt (sqr_range); v = sqrt (0.5 * range * vehicle_database [raw->vh.mob.sub_type].g_max); angle = ((raw->vh.mob.attitude [2][0] * wp_vec.x) + (raw->vh.mob.attitude [2][2] * wp_vec.z)); if (angle < 0.707) // 45 degs. { // wp behind ship #if DEBUG_MODULE debug_log ("SH_MOVE: ship cannot reach wp at vel %f m/s (max v %f), range %f, g %f", raw->vh.mob.velocity, v, range, vehicle_database [raw->vh.mob.sub_type].g_max); #endif new_velocity = bound (v, 0.0, get_local_entity_float_value (guide, FLOAT_TYPE_VELOCITY)); } else { #if DEBUG_MODULE debug_log ("SH_MOVE: ship can reach wp at vel %f m/s (max v %f), range %f, g %f", raw->vh.mob.velocity, v, range, vehicle_database [raw->vh.mob.sub_type].g_max); #endif new_velocity = get_local_entity_float_value (guide, FLOAT_TYPE_VELOCITY); } } turn_rate = 0.0; if (raw->vh.mob.velocity != 0.0) { turn_rate = fabs (vehicle_database [raw->vh.mob.sub_type].g_max / raw->vh.mob.velocity); } delta_heading = required_heading - heading; if (delta_heading <= -PI) { delta_heading += PI2; } if (delta_heading >= PI) { delta_heading -= PI2; } delta_heading = bound (delta_heading, -turn_rate, turn_rate); heading += delta_heading * get_entity_movement_delta_time (); pitch = 0.0; roll = 0.0; //////////////////////////////////////// // // attitude // //////////////////////////////////////// get_3d_transformation_matrix (raw->vh.mob.attitude, heading, rad (pitch), rad (roll)); //////////////////////////////////////// // // velocity // //////////////////////////////////////// { float maximum_acceleration, required_acceleration; required_acceleration = (new_velocity - raw->vh.mob.velocity); maximum_acceleration = get_local_entity_float_value (en, FLOAT_TYPE_MAX_ACCELERATION); raw->vh.mob.velocity += min (required_acceleration, maximum_acceleration) * get_entity_movement_delta_time (); } //////////////////////////////////////// // // position // //////////////////////////////////////// new_pos.x = raw->vh.mob.position.x + (raw->vh.mob.velocity * raw->vh.mob.zv.x * get_entity_movement_delta_time ()); new_pos.y = 0.0; new_pos.z = raw->vh.mob.position.z + (raw->vh.mob.velocity * raw->vh.mob.zv.z * get_entity_movement_delta_time ()); bound_position_to_map_volume (&new_pos); // // Calculate motion vector for view system // raw->vh.mob.motion_vector.x = (new_pos.x - raw->vh.mob.position.x) * get_one_over_delta_time (); raw->vh.mob.motion_vector.y = 0.0; raw->vh.mob.motion_vector.z = (new_pos.z - raw->vh.mob.position.z) * get_one_over_delta_time (); new_pos.y = 0.0; set_local_entity_vec3d (en, VEC3D_TYPE_POSITION, &new_pos); }