Files
Diddy-Kong-Racing/tools/python/score_display.py
Jordan Longstaff 3534199c4c Slightly improve progress messages in score display (#687)
* Slightly improve progress messages in score display

* Add specific counts of undocumented functions

* Roll back redundant calculations

* More concise Trophy Race descriptions

* Fix score locally
2025-07-22 03:04:52 +02:00

158 lines
7.9 KiB
Python

import json
import argparse
def readScoreDisplayJson(filename='./tools/python/score_progress.json'):
with open(filename, 'r') as inFile:
return json.loads(inFile.read())
TOTAL_NUMBER_OF_BALLOONS = 47
TOTAL_NUMBER_OF_KEYS = 4
TOTAL_NUMBER_OF_TT_AMULETS = 4
TOTAL_NUMBER_OF_WIZPIG_AMULETS = 4
TOTAL_NUMBER_OF_TROPHIES = 5
DEFAULT_MAX_LENGTH = 42
ROUND_NAMES = ['One', 'Two', 'Three', 'Four']
class ScoreDisplay:
def __init__(self):
self.progressNodes = readScoreDisplayJson()
def getStatus(self, percentage):
if percentage >= 100.0:
return {
"Balloon": TOTAL_NUMBER_OF_BALLOONS,
"Key": TOTAL_NUMBER_OF_KEYS,
"TTAmulet": TOTAL_NUMBER_OF_TT_AMULETS,
"WizpigAmulet": TOTAL_NUMBER_OF_WIZPIG_AMULETS,
"Trophy": TOTAL_NUMBER_OF_TROPHIES,
"Msg": "COMPLETED!"
}
out = {
"Balloon": 0,
"Key": 0,
"TTAmulet": 0,
"WizpigAmulet": 0,
"Trophy": 0,
"Msg": 'Not Started'
}
if percentage <= 0.0:
return out
numberOfCompletedNodes = int((percentage / 100.0) * len(self.progressNodes))
currentNodeProgress = ((percentage / 100.0) * len(self.progressNodes)) - numberOfCompletedNodes
for i in range(0, numberOfCompletedNodes):
rewards = self.progressNodes[i]["rewards"]
for reward in rewards:
out[reward] += rewards[reward]
out['Msg'] = self.progressNodes[numberOfCompletedNodes]['msg']
nodeType = self.progressNodes[numberOfCompletedNodes]['type']
#if nodeType != 'Task':
# out['Msg'] += '\n'
if nodeType == 'Race':
out['Msg'] += f' (Lap {int(currentNodeProgress*3)+1}/3)'
elif nodeType == 'Collecting':
collectingName = self.progressNodes[numberOfCompletedNodes]['collecting']['name']
collectingMax = self.progressNodes[numberOfCompletedNodes]['collecting']['max']
out['Msg'] += f' ({int(currentNodeProgress * collectingMax)}/{collectingMax} {collectingName}s)'
elif nodeType == 'SilverCoinsRace':
out['Msg'] += f' ({int(currentNodeProgress*9)}/8 silver coins)'
elif nodeType == 'TrophyRace':
out['Msg'] += f' (Round {ROUND_NAMES[int(min(currentNodeProgress, 1.0)*4)]}, Lap {int(currentNodeProgress*12)%3+1}/3)'
elif nodeType == 'Battle':
if currentNodeProgress < 0.34:
out['Msg'] += ' (3 opponents remain)'
elif currentNodeProgress < 0.67:
out['Msg'] += ' (2 opponents remain)'
elif currentNodeProgress < 1.00:
out['Msg'] += ' (1 opponent remains)'
return out
def makeLine(self, char, length, title=None):
if title == None:
return ' ' + (char * length) + '\n'
else:
lineSideLength = (length - len(title) - 2) // 2
leftLength = lineSideLength
rightLength = lineSideLength
if (leftLength + rightLength + len(title) + 2) < length:
rightLength += 1
if leftLength + rightLength <= 0:
if leftLength + rightLength == 0:
return ' ' + title + '\n'
else:
return ' ' + title + '\n'
else:
return ' ' + (char * leftLength) + ' ' + title + \
(' ' + char * rightLength if char != ' ' else '') + '\n'
def getGameStatusDisplay(self, status, dashLen):
out = ''
out += self.makeLine('-', dashLen, 'Game Status')
firstStatusLine = ''
secondStatusLine = ''
firstStatusLine += 'Balloons: {:}/{:},'.format(status['Balloon'], TOTAL_NUMBER_OF_BALLOONS)
firstStatusLine += ' Keys: {:}/{:},'.format(status['Key'], TOTAL_NUMBER_OF_KEYS)
firstStatusLine += ' Trophies: {:}/{:}'.format(status['Trophy'], TOTAL_NUMBER_OF_TROPHIES)
secondStatusLine += 'T.T. Amulets: {:}/{:},'.format(status['TTAmulet'], TOTAL_NUMBER_OF_TT_AMULETS)
secondStatusLine += ' Wizpig Amulets: {:}/{:}'.format(status['WizpigAmulet'], TOTAL_NUMBER_OF_WIZPIG_AMULETS)
out += self.makeLine(' ', dashLen, firstStatusLine)
out += self.makeLine(' ', dashLen, secondStatusLine)
out += self.makeLine('-', dashLen)
out += self.makeLine(' ', dashLen, status['Msg'])
return [out, dashLen]
def getDisplay(self, advOnePer, advOneNonMatchPer, advTwoPer, showFlags=3, totalDecompFunctions=0, totalGlobalAsm=0, totalNonMatching=0, totalNonEquivalent=0, totalDocumented=0, totalUndocumented=0, totalNamedFunc=0, totalUncommented=0):
advOneStatus = self.getStatus(advOnePer)
advTwoStatus = self.getStatus(advTwoPer)
if showFlags == 3:
dashLen = max(len(advOneStatus['Msg']), len(advTwoStatus['Msg']), DEFAULT_MAX_LENGTH)
elif showFlags == 1:
dashLen = max(len(advOneStatus['Msg']), DEFAULT_MAX_LENGTH)
elif showFlags == 2:
dashLen = max(len(advTwoStatus['Msg']), DEFAULT_MAX_LENGTH)
else:
dashLen = DEFAULT_MAX_LENGTH
advOneGameStatusDisplay = self.getGameStatusDisplay(advOneStatus, dashLen)
advTwoGameStatusDisplay = self.getGameStatusDisplay(advTwoStatus, dashLen)
out = ''
if showFlags & 1:
out += self.makeLine('=', dashLen)
out += self.makeLine(' ', dashLen, 'ADVENTURE ONE (ASM -> C Decompilation)')
if advOnePer >= 100.0:
out += self.makeLine('-', dashLen, '{:5.1f}% Complete'.format(advOnePer))
else:
out += self.makeLine('-', dashLen, '{:5.2f}% Complete ({:5.2f}% NON_MATCHING)'.format(advOnePer, advOneNonMatchPer))
out += self.makeLine(' ', dashLen, '# Decompiled functions: ' + str(totalDecompFunctions))
out += self.makeLine(' ', dashLen, '# GLOBAL_ASM remaining: ' + str(totalGlobalAsm))
out += self.makeLine(' ', dashLen, '# NON_MATCHING functions: ' + str(totalNonMatching))
out += self.makeLine(' ', dashLen, '# NON_EQUIVALENT WIP functions: ' + str(totalNonEquivalent))
out += advOneGameStatusDisplay[0]
if showFlags & 2:
out += self.makeLine('=', dashLen)
out += self.makeLine(' ', dashLen, ' ADVENTURE TWO (Cleanup & Documentation)')
if advTwoPer >= 100.0:
out += self.makeLine('-', dashLen, '{:5.1f}% Complete'.format(advTwoPer))
else:
out += self.makeLine('-', dashLen, '{:5.2f}% Complete'.format(advTwoPer))
out += self.makeLine(' ', dashLen, '# Documented functions: ' + str(totalDocumented))
out += self.makeLine(' ', dashLen, '# Undocumented remaining: ' + str(totalUndocumented))
out += self.makeLine(' ', dashLen, '# Functions named `func_*`: ' + str(totalNamedFunc))
out += self.makeLine(' ', dashLen, '# Functions without comments: ' + str(totalUncommented))
out += advTwoGameStatusDisplay[0]
out += self.makeLine('=', dashLen)[:-1]
return out
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="")
parser.add_argument("adventureOnePercentage", help="Value within [0.0, 100.0] that describes how complete the decomp is.")
parser.add_argument("adventureTwoPercentage", help="Value within [0.0, 100.0] that describes how complete the documentation is.")
parser.add_argument("-a", "--adventure", help="(Optional) Only shows adventure 1 or 2 based on passed in value.", choices=['1', '2'])
args = parser.parse_args()
adventureSelect = 3 # Show both adventures by default
if args.adventure != None:
adventureSelect = int(args.adventure)
scoreDisplay = ScoreDisplay()
print(scoreDisplay.getDisplay(float(args.adventureOnePercentage), float(args.adventureTwoPercentage), adventureSelect))