-
-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A python script to generate ship view #130
Changes from 4 commits
fdd6f48
1a458e5
1270946
5a79352
d8cf1f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,304 @@ | ||
import locale | ||
import math | ||
import json | ||
|
||
|
||
newline = "#n#" | ||
red = "#c1:.3:.3#" | ||
green = "#c0:1:.5#" | ||
light_grey = "#c.75:.9:1#" | ||
grey = "#c.6:.7:.8#" | ||
light_yellow = "#c.675:.925:.825#" | ||
end_color = "#-c" | ||
|
||
non_combat_speed_multiplier = 1 | ||
megajoules_multiplier = 1 | ||
|
||
llama_begin = {} | ||
|
||
with open('config.json', 'r') as file: | ||
data = json.load(file) | ||
non_combat_speed_multiplier = data['components']['drive']['non_combat_mode_multiplier'] | ||
megajoules_multiplier = data['constants']['megajoules_multiplier'] | ||
|
||
with open('units/units.json', 'r') as file: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
data = json.load(file) | ||
|
||
for ship in data: | ||
if ship['Key'] == 'Llama.begin': | ||
llama_begin = ship | ||
break | ||
|
||
|
||
# Format large number | ||
def lnum(ship_stats, key, divider = 1.0): | ||
num = float(ship_stats[key])/divider | ||
num = '{:n}'.format(num) | ||
return num | ||
|
||
def get(ship_stats, key): | ||
return ship_stats[key] | ||
|
||
def get_bool(ship_stats, key): | ||
s = ship_stats[key] | ||
if(s == 'TRUE' or s=='1'): | ||
return True | ||
return False | ||
|
||
def get_int(ship_stats, key): | ||
return int(ship_stats[key]) | ||
|
||
def get_dbl(ship_stats, key, divider = 1.0): | ||
fl = float(ship_stats[key])/divider | ||
#s = f"{key} {fl}" | ||
#print(s) | ||
return fl | ||
|
||
def get_fmt_dbl(ship_stats, key, divider = 1.0): | ||
return "{:.2f}".format(float(ship_stats[key])) | ||
|
||
def fmt_dbl(dbl): | ||
return "{:.2f}".format(dbl) | ||
|
||
def near_equal(a,b): | ||
return a-b < 0.01 and b-a < 0.01 | ||
|
||
def get_notes(ship_stats): | ||
text = f"{green}[NOTES]{newline}{newline}" | ||
text += f"{grey}#-c{ship_stats['Textual_Description']}{newline}{newline}" | ||
return text | ||
|
||
def get_variant(key): | ||
# TODO: refactor with dict and for/each | ||
if key.endswith('blank'): | ||
return '' | ||
if key.endswith('begin'): | ||
return 'Variant: #-cStock (Refurbished)' | ||
if key.endswith('stock'): | ||
return 'Variant: #-cStock' | ||
if key.endswith('civvie'): | ||
return 'Variant: #-cCivilian' | ||
if key.endswith('milspec'): | ||
return 'Variant: #-cMilspec' | ||
return '' | ||
|
||
def get_itts(itts): | ||
if(itts=='1'): | ||
return 'yes' | ||
return 'no' | ||
|
||
def get_iff(iff): | ||
if iff=='0': return 'none' | ||
if iff=='1': return 'friend/foe' | ||
if iff=='2': return 'object recognition' | ||
return iff | ||
|
||
# General | ||
def get_general(ship_stats): | ||
text = f"{green}[GENERAL INFORMATION]{newline}" | ||
text += f"#-c{newline}{light_grey}Model: #-c{ship_stats['Name']}{light_grey} {get_variant(ship_stats['Key'])}{newline}" | ||
text += f"{light_grey}Mass: #-c{lnum(ship_stats,'Mass')} metric tons{newline}" | ||
text += f"{light_grey}Hold volume: #-c{lnum(ship_stats,'Hold_Volume')} cubic meters{newline}" | ||
text += f"{light_grey}Upgrade volume: #-c{lnum(ship_stats,'Upgrade_Storage_Volume')} cubic meters{newline}" | ||
text += f"{light_grey}Fuel capacity: #-c{lnum(ship_stats,'Fuel_Capacity')} metric tons of Lithium-6{newline}{newline}" | ||
return text | ||
|
||
# Flight Characteristics | ||
def get_flight(ship_stats): | ||
mass = get_dbl(ship_stats,'Mass') | ||
moment = get_dbl(ship_stats,'Moment_Of_Inertia') | ||
divider = 9.8 * mass | ||
divider_maneuver = moment * 180 / math.pi | ||
yaw = get_dbl(ship_stats,'Maneuver_Yaw', divider_maneuver) | ||
pitch = get_dbl(ship_stats,'Maneuver_Pitch', divider_maneuver) | ||
roll = get_dbl(ship_stats,'Maneuver_Roll', divider_maneuver) | ||
|
||
forward = get_dbl(ship_stats,'Forward_Accel', divider) | ||
retro = get_dbl(ship_stats,'Retro_Accel', divider) | ||
|
||
lateral = (get_dbl(ship_stats,'Left_Accel', divider) + get_dbl(ship_stats,'Right_Accel', divider))/2 | ||
vertical = (get_dbl(ship_stats,'Top_Accel', divider) + get_dbl(ship_stats,'Bottom_Accel', divider))/2 | ||
|
||
afterburner = get_dbl(ship_stats,'Afterburner_Accel', divider) | ||
|
||
text = f"{green}[FLIGHT CHARACTERISTICS]{newline}" | ||
|
||
if(yaw == pitch and yaw == roll): | ||
text += f"#-c{newline}{light_grey}Turning response: #-c{fmt_dbl(yaw)} radians/second²{newline}" | ||
text += f"{grey} (yaw, pitch, roll)#-c{newline}" | ||
else: | ||
text += f"#-c{newline}{light_grey} yaw #-c{fmt_dbl(yaw)} radians/second²{newline}" | ||
text += f"#-c{newline}{light_grey} pitch #-c{fmt_dbl(pitch)} radians/second²{newline}" | ||
text += f"#-c{newline}{light_grey} roll #-c{fmt_dbl(roll)} radians/second²{newline}" | ||
|
||
text += f"{light_grey}Fore acceleration: #-c{fmt_dbl(forward)} gravities{newline}" | ||
text += f"{light_grey}Aft acceleration: #-c{fmt_dbl(retro)} gravities{newline}" | ||
|
||
if(lateral == vertical): | ||
text += f"{light_grey}Orthogonal acceleration: #-c{fmt_dbl(lateral)} gravities{newline}" | ||
text += f"{grey}(vertical and lateral axes)#-c{newline}" | ||
else: | ||
text += f"{light_grey} Lateral acceleration #-c{fmt_dbl(lateral)} gravities{newline}" | ||
text += f"{grey} Vertical acceleration #-c{fmt_dbl(vertical)} gravities{newline}" | ||
text += f"{light_grey}Forward acceleration with overthrust: #-c{fmt_dbl(afterburner)} gravities{newline}{newline}" | ||
return text | ||
|
||
def get_governor(ship_stats): | ||
divider = 180 / math.pi | ||
speed = get_int(ship_stats, 'Default_Speed_Governor') | ||
afterburner = get_int(ship_stats, 'Afterburner_Speed_Governor') | ||
|
||
yaw = get_dbl(ship_stats, 'Yaw_Governor',divider) | ||
#yaw_right = get_dbl(ship_stats, 'Yaw_Governor_Right') | ||
#yaw_left = get_dbl(ship_stats, 'Yaw_Governor_Left') | ||
|
||
pitch = get_dbl(ship_stats, 'Pitch_Governor',divider) | ||
#pitch_down = get_dbl(ship_stats, 'Pitch_Governor_Up') | ||
#pitch_up = get_dbl(ship_stats, 'Pitch_Governor_Down') | ||
|
||
roll = get_dbl(ship_stats, 'Roll_Governor',divider) | ||
#roll_right = get_dbl(ship_stats, 'Roll_Governor_Right') | ||
#roll_left = get_dbl(ship_stats, 'Roll_Governor_Left') | ||
|
||
text = f"{green}[GOVERNOR SETTINGS]{newline}#-c{newline}" | ||
text += f"{light_grey}Max combat speed: #-c{speed} m/s{newline}" | ||
text += f"{light_grey}Max overdrive combat speed: #-c{afterburner} m/s{newline}" | ||
text += f"{light_grey}Max non-combat speed: #-c{speed * non_combat_speed_multiplier} m/s{newline}" | ||
text += f"{light_grey}Max turn rates:#-c{newline}" | ||
text += f"{light_yellow} - yaw: #-c{fmt_dbl(yaw)} radians/second{newline}" | ||
text += f"{light_yellow} - pitch: #-c{fmt_dbl(pitch)} radians/second{newline}" | ||
text += f"{light_yellow} - roll: #-c{fmt_dbl(roll)} radians/second{newline}{newline}" | ||
return text | ||
|
||
def get_radar(ship_stats): | ||
radar_range = lnum(ship_stats,'Radar_Range',1000) | ||
max_cone = get_dbl(ship_stats,'Max_Cone', 180 / math.pi) | ||
tracking_cone = get_dbl(ship_stats,'Tracking_Cone', 180 / math.pi) | ||
lock_cone = get_dbl(ship_stats,'Lock_Cone', 180 / math.pi) | ||
itts = ship_stats['ITTS'] | ||
iff = ship_stats['Radar_Color'] | ||
|
||
text = f"{green}[TARGETTING SUBSYSTEM]{newline}#-c{newline}" | ||
text += f"{light_grey}Tracking range: #-c{radar_range} km{newline}" | ||
|
||
if(near_equal(max_cone,math.pi)): | ||
text += f"{light_grey}Tracking cone: #-cOmni-directional{newline}" | ||
else: | ||
text += f"{light_grey}Tracking cone: #-c{fmt_dbl(max_cone * 2)} radians{newline}" | ||
text += f"{light_grey} (planar angle: 2 pi means full space)#-c{newline}" | ||
text += f"{light_grey}Assisted targeting cone: #-c{fmt_dbl(tracking_cone * 2)} radians{newline}" | ||
text += f"{light_grey}Missile locking cone: #-c{fmt_dbl(lock_cone * 2)} radians{newline}" | ||
text += f"{light_grey}ITTS (Intelligent Target Tracking System) support: #-c{get_itts(itts)}{newline}" | ||
text += f"{light_grey}AFHH (Advanced Flag & Hostility Heuristics) support: #-c{get_iff(iff)} {newline}{newline}" | ||
return text | ||
|
||
def get_energy_spec_and_jump(ship_stats): | ||
reactor = get_dbl(ship_stats,'Reactor_Recharge',1/megajoules_multiplier) | ||
energy = get_dbl(ship_stats,'Primary_Capacitor',1/megajoules_multiplier) | ||
ftl_energy = get_dbl(ship_stats,'Warp_Capacitor',1/megajoules_multiplier) | ||
spec_cost = get_dbl(ship_stats,'Warp_Usage_Cost',1/megajoules_multiplier) | ||
|
||
jump_drive_installed = get_bool(ship_stats,'Jump_Drive_Present') | ||
jump_delay = get_dbl(ship_stats,'Jump_Drive_Delay') | ||
jump_cost = get_dbl(ship_stats,'Outsystem_Jump_Cost',1/megajoules_multiplier) | ||
|
||
|
||
text = f"{green}[ENERGY SUBSYSTEM]{newline}#-c{newline}" | ||
text += f"{light_grey}Recharge: #-c{reactor} MJ/s{newline}" | ||
text += f"{light_grey}Main capacitor: #-c{energy} MJ{newline}" | ||
text += f"{light_grey}SPEC capacitor: #-c{ftl_energy}0 MJ{newline}{newline}" | ||
|
||
text += f"{green}[SPEC SUBSYSTEM]{newline}#-c{newline}" | ||
text += f"{light_grey}Active SPEC Energy Requirements: #-c{spec_cost} MJ/s{newline}{newline}" | ||
|
||
text += f"{green}[JUMP SUBSYSTEM]{newline}#-c{newline}" | ||
if(jump_drive_installed): | ||
text += f"{light_grey}Energy cost for jumpnode travel: #-c{jump_cost} MJ{newline}" | ||
text += f"{light_grey}Delay: #-c{jump_delay} seconds{newline}{newline}" | ||
else: | ||
text += f"{red}No outsystem jump drive present{end_color}{newline}{newline}" | ||
|
||
return text | ||
|
||
def get_durability(ship_stats): | ||
armor = [ | ||
('Fore-starboard-high','Armor_Front_Top_Right'), | ||
('Aft-starboard-high','Armor_Back_Top_Right'), | ||
('Fore-port-high','Armor_Front_Top_Left'), | ||
('Aft-port-high','Armor_Back_Top_Left'), | ||
('Fore-starboard-low','Armor_Front_Bottom_Right'), | ||
('Aft-starboard-low','Armor_Back_Bottom_Right'), | ||
('Fore-port-low','Armor_Front_Bottom_Left'), | ||
('Aft-port-low','Armor_Back_Bottom_Left'), | ||
] | ||
|
||
shield4 = [ | ||
('Port','Shield_Front_Bottom_Left'),('Starboard','Shield_Front_Bottom_Right'),('Fore','Shield_Front_Top_Right'),('Aft','Shield_Back_Top_Left'), | ||
] | ||
|
||
shield2 = [ | ||
('Fore','Shield_Front_Top_Right'),('Aft','Shield_Back_Top_Left'), | ||
] | ||
|
||
# This is a kludge. Should go away when we refactor units.json and unit_csv.cpp | ||
shield_stat = {} | ||
num_emitters = 0 | ||
for pair in shield4: | ||
if ship_stats[pair[1]] == '': | ||
continue | ||
value = get_dbl(ship_stats,pair[1]) | ||
if value > 0: | ||
num_emitters += 1 | ||
|
||
|
||
hull = lnum(ship_stats,'Hull') | ||
shield_recharge = lnum(ship_stats,'Shield_Recharge') | ||
|
||
text = f"{green}[DURABILITY STATISTICS]{newline}#-c{newline}" | ||
text += f"{light_grey}Sustainable Hull Damage: #-c{hull} MJ{newline}{newline}" | ||
|
||
text += f"{light_grey}Armor#-c{newline}" | ||
for pair in armor: | ||
armor_stat = lnum(ship_stats, pair[1]) | ||
text += f"{light_yellow} - {pair[0]}: #-c{armor_stat} MJ{newline}" | ||
|
||
text += f"{newline}{light_grey}Shields#-c{newline}" | ||
text += f"{light_grey}Shield recharge: #-c{shield_recharge} MJ/s{newline}" | ||
if num_emitters == 0: | ||
text += f"{red}No shielding.{end_color}{newline}{newline}" | ||
elif num_emitters == 2: | ||
text += f"{light_grey}Number of shield emitter: 2{end_color}{newline}" | ||
for pair in shield2: | ||
shield_stat = lnum(ship_stats, pair[1]) | ||
text += f"{light_yellow} - {pair[0]}: #-c{shield_stat} MJ{newline}" | ||
else: | ||
text += f"{light_grey}Number of shield emitter: 4{end_color}{newline}" | ||
for pair in shield4: | ||
shield_stat = lnum(ship_stats, pair[1]) | ||
text += f"{light_yellow} - {pair[0]}: #-c{shield_stat} MJ{newline}" | ||
|
||
return text | ||
|
||
|
||
|
||
def get_ship_description(ship_stats): | ||
locale.setlocale(locale.LC_ALL, '') | ||
|
||
text = get_notes(ship_stats) | ||
text += get_general(ship_stats) | ||
text += get_flight(ship_stats) | ||
text += get_governor(ship_stats) | ||
text += get_radar(ship_stats) | ||
text += get_energy_spec_and_jump(ship_stats) | ||
text += get_durability(ship_stats) | ||
|
||
return text | ||
|
||
# Test function | ||
# Useful to check python script for correctness before running VS | ||
|
||
t = get_ship_description(llama_begin) | ||
t = t.replace('#n#','\n') | ||
print(t) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if this is for testing, then it probably should be wrapped. Usually if its a test to be called on the commandline (e.g just see the script output) then using: if __name__ == "__main__":
# python block would be appropriate. If we can control the name and calling it in a test, then may be we call it |
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since
config.json
would have to be in a specific place we need to either:(a) define a working directory for the active "game"
(b) expose these as Python Objects instead of making each script load it if it wants access. This would eliminate the
open
andjson.load
steps; and we'd have to define the interface - either as a pure Python Dictionary/Object or as something else. A first step might be just having aload
method that would load the contents and return thedata
object like you have above.I'd rather
b
between the two.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking more about this...option
a
might actually be a better solution for game asset data. But let's evolve the conversation on this beyond just this PR.