From 3c8aff8f31e4c7d50c229865766d3f8f0dec83d5 Mon Sep 17 00:00:00 2001 From: arda-guler <80536083+arda-guler@users.noreply.github.com> Date: Wed, 18 Oct 2023 20:55:54 +0300 Subject: [PATCH] Implement fuel expenditure & economy --- auto_dogfight.py | 12 +- auto_dogfight_external.py | 754 +++++++++++++++++++++++++++++ data/industry/engines/APS_7K.json | 8 +- data/industry/engines/APS_9K.json | 8 +- data/industry/engines/KS_X7.json | 6 +- data/industry/engines/KS_X9.json | 6 +- data/industry/engines/SE_7000.json | 6 +- data/industry/engines/SE_9000.json | 6 +- encounter.py | 13 +- main.py | 27 +- propulsion.py | 7 +- quick_dogfight.py | 4 +- rigidbody.py | 2 +- 13 files changed, 815 insertions(+), 44 deletions(-) create mode 100644 auto_dogfight_external.py diff --git a/auto_dogfight.py b/auto_dogfight.py index f41c85e..5181af5 100644 --- a/auto_dogfight.py +++ b/auto_dogfight.py @@ -52,7 +52,7 @@ def read_industry(): throttle_range = [0, 100] efficiency = 0.6 - air_fuel_ratio = 2000 # this is an air volume vs. fuel MASS ratio, so it is not going to agree with anything familiar of course + air_fuel_ratio = 20000 # this is an air volume vs. fuel MASS ratio, so it is not going to agree with anything familiar of course max_fuel_rate = data['fuel_consumption'] APU_fuel_rate = data['APU_fuel_consumption'] engines[data['name']] = Turbojet(data['name'], intake, compressor, turbine, nozzle, efficiency, air_fuel_ratio, max_fuel_rate, throttle_range, APU_fuel_rate, data['price'], data['manufacturer']) @@ -78,12 +78,12 @@ def window_resize(window, width, height): # READ INDUSTRY DATA airframes, engines = read_industry() player_airframe = airframes["LM Bombcat"] - player_engine = engines["SE 7000"] + player_engine = engines["APS 7K"] # INIT VESSELS print("Initializing vessels...") - init_pos = np.array([0.0, 1000.2, 0.0]) # m - init_vel = np.array([0.0, 0.0, 60.0]) # m s-1 + init_pos = np.array([0.0, 1000, 0.0]) # m + init_vel = np.array([0.0, 0.0, 100.0]) # m s-1 init_accel = np.array([0.0, 0.0, 0.0]) # m s-2 init_orient = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], @@ -360,8 +360,6 @@ def rotate_cam(rotation): velocity_conversion_factor = 1.943844 # knots altitude_conversion_factor = 3.28084 # feet - - # AUTOPILOT # pull up! @@ -412,7 +410,7 @@ def rotate_cam(rotation): player.elevator(elevator_amount) if np.dot(player.orient[1], enemy_dir) > 0: - if np.dot(player.orient[2], enemy_dir) < 0.94: + if np.dot(player.orient[2], enemy_dir) < 0.98: player.elevator(0.8) else: diff --git a/auto_dogfight_external.py b/auto_dogfight_external.py new file mode 100644 index 0000000..bbb95e4 --- /dev/null +++ b/auto_dogfight_external.py @@ -0,0 +1,754 @@ +import numpy as np +import OpenGL +from OpenGL.GL import * +from OpenGL.GLU import * +import glfw +import time +import random +import keyboard as kbd +import json +import os + +from rigidbody import * +from model import * +from graphics import * +from camera import * +from terrain import * +from ui import * +from scenery_objects import * +from sound import * +from alerts import * +from weapons import * +from propulsion import * +from city import * +from airframe import * +from encounter import * + +def read_industry(): + airframe_files = os.listdir("./data/industry/airframes/") + engine_files = os.listdir("./data/industry/engines/") + + airframes = {} + engines = {} + + # READ AIRFRAMES + for airframe_file in airframe_files: + file = open("./data/industry/airframes/" + airframe_file) + data = json.load(file) + airframes[data['name']] = Airframe(data['name'], data['manufacturer'], + data['mass'], data['cd'], data['effectiveness'], + data['cargo_space'], data['price']) + print("Loading airframe:", data['name']) + + # READ ENGINES + for engine_file in engine_files: + file = open("./data/industry/engines/" + engine_file) + data = json.load(file) + + intake = Intake(np.array([0, 0, -1]), 0.1) + compressor = Compressor(data['compressor_coeff'], 100) + turbine = Turbine(data['turbine_coeff'], data['max_rpm']) + nozzle = Nozzle(data['thrust_coeff']) + + throttle_range = [0, 100] + efficiency = 0.6 + air_fuel_ratio = 20000 # this is an air volume vs. fuel MASS ratio, so it is not going to agree with anything familiar of course + max_fuel_rate = data['fuel_consumption'] + APU_fuel_rate = data['APU_fuel_consumption'] + engines[data['name']] = Turbojet(data['name'], intake, compressor, turbine, nozzle, efficiency, air_fuel_ratio, max_fuel_rate, throttle_range, APU_fuel_rate, data['price'], data['manufacturer']) + print("Loading engine:", data['name']) + + return airframes, engines + +def main(): + + def window_resize(window, width, height): + try: + # glfw.get_framebuffer_size(window) + glViewport(0, 0, width, height) + glLoadIdentity() + gluPerspective(fov, width/height, near_clip, far_clip) + glTranslate(main_cam.pos[0], main_cam.pos[1], main_cam.pos[2]) + main_cam.orient = np.eye(3) + main_cam.rotate([0, 180, 0]) + except ZeroDivisionError: + # if the window is minimized it makes height = 0, but we don't need to update projection in that case anyway + pass + + # READ INDUSTRY DATA + airframes, engines = read_industry() + player_airframe = airframes["LM Bombcat"] + player_engine = engines["SE 7000"] + + # INIT VESSELS + print("Initializing vessels...") + init_pos = np.array([0.0, 1000, 0.0]) # m + init_vel = np.array([0.0, 0.0, 100.0]) # m s-1 + init_accel = np.array([0.0, 0.0, 0.0]) # m s-2 + init_orient = np.array([[1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 0.0, 1.0]]) + init_ang_vel = np.array([0.0, 0.0, 0.0]) # rad s-1 + init_ang_accel = np.array([0.0, 0.0, 0.0]) # rad s-2 + init_mass = player_airframe.mass # kg + init_inertia = np.array([[6000.0, 0.0, 0.0], + [0.0, 6000.0, 0.0], + [0.0, 0.0, 3000.0]]) # kg m2 + max_thrust = 5e3 # N + throttle_range = [0, 100] # % + throttle = 100 # % + prop_mass = 150 # kg + mass_flow = 0.001 # kg s-1 + + plane_model = Model("plane") + init_CoM = np.array([0.0, 0.0, 2.0]) + + cross_sections = np.array([8, 10, 2]) # m2 + Cds = np.array([0.6, 0.8, 0.1]) * player_airframe.Cd + Cdas = np.array([8, 20, 45]) + angular_damping = np.array([0.4, 0.8, 0.8]) + Cl = 1.2 + lift_moment_arm = 0.15 + + # aileron, elevator, rudder + control_effectiveness = np.array([1.8, 1.8, 2.5]) * player_airframe.effectiveness + + rear_gear_moment = 1000 # m N + brake_force = 15e3 # N + + cargo_space = player_airframe.cargo_space + + rocket_pod = RocketPod(None, np.array([0.05, -0.1, 1.2]), 30, makesound=True) + weapons = [rocket_pod] + + prop_mass = 150 + + AP = Aircraft(plane_model, init_CoM, + init_pos, init_vel, init_accel, + init_orient, init_ang_vel, init_ang_accel, + init_mass, init_inertia, player_engine, prop_mass, + cross_sections, Cds, Cdas, angular_damping, Cl, + lift_moment_arm, control_effectiveness, + rear_gear_moment, brake_force, cargo_space, weapons) + + rocket_pod.platform = AP + AP.set_thrust_percent(0) + + bodies = [AP] + + # SCENERY OBJECTS + print("Initializing scenery objects...") + pylon_model = Model("pylon") + pylon1 = SceneryObject(pylon_model, np.array([10,0,500])) + pylon2 = SceneryObject(pylon_model, np.array([-10,0,500])) + + # scenery_objects = [pylon1, pylon2] + scenery_objects = [] + + # cities + city1 = City("Yazilikaya", + + "A moderately sized agricultural city.\n\nHome to Ousteem Aerospace, which specializes in lightweight airframes.\nHome to AG Power Works, which specializes in heavy-duty propulsion.", + + [airframes["OSB Serce"], airframes["OSB Kirlangic"], + engines["APS 7K"], engines["APS 9K"]], + + {"Foodstuff": 8, + "Water": 0.7, + "Electronic Components": 300, + "Computers": 1500, + "Medicine": 380, + "Household Machinery": 400, + "Heavy Machinery": 120, + "Raw Ore": 0.1, + "Fuel": 25, + "Construction Materials": 20, + "Luxuries": 4000, + "Precious Metals": 7000}, + + np.array([0, 0, 0]), 1, + + "death_mask") + + city2 = City("Numakawa", + + "A small industrial city.\n\nHome to Matsuboshi, which specializes in heavy-lift airframes.\nHome to Kobesaki, which specializes in fuel-efficient propulsion.", + + [airframes["MSB Fuji"], airframes["MSB Kinki"], + engines["KS X7"], engines["KS X9"]], + + {"Foodstuff": 10, + "Water": 1, + "Electronic Components": 150, + "Computers": 800, + "Medicine": 410, + "Household Machinery": 300, + "Heavy Machinery": 80, + "Raw Ore": 5, + "Fuel": 60, + "Construction Materials": 18, + "Luxuries": 2500, + "Precious Metals": 15000}, + + np.array([10000, 0, -30000]), 0.5, + + "jade_empire") + + city3 = City("Meadowview", + + "A large industrial metropolis.\n\nHome to Logheat Mardin, which specializes in serial production of airframes.\nHome to Special Electricity, which specializes in serial production of propulsion systems.", + + [airframes["LM Bombcat"], airframes["LM Wasp"], + engines["SE 7000"], engines["SE 9000"]], + + {"Foodstuff": 12, + "Water": 2, + "Electronic Components": 180, + "Computers": 1000, + "Medicine": 550, + "Household Machinery": 350, + "Heavy Machinery": 100, + "Raw Ore": 3, + "Fuel": 40, + "Construction Materials": 28, + "Luxuries": 2900, + "Precious Metals": 10000}, + + np.array([40000, 0, 12000]), 1.4, + + "trigger") + + cities = [city1, city2, city3] + + testructible = Testructible(np.array([150, 0, 0])) + scenery_objects.append(testructible) + + # TERRAIN + print("Initializing terrain...") + floor = Flatland(0, Color(0.1, 0.8, 0.1)) + + # MISC PHYSICS + gravity = np.array([0.0, -9.81, 0]) + + # SOUND + print("Initializing sound (pygame.mixer)...") + init_sound() + + # GRAPHICS + print("Initializing graphics (OpenGL, glfw)...") + window_x, window_y = 1600, 900 + fov = 70 + near_clip = 0.5 + far_clip = 1e6 + + glfw.init() + window = glfw.create_window(window_x, window_y, "Kelaynak 84", None, None) + glfw.set_window_pos(window, 100, 100) + glfw.make_context_current(window) + glfw.set_window_size_callback(window, window_resize) + + gluPerspective(fov, window_x/window_y, near_clip, far_clip) + glClearColor(0, 0, 0.3, 1) + + # CAMERA + cam_pos = np.array([0, 0, 0]) + cam_orient = np.array([[-1, 0, 0], + [0, 1, 0], + [0, 0, -1]]) + main_cam = Camera("main_cam", cam_pos, cam_orient, True) + + glRotate(-180, 0, 1, 0) + main_cam.lock_to_target(bodies[0]) + main_cam.offset_amount = 60 + + def move_cam(movement): + main_cam.move(movement) + + def rotate_cam(rotation): + main_cam.rotate(rotation) + + # CAMERA CONTROLS + cam_pitch_up = "K" + cam_pitch_dn = "I" + cam_yaw_left = "J" + cam_yaw_right = "L" + cam_roll_cw = "U" + cam_roll_ccw = "O" + + plane_pitch_up = "S" + plane_pitch_dn = "W" + plane_roll_ccw = "Q" + plane_roll_cw = "E" + plane_yaw_right = "D" + plane_yaw_left = "A" + plane_throttle_up = "Z" + plane_throttle_dn = "X" + + shoot = "space" + brake = "B" + APU_start = "T" + APU_stop = "G" + + metric_key = "M" + imperial_key = "N" + + first_person_ui = False + + cam_speed = 100 + cam_rot_speed = 100 + + play_sfx("turbojet_fan", -1, 1, 0) + play_sfx("wind1", -1, 2, 0) + play_sfx("rumble", -1, 3, 0) + + rwr_snd = None + + print("Starting...") + print("= = = = = =\n") + dt = 0 + ctrl_state = [0, 0, 0] + + velocity_conversion_factor = 1 + altitude_conversion_factor = 1 + + pitch_trim = 0 + + encounter_chance = 0.01 + current_encounter = None + city_panel_shown = False + hundred_cycle = 0 + AP_city = None + player_money = 1000000 + player_cargo = {"Foodstuff": 0, + "Water": 0, + "Electronic Components": 0, + "Computers": 0, + "Medicine": 0, + "Household Machinery": 0, + "Heavy Machinery": 0, + "Raw Ore": 0, + "Fuel": 0, + "Construction Materials": 0, + "Luxuries": 0, + "Precious Metals": 0} + + last_cam_pitch = 0 + + while not glfw.window_should_close(window): + t_cycle_start = time.perf_counter() + glfw.poll_events() + + # CONTROLS + + if kbd.is_pressed(cam_pitch_up): + rotate_cam([cam_rot_speed * dt, 0, 0]) + if kbd.is_pressed(cam_pitch_dn): + rotate_cam([-cam_rot_speed * dt, 0, 0]) + if kbd.is_pressed(cam_yaw_left): + rotate_cam([0, cam_rot_speed * dt, 0]) + if kbd.is_pressed(cam_yaw_right): + rotate_cam([0, -cam_rot_speed * dt, 0]) + if kbd.is_pressed(cam_roll_cw): + rotate_cam([0, 0, cam_rot_speed * dt]) + if kbd.is_pressed(cam_roll_ccw): + rotate_cam([0, 0, -cam_rot_speed * dt]) + + for i in range(len(ctrl_state)): + ctrl_state[i] = min(max(ctrl_state[i], -1), 1) + + if kbd.is_pressed(metric_key): # superior metric units for the superior people + velocity_conversion_factor = 1 + altitude_conversion_factor = 1 + elif kbd.is_pressed(imperial_key): # inferior imperial units for Mars Climate Orbiter + velocity_conversion_factor = 1.943844 # knots + altitude_conversion_factor = 3.28084 # feet + + # AUTOPILOT + + # pull up! + AP.engine.throttle = 1 + AP.engine.APU = True + + if AP.pos[1] < 300 and np.dot(AP.orient[2], np.array([0, -1, 0]) > 0): + max_orient_1 = np.linalg.norm( np.array([0, 1, 0]) - np.dot(player.orient[2], np.array([0, 1, 0])) * player.orient[2] ) + if np.dot(AP.orient[1], np.array([0, 1, 0])) < max_orient * 0.8: + if np.dot(AP.orient[0], np.array([0, 1, 0])) < 0: + AP.aileron(-0.8) + else: + AP.aileron(0.8) + + if np.dot(AP.orient[1], np.array([0, 1, 0])) > 0: + AP.elevator(0.8) + + # dogfight + elif current_encounter: + player = AP + enemy = current_encounter.enemy + + enemy_actual_rel_pos = (enemy.pos - player.pos) + enemy_actual_dist = np.linalg.norm(enemy_actual_rel_pos) + + enemy_rel_pos = (enemy.pos - player.pos) + enemy.vel * enemy_actual_dist / 500 + enemy_dist = np.linalg.norm(enemy_rel_pos) + enemy_dir = enemy_rel_pos / enemy_dist + + max_orient_1 = np.linalg.norm( enemy_dir - np.dot(player.orient[2], enemy_dir) * player.orient[2] ) + # lift vector not aligned with player? + if np.dot(player.orient[1], enemy_dir) < max_orient_1 * 0.8: + + # should I roll clockwise? + if np.dot(player.orient[0], enemy_dir) > 0: + aileron_amount = np.dot(player.orient[0], enemy_dir) + player.aileron(0.8 * aileron_amount) + else: + aileron_amount = -np.dot(player.orient[0], enemy_dir) + player.aileron(-0.8 * aileron_amount) + + # should I pitch? + if np.linalg.norm(player.vel) < 100: + elevator_scaler = (np.linalg.norm(player.vel) - 100) / 100 + else: + elevator_scaler = 1 + elevator_amount = -(np.dot(player.orient[2], enemy_dir) - 1) * elevator_scaler + player.elevator(elevator_amount) + + if np.dot(player.orient[1], enemy_dir) > 0: + if np.dot(player.orient[2], enemy_dir) < 0.98: + player.elevator(0.8) + + else: + player.elevator(0.3) + player.weapons[0].shoot(bodies) + + # PHYSICS + + for b in bodies: + if isinstance(b, Rocket): + b.apply_accel(gravity) + b.apply_drag() + b.apply_aero_torque() + b.drain_fuel(dt) + b.apply_thrust() + b.update(dt) + b.check_target(bodies) + + if b.pos[1] < floor.height: + bodies.remove(b) + del b + + elif isinstance(b, Aircraft): + if b.pos[1] <= floor.height + 1: + if abs(b.orient[0][1]) < 0.3 and b.orient[1][1] > 0 and -0.1 < b.orient[2][1] < 0.6 \ + and b.vel[1] >= -5: # horizontal and gentle enough + b.state = "LANDED" + else: # landed on its side or upside down, or too fast + b.state = "CRASHED" + else: + b.state = "INFLIGHT" + + AP.engine.intake.air_intake_vector = -AP.orient[2] + + AP.drain_fuel(dt) + AP.apply_aero_torque() + AP.apply_angular_drag(dt) + AP.apply_drag() + AP.apply_lift() + AP.apply_thrust(dt) + AP.apply_brake() + G = np.linalg.norm(AP.accel) / 10 + AP.apply_accel(gravity) + AP.update_weapons(dt) + AP.update(dt) + + if np.linalg.norm(AP.vel): + AoA = np.arccos(max(min(np.dot(AP.vel, AP.orient[2]) / np.linalg.norm(AP.vel), 1), -1)) + AoA = np.rad2deg(AoA) + else: + AoA = 0 + + # hit flat ground + for b in bodies: + if b.pos[1] < floor.height + 1: + b.pos[1] = 1 + b.vel[1] = 0 + b.vel = b.vel - b.vel * 0.05 * dt + + main_cam.move_with_lock(dt) + # main_cam.rotate_with_lock(dt) + + if not current_encounter == None: + current_encounter.update(gravity, bodies, dt) + rwr_snd = current_encounter.rwr(bodies) + if current_encounter.state == "DOWN": + bodies.remove(current_encounter.enemy) + current_encounter = None + + if AP_city == None: + play_bgm("pluvious") + else: + play_bgm(AP_city.bgm) + + if rwr_snd and not get_channel_busy(6): + if not (rwr_snd == "rwr_new" or rwr_snd == "rwr_lost"): + play_sfx(rwr_snd, -1, 6) + else: + play_sfx(rwr_snd, 0, 6) + elif not rwr_snd or rwr_snd == "rwr_new" or rwr_snd == "rwr_lost": + stop_channel(6) + + # ROTATE Cam + if current_encounter: + enemy = current_encounter.enemy + player = AP + + cam_dir = -main_cam.orient[2] + direction = -main_cam.orient[2] + direction[1] = 0 + direction = direction / np.linalg.norm(direction) + try: + angle = np.arccos(min(max(np.dot(cam_dir, direction), -0.999), 0.999)) + except: + angle = 0 + + rotate_cam([-angle, 0, 0]) + + direction = enemy.pos - player.pos + direction[1] = 0 + direction = direction / np.linalg.norm(direction) + + try: + angle = np.arccos(min(max(np.dot(cam_dir, direction), -0.999), 0.999)) + except: + angle = 0 + + if np.dot(main_cam.orient[0], direction) > 0: + angle = - angle + rotate_cam([0, angle, 0]) + + direction = enemy.pos - player.pos + direction = direction / np.linalg.norm(direction) + + try: + angle = np.arccos(min(max(np.dot(cam_dir, direction), -0.999), 0.999)) + except: + angle = 0 + + rotate_cam([angle, 0, 0]) + + # GRAPHICS + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) + drawScene(main_cam, floor, bodies, cities, scenery_objects, ctrl_state, first_person_ui) + + alt_string = "Alt " + str(int(AP.pos[1] * altitude_conversion_factor)) + vel_string = "Vel " + str(int(np.linalg.norm(AP.vel) * velocity_conversion_factor)) + throttle_str = "Throttle " + str(int(AP.engine.throttle * 100)) + rpm_str = " RPM " + str(int(AP.engine.compressor.rpm)) + prop_str = "PROP " + str(int(AP.prop_mass)) + + if AP_city: + city_str = AP_city.name + else: + city_str = "Wilderness" + + if AP.engine.APU: + APU_str = "APU ACTV" + else: + APU_str = "APU DSBL" + + AoA_str = "AOA " + str(round(AoA, 2)) + G_str = "G " + str(round(G, 2)) + rockets_str = "RKT " + str(AP.weapons[0].num_rockets) + + magenta = Color(1, 0, 1) + red = Color(1, 0, 0) + + AoA_color = magenta + G_color = magenta + vel_color = magenta + alt_color = magenta + throttle_color = magenta + rpm_color = magenta + APU_color = magenta + rockets_color = magenta + prop_color = magenta + city_color = magenta + + if np.linalg.norm(AP.vel) < 50 and AoA > 10: + vel_color = red + AoA_color = red + if AP.engine.throttle < 100: + throttle_color = red + + if G > 9: + G_color = red + + if AP.pos[1] < 1000 and AP.vel[1] < 0 and AP.pos[1] / -AP.vel[1] < 3: + alt_color = red + + if AP.weapons[0].num_rockets <= 3: + rockets_color = red + + render_AN(alt_string, alt_color, [4, 5], main_cam, fpu=first_person_ui) + render_AN(vel_string, vel_color, [-7, 5], main_cam, fpu=first_person_ui) + render_AN(throttle_str, throttle_color, [-8, -4.5], main_cam, fpu=first_person_ui) + render_AN(rpm_str, rpm_color, [-8, -5], main_cam, font_size=0.05, fpu=first_person_ui) + render_AN(AoA_str, AoA_color, [-7, 4.5], main_cam, font_size=0.05, fpu=first_person_ui) + render_AN(G_str, G_color, [4, 4.5], main_cam, font_size=0.05, fpu=first_person_ui) + render_AN(rockets_str, rockets_color, [2, -5.5], main_cam, font_size=0.05, fpu=first_person_ui) + render_AN(APU_str, APU_color, [-8, -5.4], main_cam, font_size=0.05, fpu=first_person_ui) + # render_AN(prop_str, prop_color, [-8, -5.8], main_cam, font_size=0.05, fpu=first_person_ui) + render_AN(city_str, city_color, [0.5, -3.8], main_cam, font_size=0.08, fpu=first_person_ui) + + glfw.swap_buffers(window) + + set_channel_volume(1, AP.engine.compressor.rpm/AP.engine.turbine.max_rpm * 0.5) # engine + set_channel_volume(2, min(np.linalg.norm(AP.vel) / 500, 1) * 0.5) # airflow + set_channel_volume(3, min(G / 10, 1) * 0.5) # airflow disturbance + + do_warnings(AP, AoA, G) + + if AP.state == "LANDED" and np.linalg.norm(AP.vel) < 0.5 and AP_city and not city_panel_shown: + render_AN("See the console window for city operations.", magenta, [-5, 2], main_cam, font_size=0.08, fpu=first_person_ui) + glfw.swap_buffers(window) + + AP.vel = np.array([0, 0, 0]) + AP.ang_vel = np.array([0, 0, 0]) + AP.engine.compressor.rpm = 0 + + # welcoming to the city + print("") + print("Welcome to " + AP_city.name + "!") + print("") + print(AP_city.desc) + + in_city = True + while in_city: + print("\nPlease make a choice: 1) Buy Commodities, 2) Sell Commodities, 3) Buy Aircraft Components, 9) Leave City") + choice = input(" > ") + + if choice: + choice = int(choice) + + if choice == 1: + for key in AP_city.commodities: + print("\nYour cash:", player_money) + print("\nFree cargo space:", AP.cargo_space - sum(player_cargo.values())) + print("Commodity: " + key + "\nPrice: " + str(AP_city.commodities[key])) + buy_units = input("How many units would you like to buy? > ") + + if buy_units: + buy_units = int(buy_units) + else: + buy_units = 0 + + if buy_units * AP_city.commodities[key] > player_money: + print("You do not have enough money to buy " + str(buy_units) + " units of " + key + "!") + elif buy_units + sum(player_cargo.values()) > AP.cargo_space: + print("You do not have enough cargo space to buy " + str(buy_units) + " units of " + key + "!") + else: + player_money -= buy_units * AP_city.commodities[key] + player_cargo[key] += buy_units + + elif choice == 2: + for key in player_cargo: + if player_cargo[key] > 0: + + print("\nCommodity: " + key + "\nPrice: " + str(AP_city.commodities[key]) + "\nUnits in hold: " + str(player_cargo[key])) + sell_units = int(input("How many units would you like to sell? > ")) + + if sell_units: + sell_units = int(sell_units) + else: + sell_units = 0 + + if sell_units > player_cargo[key]: + print("You do not have " + str(sell_units) + " units of " + key + " in your cargo hold!") + else: + player_money += sell_units * AP_city.commodities[key] + player_cargo[key] -= sell_units + print("Your cash:", player_money) + + elif choice == 3: + for part in AP_city.parts: + print("\nYour cash:", player_money) + print("Component: " + part.name + "\nManufacturer: " + part.manufacturer + "\nPrice: " + str(part.price)) + buy_yn = input("Would you like to buy this component (y/N)? > ") + + if buy_yn.lower() == "y": + if part.price <= player_money: + if part.type == "airframe": + if sum(player_cargo.values()) > part.cargo_space: + print("The new airframe does not have enough cargo space to hold your cargo!") + else: + player_airframe = part + print("Purchased " + part.name + "!") + elif part.type == "engine": + player_engine = part + print("Purchased " + part.name + "!") + + player_money -= part.price + + AP.mass = player_airframe.mass + AP.Cds = np.array([0.6, 0.8, 0.1]) * player_airframe.Cd + AP.control_effectiveness = np.array([1.8, 1.8, 2.5]) * player_airframe.effectiveness + AP.cargo_space = player_airframe.cargo_space + + AP.engine = player_engine + + else: + print("You do not have enough money!") + + elif choice == 9: + in_city = False + + city_panel_shown = True + print("Please DO NOT CLOSE THIS WINDOW. You may return to the flight screen.") + + if city_panel_shown and AP.state != "LANDED": + city_panel_shown = False + + if hundred_cycle > 99: + hundred_cycle = 0 + + # update which city the player is in + city_found = False + + for c in cities: + if np.linalg.norm(AP.pos + c.pos) < c.size * 1e4: + city_found = True + + if not c == AP_city: + AP_city = c + + if current_encounter == None: + play_bgm(c.bgm) + + break + + if not city_found: + if not AP_city == None: + AP_city = None + if current_encounter == None: + play_bgm("pluvious") + + # ENCOUNTERS + if AP.state == "INFLIGHT" and current_encounter == None: + chance = random.uniform(0, 1) + if chance < encounter_chance: + current_encounter = Encounter(AP, "action", airframes, engines) + bodies.append(current_encounter.enemy) + + hundred_cycle += 1 + + dt = time.perf_counter() - t_cycle_start + + glfw.destroy_window(window) + stop_channel(1) + stop_channel(2) + stop_channel(3) + stop_channel(4) + stop_channel(5) + stop_channel(6) + fade_out_bgm() + +main() diff --git a/data/industry/engines/APS_7K.json b/data/industry/engines/APS_7K.json index e832073..58e1ae4 100644 --- a/data/industry/engines/APS_7K.json +++ b/data/industry/engines/APS_7K.json @@ -2,11 +2,11 @@ "type": "engine", "name": "APS 7K", "manufacturer": "AG Power Works", - "thrust_coeff": 5.3, - "turbine_coeff": 365, + "thrust_coeff": 6, + "turbine_coeff": 10, "compressor_coeff": 0.155, "max_rpm": 7000, - "fuel_consumption": 1, - "APU_fuel_consumption": 0.5, + "fuel_consumption": 0.1, + "APU_fuel_consumption": 0.05, "price": 45000 } diff --git a/data/industry/engines/APS_9K.json b/data/industry/engines/APS_9K.json index c2379ea..1c675a7 100644 --- a/data/industry/engines/APS_9K.json +++ b/data/industry/engines/APS_9K.json @@ -2,11 +2,11 @@ "type": "engine", "name": "APS 9K", "manufacturer": "AG Power Works", - "thrust_coeff": 5.3, - "turbine_coeff": 380, + "thrust_coeff": 6, + "turbine_coeff": 12, "compressor_coeff": 0.165, "max_rpm": 9000, - "fuel_consumption": 1.2, - "APU_fuel_consumption": 0.5, + "fuel_consumption": 0.12, + "APU_fuel_consumption": 0.05, "price": 120000 } diff --git a/data/industry/engines/KS_X7.json b/data/industry/engines/KS_X7.json index 426a5d3..8bf4efd 100644 --- a/data/industry/engines/KS_X7.json +++ b/data/industry/engines/KS_X7.json @@ -3,10 +3,10 @@ "name": "KS X7", "manufacturer": "Kobesaki", "thrust_coeff": 5.15, - "turbine_coeff": 360, + "turbine_coeff": 15, "compressor_coeff": 0.15, "max_rpm": 7000, - "fuel_consumption": 0.5, - "APU_fuel_consumption": 0.2, + "fuel_consumption": 0.05, + "APU_fuel_consumption": 0.02, "price": 50000 } diff --git a/data/industry/engines/KS_X9.json b/data/industry/engines/KS_X9.json index 430129b..3a70ec7 100644 --- a/data/industry/engines/KS_X9.json +++ b/data/industry/engines/KS_X9.json @@ -3,10 +3,10 @@ "name": "KS X9", "manufacturer": "Kobesaki", "thrust_coeff": 5.15, - "turbine_coeff": 375, + "turbine_coeff": 19, "compressor_coeff": 0.16, "max_rpm": 9000, - "fuel_consumption": 0.7, - "APU_fuel_consumption": 0.5, + "fuel_consumption": 0.07, + "APU_fuel_consumption": 0.05, "price": 90000 } diff --git a/data/industry/engines/SE_7000.json b/data/industry/engines/SE_7000.json index 6b32bd6..2338d6a 100644 --- a/data/industry/engines/SE_7000.json +++ b/data/industry/engines/SE_7000.json @@ -3,10 +3,10 @@ "name": "SE 7000", "manufacturer": "Special Electricity", "thrust_coeff": 5, - "turbine_coeff": 350, + "turbine_coeff": 7.5, "compressor_coeff": 0.15, "max_rpm": 7000, - "fuel_consumption": 0.6, - "APU_fuel_consumption": 0.4, + "fuel_consumption": 0.06, + "APU_fuel_consumption": 0.04, "price": 15000 } diff --git a/data/industry/engines/SE_9000.json b/data/industry/engines/SE_9000.json index c2d7ced..352df24 100644 --- a/data/industry/engines/SE_9000.json +++ b/data/industry/engines/SE_9000.json @@ -3,10 +3,10 @@ "name": "SE 9000", "manufacturer": "Special Electricity", "thrust_coeff": 5, - "turbine_coeff": 380, + "turbine_coeff": 9, "compressor_coeff": 0.16, "max_rpm": 9000, - "fuel_consumption": 0.8, - "APU_fuel_consumption": 0.4, + "fuel_consumption": 0.08, + "APU_fuel_consumption": 0.05, "price": 25000 } diff --git a/encounter.py b/encounter.py index df4096b..91534de 100644 --- a/encounter.py +++ b/encounter.py @@ -19,10 +19,17 @@ def generate_enemy(self, airframes, engines): player = self.player enemy_airframe_key = random.choice(list(airframes.keys())) - enemy_engine_key = random.choice(list(engines.keys())) - enemy_airframe = airframes[enemy_airframe_key] - enemy_engine = engines[enemy_engine_key] + + enemy_engine = None + # do not pick the same engine as the player + # due to a Python quirk, they become the same object + # whatever the computer controlled enemy does to the engine + # it happens to the player's engine too + # TODO: Fix this. + while ((not enemy_engine) or (enemy_engine == player.engine)): + enemy_engine_key = random.choice(list(engines.keys())) + enemy_engine = engines[enemy_engine_key] # INIT VESSEL init_pos = player.pos + np.array([random.uniform(-1000, 1000), random.uniform(100, 500), random.uniform(-1000, 1000)]) # m diff --git a/main.py b/main.py index 6b90d07..0f290f8 100644 --- a/main.py +++ b/main.py @@ -52,7 +52,7 @@ def read_industry(): throttle_range = [0, 100] efficiency = 0.6 - air_fuel_ratio = 2000 # this is an air volume vs. fuel MASS ratio, so it is not going to agree with anything familiar of course + air_fuel_ratio = 20000 # this is an air volume vs. fuel MASS ratio, so it is not going to agree with anything familiar of course max_fuel_rate = data['fuel_consumption'] APU_fuel_rate = data['APU_fuel_consumption'] engines[data['name']] = Turbojet(data['name'], intake, compressor, turbine, nozzle, efficiency, air_fuel_ratio, max_fuel_rate, throttle_range, APU_fuel_rate, data['price'], data['manufacturer']) @@ -94,11 +94,9 @@ def window_resize(window, width, height): init_inertia = np.array([[6000.0, 0.0, 0.0], [0.0, 6000.0, 0.0], [0.0, 0.0, 3000.0]]) # kg m2 - max_thrust = 5e3 # N throttle_range = [0, 100] # % throttle = 100 # % prop_mass = 150 # kg - mass_flow = 0.001 # kg s-1 plane_model = Model("plane_cockpit") init_CoM = np.array([0.0, 0.0, 2.0]) @@ -161,7 +159,6 @@ def window_resize(window, width, height): "Household Machinery": 400, "Heavy Machinery": 120, "Raw Ore": 0.1, - "Fuel": 25, "Construction Materials": 20, "Luxuries": 4000, "Precious Metals": 7000}, @@ -185,7 +182,6 @@ def window_resize(window, width, height): "Household Machinery": 300, "Heavy Machinery": 80, "Raw Ore": 5, - "Fuel": 60, "Construction Materials": 18, "Luxuries": 2500, "Precious Metals": 15000}, @@ -209,7 +205,6 @@ def window_resize(window, width, height): "Household Machinery": 350, "Heavy Machinery": 100, "Raw Ore": 3, - "Fuel": 40, "Construction Materials": 28, "Luxuries": 2900, "Precious Metals": 10000}, @@ -326,7 +321,6 @@ def rotate_cam(rotation): "Household Machinery": 0, "Heavy Machinery": 0, "Raw Ore": 0, - "Fuel": 0, "Construction Materials": 0, "Luxuries": 0, "Precious Metals": 0} @@ -559,7 +553,7 @@ def rotate_cam(rotation): render_AN(G_str, G_color, [4, 4.5], main_cam, font_size=0.05, fpu=first_person_ui) render_AN(rockets_str, rockets_color, [2, -5.5], main_cam, font_size=0.05, fpu=first_person_ui) render_AN(APU_str, APU_color, [-8, -5.4], main_cam, font_size=0.05, fpu=first_person_ui) - # render_AN(prop_str, prop_color, [-8, -5.8], main_cam, font_size=0.05, fpu=first_person_ui) + render_AN(prop_str, prop_color, [-8, -5.8], main_cam, font_size=0.05, fpu=first_person_ui) render_AN(city_str, city_color, [0.5, -3.8], main_cam, font_size=0.08, fpu=first_person_ui) glfw.swap_buffers(window) @@ -586,7 +580,7 @@ def rotate_cam(rotation): in_city = True while in_city: - print("\nPlease make a choice: 1) Buy Commodities, 2) Sell Commodities, 3) Buy Aircraft Components, 9) Leave City") + print("\nPlease make a choice: 1) Buy Commodities, 2) Sell Commodities, 3) Buy Aircraft Components, 4) Refuel, 9) Leave City") choice = input(" > ") if choice: @@ -659,7 +653,20 @@ def rotate_cam(rotation): AP.engine = player_engine else: - print("You do not have enough money!") + print("You do not have enough money!") + + elif choice == 4: + if AP.prop_mass < 150: + fuel_cost = (150 - AP.prop_mass) * 0.3 + if player_money > fuel_cost: + AP.prop_mass = 150 + player_money = player_money - fuel_cost + print("Your fuel tank has been filled.") + print("Your cash:", player_money) + else: + print("You do not have enough money to refuel! " + str(int(fuel_cost) + 1) + " credits required.") + else: + print("Your fuel tank is full.") elif choice == 9: in_city = False diff --git a/propulsion.py b/propulsion.py index fc35da6..02f20a4 100644 --- a/propulsion.py +++ b/propulsion.py @@ -63,9 +63,14 @@ def __init__(self, name, intake, compressor, turbine, nozzle, efficiency, air_fu self.thrust = 0 self.fuel_rate = 0 - def compute_thrust(self, vel, dt): + def compute_thrust(self, prop_mass, vel, dt): self.throttle = max(min(self.throttle, 1), 0) intake_air_momentum = self.intake.compute_air_intake(vel) + + if prop_mass <= 0: + self.thrust = 0 + self.compressor.rpm = self.compressor.rpm * (1 - 0.2 * dt) + return if not self.compressor.state == "STALL": intake_air_suction = self.compressor.compute_suction() diff --git a/quick_dogfight.py b/quick_dogfight.py index 5d873cf..e63b45e 100644 --- a/quick_dogfight.py +++ b/quick_dogfight.py @@ -52,7 +52,7 @@ def read_industry(): throttle_range = [0, 100] efficiency = 0.6 - air_fuel_ratio = 2000 # this is an air volume vs. fuel MASS ratio, so it is not going to agree with anything familiar of course + air_fuel_ratio = 20000 # this is an air volume vs. fuel MASS ratio, so it is not going to agree with anything familiar of course max_fuel_rate = data['fuel_consumption'] APU_fuel_rate = data['APU_fuel_consumption'] engines[data['name']] = Turbojet(data['name'], intake, compressor, turbine, nozzle, efficiency, air_fuel_ratio, max_fuel_rate, throttle_range, APU_fuel_rate, data['price'], data['manufacturer']) @@ -132,7 +132,7 @@ def window_resize(window, width, height): rear_gear_moment, brake_force, cargo_space, weapons) rocket_pod.platform = AP - AP.set_thrust_percent(0) + AP.set_thrust_percent(100) bodies = [AP] diff --git a/rigidbody.py b/rigidbody.py index ca1b8ab..282c1fb 100644 --- a/rigidbody.py +++ b/rigidbody.py @@ -295,7 +295,7 @@ def drain_fuel(self, dt): self.engine.thrust = 0 def apply_thrust(self, dt): - self.engine.compute_thrust(self.vel, dt) + self.engine.compute_thrust(self.prop_mass, self.vel, dt) self.apply_force(self.orient[2] * self.engine.thrust) def set_thrust_percent(self, percentage):