Created
May 15, 2022 10:33
-
-
Save Encritary/7c6615e6f46e13d6da7efc1fdc6f67d5 to your computer and use it in GitHub Desktop.
Artifact command generation for Grasscutter by entering stats
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json, itertools, sys | |
ARTIFACT_PATH = "resources/ExcelBinOutput/ReliquaryExcelConfigData.json" | |
MAINSTAT_PATH = "resources/ExcelBinOutput/ReliquaryMainPropExcelConfigData.json" | |
AFFIX_PATH = "resources/ExcelBinOutput/ReliquaryAffixExcelConfigData.json" | |
EQUIP_TYPES = { | |
"EQUIP_BRACER": "flower", | |
"EQUIP_NECKLACE": "plume", | |
"EQUIP_SHOES": "sands", | |
"EQUIP_RING": "goblet", | |
"EQUIP_DRESS": "circlet" | |
} | |
STATS = { | |
"hp": "FIGHT_PROP_HP", | |
"hp%": "FIGHT_PROP_HP_PERCENT", | |
"atk": "FIGHT_PROP_ATTACK", | |
"atk%": "FIGHT_PROP_ATTACK_PERCENT", | |
"def": "FIGHT_PROP_DEFENSE", | |
"def%": "FIGHT_PROP_DEFENSE_PERCENT", | |
"er": "FIGHT_PROP_CHARGE_EFFICIENCY", | |
"em": "FIGHT_PROP_ELEMENT_MASTERY", | |
"hb": "FIGHT_PROP_HEAL_ADD", | |
"cdmg": "FIGHT_PROP_CRITICAL_HURT", | |
"cr": "FIGHT_PROP_CRITICAL", | |
"phys%": "FIGHT_PROP_PHYSICAL_ADD_HURT", | |
"dendro%": "FIGHT_PROP_GRASS_ADD_HURT", | |
"geo%": "FIGHT_PROP_ROCK_ADD_HURT", | |
"anemo%": "FIGHT_PROP_WIND_ADD_HURT", | |
"hydro%": "FIGHT_PROP_WATER_ADD_HURT", | |
"cryo%": "FIGHT_PROP_ICE_ADD_HURT", | |
"electro%": "FIGHT_PROP_ELEC_ADD_HURT", | |
"pyro%": "FIGHT_PROP_FIRE_ADD_HURT" | |
} | |
with open(ARTIFACT_PATH, "r") as f: | |
artifactData = json.load(f) | |
with open(MAINSTAT_PATH, "r") as f: | |
mainStatData = json.load(f) | |
with open(AFFIX_PATH, "r") as f: | |
affixData = json.load(f) | |
inp = None | |
if len(sys.argv) == 2: | |
inp = open(sys.argv[1], "r") | |
input = lambda *args: inp.readline().rstrip() | |
elif len(sys.argv) > 2: | |
print("Usage: python3 artifact.py [artifact file]") | |
exit(1) | |
itemId = input("Artifact item ID: ") | |
if not itemId.isnumeric(): | |
print("Artifact item ID must be numeric") | |
exit(1) | |
itemId = int(itemId) | |
item = None | |
for it in artifactData: | |
if it["Id"] == itemId: | |
item = it | |
break | |
else: | |
print("Unknown artifact item ID") | |
exit(1) | |
print("Artifact rarity:", str(item["RankLevel"]) + "*") | |
print("Artifact type:", EQUIP_TYPES[item["EquipType"]]) | |
level = input("Level (0-" + str(item["MaxLevel"] - 1) + "): ") | |
if not level.isnumeric(): | |
print("Level must be numeric") | |
exit(1) | |
level = int(level)+1 | |
if not (1 <= level <= item["MaxLevel"]): | |
print("Level must be from 0 to", item["MaxLevel"]-1) | |
exit(1) | |
mainStat = input("Main stat: ") | |
if mainStat not in STATS: | |
print("Unknown main stat:" + mainStat + ". Available:", ", ".join(STATS.keys())) | |
exit(1) | |
mainStat = STATS[mainStat] | |
maxPropDepotId = -1 | |
mainStatID = -1 | |
for cand in mainStatData: | |
if cand["PropType"] == mainStat and cand["PropDepotId"] <= item["MainPropDepotId"] and cand["PropDepotId"] >= maxPropDepotId: | |
maxPropDepotId = cand["PropDepotId"] | |
mainStatID = cand["Id"] | |
if mainStatID == -1: | |
print("Couldn't find main stat") | |
exit(1) | |
result = "giveart " + str(itemId) + " " + str(mainStatID) | |
maxSubStatRolls = len(item["AddPropLevels"]) + 1 | |
subStatArgs = [] | |
if inp is None: | |
print("Sub stats:") | |
line = input() | |
cnt = 0 | |
while line != "": | |
cnt += 1 | |
if cnt > item["AppendPropNum"]: | |
print("Too many sub stats. Max:", item["AppendPropNum"]) | |
exit(1) | |
statName, valueStr = line.split() | |
if statName not in STATS: | |
print("Unknown stat:" + statName + ". Available:", ", ".join(STATS.keys())) | |
exit(1) | |
if not valueStr.replace(".", "", 1).isnumeric(): | |
print("Stat value must be a positive real number") | |
exit(1) | |
value = float(valueStr) | |
if (statName in ["cr", "cdmg", "er"] or statName[-1] == "%") and value > 1.0: | |
value /= 100.0 | |
cands = [] | |
for cand in affixData: | |
if cand["Weight"] > 150: # Test value | |
continue | |
if cand["PropType"] != STATS[statName]: | |
continue | |
if cand["DepotId"] != item["RankLevel"] * 100 + 1: | |
continue | |
cands.append(cand) | |
minDiff = float('inf') | |
bestGuess = None | |
for guess in itertools.product(range(maxSubStatRolls+1), repeat=len(cands)): | |
if sum(guess) > maxSubStatRolls: | |
continue | |
guessVal = 0.0 | |
for i, n in enumerate(guess): | |
guessVal += cands[i]["PropValue"] * n | |
if abs(value-guessVal) < minDiff: | |
minDiff = abs(value-guessVal) | |
bestGuess = guess | |
if bestGuess is None: | |
print("Couldn't guess correct artifact sub stat value") | |
exit(1) | |
for i, n in enumerate(bestGuess): | |
if n > 0: | |
subStatArgs.append(str(cands[i]["Id"]) + "," + str(n)) | |
if cnt == item["AppendPropNum"]: | |
line = "" | |
else: | |
line = input() | |
if len(subStatArgs) > 0: | |
result += " " + " ".join(subStatArgs) | |
result += " " + str(level) | |
print(result) | |
if inp is not None: | |
inp.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
File input example: