Skip to content

Commit

Permalink
Merge branch 'release/3.5.0-a1'
Browse files Browse the repository at this point in the history
  • Loading branch information
aussig committed Feb 17, 2024
2 parents 0ac0e41 + 6a071b6 commit bae3ac1
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 64 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Change Log

## v3.5.0-a1 - 2024-02-17

### New Features:

* New 'detailed INF' report. This is optional and is off by default, but when enabled gives a detailed breakdown of the number of missions completed at each +INF level. For example: `INF +19 (➋ x 4 ➌ x 2 ➎ x 1)` means you completed 4 missions awarding ++, 2 mission awarding +++ and 1 mission awarding +++++. Manually tallied INF is simply added or removed from the overall total.
* When secondary INF reporting is switched on, now indicate which INF is primary `🅟` and which is secondary `🅢`. This can also combine with detailed INF reporting to give a full breakdown of primary and secondary INF.
* New 'detailed Trade' checkbox, which is on by default and when enabled shows the full trade breakdown into brackets 🆉 | 🅻 | 🅼 | 🅷, when disabled all brackets are combined into simple totals for trade purchase and trade profit.
* When multiple CMDRs are selected in the CMDR window, 'Post to Discord' now posts a concise table containing all the CMDRs in the list, with Inara and Inara squadron links where available. With a single CMDR selected, posting is exactly the same as it was.

### Changes:

* Activity windows (latest Tally / previous Tally) will now remember their positions and sizes within a game session.
* Re-opening an already open activity window (latest Tally / previous Tally) will no longer open two copies of the same window. Instead, the old one will be closed and a new fresh window with latest data opened at the same position and size on screen.


## v3.4.0 - 2024-02-09

### New Features:
Expand Down
175 changes: 137 additions & 38 deletions bgstally/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def mission_completed(self, journal_entry: dict, mission_log: MissionLog):
effect_faction_name = mission.get('TargetFaction', "")

if faction_effect['Influence'] != []:
inf = len(faction_effect['Influence'][0]['Influence'])
inf_index:str = str(len(faction_effect['Influence'][0]['Influence'])) # Index into dict containing detailed INF breakdown
inftrend = faction_effect['Influence'][0]['Trend']
for system_address, system in self.systems.items():
if str(faction_effect['Influence'][0]['SystemAddress']) != system_address: continue
Expand All @@ -313,16 +313,16 @@ def mission_completed(self, journal_entry: dict, mission_log: MissionLog):

if inftrend == "UpGood" or inftrend == "DownGood":
if effect_faction_name == journal_entry['Faction']:
faction['MissionPoints'] += inf
faction['MissionPoints'][inf_index] += 1
self.bgstally.ui.show_system_report(system_address) # Only show system report for primary INF
else:
faction['MissionPointsSecondary'] += inf
faction['MissionPointsSecondary'][inf_index] += 1
else:
if effect_faction_name == journal_entry['Faction']:
faction['MissionPoints'] -= inf
faction['MissionPoints'][inf_index] -= 1
self.bgstally.ui.show_system_report(system_address) # Only show system report for primary INF
else:
faction['MissionPointsSecondary'] -= inf
faction['MissionPointsSecondary'][inf_index] -= 1

elif mission is not None: # No influence specified for faction effect
for system_address, system in self.systems.items():
Expand All @@ -334,7 +334,7 @@ def mission_completed(self, journal_entry: dict, mission_log: MissionLog):
if (faction['FactionState'] in STATES_ELECTION and journal_entry['Name'] in MISSIONS_ELECTION) \
or (faction['FactionState'] in STATES_WAR and journal_entry['Name'] in MISSIONS_WAR) \
and effect_faction_name == journal_entry['Faction']:
faction['MissionPoints'] += 1
faction['MissionPoints']['1'] += 1
self.bgstally.ui.show_system_report(system_address) # Only show system report for primary INF

# Thargoid War
Expand Down Expand Up @@ -986,7 +986,7 @@ def _get_new_faction_data(self, faction_name, faction_state):
Get a new data structure for storing faction data
"""
return {'Faction': faction_name, 'FactionState': faction_state, 'Enabled': self.bgstally.state.EnableSystemActivityByDefault.get(),
'MissionPoints': 0, 'MissionPointsSecondary': 0,
'MissionPoints': {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0, 'm': 0}, 'MissionPointsSecondary': {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0, 'm': 0},
'TradeProfit': 0, 'TradePurchase': 0, 'BlackMarketProfit': 0, 'Bounties': 0, 'CartData': 0, 'ExoData': 0,
'TradeBuy': [{'items': 0, 'value': 0}, {'items': 0, 'value': 0}, {'items': 0, 'value': 0}, {'items': 0, 'value': 0}],
'TradeSell': [{'items': 0, 'value': 0, 'profit': 0}, {'items': 0, 'value': 0, 'profit': 0}, {'items': 0, 'value': 0, 'profit': 0}, {'items': 0, 'value': 0, 'profit': 0}],
Expand Down Expand Up @@ -1091,13 +1091,20 @@ def _update_faction_data(self, faction_data: Dict, faction_state: str = None):
# From < 3.2.0 to 3.2.0
for station in faction_data['TWStations'].values():
if not 'reactivate' in station: station['reactivate'] = 0
# From < 3.5.0 to 3.5.0
if not type(faction_data.get('MissionPoints', 0)) == dict:
faction_data['MissionPoints'] = {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0, 'm': int(faction_data.get('MissionPoints', 0))}
if not type(faction_data.get('MissionPointsSecondary', 0)) == dict:
faction_data['MissionPointsSecondary'] = {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0, 'm': int(faction_data.get('MissionPointsSecondary', 0))}


def _is_faction_data_zero(self, faction_data: Dict):
"""
Check whether all information is empty or zero for a faction
Check whether all information is empty or zero for a faction. _update_faction_data() is always called before this
so we can always assume here that the data is in the very latest structure.
"""
return int(faction_data['MissionPoints']) == 0 and int(faction_data['MissionPointsSecondary']) == 0 and \
return sum((1 if k == 'm' else int(k)) * int(v) for k, v in faction_data['MissionPoints'].items()) == 0 and \
sum((1 if k == 'm' else int(k)) * int(v) for k, v in faction_data['MissionPointsSecondary'].items()) == 0 and \
int(faction_data['TradeProfit']) == 0 and int(faction_data['TradePurchase']) == 0 and int(faction_data['BlackMarketProfit']) == 0 and \
sum(int(d['value']) for d in faction_data['TradeBuy']) == 0 and \
sum(int(d['value']) for d in faction_data['TradeSell']) == 0 and \
Expand Down Expand Up @@ -1157,37 +1164,10 @@ def _generate_faction_text(self, faction: dict, discord: bool):
# Force plain text if we are not posting to Discord
fp:bool = not discord

inf = faction['MissionPoints']
if self.bgstally.state.IncludeSecondaryInf.get() == CheckStates.STATE_ON: inf += faction['MissionPointsSecondary']

if faction['FactionState'] in STATES_ELECTION:
activity_text += f"{blue('ElectionINF', fp=fp)} {green(f'+{inf}', fp=fp)} " if inf > 0 else f"{blue('ElectionINF', fp=fp)} {green(inf, fp=fp)} " if inf < 0 else ""
elif faction['FactionState'] in STATES_WAR:
activity_text += f"{blue('WarINF', fp=fp)} {green(f'+{inf}', fp=fp)} " if inf > 0 else f"{blue('WarINF', fp=fp)} {green(inf, fp=fp)} " if inf < 0 else ""
else:
activity_text += f"{blue('INF', fp=fp)} {green(f'+{inf}', fp=fp)} " if inf > 0 else f"{blue('INF', fp=fp)} {green(inf, fp=fp)} " if inf < 0 else ""

activity_text += self._build_inf_text(faction['MissionPoints'], faction['MissionPointsSecondary'], faction['FactionState'], discord)
activity_text += f"{red('BVs', fp=fp)} {green(human_format(faction['Bounties']), fp=fp)} " if faction['Bounties'] != 0 else ""
activity_text += f"{red('CBs', fp=fp)} {green(human_format(faction['CombatBonds']), fp=fp)} " if faction['CombatBonds'] != 0 else ""
if faction['TradePurchase'] > 0:
# Legacy - Used a single value for purchase value / profit
activity_text += f"{cyan('TrdPurchase', fp=fp)} {green(human_format(faction['TradePurchase']), fp=fp)} " if faction['TradePurchase'] != 0 else ""
activity_text += f"{cyan('TrdProfit', fp=fp)} {green(human_format(faction['TradeProfit']), fp=fp)} " if faction['TradeProfit'] != 0 else ""
else:
# Modern - Split into values per supply / demand bracket
if sum(int(d['value']) for d in faction['TradeBuy']) > 0:
# Buy brackets currently range from 0 - 3
activity_text += f"{cyan('TrdBuy', fp=fp)} " \
+ f"{'🅻' if discord else '[L]'}:{green(human_format(faction['TradeBuy'][1]['value']), fp=fp)} " \
+ f"{'🅼' if discord else '[M]'}:{green(human_format(faction['TradeBuy'][2]['value']), fp=fp)} " \
+ f"{'🅷' if discord else '[H]'}:{green(human_format(faction['TradeBuy'][3]['value']), fp=fp)} "
if sum(int(d['value']) for d in faction['TradeSell']) > 0:
# Sell brackets currently range from 0 - 3
activity_text += f"{cyan('TrdProfit', fp=fp)} " \
+ f"{'🆉' if discord else '[Z]'}:{green(human_format(faction['TradeSell'][0]['profit']), fp=fp)} " \
+ f"{'🅻' if discord else '[L]'}:{green(human_format(faction['TradeSell'][1]['profit']), fp=fp)} " \
+ f"{'🅼' if discord else '[M]'}:{green(human_format(faction['TradeSell'][2]['profit']), fp=fp)} " \
+ f"{'🅷' if discord else '[H]'}:{green(human_format(faction['TradeSell'][3]['profit']), fp=fp)} "
activity_text += self._build_trade_text(faction['TradePurchase'], faction['TradeProfit'], faction['TradeBuy'], faction['TradeSell'], discord)
activity_text += f"{cyan('TrdBMProfit', fp=fp)} {green(human_format(faction['BlackMarketProfit']), fp=fp)} " if faction['BlackMarketProfit'] != 0 else ""
activity_text += f"{white('Expl', fp=fp)} {green(human_format(faction['CartData']), fp=fp)} " if faction['CartData'] != 0 else ""
# activity_text += f"{grey('Exo', fp=fp)} {green(human_format(faction['ExoData']), fp=fp)} " if faction['ExoData'] != 0 else ""
Expand Down Expand Up @@ -1295,6 +1275,125 @@ def _generate_tw_system_text(self, system: dict, discord: bool):
return system_text


def _build_inf_text(self, inf_data: dict, secondary_inf_data: dict, faction_state: str, discord: bool) -> str:
"""
Create a completel summary of INF for the faction, including both primary and secondary if user has requested
Args:
inf_data (dict): Dict containing INF, key = '1' - '5' or 'm'
secondary_inf_data (dict): Dict containing secondary INF, key = '1' - '5' or 'm'
faction_state (str): Current faction state
discord (bool): True if creating for Discord
Returns:
str: INF summary
"""
text:str = ""
# Force plain text if we are not posting to Discord
fp:bool = not discord

inf:int = sum((1 if k == 'm' else int(k)) * int(v) for k, v in inf_data.items())
inf_sec:int = sum((1 if k == 'm' else int(k)) * int(v) for k, v in secondary_inf_data.items())

if inf != 0 or (inf_sec != 0 and self.bgstally.state.IncludeSecondaryInf.get() == CheckStates.STATE_ON):
if faction_state in STATES_ELECTION:
text += f"{blue('ElectionINF', fp=fp)} "
elif faction_state in STATES_WAR:
text += f"{blue('WarINF', fp=fp)} "
else:
text += f"{blue('INF', fp=fp)} "

if self.bgstally.state.IncludeSecondaryInf.get() == CheckStates.STATE_ON:
text += self._build_inf_individual(inf, inf_data, "🅟" if discord else "[P]", discord)
text += self._build_inf_individual(inf_sec, secondary_inf_data, "🅢" if discord else "[S]", discord)
else:
text += self._build_inf_individual(inf, inf_data, "", discord)

return text


def _build_inf_individual(self, inf:int, inf_data: dict, prefix: str, discord: bool) -> str:
"""
Create a summary of either primary or secondary INF, with detailed breakdown if user has requested
Args:
inf (int): Total INF
inf_data (dict): dict containing INF, key = '1' - '5' or 'm'
prefix (str): Prefix label (🅟 or 🅢 or empty)
discord (bool): True if creating for Discord
Returns:
str: INF summary
"""
text:str = ""
if inf == 0: return text

# Force plain text if we are not posting to Discord
fp:bool = not discord

inf_str:str = f"{'+' if inf > 0 else ''}{inf}"
text += f"{prefix}{green(inf_str, fp=fp)} "

if self.bgstally.state.DetailedInf.get() == CheckStates.STATE_ON:
detailed_inf:str = ""
if inf_data.get('1', 0) != 0: detailed_inf += f"{'➊' if discord else '+'} x {inf_data['1']} "
if inf_data.get('2', 0) != 0: detailed_inf += f"{'➋' if discord else '++'} x {inf_data['2']} "
if inf_data.get('3', 0) != 0: detailed_inf += f"{'➌' if discord else '+++'} x {inf_data['3']} "
if inf_data.get('4', 0) != 0: detailed_inf += f"{'➍' if discord else '++++'} x {inf_data['4']} "
if inf_data.get('5', 0) != 0: detailed_inf += f"{'➎' if discord else '+++++'} x {inf_data['5']} "
if detailed_inf != "": text += f"({detailed_inf.rstrip()}) "

return text


def _build_trade_text(self, trade_purchase: int, trade_profit: int, trade_buy: list, trade_sell: list, discord: bool) -> str:
"""
Create a summary of trade, with detailed breakdown if user has requested
Args:
trade_purchase (int): Legacy total trade purchase value (before trade was tracked in brackets).
trade_profit (int): Legacy total trade profit value (before trade was tracked in brackets).
trade_buy (list): List of trade purchases with each entry corresponding to a trade bracket.
trade_sell (list): List of trade sales with each entry corresponding to a trade bracket.
discord (bool): True if creating for Discord
Returns:
str: Trade summary
"""
text:str = ""

# Force plain text if we are not posting to Discord
fp:bool = not discord

if trade_purchase > 0:
# Legacy - Used a single value for purchase value / profit
text += f"{cyan('TrdPurchase', fp=fp)} {green(human_format(trade_purchase), fp=fp)} " if trade_purchase != 0 else ""
text += f"{cyan('TrdProfit', fp=fp)} {green(human_format(trade_profit), fp=fp)} " if trade_profit != 0 else ""
elif self.bgstally.state.DetailedTrade.get() == CheckStates.STATE_OFF:
# Modern, simple trade report - Combine buy at all brackets and profit at all brackets
buy_total:int = sum(int(d['value']) for d in trade_buy)
profit_total:int = sum(int(d['value']) for d in trade_sell)
text += f"{cyan('TrdBuy', fp=fp)} {green(human_format(buy_total), fp=fp)} " if buy_total != 0 else ""
text += f"{cyan('TrdProfit', fp=fp)} {green(human_format(profit_total), fp=fp)} " if profit_total != 0 else ""
else:
# Modern, detailed trade report - Split into values per supply / demand bracket
if sum(int(d['value']) for d in trade_buy) > 0:
# Buy brackets currently range from 1 - 3
text += f"{cyan('TrdBuy', fp=fp)} " \
+ f"{'🅻' if discord else '[L]'}:{green(human_format(trade_buy[1]['value']), fp=fp)} " \
+ f"{'🅼' if discord else '[M]'}:{green(human_format(trade_buy[2]['value']), fp=fp)} " \
+ f"{'🅷' if discord else '[H]'}:{green(human_format(trade_buy[3]['value']), fp=fp)} "
if sum(int(d['value']) for d in trade_sell) > 0:
# Sell brackets currently range from 0 - 3
text += f"{cyan('TrdProfit', fp=fp)} " \
+ f"{'🆉' if discord else '[Z]'}:{green(human_format(trade_sell[0]['profit']), fp=fp)} " \
+ f"{'🅻' if discord else '[L]'}:{green(human_format(trade_sell[1]['profit']), fp=fp)} " \
+ f"{'🅼' if discord else '[M]'}:{green(human_format(trade_sell[2]['profit']), fp=fp)} " \
+ f"{'🅷' if discord else '[H]'}:{green(human_format(trade_sell[3]['profit']), fp=fp)} "

return text


def _build_cz_text(self, cz_data: dict, prefix: str, discord: bool) -> str:
"""
Create a summary of Conflict Zone activity
Expand Down
6 changes: 4 additions & 2 deletions bgstally/apimanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@ def _build_api_activity(self, activity:Activity, cmdr:str):
if faction.get('ExoData', "0") != "0": api_faction['exobiology'] = faction['ExoData']
if faction.get('CartData', "0") != "0": api_faction['exploration'] = faction['CartData']
if faction.get('Scenarios', "0") != "0": api_faction['scenarios'] = faction['Scenarios']
if faction.get('MissionPoints', "0") != "0": api_faction['infprimary'] = faction['MissionPoints']
if faction.get('MissionPointsSecondary', "0") != "0": api_faction['infsecondary'] = faction['MissionPointsSecondary']
inf_primary:int = sum((1 if k == 'm' else int(k)) * int(v) for k, v in faction['MissionPoints'].items())
inf_secondary:int = sum((1 if k == 'm' else int(k)) * int(v) for k, v in faction['MissionPointsSecondary'].items())
if inf_primary != 0: api_faction['infprimary'] = str(inf_primary)
if inf_secondary != 0: api_faction['infsecondary'] = str(inf_secondary)
if faction.get('MissionFailed', "0") != "0": api_faction['missionfails'] = faction['MissionFailed']
if faction.get('GroundMurdered', "0") != "0": api_faction['murdersground'] = faction['GroundMurdered']
if faction.get('Murdered', "0") != "0": api_faction['murdersspace'] = faction['Murdered']
Expand Down
4 changes: 4 additions & 0 deletions bgstally/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def load(self):
self.EnableOverlayTWProgress:tk.StringVar = tk.StringVar(value=config.get_str('BGST_EnableOverlayTWProgress', default=CheckStates.STATE_ON))
self.EnableOverlaySystem:tk.StringVar = tk.StringVar(value=config.get_str('BGST_EnableOverlaySystem', default=CheckStates.STATE_ON))
self.EnableSystemActivityByDefault:tk.StringVar = tk.StringVar(value=config.get_str('BGST_EnableSystemActivityByDefault', default=CheckStates.STATE_ON))
self.DetailedInf:tk.StringVar = tk.StringVar(value=config.get_str('BGST_DetailedInf', default=CheckStates.STATE_OFF))
self.DetailedTrade:tk.StringVar = tk.StringVar(value=config.get_str('BGST_DetailedTrade', default=CheckStates.STATE_ON))

# TODO: Legacy values, used to migrate initial state, remove in future version
self.DiscordBGSWebhook:tk.StringVar = tk.StringVar(value=config.get_str('XDiscordWebhook', default=""))
Expand Down Expand Up @@ -83,6 +85,8 @@ def save(self):
config.set('BGST_EnableOverlayTWProgress', self.EnableOverlayTWProgress.get())
config.set('BGST_EnableOverlaySystem', self.EnableOverlaySystem.get())
config.set('BGST_EnableSystemActivityByDefault', self.EnableSystemActivityByDefault.get())
config.set('BGST_DetailedInf', self.DetailedInf.get())
config.set('BGST_DetailedTrade', self.DetailedTrade.get())

# Persistent values
config.set('XCurrentSystemID', self.current_system_id if self.current_system_id != None else "")
Expand Down
Loading

0 comments on commit bae3ac1

Please sign in to comment.