Added coloured text using Rich, also better storage handling.

This commit is contained in:
Chris Davoren 2023-07-11 17:26:52 +10:00
parent 8897a97777
commit d507c1b1d1
2 changed files with 145 additions and 101 deletions

View File

@ -1,3 +1,8 @@
beautifulsoup4==4.12.2 beautifulsoup4==4.12.2
colorama==0.4.6
lxml==4.9.2 lxml==4.9.2
markdown-it-py==3.0.0
mdurl==0.1.2
Pygments==2.15.1
rich==13.4.2
soupsieve==2.4.1 soupsieve==2.4.1

View File

@ -1,60 +1,76 @@
import sys, argparse, copy, os, datetime import argparse
import copy
import os
import datetime
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import rich
from rich.console import Console
# Only "normal storage" is used to insert requested items
normal_storage_ids = [82, 632]
storage_ids = {
82: {"name": "Small storage", "capacity": 50},
632: {"name": "Large storage", "capacity": 250},
3062: {"name": "Body storage", "capacity": 20},
}
char_skills = { char_skills = {
1 : "Piloting", # Likely deprecated 1: "Piloting", # Likely deprecated - now using code 22
2 : "Mining", 2: "Mining",
3 : "Botany", 3: "Botany",
4 : "Construction", 4: "Construction",
5 : "Industry", 5: "Industry",
6 : "Medical", 6: "Medical",
7 : "Gunner", 7: "Gunner",
8 : "Shielding", 8: "Shielding",
9 : "Operations", 9: "Operations",
10 : "Weapons", 10: "Weapons",
11 : "Unknown-1", 11: "Unknown-1",
12 : "Logistics", # All characters seem to have this but it doesn't show up in the UI 12: "Logistics", # All characters seem to have this but it doesn't show up in the UI
13 : "Unknown-2-enigma", # All characters seem to have this skill but what is it for? 13: "Unknown-2-enigma", # All characters seem to have this skill but what is it for?
14 : "Navigation", 14: "Navigation",
15 : "Unknown-3", 15: "Unknown-3",
16 : "Research", 16: "Research",
22 : "Piloting", # Appears to be the new piloting code? 22: "Piloting", # Appears to be the new piloting code?
} }
char_attributes = { char_attributes = {
210 : "Bravery", 210: "Bravery",
214 : "Preception", 214: "Preception",
213 : "Intelligence", 213: "Intelligence",
212 : "Zest", 212: "Zest",
} }
char_traits = { char_traits = {
"Iron Stomach" : 1533, "Iron Stomach": 1533,
"Spacefarer" : 1045, "Spacefarer": 1045,
"Confident" : 1046, "Confident": 1046,
"Hard Working" : 1041, "Hard Working": 1041,
"Antisocial" : 1037, "Antisocial": 1037,
"Nyctophilia" : 1534, "Nyctophilia": 1534,
"Wimp" : 655, "Wimp": 655,
"Clumsy" : 656, "Clumsy": 656,
"Charming" : 1048, "Charming": 1048,
"Bloodlust" : 1036, "Bloodlust": 1036,
"Fast Learner" : 1039, "Fast Learner": 1039,
"Minimalist" : 1535, "Minimalist": 1535,
"Lazy" : 1040, "Lazy": 1040,
"Neurotic" : 1047, "Neurotic": 1047,
"Alien Lover" : 2082, "Alien Lover": 2082,
"Needy" : 1038, "Needy": 1038,
"Peace-loving" : 1043, "Peace-loving": 1043,
"Suicidal" : 1034, "Suicidal": 1034,
"Smart" : 1035, "Smart": 1035,
"Psychopath" : 1042, "Psychopath": 1042,
"Hero" : 191, "Hero": 191,
"Talkative" : 1560, "Talkative": 1560,
"Iron-Willed" : 1044, "Iron-Willed": 1044,
"Gourmand" : 1562, "Gourmand": 1562,
} }
class ItemCodeDatabase: class ItemCodeDatabase:
def __init__(self, database_filename): def __init__(self, database_filename):
@ -88,6 +104,7 @@ class StorageArea:
def __init__(self, tag): def __init__(self, tag):
self.tag = tag self.tag = tag
self.type_id = None
self.items = [] self.items = []
self.is_abnormal_storage = False self.is_abnormal_storage = False
@ -148,15 +165,24 @@ class Character:
return new_char return new_char
def print_summary(self): def print_summary(self):
print("Name: {}".format(self.name)) console = Console(highlight=False)
print() console.print("Name: [bright_cyan]{}[/]".format(self.name))
print("Skills:") console.print()
for skill in self.skills: console.print("Skills:")
print("{} : {}".format(char_skills[skill['id']], skill['level'])) for i, skill in enumerate(self.skills):
print() row_string = "{:20} [bright_cyan]{:2}[/]".format(char_skills[skill['id']], skill['level'])
print("Attributes:") if i % 2 == 0:
for attribute in self.attributes: console.print(" [on #222222]{}[/]".format(row_string))
print("{} : {}".format(char_attributes[attribute['id']], attribute['points'])) else:
console.print(" {}".format(row_string))
console.print()
console.print("Attributes:")
for i, attribute in enumerate(self.attributes):
row_string = "{:12} [bright_cyan]{:2}[/]".format(char_attributes[attribute['id']], attribute['points'])
if i % 2 == 0:
console.print(" [on #222222]{}[/]".format(row_string))
else:
console.print(" {}".format(row_string))
def __repr__(self): def __repr__(self):
return "{} - Skills: {} - Attributes: {}".format(self.name, repr(self.skills), repr(self.attributes)) return "{} - Skills: {} - Attributes: {}".format(self.name, repr(self.skills), repr(self.attributes))
@ -223,6 +249,7 @@ class GameData:
ship_name = ship_tag['sname'] ship_name = ship_tag['sname']
# print(ship_name) # print(ship_name)
# Forgotten - why did I specify owner as required here - what ships don't have owners?
owner_node = ship_tag.find('settings', owner=True) owner_node = ship_tag.find('settings', owner=True)
ship_owner = owner_node['owner'] ship_owner = owner_node['owner']
ship_state = owner_node['state'] ship_state = owner_node['state']
@ -233,8 +260,10 @@ class GameData:
# Step 3 - Storage area data # Step 3 - Storage area data
for inv_tag in ship_tag.find_all('feat', eatAllowed=True): for inv_tag in ship_tag.find_all('feat', eatAllowed=True):
storage_area = StorageArea(inv_tag.find('inv')) storage_area = StorageArea(inv_tag.find('inv'))
# This is a GUESS # 632 is the "m" id code for LARGE storage
if inv_tag.find('env'): storage_area.type_id = int(inv_tag.parent.parent['m'])
if storage_area.type_id not in normal_storage_ids: # TODO: Un-hardcode this AND take into account SMALL storage
# rich.print("[on #222222]Found abnormal storage id [#666666]{}[/][/]".format(inv_tag.parent.parent['m']))
storage_area.is_abnormal_storage = True storage_area.is_abnormal_storage = True
ship.add_storage_area(storage_area) ship.add_storage_area(storage_area)
# Items within storage area # Items within storage area
@ -248,7 +277,7 @@ class GameData:
# Step 4 - Character data # Step 4 - Character data
for character_list in ship_tag.find_all('characters'): for character_list in ship_tag.find_all('characters'):
character_tags = character_list.find_all('c', attrs={'name':True}) character_tags = character_list.find_all('c', attrs={'name': True})
for character_tag in character_tags: for character_tag in character_tags:
char_name = character_tag['name'] char_name = character_tag['name']
character = Character(char_name, character_tag) character = Character(char_name, character_tag)
@ -263,11 +292,11 @@ class GameData:
skill_exp = sk_tag['exp'] skill_exp = sk_tag['exp']
skill_expd = sk_tag['expd'] skill_expd = sk_tag['expd']
skill_dict = { skill_dict = {
'id' : int(skill_id), 'id': int(skill_id),
'level' : int(skill_level), 'level': int(skill_level),
'mxn' : int(skill_mxn), 'mxn': int(skill_mxn),
'exp' : int(skill_exp), 'exp': int(skill_exp),
'expd' : int(skill_expd), 'expd': int(skill_expd),
} }
skills.append(skill_dict) skills.append(skill_dict)
character.set_skills(skills) character.set_skills(skills)
@ -278,13 +307,12 @@ class GameData:
attribute_id = a_tag['id'] attribute_id = a_tag['id']
attribute_points = a_tag['points'] attribute_points = a_tag['points']
attribute_dict = { attribute_dict = {
'id' : int(attribute_id), 'id': int(attribute_id),
'points' : int(attribute_points), 'points': int(attribute_points),
} }
attributes.append(attribute_dict) attributes.append(attribute_dict)
character.set_attributes(attributes) character.set_attributes(attributes)
def writeback(self): def writeback(self):
def replace_id(dict, old_key, new_key): def replace_id(dict, old_key, new_key):
dict_copy = dict.copy() dict_copy = dict.copy()
@ -333,7 +361,7 @@ class GameData:
area_tag = storage_area.tag area_tag = storage_area.tag
area_tag.clear() area_tag.clear()
for item in storage_area.items: for item in storage_area.items:
tag_dict = { 'elementaryId' : item.code, 'inStorage' : item.quantity, 'onTheWayIn' : 0, 'onTheWayOut' : 0} tag_dict = {'elementaryId': item.code, 'inStorage': item.quantity, 'onTheWayIn': 0, 'onTheWayOut': 0}
new_tag = self.soup.new_tag('s', attrs=tag_dict) new_tag = self.soup.new_tag('s', attrs=tag_dict)
area_tag.append(new_tag) area_tag.append(new_tag)
@ -364,40 +392,53 @@ class GameData:
return return
def print_detailed_character_summary(self): def print_detailed_character_summary(self):
console = Console(highlight=False)
for ship in self.ships: for ship in self.ships:
console.print("Listing characters for ship [magenta]{}[/] (owned by [bright_cyan]{}[/], state [bright_cyan]{}[/]):".format(ship.name, ship.owner, ship.state))
if len(ship.characters) == 0: if len(ship.characters) == 0:
print("Ship {} (owned by {}, state {}) has no characters, skipping...".format(ship.name, ship.owner, ship.state)) console.print(" This ship has no characters, skipping...")
print() console.print()
console.print()
continue continue
print("Listing characters for ship {} (owned by {}, state {}):".format(ship.name, ship.owner, ship.state))
print('-----')
for character in ship.characters: for character in ship.characters:
character.print_summary() character.print_summary()
print('-----') console.print('-----')
print() console.print()
def print_detailed_item_summary(self): def print_detailed_item_summary(self):
console = Console(highlight=False)
for ship in self.ships: for ship in self.ships:
rich.print("Inventory for ship [#aa00ff]{}[/] (owned by [bright_cyan]{}[/], state [bright_cyan]{}[/]):".format(ship.name, ship.owner, ship.state))
if len(ship.storage_areas) == 0: if len(ship.storage_areas) == 0:
print("Ship {} (owned by {}, state {}) has no storage areas, skipping...".format(ship.name, ship.owner, ship.state)) console.print(" This ship has no storage areas, skipping...")
print() console.print()
console.print()
continue continue
print("Inventory for ship {} (owned by {}, state {})".format(ship.name, ship.owner, ship.state))
for index, storage_area in enumerate(ship.storage_areas): for index, storage_area in enumerate(ship.storage_areas):
console.print(" Storage area [bright_cyan]{}[/] (type: [bright_cyan]{}[/], abnormal storage: [bright_cyan]{}[/]):".format(index, storage_ids[storage_area.type_id]["name"], storage_area.is_abnormal_storage))
if len(storage_area.items) == 0: if len(storage_area.items) == 0:
print(" Storage area {} is empty.".format(index)) console.print(" This storage area is empty.")
console.print()
continue continue
print(" Storage area {} (abnormal storage: {}):".format(index, storage_area.is_abnormal_storage)) console.print("[bold] {:>4} {:30} {:>3}[/]".format("ID", "Name", "#"))
for item in storage_area.items: total_quantity = 0
print(" {:4}: {} - {}".format(item.code, item.name, item.quantity)) for i, item in enumerate(storage_area.items):
row_string = " {:4} {:30} {:3}".format(item.code, item.name, item.quantity)
print() total_quantity += item.quantity
print() if i % 2 == 0:
console.print(" [on #222222]{}[/]".format(row_string))
else:
console.print(" {}".format(row_string))
storage_area_capacity = storage_ids[storage_area.type_id]["capacity"]
console.print(" Total quantity: [bright_cyan]{}[/]/[bright_cyan]{}[/]".format(total_quantity, storage_area_capacity))
if total_quantity > storage_area_capacity:
console.print(" [dark_orange]WARNING: Storage capacity exceeded[/]")
console.print()
console.print()
def print_summary(self): def print_summary(self):
print("Start game summary:") print("Start game summary:")
@ -503,7 +544,6 @@ def inventory(soup, add_code, add_quantity):
storage_space_counter += 1 storage_space_counter += 1
print('-----') print('-----')
print('Item total summary:') print('Item total summary:')
print() print()
for item in item_tracking.items(): for item in item_tracking.items():
@ -538,9 +578,9 @@ def list_ships(soup):
ship_owner = owner_node['owner'] ship_owner = owner_node['owner']
ship_state = owner_node['state'] ship_state = owner_node['state']
print('Ship found: {} owned by {} (state: {})'.format(ship_name, ship_owner, ship_state)) print('Ship found: {} owned by {} (state: {})'.format(ship_name, ship_owner, ship_state))
# print(settings_node) # print(settings_node)
def parse_item_file(filename): def parse_item_file(filename):
results = [] results = []
@ -591,7 +631,8 @@ def main():
soup = BeautifulSoup(full_text, "xml") soup = BeautifulSoup(full_text, "xml")
item_code_database = ItemCodeDatabase('item_ids.txt') item_code_database = ItemCodeDatabase('item_ids.txt')
print("Item code database successfully loaded") print("Item code database successfully loaded.")
print()
game_data = GameData(soup, item_code_database) game_data = GameData(soup, item_code_database)
game_data.populate() game_data.populate()
@ -620,7 +661,7 @@ def main():
if args.add_item_set: if args.add_item_set:
item_list = parse_item_file(args.add_item_set[0]) item_list = parse_item_file(args.add_item_set[0])
# print(item_list) # print(item_list)
print ("Items to be added:") print("Items to be added:")
for (item_code, item_quantity) in item_list: for (item_code, item_quantity) in item_list:
print("{} : {}".format(item_code_database.get_name_from_code(item_code), item_quantity)) print("{} : {}".format(item_code_database.get_name_from_code(item_code), item_quantity))
game_data.add_item(item_code, item_quantity) game_data.add_item(item_code, item_quantity)
@ -635,9 +676,6 @@ def main():
game_data.writeback() game_data.writeback()
if args.test_gamedata: if args.test_gamedata:
item_code_database = ItemCodeDatabase('item_ids.txt')
print("Item code database successfully loaded")
game_data = GameData(soup, item_code_database) game_data = GameData(soup, item_code_database)
game_data.populate() game_data.populate()
# game_data.add_item(1759, 100) # game_data.add_item(1759, 100)
@ -675,5 +713,6 @@ def main():
f.write(sansfirstline) f.write(sansfirstline)
f.close() f.close()
if __name__ == "__main__": if __name__ == "__main__":
main() main()