diff --git a/README.md b/README.md index ec9953c..3ca1cce 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Alpha. One season, bandit raids plus two friendly events, six buildings. without a smithy. Past quartz each piece needs a shard from that tier's area. Rings for offence (swing speed, crit, power), amulets for defence (max HP, ward, regen), trinkets for the rest (XP, double gathering, - masterwork). + double crafting), each in its own tab. - **Tools and stations:** smiths make every tool: one per gathering skill and one per crafting skill (chisel, gem drill, hammer, cleaver, mortar and pestle, awl, shears). Builders make every station, a second slot for each diff --git a/game.py b/game.py index 62ac598..bb24700 100644 --- a/game.py +++ b/game.py @@ -236,7 +236,8 @@ PROSPECT_ODDS = 0.15 # ...for this chance at its gem GEM_ROCKS = {"mine_stone": "quartz", "mine_copper": "quartz", "mine_iron": "garnet", "mine_coal": "amethyst", "mine_mithril": "sapphire", "mine_adamantite": "emerald", "mine_runite": "diamond"} PROSPECT = ["stone", "iron_ore", "coal", "mithril_ore", "adamantite_ore", "runite_ore"] -# Rings for offence, amulets for defence, trinkets for everything else. +# Rings for offence, amulets for defence, trinkets for everything else; each its own tab. +JEWEL_GROUPS = {"ring": "Rings", "amulet": "Amulets", "trinket": "Trinkets"} # key, slot, noun, stat, bonus by tier, bands, gems, shards, level above the tier, glyph JEWELLERY = [ ("haste", "ring", "ring of haste", "haste", [5, 7, 8, 9, 10, 11], 1, 1, 1, 2, "diamond-ring"), @@ -247,7 +248,7 @@ JEWELLERY = [ ("mending", "amulet", "amulet of mending", "regen", [10, 20, 30, 40, 50, 60], 2, 1, 2, 8, "tribal-pendant"), ("hourglass", "trinket", "hourglass", "xp", [2, 3, 5, 6, 8, 10], 1, 2, 1, 5, "hourglass"), ("charm", "trinket", "lucky charm", "double", [2, 3, 4, 5, 6, 8], 1, 2, 1, 5, "clover"), - ("loupe", "trinket", "loupe", "masterwork", [2, 3, 4, 4, 5, 6], 1, 2, 1, 5, "magnifying-glass"), + ("loupe", "trinket", "loupe", "craft_double", [2, 3, 4, 4, 5, 6], 1, 2, 1, 5, "magnifying-glass"), ] # How each jewellery bonus reads: name, unit, glyph, tint (and a sign, if it takes away). JEWEL_STATS = { @@ -259,7 +260,7 @@ JEWEL_STATS = { "regen": {"name": "HP regen", "unit": "%", "glyph": "heart-plus", "tint": "#e06a8a"}, "xp": {"name": "XP", "unit": "%", "glyph": "sparkles", "tint": "#f0d060"}, "double": {"name": "Double gathering", "unit": "%", "glyph": "clover", "tint": "#5fae5a"}, - "masterwork": {"name": "Masterwork", "unit": "%", "glyph": "anvil", "tint": "#d07a3c"}, + "craft_double": {"name": "Double crafting", "unit": "%", "glyph": "anvil", "tint": "#d07a3c"}, } ITEMS["silver_ore"] = {"name": "Silver ore", "icon": "⚪", "value": 6} ITEMS["silver_bar"] = {"name": "Silver bar", "icon": "🥈", "value": 15, "tier": 1} @@ -867,12 +868,12 @@ def _jewel_actions() -> list[dict]: {"gold_bar": 1}, group="Bars"), ] for tier, (gem, gem_name, *_rest) in enumerate(GEMS): - for key, _slot, noun, _stat, _bonus, bands, gems, shards, offset, _glyph in JEWELLERY: + for key, slot, noun, _stat, _bonus, bands, gems, shards, offset, _glyph in JEWELLERY: inputs = {BANDS[tier]: bands, gem: gems, **({reagent("jewel", tier): shards} if tier else {})} actions.append(_action(f"jc_{gem}_{key}", "jewelcrafting", f"{gem_name} {noun}", TIER_LEVELS[tier] + offset, 35 + 5 * (bands + gems) + 2 * tier, round([15, 32, 52, 75, 100, 130][tier] * (bands + gems) / 2), - inputs, {f"{gem}_{key}": 1}, group=gem_name)) + inputs, {f"{gem}_{key}": 1}, group=JEWEL_GROUPS[slot])) return actions @@ -2064,7 +2065,7 @@ def _complete(world: dict, player: dict, action: dict, now: float, rng: random.R cls = CLASSES[player["cls"]] lucky = bool(action["outputs"]) and ( action["skill"] in cls["craft"] or (player["cls"] == "miner" and action["skill"] == "mining") - ) and rng.random() < LUCKY_CHANCE + gear_bonus(player, "masterwork") / 100 + ) and rng.random() < LUCKY_CHANCE outputs = {item: qty * (2 if lucky else 1) for item, qty in action["outputs"].items()} if lucky: summary["lucky"] += 1 @@ -2072,7 +2073,9 @@ def _complete(world: dict, player: dict, action: dict, now: float, rng: random.R for item, qty in action["outputs"].items(): outputs[item] += qty summary["lucky"] += 1 - if action["skill"] in GATHERING and rng.random() < gear_bonus(player, "double") / 100: + # Lucky charm doubles gathering, loupe doubles crafting, for any class. + jewel = "double" if action["skill"] in GATHERING else "craft_double" if action["skill"] in CRAFTING else None + if jewel and action["outputs"] and rng.random() < gear_bonus(player, jewel) / 100: for item, qty in action["outputs"].items(): outputs[item] += qty summary["lucky"] += 1 diff --git a/tests/test_game.py b/tests/test_game.py index 36f0a55..94dc2c2 100644 --- a/tests/test_game.py +++ b/tests/test_game.py @@ -1723,6 +1723,20 @@ class JewelcraftingTest(unittest.TestCase): p["equip"]["ring"] = "garnet_might" self.assertEqual(game.combat_power(p), game.combat_power({**p, "equip": {**p["equip"], "ring": None}}) + 4) + def test_loupe_doubles_any_class_crafts(self): + self.ann["xp"]["jewelcrafting"] = game.XP_TABLE[15] + for loupe, rings in ((None, 1), ("quartz_loupe", 2)): + self.ann["equip"]["trinket"] = loupe + self.ann["bank"] = {"copper_bar": 1, "quartz": 1} + game.start_action(self.world, self.ann, "jc_quartz_haste", T0) + game.process_player(self.world, self.ann, T0 + 1000, Always()) + self.assertEqual(self.ann["bank"], {"quartz_haste": rings}) + + def test_jewellery_tabs_by_slot(self): + self.assertEqual(game.ACTION_BY_ID["jc_garnet_haste"]["group"], "Rings") + self.assertEqual(game.ACTION_BY_ID["jc_diamond_warding"]["group"], "Amulets") + self.assertEqual(game.ACTION_BY_ID["jc_quartz_loupe"]["group"], "Trinkets") + def test_miners_cut_jewels(self): self.assertIn("jewelcrafting", game.CLASSES["miner"]["fast"]) self.assertIn("jewelcrafting", game.CLASSES["miner"]["craft"])