1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-16 17:12:21 +03:00

Merge pull request #1013 from EDCD/enhancement/odyssey-alpha4-suits

Odyssey Alpha4 extra suits-related support
This commit is contained in:
Athanasius 2021-04-30 12:30:06 +01:00 committed by GitHub
commit ae49797632
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 173 additions and 58 deletions

View File

@ -537,16 +537,15 @@ Content of `state` (updated to the current journal entry):
| `Data` | `dict` | 'Data' MicroResources in Odyssey, `int` count each. |
| `BackPack` | `dict` | `dict` of Odyssey MicroResources in backpack. |
| `SuitCurrent` | `dict` | CAPI-returned data of currently worn suit. NB: May be `None` if no data. |
| `Suits` | `dict` or `list`[1] | CAPI-returned data of owned suits. NB: Type depends on if array is sparse or not. May be `None` if no data. |
| `Suits` | `dict`[1] | CAPI-returned data of owned suits. NB: May be `None` if no data. |
| `SuitLoadoutCurrent` | `dict` | CAPI-returned data of current Suit Loadout. NB: May be `None` if no data. |
| `SuitLoadouts` | `dict` | CAPI-returned data of all Suit Loadouts. NB: May be `None` if no data. |
| `SuitLoadouts` | `dict`[1] | CAPI-returned data of all Suit Loadouts. NB: May be `None` if no data. |
[1] - With `Suits` there's a caveat depending on all the slots from 0 are
contiguously used or not. If they are then the type is `list` and
indexing is purely numeric. If not then it's a `dict` and you need to index it
with a string. There is `companion.index_possibly_sparse_list()` to aid
with this, or if you wish to iterate over it use `companion.listify()` to
fill in any gaps and have a `list` to operate on.
[1] - Some data from the CAPI is sometimes returned as a `list` (when all
members are present) and other times as an integer-keyed `dict` (when at
least one member is missing, so the indices are not contiguous). We choose to
always convert to the integer-keyed `dict` form so that code utilising the data
is simpler.
New in version 4.1.6:

View File

@ -680,13 +680,24 @@ class Session(object):
return
monitor.state['SuitCurrent'] = current_suit
monitor.state['Suits'] = data.get('suits')
# It's easier to always have this in the 'sparse array' dict form
suits = data.get('suits')
if isinstance(suits, list):
monitor.state['Suits'] = dict(enumerate(suits))
else:
monitor.state['Suits'] = suits
if (suit_loadouts := data.get('loadouts')) is None:
logger.warning('CAPI data had "suit" but no (suit) "loadouts"')
monitor.state['SuitLoadoutCurrent'] = data.get('loadout')
monitor.state['SuitLoadouts'] = suit_loadouts
# It's easier to always have this in the 'sparse array' dict form
if isinstance(suit_loadouts, list):
monitor.state['SuitLoadouts'] = dict(enumerate(suit_loadouts))
else:
monitor.state['SuitLoadouts'] = suit_loadouts
def close(self) -> None:
"""Close CAPI authorization session."""

View File

@ -151,9 +151,9 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
'Data': defaultdict(int), # Backpack Data
},
'SuitCurrent': None,
'Suits': None,
'Suits': {},
'SuitLoadoutCurrent': None,
'SuitLoadouts': None,
'SuitLoadouts': {},
}
def start(self, root: 'tkinter.Tk') -> bool: # noqa: CCR001
@ -949,20 +949,46 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
# • SuitName
# • LoadoutID
# • LoadoutName
pass
# alpha4:
# { "timestamp":"2021-04-29T09:37:08Z", "event":"CreateSuitLoadout", "SuitID":1698364940285172,
# "SuitName":"tacticalsuit_class1", "SuitName_Localised":"Dominator Suit", "LoadoutID":4293000001,
# "LoadoutName":"Dom L/K/K", "Modules":[
# {
# "SlotName":"PrimaryWeapon1",
# "SuitModuleID":1698364962722310,
# "ModuleName":"wpn_m_assaultrifle_laser_fauto",
# "ModuleName_Localised":"TK Aphelion"
# },
# { "SlotName":"PrimaryWeapon2",
# "SuitModuleID":1698364956302993, "ModuleName":"wpn_m_assaultrifle_kinetic_fauto",
# "ModuleName_Localised":"Karma AR-50" }, { "SlotName":"SecondaryWeapon",
# "SuitModuleID":1698292655291850, "ModuleName":"wpn_s_pistol_kinetic_sauto",
# "ModuleName_Localised":"Karma P-15" } ] }
new_loadout = {
'loadoutSlotId': self.suit_loadout_id_from_loadoutid(entry['LoadoutID']),
'suit': {
'name': entry['SuitName'],
'locName': entry.get('SuitName_Localised', entry['SuitName']),
'suitId': entry['SuitID'],
},
'mame': entry['LoadoutName'],
'slots': self.suit_loadout_slots_array_to_dict(entry['Modules']),
}
self.state['SuitLoadouts'][new_loadout['loadoutSlotId']] = new_loadout
elif event_type == 'DeleteSuitLoadout':
# We should remove this from the monitor.state record of loadouts. The slotid
# could end up valid due to CreateSuitLoadout events, but we won't have the
# correct new loadout data until next CAPI pull.
loadoutid = entry['LoadoutID']
slotid = self.suit_loadout_id_from_loadoutid(loadoutid)
# This might be a Loadout that was created after our last CAPI pull.
# alpha4:
# { "timestamp":"2021-04-29T10:32:27Z", "event":"DeleteSuitLoadout", "SuitID":1698365752966423,
# "SuitName":"explorationsuit_class1", "SuitName_Localised":"Artemis Suit", "LoadoutID":4293000003,
# "LoadoutName":"Loadout 1" }
loadout_id = self.suit_loadout_id_from_loadoutid(entry['LoadoutID'])
try:
self.state['SuitLoadouts'].pop(f'{slotid}')
self.state['SuitLoadouts'].pop(f'{loadout_id}')
except KeyError:
logger.exception(f"slot id {slotid} doesn't exist, not in last CAPI pull ?")
# This should no longer happen, as we're now handling CreateSuitLoadout properly
logger.exception(f"loadout slot id {loadout_id} doesn't exist, not in last CAPI pull ?")
elif event_type == 'RenameSuitLoadout':
# alpha4
@ -971,13 +997,35 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
# • SuitName
# • LoadoutID
# • Loadoutname
pass
# alpha4:
# { "timestamp":"2021-04-29T10:35:55Z", "event":"RenameSuitLoadout", "SuitID":1698365752966423,
# "SuitName":"explorationsuit_class1", "SuitName_Localised":"Artemis Suit", "LoadoutID":4293000003,
# "LoadoutName":"Art L/K" }
loadout_id = self.suit_loadout_id_from_loadoutid(entry['LoadoutID'])
try:
self.state['SuitLoadouts'][loadout_id]['name'] = entry['LoadoutName']
except KeyError:
logger.exception(f"loadout slot id {loadout_id} doesn't exist, not in last CAPI pull ?")
# `BuySuit` has no useful info as of 4.0.0.13
elif event_type == 'BuySuit':
# alpha4 - should have 'SuitID'
# alpha4 :
# { "timestamp":"2021-04-29T09:03:37Z", "event":"BuySuit", "Name":"UtilitySuit_Class1",
# "Name_Localised":"Maverick Suit", "Price":150000, "SuitID":1698364934364699 }
self.state['Suits'][entry['SuitID']] = {
'name': entry['Name'],
'locName': entry.get('Name_Localised', entry['Name']),
'id': None, # Is this an FDev ID for suit type ?
'suitId': entry['SuitID'],
'slots': [],
}
# update credits
pass
if price := entry.get('Price') is None:
logger.error(f"BuySuit didn't contain Price: {entry}")
else:
self.state['Credits'] -= price
elif event_type == 'SellSuit':
# Remove from known suits
@ -990,8 +1038,21 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
# • Name
# • Price
# • SuitID
# alpha4:
# { "timestamp":"2021-04-29T09:15:51Z", "event":"SellSuit", "SuitID":1698364937435505,
# "Name":"explorationsuit_class1", "Name_Localised":"Artemis Suit", "Price":90000 }
try:
self.state['Suits'].pop(entry['SuitID'])
except KeyError:
logger.exception(f"SellSuit for a suit we didn't know about? {entry['SuitID']}")
# update credits total
pass
if price := entry.get('Price') is None:
logger.error(f"SellSuit didn't contain Price: {entry}")
else:
self.state['Credits'] += price
elif event_type == 'UpgradeSuit':
# alpha4
@ -1002,51 +1063,72 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
# • SuitID
# • Class
# • Cost
# Update credits total ?
# Update credits total ? It shouldn't even involve credits!
# Actual alpha4 - need to grind mats
pass
# `LoadoutEquipModule` has no instance-specific ID as of 4.0.0.13
elif event_type == 'LoadoutEquipModule':
# alpha4 - should have necessary IDs
pass
# alpha4:
# { "timestamp":"2021-04-29T11:11:13Z", "event":"LoadoutEquipModule", "LoadoutName":"Dom L/K/K",
# "SuitID":1698364940285172, "SuitName":"tacticalsuit_class1", "SuitName_Localised":"Dominator Suit",
# "LoadoutID":4293000001, "SlotName":"PrimaryWeapon2", "ModuleName":"wpn_m_assaultrifle_laser_fauto",
# "ModuleName_Localised":"TK Aphelion", "SuitModuleID":1698372938719590 }
loadout_id = self.suit_loadout_id_from_loadoutid(entry['LoadoutID'])
self.state['SuitLoadouts'][loadout_id]['slots'][entry['SlotName']] = {
'name': entry['ModuleName'],
'locName': entry.get('ModuleName_Localised', entry['ModuleName']),
'id': None,
'weaponrackId': entry['SuitModuleID'],
'locDescription': '',
}
elif event_type == 'LoadoutRemoveModule':
# alpha4
# This event is logged when a player removes a weapon from a suit loadout
#
# Parameters:
# • SuitID
# • SuitName
# • LoadoutID
# • LoadoutName
# • ModuleName: weapon or other item removed from loadout
# • SuitModuleID
pass
# alpha4 - triggers if selecting an already-equipped weapon into a different slot
# { "timestamp":"2021-04-29T11:11:13Z", "event":"LoadoutRemoveModule", "LoadoutName":"Dom L/K/K",
# "SuitID":1698364940285172, "SuitName":"tacticalsuit_class1", "SuitName_Localised":"Dominator Suit",
# "LoadoutID":4293000001, "SlotName":"PrimaryWeapon1", "ModuleName":"wpn_m_assaultrifle_laser_fauto",
# "ModuleName_Localised":"TK Aphelion", "SuitModuleID":1698372938719590 }
loadout_id = self.suit_loadout_id_from_loadoutid(entry['LoadoutID'])
self.state['SuitLoadouts'][loadout_id]['slots'].pop(entry['SlotName'])
# `BuyWeapon` has no instance-specific ID as of 4.0.0.13
elif event_type == 'BuyWeapon':
# alpha4
# Parameters:
# • Name
# • Price
# • SuitModuleID
# { "timestamp":"2021-04-29T11:10:51Z", "event":"BuyWeapon", "Name":"Wpn_M_AssaultRifle_Laser_FAuto",
# "Name_Localised":"TK Aphelion", "Price":125000, "SuitModuleID":1698372938719590 }
# update credits
pass
if price := entry.get('Price') is None:
logger.error(f"BuyWeapon didn't contain Price: {entry}")
else:
self.state['Credits'] -= price
# `SellWeapon` has no instance-specific ID as of 4.0.0.13
elif event_type == 'SellWeapon':
# alpha4
# This event is logged when a player sells a hand weapon
#
# Parameters:
# • Name
# • Price
# • SuitModuleID
# Update credits total
pass
# We're not actually keeping track of all owned weapons, only those in
# Suit Loadouts.
# alpha4:
# { "timestamp":"2021-04-29T10:50:34Z", "event":"SellWeapon", "Name":"wpn_m_assaultrifle_laser_fauto",
# "Name_Localised":"TK Aphelion", "Price":75000, "SuitModuleID":1698364962722310 }
# We need to look over all Suit Loadouts for ones that used this specific weapon
# and update them to entirely empty that slot.
for sl in self.state['SuitLoadouts']:
for w in self.state['SuitLoadouts'][sl]['slots']:
if self.state['SuitLoadouts'][sl]['slots'][w]['weaponrackId'] == entry['SuitModuleID']:
self.state['SuitLoadouts'][sl]['slots'].pop(w)
# We've changed the dict, so iteration breaks, but also the weapon
# could only possibly have been here once.
break
# Update credits total
if price := entry.get('Price') is None:
logger.error(f"SellWeapon didn't contain Price: {entry}")
else:
self.state['Credits'] += price
# `UpgradeWeapon` has no instance-specific ID as of 4.0.0.13
elif event_type == 'UpgradeWeapon':
# We're not actually keeping track of all owned weapons, only those in
# Suit Loadouts.
# alpha4
pass
@ -1592,6 +1674,29 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
return out
def suit_loadout_slots_array_to_dict(self, loadout: dict) -> dict:
"""
Return a CAPI-style Suit loadout from a Journal style dict.
:param loadout: e.g. Journal 'CreateSuitLoadout'->'Modules'.
:return: CAPI-style dict for a suit loadout.
"""
loadout_slots = {x['SlotName']: x for x in loadout}
slots = {}
for s in ('PrimaryWeapon1', 'PrimaryWeapon2', 'SecondaryWeapon'):
if loadout_slots.get(s) is None:
continue
slots[s] = {
'name': loadout_slots[s]['ModuleName'],
'id': None, # FDevID ?
'weaponrackId': loadout_slots[s]['SuitModuleID'],
'locName': loadout_slots[s].get('ModuleName_Localised', loadout_slots[s]['ModuleName']),
'locDescription': '',
}
return slots
# singleton
monitor = EDLogs()