From 843f903fd34095e38261ff25ac912e170aa61937 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 30 Jul 2024 22:00:02 -0600 Subject: [PATCH 01/69] Init --- worlds/civ_6 | 1 + 1 file changed, 1 insertion(+) create mode 160000 worlds/civ_6 diff --git a/worlds/civ_6 b/worlds/civ_6 new file mode 160000 index 000000000000..f299b3ee8214 --- /dev/null +++ b/worlds/civ_6 @@ -0,0 +1 @@ +Subproject commit f299b3ee8214dfbabf1370e9cf449b68e90d40b0 From 15d7e3c54dd549c4c5d2314c10c64ee3bba0462e Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 30 Jul 2024 22:20:36 -0600 Subject: [PATCH 02/69] remove submodule --- worlds/civ_6 | 1 - 1 file changed, 1 deletion(-) delete mode 160000 worlds/civ_6 diff --git a/worlds/civ_6 b/worlds/civ_6 deleted file mode 160000 index f299b3ee8214..000000000000 --- a/worlds/civ_6 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f299b3ee8214dfbabf1370e9cf449b68e90d40b0 From 1f522e819df88f6b769ae60d34592124ffa9b16a Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 30 Jul 2024 22:22:01 -0600 Subject: [PATCH 03/69] Init --- worlds/civ_6/Civ6Client.py | 324 +++++++ worlds/civ_6/CivVIInterface.py | 118 +++ worlds/civ_6/Civilization VI.yaml | 58 ++ worlds/civ_6/Container.py | 231 +++++ worlds/civ_6/Data.py | 79 ++ worlds/civ_6/DeathLink.py | 76 ++ worlds/civ_6/Enum.py | 19 + worlds/civ_6/Items.py | 299 ++++++ worlds/civ_6/LICENSE.md | 21 + worlds/civ_6/Locations.py | 196 ++++ worlds/civ_6/Options.py | 105 +++ worlds/civ_6/ProgressiveDistricts.py | 27 + worlds/civ_6/README.md | 53 ++ worlds/civ_6/Regions.py | 185 ++++ worlds/civ_6/Rules.py | 63 ++ worlds/civ_6/TunerClient.py | 108 +++ worlds/civ_6/__init__.py | 195 ++++ worlds/civ_6/data/boosts.json | 923 +++++++++++++++++++ worlds/civ_6/data/era_required_items.json | 72 ++ worlds/civ_6/data/existing_civics.json | 429 +++++++++ worlds/civ_6/data/existing_tech.json | 541 +++++++++++ worlds/civ_6/data/goody_hut_rewards.json | 77 ++ worlds/civ_6/data/new_civic_prereqs.json | 87 ++ worlds/civ_6/data/new_civics.json | 63 ++ worlds/civ_6/data/new_tech.json | 79 ++ worlds/civ_6/data/new_tech_prereqs.json | 104 +++ worlds/civ_6/data/progressive_districts.json | 64 ++ worlds/civ_6/docs/boostsanity.md | 15 + worlds/civ_6/docs/en_Civilization VI.md | 53 ++ worlds/civ_6/docs/setup_en.md | 111 +++ worlds/civ_6/requirements.txt | 0 worlds/civ_6/test/TestBoostsanity.py | 50 + worlds/civ_6/test/TestGoodyHuts.py | 125 +++ worlds/civ_6/test/TestRegionRequirements.py | 238 +++++ worlds/civ_6/test/TestStartingHints.py | 90 ++ worlds/civ_6/test/__init__.py | 8 + 36 files changed, 5286 insertions(+) create mode 100644 worlds/civ_6/Civ6Client.py create mode 100644 worlds/civ_6/CivVIInterface.py create mode 100644 worlds/civ_6/Civilization VI.yaml create mode 100644 worlds/civ_6/Container.py create mode 100644 worlds/civ_6/Data.py create mode 100644 worlds/civ_6/DeathLink.py create mode 100644 worlds/civ_6/Enum.py create mode 100644 worlds/civ_6/Items.py create mode 100644 worlds/civ_6/LICENSE.md create mode 100644 worlds/civ_6/Locations.py create mode 100644 worlds/civ_6/Options.py create mode 100644 worlds/civ_6/ProgressiveDistricts.py create mode 100644 worlds/civ_6/README.md create mode 100644 worlds/civ_6/Regions.py create mode 100644 worlds/civ_6/Rules.py create mode 100644 worlds/civ_6/TunerClient.py create mode 100644 worlds/civ_6/__init__.py create mode 100644 worlds/civ_6/data/boosts.json create mode 100644 worlds/civ_6/data/era_required_items.json create mode 100644 worlds/civ_6/data/existing_civics.json create mode 100644 worlds/civ_6/data/existing_tech.json create mode 100644 worlds/civ_6/data/goody_hut_rewards.json create mode 100644 worlds/civ_6/data/new_civic_prereqs.json create mode 100644 worlds/civ_6/data/new_civics.json create mode 100644 worlds/civ_6/data/new_tech.json create mode 100644 worlds/civ_6/data/new_tech_prereqs.json create mode 100644 worlds/civ_6/data/progressive_districts.json create mode 100644 worlds/civ_6/docs/boostsanity.md create mode 100644 worlds/civ_6/docs/en_Civilization VI.md create mode 100644 worlds/civ_6/docs/setup_en.md create mode 100644 worlds/civ_6/requirements.txt create mode 100644 worlds/civ_6/test/TestBoostsanity.py create mode 100644 worlds/civ_6/test/TestGoodyHuts.py create mode 100644 worlds/civ_6/test/TestRegionRequirements.py create mode 100644 worlds/civ_6/test/TestStartingHints.py create mode 100644 worlds/civ_6/test/__init__.py diff --git a/worlds/civ_6/Civ6Client.py b/worlds/civ_6/Civ6Client.py new file mode 100644 index 000000000000..973a794a4f45 --- /dev/null +++ b/worlds/civ_6/Civ6Client.py @@ -0,0 +1,324 @@ +import asyncio +import logging +import traceback +from typing import Dict, List + +from CommonClient import ClientCommandProcessor, CommonContext, get_base_parser, logger, server_loop, gui_enabled +from .Data import get_progressive_districts_data +from .DeathLink import handle_check_deathlink +from NetUtils import ClientStatus +import Utils +from .CivVIInterface import CivVIInterface, ConnectionState +from .Enum import CivVICheckType +from .Items import CivVIItemData, generate_item_table, get_item_by_civ_name +from .Locations import generate_era_location_table +from .TunerClient import TunerErrorException, TunerTimeoutException + + +class CivVICommandProcessor(ClientCommandProcessor): + def __init__(self, ctx: CommonContext): + super().__init__(ctx) + + def _cmd_deathlink(self): + """Toggle deathlink from client. Overrides default setting.""" + if isinstance(self.ctx, CivVIContext): + self.ctx.death_link_enabled = not self.ctx.death_link_enabled + self.ctx.death_link_just_changed = True + Utils.async_start(self.ctx.update_death_link( + self.ctx.death_link_enabled), name="Update Deathlink") + self.ctx.logger.info( + f"Deathlink is now {'enabled' if self.ctx.death_link_enabled else 'disabled'}") + + def _cmd_resync(self): + """Resends all items to client, and has client resend all locations to server. This can take up to a minute if the player has received a lot of items""" + if isinstance(self.ctx, CivVIContext): + logger.info("Resyncing...") + asyncio.create_task(self.ctx.resync()) + + def _cmd_toggle_progressive_eras(self): + """If you get stuck for some reason and unable to continue your game, you can run this command to disable the defeat that comes from pushing past the max unlocked era """ + if isinstance(self.ctx, CivVIContext): + print("Toggling progressive eras, stand by...") + self.ctx.is_pending_toggle_progressive_eras = True + + +class CivVIContext(CommonContext): + is_pending_death_link_reset = False + is_pending_toggle_progressive_eras = False + command_processor = CivVICommandProcessor + game = "Civilization VI" + items_handling = 0b111 + tuner_sync_task = None + game_interface: CivVIInterface + location_name_to_civ_location = {} + location_name_to_id = {} + item_id_to_civ_item: Dict[int, CivVIItemData] = {} + item_table: Dict[str, CivVIItemData] = {} + processing_multiple_items = False + received_death_link = False + death_link_message = "" + death_link_enabled = False + + death_link_just_changed = False + # Used to prevent the deathlink from triggering when someone re enables it + + logger = logger + progressive_items_by_type = get_progressive_districts_data() + item_name_to_id = { + item.name: item.code for item in generate_item_table().values()} + connection_state = ConnectionState.DISCONNECTED + + def __init__(self, server_address, password): + super().__init__(server_address, password) + self.game_interface = CivVIInterface(logger) + location_by_era = generate_era_location_table() + self.item_table = generate_item_table() + + # Get tables formatted in a way that is easier to use here + for era, locations in location_by_era.items(): + for item_name, location in locations.items(): + self.location_name_to_id[location.name] = location.code + self.location_name_to_civ_location[location.name] = location + + for item_name, item in self.item_table.items(): + self.item_id_to_civ_item[item.code] = item + + async def resync(self): + if self.processing_multiple_items: + logger.info( + "Waiting for items to finish processing, try again later") + return + await self.game_interface.resync() + await handle_receive_items(self, -1) + logger.info("Resynced") + + def on_deathlink(self, data: Utils.Dict[str, Utils.Any]) -> None: + super().on_deathlink(data) + text = data.get("cause", "") + if text: + message = text + else: + message = f"Received from {data['source']}" + self.death_link_message = message + self.received_death_link = True + + async def server_auth(self, password_requested: bool = False): + if password_requested and not self.password: + await super(CivVIContext, self).server_auth(password_requested) + await self.get_username() + await self.send_connect() + + def run_gui(self): + from kvui import GameManager + + class CivVIManager(GameManager): + logging_pairs = [ + ("Client", "Archipelago") + ] + base_title = "Archipelago Civlization VI Client" + + self.ui = CivVIManager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + + def on_package(self, cmd: str, args: dict): + if cmd == "Connected": + self.slot_data = args["slot_data"] + if "death_link" in args["slot_data"]: + self.death_link_enabled = bool(args["slot_data"]["death_link"]) + Utils.async_start(self.update_death_link( + bool(args["slot_data"]["death_link"]))) + + +def update_connection_status(ctx: CivVIContext, status): + if ctx.connection_state == status: + return + elif status == ConnectionState.IN_GAME: + ctx.logger.info("Connected to Civ VI") + elif status == ConnectionState.IN_MENU: + ctx.logger.info("Connected to Civ VI, waiting for game to start") + elif status == ConnectionState.DISCONNECTED: + ctx.logger.info("Disconnected from Civ VI, attempting to reconnect...") + + ctx.connection_state = status + + +async def tuner_sync_task(ctx: CivVIContext): + logger.info("Starting CivVI connector") + while not ctx.exit_event.is_set(): + if not ctx.slot: + await asyncio.sleep(3) + continue + else: + try: + if ctx.processing_multiple_items == True: + logger.debug("Waiting for items to finish processing") + await asyncio.sleep(3) + else: + state = await ctx.game_interface.is_in_game() + update_connection_status(ctx, state) + if state == ConnectionState.IN_GAME: + await _handle_game_ready(ctx) + else: + await asyncio.sleep(3) + except TunerTimeoutException: + logger.error( + "Timeout occurred while receiving data from Civ VI, this usually isn't a problem unless you see it repeatedly") + await asyncio.sleep(3) + except Exception as e: + if isinstance(e, TunerErrorException): + logger.debug(str(e)) + else: + logger.debug(traceback.format_exc()) + + await asyncio.sleep(3) + continue + + +async def handle_toggle_progressive_eras(ctx: CivVIContext): + if ctx.is_pending_toggle_progressive_eras: + ctx.is_pending_toggle_progressive_eras = False + current = await ctx.game_interface.get_max_allowed_era() + if current > -1: + await ctx.game_interface.set_max_allowed_era(-1) + logger.info("Disabled progressive eras") + else: + count = 0 + for _, network_item in enumerate(ctx.items_received): + item: CivVIItemData = ctx.item_id_to_civ_item[network_item.item] + if item.item_type == CivVICheckType.ERA: + count += 1 + await ctx.game_interface.set_max_allowed_era(count) + logger.info(f"Enabled progressive eras, set to {count}") + + +async def handle_checked_location(ctx: CivVIContext): + checked_locations = await ctx.game_interface.get_checked_locations() + checked_location_ids = [location.code for location_name, location in ctx.location_name_to_civ_location.items( + ) if location_name in checked_locations] + + await ctx.send_msgs([{"cmd": "LocationChecks", "locations": checked_location_ids}]) + + +async def handle_receive_items(ctx: CivVIContext, last_received_index_override: int = None): + try: + last_received_index = last_received_index_override or await ctx.game_interface.get_last_received_index() + if len(ctx.items_received) - last_received_index > 1: + logger.debug("Multiple items received") + ctx.processing_multiple_items = True + + progressive_districts: List[CivVIItemData] = [] + progressive_eras: List[CivVIItemData] = [] + for index, network_item in enumerate(ctx.items_received): + + # Track these separately so if we replace "PROGRESSIVE_DISTRICT" with a specific tech, we can still check if need to add it to the list of districts + item: CivVIItemData = ctx.item_id_to_civ_item[network_item.item] + item_to_send: CivVIItemData = ctx.item_id_to_civ_item[network_item.item] + if index > last_received_index: + if item.item_type == CivVICheckType.PROGRESSIVE_DISTRICT: + # if the item is progressive, then check how far in that progression type we are and send the appropriate item + count = sum( + 1 for count_item in progressive_districts if count_item.civ_name == item.civ_name) + + if count >= len(ctx.progressive_items_by_type[item.civ_name]): + logger.error( + f"Received more progressive items than expected for {item.civ_name}") + continue + + item_civ_name = ctx.progressive_items_by_type[item.civ_name][count] + actual_item_name = get_item_by_civ_name(item_civ_name, ctx.item_table).name + item_to_send = ctx.item_table[actual_item_name] + + sender = ctx.player_names[network_item.player] + if item.item_type == CivVICheckType.ERA: + count = len(progressive_eras) + 1 + await ctx.game_interface.give_item_to_player(item_to_send, sender, count) + elif item.item_type == CivVICheckType.GOODY: + item_to_send.civ_vi_id = item_to_send.civ_name + await ctx.game_interface.give_item_to_player(item_to_send, sender) + else: + await ctx.game_interface.give_item_to_player(item_to_send, sender) + await asyncio.sleep(0.02) + + if item.item_type == CivVICheckType.PROGRESSIVE_DISTRICT: + progressive_districts.append(item) + elif item.item_type == CivVICheckType.ERA: + progressive_eras.append(item) + + if ctx.processing_multiple_items: + logger.debug("DONE") + ctx.processing_multiple_items = False + finally: + # If something errors out, then unblock item processing + ctx.processing_multiple_items = False + + +async def handle_check_goal_complete(ctx: CivVIContext): + # logger.debug("Sending Goal Complete") + result = await ctx.game_interface.check_victory() + if result: + logger.info("Sending Victory to server!") + await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) + + +async def _handle_game_ready(ctx: CivVIContext): + if ctx.server: + if not ctx.slot: + await asyncio.sleep(3) + return + + await handle_receive_items(ctx) + await handle_checked_location(ctx) + await handle_check_goal_complete(ctx) + + if ctx.death_link_enabled: + await handle_check_deathlink(ctx) + + # process pending commands + await handle_toggle_progressive_eras(ctx) + await asyncio.sleep(3) + else: + logger.info("Waiting for player to connect to server") + await asyncio.sleep(3) + + +def main(connect=None, password=None, name=None): + Utils.init_logging("Civilization VI Client") + + async def _main(connect, password, name): + ctx = CivVIContext(connect, password) + ctx.auth = name + ctx.server_task = asyncio.create_task( + server_loop(ctx), name="ServerLoop") + if gui_enabled: + ctx.run_gui() + await asyncio.sleep(1) + + ctx.tuner_sync_task = asyncio.create_task( + tuner_sync_task(ctx), name="TunerSync") + + await ctx.exit_event.wait() + ctx.server_address = None + + await ctx.shutdown() + + if ctx.tuner_sync_task: + await asyncio.sleep(3) + await ctx.tuner_sync_task + + import colorama + + colorama.init() + asyncio.run(_main(connect, password, name)) + colorama.deinit() + + +def debug_main(): + parser = get_base_parser() + parser.add_argument('--name', default=None, + help="Slot Name to connect as.") + parser.add_argument('--debug', default=None, + help="debug mode, additional logging") + args = parser.parse_args() + if args.debug: + logger.setLevel(logging.DEBUG) + main(args.connect, args.password, args.name) diff --git a/worlds/civ_6/CivVIInterface.py b/worlds/civ_6/CivVIInterface.py new file mode 100644 index 000000000000..45cf15d09bbc --- /dev/null +++ b/worlds/civ_6/CivVIInterface.py @@ -0,0 +1,118 @@ +from enum import Enum +from logging import Logger +from typing import List + +from .Items import CivVIItemData +from .TunerClient import TunerClient, TunerConnectionException, TunerTimeoutException + +class ConnectionState(Enum): + DISCONNECTED = 0 + IN_GAME = 1 + IN_MENU = 2 + + + +class CivVIInterface: + logger: Logger + tuner: TunerClient + last_error: str = None + + def __init__(self, logger: Logger): + self.logger = logger + self.tuner = TunerClient(logger) + + async def is_in_game(self) -> ConnectionState: + command = "IsInGame()" + try: + result = await self.tuner.send_game_command(command) + if result == "false": + return ConnectionState.IN_MENU + self.last_error = None + return ConnectionState.IN_GAME + except TunerTimeoutException: + self.print_connection_error( + "Not connected to game, waiting for connection to be available") + return ConnectionState.DISCONNECTED + except TunerConnectionException as e: + if "The remote computer refused the network connection" in str(e): + self.print_connection_error( + "Unable to connect to game. Verify that the tuner is enabled. Attempting to reconnect") + else: + self.print_connection_error( + "Not connected to game, waiting for connection to be available") + return ConnectionState.DISCONNECTED + except Exception as e: + if "attempt to index a nil valuestack traceback" in str(e) \ + or ".. is not supported for string .. nilstack traceback" in str(e): + return ConnectionState.IN_MENU + + def print_connection_error(self, error: str) -> None: + if error != self.last_error: + self.last_error = error + self.logger.info(error) + + async def give_item_to_player(self, item: CivVIItemData, sender: str = "", amount: int = 1) -> None: + if isinstance(item.civ_vi_id, str): + item_id = f'"{item.civ_vi_id}"' + else: + item_id = item.civ_vi_id + + command = f"HandleReceiveItem({item_id}, \"{item.name}\", \"{item.item_type.value}\", \"{sender}\", {amount})" + await self.tuner.send_game_command(command) + + async def resync(self) -> None: + """Has the client resend all the checked locations""" + command = "Resync()" + await self.tuner.send_game_command(command) + + async def check_victory(self) -> bool: + command = "ClientGetVictory()" + result = await self.tuner.send_game_command(command) + return result == "true" + + async def get_checked_locations(self) -> List[str]: + command = "GetUnsentCheckedLocations()" + result = await self.tuner.send_game_command(command, 2048 * 4) + return result.split(",") + + async def get_deathlink(self) -> str: + """returns either "false" or the name of the unit that killed the player's unit""" + command = "ClientGetDeathLink()" + result = await self.tuner.send_game_command(command) + return result + + async def kill_unit(self, message: str) -> None: + command = f"KillUnit(\"{message}\")" + await self.tuner.send_game_command(command) + + async def get_last_received_index(self) -> int: + command = "ClientGetLastReceivedIndex()" + result = await self.tuner.send_game_command(command) + return int(result) + + async def send_notification(self, item: CivVIItemData, sender="someone") -> None: + command = f"GameCore.NotificationManager:SendNotification(GameCore.NotificationTypes.USER_DEFINED_2, \"{item.name} Received\", \"You have received {item.name} from \" .. \"{sender}\", 0, {item.civ_vi_id})" + await self.tuner.send_command(command) + + async def decrease_gold_by_percent(self, percent: int, message: str) -> None: + command = f"DecreaseGoldByPercent({percent}, \"{message}\")" + await self.tuner.send_game_command(command) + + async def decrease_faith_by_percent(self, percent: int, message: str) -> None: + command = f"DecreaseFaithByPercent({percent}, \"{message}\")" + await self.tuner.send_game_command(command) + + async def decrease_era_score_by_amount(self, amount: int, message: str) -> None: + command = f"DecreaseEraScoreByAmount({amount}, \"{message}\")" + await self.tuner.send_game_command(command) + + async def set_max_allowed_era(self, count: int) -> None: + command = f"SetMaxAllowedEra(\"{count}\")" + await self.tuner.send_game_command(command) + + async def get_max_allowed_era(self) -> int: + command = "ClientGetMaxAllowedEra()" + result = await self.tuner.send_game_command(command) + if result == "": + return -1 + return int(result) diff --git a/worlds/civ_6/Civilization VI.yaml b/worlds/civ_6/Civilization VI.yaml new file mode 100644 index 000000000000..5540f0223066 --- /dev/null +++ b/worlds/civ_6/Civilization VI.yaml @@ -0,0 +1,58 @@ +Civilization VI: + progression_balancing: 50 + accessibility: items + + # Determines what progressive items (if any) should be included. + # districts_only: Each tech/civic that would normally unlock a district or district building now has a logical progression. Example: TECH_BRONZE_WORKING is now PROGRESSIVE_ENCAMPMENT + # eras_and_districts: Players will be defeated if they play until the world era advances beyond the currently unlocked maximum era. A notification will be shown as the end of the era approaches letting the player know if they don't have enough progressive era items. Unlocked eras can be seen in both the tech and civic trees. Includes all progressive districts. + # none: No progressive items will be included. This means you can get district upgrades that won't be usable until the relevant district is unlocked. + progression_style: "districts_only" + + # Shuffles the goody hut rewards. Goody huts will only contain junk items and location checks are received sequentially (GOODY_HUT_1, GOODY_HUT_2, etc). + # true: goody hut rewards are shuffled in + # false: goody hut rewards are not shuffled in and will behave as normal + shuffle_goody_hut_rewards: true + + # Boosts for Civics/Techs are location checks. Boosts can now be triggered even if + # the item has already been researched. If it is dependent upon a unit that is now + # obsolete, you can click toggle on/off the relevant tech in the tech tree. + boostsanity: false + + # Controls if/what items in the tech/civics trees are pre hinted for the multiworld. + # all: All items in the tech & civics trees are pre hinted. + # progression_items: Only locations in the trees containing progression items are pre hinted. + # no_junk: Pre hint the progression and useful items. + # none: No items are pre hinted. + pre_hint_items: "progression_items" + + # Controls the cost of techs and civics. + # cheap: Techs and civics cost 50% of the normal cost. + # default: Techs and civics cost the normal amount. + # expensive: Techs and civics cost 150% of the normal cost. + research_cost_multiplier: cheap + + # If boostsanity is enabled, this will prevent any boosts that are 'missable' from having progression items. Disabling this will potentially require multiple playthroughs to complete the seed. + exclude_missable_boosts: "true" + + # Each Tech and Civic Location will have a title of 'Unrevealed' until its prereqs have been researched. Note that hints will still be pre collected if that option is enabled. + hide_item_names: "false" + + # If enabled, an advisor icon will be added to any location that contains a progression item + advisor_show_progression_items: "true" + + # 'true' or 'false' + death_link: "false" + + # Determines the effect that is applied when a player dies. + # gold: The player's gold is reduced by the amount specified in death_link_effect_percent. + # faith: The player's faith is reduced by the amount specified in death_link_effect_percent. + # era_score: The player's era score is reduced by 1. + death_link_effect: unit_killed + + # The percentage of the effect that is applied for gold and faith. Era score decreases by 1 each time. + # 0 - 100 + death_link_effect_percent: 20 + +description: "Generated by https://archipelago.gg." +game: Civilization VI +name: PlayerName diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py new file mode 100644 index 000000000000..5d324eea5c6c --- /dev/null +++ b/worlds/civ_6/Container.py @@ -0,0 +1,231 @@ +from dataclasses import dataclass +import os +from typing import TYPE_CHECKING, List +import zipfile +from BaseClasses import ItemClassification, Location +from worlds.Files import APContainer + +from .Enum import CivVICheckType +from .Locations import CivVILocation, CivVILocationData +from .Options import CivVIOptions + +if TYPE_CHECKING: + from worlds.civ_6 import CivVIWorld + + +# Python fstrings don't allow backslashes, so we use this workaround +nl = "\n" +tab = "\t" +apo = "\'" + + +@dataclass +class CivTreeItem: + name: str + cost: int + ui_tree_row: int + + +class CivVIContainer(APContainer): + """ + Responsible for generating the dynamic mod files for the Civ VI multiworld + """ + game: str = "Civilization VI" + + def __init__(self, patch_data: dict, base_path: str, output_directory: str, + player=None, player_name: str = "", server: str = ""): + self.patch_data = patch_data + self.file_path = base_path + container_path = os.path.join(output_directory, base_path + ".zip") + super().__init__(container_path, player, player_name, server) + + def write_contents(self, opened_zipfile: zipfile.ZipFile) -> None: + for filename, yml in self.patch_data.items(): + opened_zipfile.writestr(filename, yml) + super().write_contents(opened_zipfile) + + +def get_cost(world, location: CivVILocationData) -> int: + """ + Returns the cost of the item based on the game options + """ + options: CivVIOptions = world.options + multiplier = options.research_cost_multiplier + return int(world.location_table[location.name].cost * multiplier) + + +def get_formatted_player_name(world, player) -> str: + """ + Returns the name of the player in the world + """ + if player != world.player: + return f"{world.multiworld.player_name[player]}{apo}s" + else: + return "Your" + + +def get_advisor_type(world: 'CivVIWorld', location: Location) -> str: + if world.options.advisor_show_progression_items.value and location.item.classification == ItemClassification.progression: + return "ADVISOR_PROGRESSIVE" + else: + return "ADVISOR_GENERIC" + + +def generate_new_items(world: 'CivVIWorld') -> str: + """ + Generates the XML for the new techs/civics as well as the blockers used to prevent players from researching their own items + """ + locations: List[CivVILocation] = world.multiworld.get_locations( + world.player) + techs = [location for location in locations if location.location_type == + CivVICheckType.TECH] + civics = [location for location in locations if location.location_type == + CivVICheckType.CIVIC] + + boost_techs = [] + boost_civics = [] + + hidden_techs = [] + hidden_civics = [] + if world.options.boostsanity.value: + boost_techs = [location for location in locations if location.location_type == CivVICheckType.BOOST and location.name.split("_")[1] == "TECH"] + boost_civics = [location for location in locations if location.location_type == CivVICheckType.BOOST and location.name.split("_")[1] == "CIVIC"] + techs += boost_techs + civics += boost_civics + + if world.options.hide_item_names.value: + hidden_techs = [tech.name for tech in techs] + hidden_civics = [civic.name for civic in civics] + + return f""" + + + + + {"".join([f'{tab}{nl}' for + tech in techs])} + {"".join([f'{tab}{nl}' for + civic in civics])} + + + +{"".join([f'{tab}{nl}' + for location in techs])} + + + {"".join([f'{tab}{nl}' for location in boost_techs])} + + + +{"".join([f'{tab}{nl}' + for location in civics])} + + + {"".join([f'{tab}{nl}' for location in boost_civics])} + + + + {"".join([f'{tab}{nl}' for location in hidden_civics])} + + + + {"".join([f'{tab}{nl}' for location in hidden_techs])} + + + + """ + + +def generate_setup_file(world) -> str: + """ + Generates the Lua for the setup file. This sets initial variables and state that affect gameplay around Progressive Eras + """ + setup = "-- Setup" + if world.options.progression_style.current_key == "eras_and_districts": + setup += f""" + -- Init Progressive Era Value if it hasn't been set already + if Game.GetProperty("MaxAllowedEra") == nil then + print("Setting MaxAllowedEra to 0") + Game.SetProperty("MaxAllowedEra", 0) + end + """ + + if world.options.boostsanity.value: + setup += f""" + -- Init Boosts + if Game.GetProperty("BoostsAsChecks") == nil then + print("Setting Boosts As Checks to True") + Game.SetProperty("BoostsAsChecks", true) + end + """ + return setup + + +def generate_goody_hut_sql(world) -> str: + """ + Generates the SQL for the goody huts or an empty string if they are disabled since the mod expects the file to be there + """ + + if world.options.shuffle_goody_hut_rewards.value: + return f""" + UPDATE GoodyHutSubTypes SET Description = NULL WHERE GoodyHut NOT IN ('METEOR_GOODIES', 'GOODYHUT_SAILOR_WONDROUS', 'DUMMY_GOODY_BUILDIER') AND Weight > 0; + +INSERT INTO Modifiers + (ModifierId, ModifierType, RunOnce, Permanent, SubjectRequirementSetId) +SELECT ModifierID||'_AI', ModifierType, RunOnce, Permanent, 'PLAYER_IS_AI' +FROM Modifiers +WHERE EXISTS ( + SELECT ModifierId + FROM GoodyHutSubTypes + WHERE Modifiers.ModifierId = GoodyHutSubTypes.ModifierId AND GoodyHutSubTypes.GoodyHut NOT IN ('METEOR_GOODIES', 'GOODYHUT_SAILOR_WONDROUS', 'DUMMY_GOODY_BUILDIER') AND GoodyHutSubTypes.Weight > 0); + +INSERT INTO ModifierArguments + (ModifierId, Name, Type, Value) +SELECT ModifierID||'_AI', Name, Type, Value +FROM ModifierArguments +WHERE EXISTS ( + SELECT ModifierId + FROM GoodyHutSubTypes + WHERE ModifierArguments.ModifierId = GoodyHutSubTypes.ModifierId AND GoodyHutSubTypes.GoodyHut NOT IN ('METEOR_GOODIES', 'GOODYHUT_SAILOR_WONDROUS', 'DUMMY_GOODY_BUILDIER') AND GoodyHutSubTypes.Weight > 0); + +UPDATE GoodyHutSubTypes +SET ModifierID = ModifierID||'_AI' +WHERE GoodyHut NOT IN ('METEOR_GOODIES', 'GOODYHUT_SAILOR_WONDROUS', 'DUMMY_GOODY_BUILDIER') AND Weight > 0; + + """ + else: + return "-- Goody Huts are disabled, no changes needed" + + +def generate_update_boosts_sql(world) -> str: + """ + Generates the SQL for existing boosts in boostsanity or an empty string if they are disabled since the mod expects the file to be there + """ + + if world.options.boostsanity.value: + return f""" +UPDATE Boosts +SET TechnologyType = 'BOOST_' || TechnologyType +WHERE TechnologyType IS NOT NULL; +UPDATE Boosts +SET CivicType = 'BOOST_' || CivicType +WHERE CivicType IS NOT NULL AND CivicType NOT IN ('CIVIC_CORPORATE_LIBERTARIANISM', 'CIVIC_DIGITAL_DEMOCRACY', 'CIVIC_SYNTHETIC_TECHNOCRACY', 'CIVIC_NEAR_FUTURE_GOVERNANCE'); + """ + else: + return "-- Boostsanity is disabled, no changes needed" diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py new file mode 100644 index 000000000000..a27893193301 --- /dev/null +++ b/worlds/civ_6/Data.py @@ -0,0 +1,79 @@ +from dataclasses import dataclass +import json +import os +import pkgutil +from typing import List + + +_cache = {} + + +def _get_data(key: str): + global _cache + if key not in _cache: + path = os.path.join("data", f"{key}.json") + _cache[key] = json.loads( + pkgutil.get_data(__name__, path).decode()) + return _cache[key] + + +def get_boosts_data(): + return _get_data("boosts") +@dataclass +class CivVIBoostData(): + Type: str + EraType: str + Prereq: List[str] + PrereqRequiredCount: int + Classification: str + + +def get_boosts_data() -> List[CivVIBoostData]: + boosts_json = _get_data("boosts") + boosts = [] + for boost in boosts_json: + boosts.append(CivVIBoostData( + Type=boost['Type'], + EraType=boost['EraType'], + Prereq=boost['Prereq'], + PrereqRequiredCount=boost['PrereqRequiredCount'], + Classification=boost['Classification'] + )) + + return boosts + + +def get_era_required_items_data(): + return _get_data("era_required_items") + + +def get_existing_civics_data(): + return _get_data("existing_civics") + + +def get_existing_techs_data(): + return _get_data("existing_tech") + + +def get_goody_hut_rewards_data(): + return _get_data("goody_hut_rewards") + + +def get_new_civic_prereqs_data(): + return _get_data("new_civic_prereqs") + + +def get_new_civics_data(): + return _get_data("new_civics") + + +def get_new_tech_prereqs_data(): + return _get_data("new_tech_prereqs") + + +def get_new_techs_data(): + return _get_data("new_tech") + + +def get_progressive_districts_data(): + return _get_data("progressive_districts") diff --git a/worlds/civ_6/DeathLink.py b/worlds/civ_6/DeathLink.py new file mode 100644 index 000000000000..3bf4ceef1d35 --- /dev/null +++ b/worlds/civ_6/DeathLink.py @@ -0,0 +1,76 @@ +import random + +from CommonClient import CommonContext + +# any is also an option but should not be considered an effect +DEATH_LINK_EFFECTS = ["Gold", "Faith", "Era Score", "Unit Killed"] + + +async def handle_receive_deathlink(ctx: CommonContext, message): + """Resolves the effects of a deathlink received from the multiworld based on the options selected by the player""" + chosen_effect = ctx.slot_data["death_link_effect"] + effect: str = "Gold" + if chosen_effect == "Any Except Era Score": + effect = random.choice( + [effect for effect in DEATH_LINK_EFFECTS if effect != "Era Score"]) + else: + effect = chosen_effect if chosen_effect != "Any" else random.choice( + DEATH_LINK_EFFECTS) + + percent = ctx.slot_data["death_link_effect_percent"] + if effect == "Gold": + ctx.logger.info(f"Decreasing gold by {percent}%") + await ctx.game_interface.decrease_gold_by_percent(percent, message) + elif effect == "Faith": + ctx.logger.info(f"Decreasing faith by {percent}%") + await ctx.game_interface.decrease_faith_by_percent(percent, message) + elif effect == "Era Score": + ctx.logger.info("Decreasing era score by 1") + await ctx.game_interface.decrease_era_score_by_amount(1, message) + elif effect == "Unit Killed": + ctx.logger.info("Destroying a random unit") + await ctx.game_interface.kill_unit(message) + + +async def handle_check_deathlink(ctx: CommonContext): + """Checks if the local player should send out a deathlink to the multiworld as well as if we should respond to any pending deathlinks sent to us """ + # check if we received a death link + if ctx.received_death_link: + ctx.received_death_link = False + await handle_receive_deathlink(ctx, ctx.death_link_message) + + # Check if we should send out a death link + result = await ctx.game_interface.get_deathlink() + if ctx.death_link_just_changed: + ctx.death_link_just_changed = False + return + if result != "false": + messages = [f"lost a unit to a {result}", + f"offered a sacrifice to the great {result}", + f"was killed by a {result}", + f"made a donation to the {result} fund", + f"made a tactical error", + f"picked a fight with a {result} and lost", + f"tried to befriend an enemy {result}", + f"used a {result} to reduce their military spend", + f"was defeated by a {result} in combat", + f"bravely struck a {result} and paid the price", + f"had a lapse in judgement against a {result}", + f"learned at the hands of a {result}", + f"attempted to non peacefully negotiate with a {result}", + f"was outsmarted by a {result}", + f"received a lesson from a {result}", + f"now understands the importance of not fighting a {result}", + f"let a {result} get the better of them", + f"allowed a {result} to show them the error of their ways", + f"heard the tragedy of Darth Plageuis the Wise from a {result}", + f"refused to join a {result} in their quest for power", + f"was tired of sitting in BK and decided to fight a {result} instead", + f"purposely lost to a {result} as a cry for help", + f"is wanting to remind everyone that they are here to have fun and not to win", + f"is reconisdering their pursuit of a domination victory", + f"had their plans toppled by a {result}", + ] + player = ctx.player_names[ctx.slot] + message = random.choice(messages) + await ctx.send_death(f"{player} {message}") diff --git a/worlds/civ_6/Enum.py b/worlds/civ_6/Enum.py new file mode 100644 index 000000000000..b57a5944ab8d --- /dev/null +++ b/worlds/civ_6/Enum.py @@ -0,0 +1,19 @@ +from enum import Enum +class EraType(Enum): + ERA_ANCIENT = "ERA_ANCIENT" + ERA_CLASSICAL = "ERA_CLASSICAL" + ERA_MEDIEVAL = "ERA_MEDIEVAL" + ERA_RENAISSANCE = "ERA_RENAISSANCE" + ERA_INDUSTRIAL = "ERA_INDUSTRIAL" + ERA_MODERN = "ERA_MODERN" + ERA_ATOMIC = "ERA_ATOMIC" + ERA_INFORMATION = "ERA_INFORMATION" + ERA_FUTURE = "ERA_FUTURE" + +class CivVICheckType(Enum): + TECH = "TECH" + CIVIC = "CIVIC" + PROGRESSIVE_DISTRICT = "PROGRESSIVE_DISTRICT" + ERA = "ERA" + GOODY = "GOODY" + BOOST = "BOOST" \ No newline at end of file diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py new file mode 100644 index 000000000000..c521e8eed2d7 --- /dev/null +++ b/worlds/civ_6/Items.py @@ -0,0 +1,299 @@ +from enum import Enum +import json +import os +import pkgutil +import random +from typing import Dict, List, Optional +import typing +from BaseClasses import Item, ItemClassification +from .Data import get_era_required_items_data, get_existing_civics_data, get_existing_techs_data, get_goody_hut_rewards_data, get_progressive_districts_data +from .Enum import CivVICheckType, EraType +from .ProgressiveDistricts import get_flat_progressive_districts +CIV_VI_AP_ITEM_ID_BASE = 5041000 + +NON_PROGRESSION_DISTRICTS = [ + "PROGRESSIVE_PRESERVE", + "PROGRESSIVE_NEIGHBORHOOD" +] + + +# Items required as progression for boostsanity mode +BOOSTSANITY_PROGRESSION_ITEMS = [ + "TECH_THE_WHEEL", + "TECH_MASONRY", + "TECH_ARCHERY", + "TECH_ENGINEERING", + "TECH_CONSTRUCTION", + "TECH_GUNPOWDER", + "TECH_MACHINERY", + "TECH_SIEGE_TACTICS", + "TECH_STIRRUPS", + "TECH_ASTRONOMY", + "TECH_BALLISTICS", + "TECH_STEAM_POWER", + "TECH_SANITATION", + "TECH_COMPUTERS", + "TECH_COMBUSTION", + "TECH_TELECOMMUNICATIONS", + "TECH_ROBOTICS", + "CIVIC_FEUDALISM", + "CIVIC_GUILDS", + "CIVIC_THE_ENLIGHTENMENT", + "CIVIC_MERCANTILISM", + "CIVIC_CONSERVATION", + "CIVIC_CIVIL_SERVICE", + "CIVIC_GLOBALIZATION", + "CIVIC_COLD_WAR", + "CIVIC_URBANIZATION", + "CIVIC_NATIONALISM", + "CIVIC_MOBILIZATION", + "PROGRESSIVE_NEIGHBORHOOD", + "PROGRESSIVE_PRESERVE" +] + + +class FillerItemRarity(Enum): + COMMON = "COMMON" + UNCOMMON = "UNCOMMON" + RARE = "RARE" + + +FILLER_DISTRIBUTION: Dict[FillerItemRarity, float] = { + FillerItemRarity.RARE: 0.025, + FillerItemRarity.UNCOMMON: .2, + FillerItemRarity.COMMON: 0.775, +} + + +class FillerItemData: + name: str + type: str + rarity: FillerItemRarity + civ_name: str + + def __init__(self, data: Dict[str, str]): + self.name = data["Name"] + self.rarity = FillerItemRarity(data["Rarity"]) + self.civ_name = data["Type"] + + +def get_filler_item_data() -> Dict[str, FillerItemData]: + """ + Returns a dictionary of filler items with their data + """ + goody_huts: List[Dict[str, str]] = get_goody_hut_rewards_data() + # Create a FillerItemData object for each item + cached_filler_items = {item["Name"]: FillerItemData(item) for item in goody_huts} + + return cached_filler_items + + +class CivVIItemData: + civ_vi_id: int + classification: ItemClassification + name: str + code: int + cost: int + item_type: CivVICheckType + progression_name: Optional[str] + civ_name: Optional[str] + + def __init__(self, name, civ_vi_id: int, cost: int, item_type: CivVICheckType, id_offset: int, classification: ItemClassification, progression_name: Optional[str], civ_name: Optional[str] = None): + self.classification = classification + self.civ_vi_id = civ_vi_id + self.name = name + self.code = civ_vi_id + CIV_VI_AP_ITEM_ID_BASE + id_offset + self.cost = cost + self.item_type = item_type + self.progression_name = progression_name + self.civ_name = civ_name + + +class CivVIItem(Item): + game: str = "Civilization VI" + civ_vi_id: int + item_type: CivVICheckType + + def __init__(self, item: CivVIItemData, player: int, classification: ItemClassification = None): + super().__init__(item.name, classification or item.classification, item.code, player) + self.civ_vi_id = item.civ_vi_id + self.item_type = item.item_type + + +def format_item_name(name: str) -> str: + name_parts = name.split("_") + return " ".join([part.capitalize() for part in name_parts]) + + +def get_item_by_civ_name(item_name: typing.List[str], item_table: typing.Dict[str, 'CivVIItemData']) -> 'CivVIItemData': + """Gets the names of the items in the item_table""" + for item in item_table.values(): + if item_name == item.civ_name: + return item + + raise Exception(f"Item {item_name} not found in item_table") + + +def _generate_tech_items(id_base: int, required_items: List[str], progressive_items: Dict[str, str]) -> List[CivVIItemData]: + # Generate Techs + existing_techs = get_existing_techs_data() + tech_table = {} + + tech_id = 0 + for tech in existing_techs: + classification = ItemClassification.useful + name = tech["Name"] + civ_name = tech["Type"] + if civ_name in required_items: + classification = ItemClassification.progression + progression_name = None + check_type = CivVICheckType.TECH + if civ_name in progressive_items.keys(): + progression_name = format_item_name(progressive_items[civ_name]) + + tech_table[name] = CivVIItemData( + name=name, + civ_vi_id=tech_id, + cost=tech["Cost"], + item_type=check_type, + id_offset=id_base, + classification=classification, + progression_name=progression_name, + civ_name=civ_name + ) + + tech_id += 1 + + return tech_table + + +def _generate_civics_items(id_base: int, required_items: List[str], progressive_items: Dict[str, str]) -> List[CivVIItemData]: + civic_id = 0 + civic_table = {} + existing_civics = get_existing_civics_data() + + for civic in existing_civics: + name = civic["Name"] + civ_name = civic["Type"] + progression_name = None + check_type = CivVICheckType.CIVIC + + if civ_name in progressive_items.keys(): + progression_name = format_item_name(progressive_items[civ_name]) + + classification = ItemClassification.useful + if civ_name in required_items: + classification = ItemClassification.progression + + civic_table[name] = CivVIItemData( + name=name, + civ_vi_id=civic_id, + cost=civic["Cost"], + item_type=check_type, + id_offset=id_base, + classification=classification, + progression_name=progression_name, + civ_name=civ_name + ) + + civic_id += 1 + + return civic_table + + +def _generate_progressive_district_items(id_base: int) -> List[CivVIItemData]: + progressive_table = {} + progressive_id_base = 0 + progressive_items = get_progressive_districts_data() + for item_name in progressive_items.keys(): + progression = ItemClassification.progression + if item_name in NON_PROGRESSION_DISTRICTS: + progression = ItemClassification.useful + name = format_item_name(item_name) + progressive_table[name] = CivVIItemData( + name=name, + civ_vi_id=progressive_id_base, + cost=0, + item_type=CivVICheckType.PROGRESSIVE_DISTRICT, + id_offset=id_base, + classification=progression, + progression_name=None, + civ_name=item_name + ) + progressive_id_base += 1 + return progressive_table + + +def _generate_progressive_era_items(id_base: int) -> List[CivVIItemData]: + """Generates the single progressive district item""" + era_table = {} + # Generate progressive eras + progressive_era_name = format_item_name("PROGRESSIVE_ERA") + era_table[progressive_era_name] = CivVIItemData( + name=progressive_era_name, + civ_vi_id=0, + cost=0, + item_type=CivVICheckType.ERA, + id_offset=id_base, + classification=ItemClassification.progression, + progression_name=None, + civ_name="PROGRESSIVE_ERA" + ) + return era_table + + +def _generate_goody_hut_items(id_base: int) -> List[CivVIItemData]: + # Generate goody hut items + goody_huts = get_filler_item_data() + goody_table = {} + goody_base = 0 + for value in goody_huts.values(): + goody_table[value.name] = CivVIItemData( + name=value.name, + civ_vi_id=goody_base, + cost=0, + item_type=CivVICheckType.GOODY, + id_offset=id_base, + classification=ItemClassification.filler, + progression_name=None, + civ_name=value.civ_name + ) + goody_base += 1 + return goody_table + + +def generate_item_table() -> Dict[str, CivVIItemData]: + era_required_items = get_era_required_items_data() + required_items: List[str] = [] + for key, value in era_required_items.items(): + required_items += value + + progressive_items = get_flat_progressive_districts() + + item_table = {} + + def get_id_base(): + return len(item_table.keys()) + + item_table = {**item_table, **_generate_tech_items(get_id_base(), required_items, progressive_items)} + item_table = {**item_table, **_generate_civics_items(get_id_base(), required_items, progressive_items)} + item_table = {**item_table, **_generate_progressive_district_items(get_id_base())} + item_table = {**item_table, **_generate_progressive_era_items(get_id_base())} + item_table = {**item_table, **_generate_goody_hut_items(get_id_base())} + + return item_table + + +def get_items_by_type(item_type: CivVICheckType, item_table: Dict[str, CivVIItemData]) -> List[CivVIItemData]: + """ + Returns a list of items that match the given item type + """ + return [item for item in item_table.values() if item.item_type == item_type] + + +def get_random_filler_by_rarity(rarity: FillerItemRarity, item_table: Dict[str, CivVIItemData]) -> CivVIItemData: + """ + Returns a random filler item by rarity + """ + items = [item for item in get_filler_item_data().values() if item.rarity == rarity] + return items[random.randint(0, len(items) - 1)] diff --git a/worlds/civ_6/LICENSE.md b/worlds/civ_6/LICENSE.md new file mode 100644 index 000000000000..7671a45b3046 --- /dev/null +++ b/worlds/civ_6/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2024 tanjo3 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py new file mode 100644 index 000000000000..3d08c37e66ee --- /dev/null +++ b/worlds/civ_6/Locations.py @@ -0,0 +1,196 @@ +from dataclasses import dataclass +from typing import Any, List, Optional, Dict +from BaseClasses import Location, LocationProgressType, Region + +from .Data import get_boosts_data, get_new_civic_prereqs_data, get_new_civics_data, get_new_tech_prereqs_data, get_new_techs_data + +from .Enum import CivVICheckType, EraType + +CIV_VI_AP_LOCATION_ID_BASE = 5041000 + +# Locs that should have progression items (keypoint techs/civics, ~1 per era) +PRIORITY_LOCATIONS = [ + "TECH_ANCEINT_09", + "TECH_CLASSICAL_15", + "TECH_MEDIEVAL_20", + "TECH_RENAISSANCE_33", + "TECH_INDUSTRIAL_35", + "TECH_MODERN_47", + "TECH_ATOMIC_51", + "TECH_INFORMATION_59", + + "CIVIC_ANCIENT_04", + "CIVIC_CLASSICAL_08", + "CIVIC_MEDIEVAL_19", + "CIVIC_RENAISSANCE_26", + "CIVIC_INDUSTRIAL_33", + "CIVIC_MODERN_39", + "CIVIC_ATOMIC_46", + "CIVIC_INFORMATION_48", + + "ERA_CLASSICAL", + "ERA_MEDIEVAL", + "ERA_RENAISSANCE", + "ERA_INDUSTRIAL", + "ERA_MODERN", + "ERA_ATOMIC", + "ERA_INFORMATION", + "ERA_FUTURE" +] + +# Locs that should not have progression items (future techs/civics) +EXCLUDED_LOCATIONS = [ + "GOODY_HUT_1", + "GOODY_HUT_2", + "GOODY_HUT_3", + "GOODY_HUT_4", + "GOODY_HUT_5", + "GOODY_HUT_6", + "GOODY_HUT_7", + "GOODY_HUT_8", + "GOODY_HUT_9", + "GOODY_HUT_10", +] + + +class CivVILocationData(): + game: str = "Civilization VI" + name: str + cost: int + uiTreeRow: int + civ_id: int + code: int + era_type: EraType + location_type: CivVICheckType + pre_reqs: List[str] + + def __init__(self, name: str, cost: int, uiTreeRow: int, id: int, era_type: EraType, location_type: CivVICheckType, pre_reqs: Optional[List[str]] = None): + self.name = name + self.cost = cost + self.uiTreeRow = uiTreeRow + self.civ_id = id + self.code = id + CIV_VI_AP_LOCATION_ID_BASE + self.era_type = era_type + self.pre_reqs = pre_reqs + self.location_type = location_type + + +class CivVILocation(Location): + game: str = "Civilization VI" + location_type: CivVICheckType + + def __init__(self, player: int, name: str = '', address: Optional[int] = None, parent: Optional[Region] = None): + super().__init__(player, name, address, parent) + if name.split("_")[0] == "TECH": + self.location_type = CivVICheckType.TECH + elif name.split("_")[0] == "CIVIC": + self.location_type = CivVICheckType.CIVIC + elif name.split("_")[0] == "ERA": + self.location_type = CivVICheckType.ERA + elif name.split("_")[0] == "GOODY": + self.location_type = CivVICheckType.GOODY + elif name.split("_")[0] == "BOOST": + self.location_type = CivVICheckType.BOOST + + if self.name in PRIORITY_LOCATIONS: + self.progress_type = LocationProgressType.PRIORITY + elif self.name in EXCLUDED_LOCATIONS: + self.progress_type = LocationProgressType.EXCLUDED + else: + self.progress_type = LocationProgressType.DEFAULT + + if self.location_type == CivVICheckType.BOOST: + boost_data_list = get_boosts_data() + boost_data = next((boost for boost in boost_data_list if boost.Type == name), None) + if boost_data and boost_data.Classification == "EXCLUDED": + self.progress_type = LocationProgressType.EXCLUDED + + + + +def generate_flat_location_table() -> Dict[str, CivVILocationData]: + """ + Generates a flat location table in the following format: + { + "TECH_AP_ANCIENT_00": CivVILocationData, + "TECH_AP_ANCIENT_01": CivVILocationData, + "CIVIC_AP_ANCIENT_00": CivVILocationData, + ... + } + """ + era_locations = generate_era_location_table() + flat_locations = {} + for era_type, locations in era_locations.items(): + for location_id, location_data in locations.items(): + flat_locations[location_id] = location_data + return flat_locations + + +def generate_era_location_table() -> Dict[EraType, Dict[str, CivVILocationData]]: + """ + Uses the data from existing_tech.json to generate a location table in the following format: + { + "ERA_ANCIENT": { + "TECH_AP_ANCIENT_00": CivVILocationData, + "TECH_AP_ANCIENT_01": CivVILocationData, + "CIVIC_AP_ANCIENT_00": CivVILocationData, + }, + ... + } + """ + new_tech_prereqs = get_new_tech_prereqs_data() + + new_techs = get_new_techs_data() + era_locations = {} + id_base = 0 +# Techs + for data in new_techs: + era_type = data['EraType'] + if era_type not in era_locations: + era_locations[era_type] = {} + + prereq_data = [ + item for item in new_tech_prereqs if item['Technology'] == data['Type']] + + era_locations[era_type][data["Type"]] = CivVILocationData( + data["Type"], data['Cost'], data['UITreeRow'], id_base, era_type, CivVICheckType.TECH, prereq_data) + id_base += 1 +# Civics + new_civic_prereqs = get_new_civic_prereqs_data() + new_civics = get_new_civics_data() + + for data in new_civics: + era_type = data['EraType'] + if era_type not in era_locations: + era_locations[era_type] = {} + prereq_data = [ + item for item in new_civic_prereqs if item['Civic'] == data['Type']] + era_locations[era_type][data["Type"]] = CivVILocationData( + data["Type"], data['Cost'], data['UITreeRow'], id_base, era_type, CivVICheckType.CIVIC, prereq_data) + id_base += 1 + +# Eras + eras = list(EraType) + for i in range(len(EraType)): + location_era = eras[i].name + + if location_era == "ERA_ANCIENT": + continue + + era_locations[location_era][location_era] = CivVILocationData( + location_era, 0, 0, id_base, location_era, CivVICheckType.ERA) + id_base += 1 +# Goody Huts, defaults to 10 goody huts as location checks (rarely will a player get more than this) + for i in range(10): + era_locations[EraType.ERA_ANCIENT.value]["GOODY_HUT_" + str(i+1)] = CivVILocationData( + "GOODY_HUT_" + str(i+1), 0, 0, id_base, EraType.ERA_ANCIENT, CivVICheckType.GOODY) + id_base += 1 +# Boosts + boosts = get_boosts_data() + for boost in boosts: + location = CivVILocationData( + boost.Type, 0, 0, id_base, boost.EraType, CivVICheckType.BOOST, pre_reqs=boost.Prereq) + era_locations[boost.EraType][boost.Type] = location + id_base += 1 + + return era_locations diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py new file mode 100644 index 000000000000..40a22a5cd4ee --- /dev/null +++ b/worlds/civ_6/Options.py @@ -0,0 +1,105 @@ +from dataclasses import dataclass +from Options import Choice, DeathLink, DefaultOnToggle, PerGameCommonOptions, Range, Toggle + + +class ProgressionStyle(Choice): + """Determines what progressive items (if any) should be included.\n\n + Districts Only: Each tech/civic that would normally unlock a district or district building now has a logical progression. Example: TECH_BRONZE_WORKING is now PROGRESSIVE_ENCAMPMENT\n\n + Eras and Districts: Players will be defeated if they play until the world era advances beyond the currently unlocked maximum era. + A notification will be shown as the end of the era approaches letting the player know if they don't have enough progressive era items. + Unlocked eras can be seen in both the tech and civic trees. Includes all progressive districts.\n\n + None: No progressive items will be included. This means you can get district upgrades that won't be usable until the relevant district is unlocked. + """ + display_name = "Progression Style" + option_districts_only = "Districts Only" + option_eras_and_districts = "Eras and Districts" + option_none = "None" + default = "districts_only" + + +class ShuffleGoodyHuts(DefaultOnToggle): + """Shuffles the goody hut rewards. Goody huts will only contain junk items and location checks are received sequentially (GOODY_HUT_1, GOODY_HUT_2, etc).""" + display_name = "Shuffle Goody Hut Rewards" + default = True + + +class BoostSanity(Toggle): + """Boosts for Civics/Techs are location checks. Boosts can now be triggered even if the item has already been researched. If it is dependent upon a unit that is now obsolete, you can click toggle on/off the relevant tech in the tech tree.""" + default = False + + +class ExcludeMissableBoosts(Toggle): + """If boostsanity is enabled, this will prevent any boosts that are 'missable' from having progression items. Disabling this will potentially require multiple playthroughs to complete the seed.""" + default = True + + +class ResearchCostMultiplier(Choice): + """Multiplier for research cost of techs and civics, higher values make research more expensive. Cheap = 0.5x, Expensive = 1.5x. Default is 1. """ + display_name = "Tech/Civic Cost Multiplier" + option_cheap = 0.5 + option_default = 1 + option_expensive = 1.5 + default = 1 + + +class PreHintItems(Choice): + """Controls if/what items in the tech/civics trees are pre hinted for the multiworld.\n + All : All items in the tech & civics trees are pre hinted.\n + Progression items: Only locations in the trees containing progression items are pre hinted.\n + No Junk: Pre hint the progression and useful items.\n + None: No items are pre hinted. + """ + display_name = "Tech/Civic Tree Pre Hinted Items" + option_all = "All Tree Locations" + option_progression_items = "Progression Tree Locations" + option_no_junk = "No Junk" + option_none = "None" + default = "progression_items" + + +class HideItemNames(Toggle): + """Each Tech and Civic Location will have a title of 'Unrevealed' until its prereqs have been researched. Note that hints will still be pre collected if that option is enabled.""" + default = False + + +class InGameFlagProgressionItems(DefaultOnToggle): + """If enabled, an advisor icon will be added to any location that contains a progression item""" + default = True + + +class DeathLinkEffect(Choice): + """What happens when a unit dies. Default is Unit Killed.\n + Faith, and Gold will be decreased by the amount specified in 'Death Link Effect Percent'. \n + Era score is decrased by 1.\n + Any will select any of these options any time a death link is received.""" + display_name = "Death Link Effect" + option_unit_killed = "Unit Killed" + option_era_score = "Era Score" + option_gold = "Gold" + option_faith = "Faith" + option_any = "Any" + option_any_except_era_score = "Any Except Era Score" + default = "unit_killed" + + +class DeathLinkEffectPercent(Range): + """The percentage of the effect that will be applied. Only applicable for Gold and Faith effects. Default is 20%""" + display_name = "Death Link Effect Percent" + default = 20 + range_start = 1 + range_end = 100 + + +@dataclass +class CivVIOptions(PerGameCommonOptions): + progression_style: ProgressionStyle + shuffle_goody_hut_rewards: ShuffleGoodyHuts + boostsanity: BoostSanity + exclude_missable_boosts: ExcludeMissableBoosts + research_cost_multiplier: ResearchCostMultiplier + pre_hint_items: PreHintItems + hide_item_names: HideItemNames + advisor_show_progression_items: InGameFlagProgressionItems + death_link_effect: DeathLinkEffect + death_link_effect_percent: DeathLinkEffectPercent + death_link: DeathLink diff --git a/worlds/civ_6/ProgressiveDistricts.py b/worlds/civ_6/ProgressiveDistricts.py new file mode 100644 index 000000000000..b22f7be7f581 --- /dev/null +++ b/worlds/civ_6/ProgressiveDistricts.py @@ -0,0 +1,27 @@ +from typing import Dict, List + +from .Data import get_progressive_districts_data + +def get_flat_progressive_districts() -> Dict[str, str]: + """Returns a dictionary of all items that are associated with a progressive item. + Key is the item name ("TECH_WRITING") and the value is the associated progressive + item ("PROGRESSIVE_CAMPUS")""" + progressive_districts = get_progressive_districts_data() + flat_progressive_techs = {} + for key, value in progressive_districts.items(): + for item in value: + flat_progressive_techs[item] = key + return flat_progressive_techs + + +def convert_items_to_have_progression(items: List[str]): + """ converts a list of items to instead be their associated progressive item if + they have one. ["TECH_MINING", "TECH_WRITING"] -> ["TECH_MINING", "PROGRESSIVE_CAMPUS]""" + flat_progressive_techs = get_flat_progressive_districts() + new_list = [] + for item in items: + if item in flat_progressive_techs.keys(): + new_list.append(flat_progressive_techs[item]) + else: + new_list.append(item) + return new_list diff --git a/worlds/civ_6/README.md b/worlds/civ_6/README.md new file mode 100644 index 000000000000..d510583d70e0 --- /dev/null +++ b/worlds/civ_6/README.md @@ -0,0 +1,53 @@ +# Civlization 6 Archipelago + +## Setup Guide +For setup instructions go [here](./docs/setup_en.md). + +## What does randomization do to this game? + +In Civilization VI, the tech and civic trees are both shuffled. This presents some interesting ways to play the game in a non-standard way. If you are feeling adventurous, you can enable the `boostsanity` option in order to really change up the way you normally would play a Civ game. Details on the option can be found [here](./docs/boostsanity.md) + +There are a few changes that the Archipelago mod introduces in order to make this playable/fun. These are detailed in the __FAQ__ section below. + +## What is the goal of Civilization VI when randomized? +The goal of randomized Civlization VI remains the same. Pursue any victory type you have enabled in your game settings, the one you normally go for may or may not be feasible based on how things have been changed up! + +## Which items can be in another player's world? +All technologies and civics can be found in another player's world. + +## What does another world's item look like in Civilization VI? +Each item from another world is represented as a researchable tech/civic in your normal tech/civic trees. + +## When the player receives an item, what happens? +A short period after receiving an item, you will get a notification indicating you have discovered the relevant tech/civic. You will also get the regular popup that details what the given item has unlocked for you. + +## FAQs +- Do I need the DLC to play this? + - Yes, you need both Rise & Fall and Gathering Storm. If there is enough interest then I can eventually add support for Archipellago runs that don't require both expansions. + +- Does this work with Multiplayer? + - It does not and, despite my best efforts, probably won't until there's a new way for external programs to be able to interact with the game. + +- Does my mod that reskins Barbarians as various Pro Wrestlers work with this?? + - Only one way to find out! Any mods that modify techs/civics will most likely cause issues, though. + +- "Help! I can't see any of the items that have been sent to me!" + - Both trees by default will show you the researchable Archipelago locations. To view the normal tree, you can click "Toggle Archipelago Tree" on the top left corner of the tree view. + +- "Oh no! I received the Machinery tech and now instead of getting an Archer next turn, I have to wait an additional 10 turns to get a Crossbowman!" + - Vanilla prevents you from building units of the same class from an earlier tech level after you have researched a later variant. For example, this could be problematic if someone unlocks Crossbowmen for you right out the gate since you won't be able to make Archers (which have a much lower production cost). + + - Solution: You can now go in to the tech tree, click "Toggle Archipelago Tree" to view your unlocked techs, and then can click any tech you have unlocked to toggle whether it is currently active or not. __NOTE__: This is an experimental feature and may yield some unexpected behaviors. Please DM `@Hesto2` on Discord if you run into any issues. + +- I enabled `progressive districts` but I have no idea techs/civics what items are locked behind progression now! + - Any technology or civic that grants you a new building in a district (or grants you the district itself) is now locked behind a progressive item. For example, `PROGRESSIVE_CAMPUS` would give you these items in the following order: + 1. `TECH_WRITING` + 2. `TECH_EDUCATION` + 3. `TECH_CHEMISTRY` + - If you want to see the details around each item, you can review [this file](./data/progressive_districts.json) + +- "How does DeathLink work? Am I going to have to start a new game every time one of my friends dies??" + - Heavens no, my fellow Archipelago appreciator. When configuring your Archipelago options for Civilization on the options page, there are several choices available for you to fine tune the way you'd like to be punished for the follies of your friends. These include: Having a random unit destroyed, losing a percentage of gold or faith, or even losing a point on your era score. If you can't make up your mind, you can elect to have any of them be selected every time a death link is sent your way. + + - In the event you lose one of your units in combat (this means captured units don't count), then you will send a death link event to the rest of your friends. + diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py new file mode 100644 index 000000000000..0fae53dcc766 --- /dev/null +++ b/worlds/civ_6/Regions.py @@ -0,0 +1,185 @@ +import typing +from BaseClasses import CollectionState, LocationProgressType, Region +from .Data import get_era_required_items_data, get_progressive_districts_data +from .Items import format_item_name, get_item_by_civ_name +from .Enum import CivVICheckType, EraType +from .Locations import CivVILocation +from .ProgressiveDistricts import get_flat_progressive_districts +from .Options import CivVIOptions + +if typing.TYPE_CHECKING: + from . import CivVIWorld + from Items import CivVIItemData + + +def get_required_items_for_era(era: EraType): + """Gets the specific techs/civics that are required for the specified era""" + era_required_items = {} + era_required_items = get_era_required_items_data() + return era_required_items[era.value] + + +def get_cumulative_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool = True, item_table: typing.Dict[str, 'CivVIItemData'] = None) -> typing.List['CivVIItemData']: + """Gets the specific techs/civics that are required for the specified era as well as all previous eras""" + cumulative_prereqs = [] + era_required_items = {} + era_required_items = get_era_required_items_data() + + for era in EraType: + cumulative_prereqs += era_required_items[era.value] + if era == end_era: + break + # If we are excluding progressive items, we need to remove them from the list of expected items (TECH_BRONZE_WORKING won't be here since it will be PROGRESSIVE_ENCAMPMENT) + if exclude_progressive_items: + flat_progressive_items = get_flat_progressive_districts() + prereqs_without_progressive_items = [] + for item in cumulative_prereqs: + if item in flat_progressive_items: + continue + else: + prereqs_without_progressive_items.append(item) + + return [get_item_by_civ_name(prereq, item_table) for prereq in prereqs_without_progressive_items] + + return [get_item_by_civ_name(prereq, item_table) for prereq in cumulative_prereqs] + + +def has_required_progressive_districts(state: CollectionState, era: EraType, player: int): + """ If player has progressive items enabled, it will count how many progressive techs it should have, otherwise return the default array""" + progressive_districts = get_progressive_districts_data() + + item_table = state.multiworld.worlds[player].item_table + # Verify we can still reach non progressive items + all_previous_items_no_progression = get_cumulative_prereqs_for_era( + era, True, item_table) + if not state.has_all([item.name for item in all_previous_items_no_progression], player): + return False + + # Verify we have the correct amount of progressive items + all_previous_items = get_cumulative_prereqs_for_era( + era, False, item_table) + required_counts: typing.Dict[str, int] = {} + + for key, value in progressive_districts.items(): + required_counts[key] = 0 + for item in all_previous_items: + if item.civ_name in value: + required_counts[key] += 1 + + for key, value in required_counts.items(): + has_amount = state.has(format_item_name(key), player, required_counts[key]) + if not has_amount: + return False + return True + + +def has_required_progressive_eras(state: CollectionState, era: EraType, player: int): + """Checks, for the given era, how many are required to proceed to the next era. Ancient = 1, Classical = 2, etc. Assumes 2 required for classical and starts from there so as to decrease odds of hard locking without the turns to get the items""" + if era == EraType.ERA_FUTURE or era == EraType.ERA_INFORMATION: + return True + + eras = [e.value for e in EraType] + era_index = eras.index(era.value) + return state.has(format_item_name("PROGRESSIVE_ERA"), player, era_index + 2) + + +def has_required_items(state: CollectionState, era: EraType, player: int, has_progressive_districts: bool, has_progressive_eras: bool): + required_items = False + required_eras = False + if has_progressive_districts: + required_items = has_required_progressive_districts(state, era, player) + else: + era_required_items = [get_item_by_civ_name(item, state.multiworld.worlds[player].item_table).name for item in get_era_required_items_data()[era.value]] + required_items = state.has_all(era_required_items, player) + + if has_progressive_eras: + required_eras = has_required_progressive_eras(state, era, player) + else: + required_eras = True + + return required_items and required_eras + + +def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): + menu = Region("Menu", player, world.multiworld) + world.multiworld.regions.append(menu) + + has_progressive_items = options.progression_style.current_key != "none" + has_progressive_eras = options.progression_style.current_key == "eras_and_districts" + has_goody_huts = options.shuffle_goody_hut_rewards.value + has_boosts = options.boostsanity.value + + regions: typing.List[Region] = [] + for era in EraType: + era_region = Region(era.value, player, world.multiworld) + era_locations = {location.name: location.code for key, + location in world.location_by_era[era.value].items()} + + # If progressive_eras is not enabled, then era check types from the era_locations + if not has_progressive_eras: + era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "ERA"} + if not has_goody_huts: + era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "GOODY"} + if not has_boosts: + era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "BOOST"} + + era_region.add_locations(era_locations, CivVILocation) + + regions.append(era_region) + world.multiworld.regions.append(era_region) + + menu.connect(world.get_region(EraType.ERA_ANCIENT.value)) + + world.get_region(EraType.ERA_ANCIENT.value).connect( + world.get_region(EraType.ERA_CLASSICAL.value), None, + lambda state: has_required_items( + state, EraType.ERA_ANCIENT, player, has_progressive_items, has_progressive_eras) + ) + + world.get_region(EraType.ERA_CLASSICAL.value).connect( + world.get_region(EraType.ERA_MEDIEVAL.value), None, lambda state: has_required_items( + state, EraType.ERA_CLASSICAL, player, has_progressive_items, has_progressive_eras) + ) + + world.get_region(EraType.ERA_MEDIEVAL.value).connect( + world.get_region(EraType.ERA_RENAISSANCE.value), None, lambda state: has_required_items( + state, EraType.ERA_MEDIEVAL, player, has_progressive_items, has_progressive_eras) + ) + + world.get_region(EraType.ERA_RENAISSANCE.value).connect( + world.get_region(EraType.ERA_INDUSTRIAL.value), None, lambda state: has_required_items( + state, EraType.ERA_RENAISSANCE, player, has_progressive_items, has_progressive_eras) + ) + + world.get_region(EraType.ERA_INDUSTRIAL.value).connect( + world.get_region(EraType.ERA_MODERN.value), None, lambda state: has_required_items( + state, EraType.ERA_INDUSTRIAL, player, has_progressive_items, has_progressive_eras) + ) + + world.get_region(EraType.ERA_MODERN.value).connect( + world.get_region(EraType.ERA_ATOMIC.value), None, lambda state: has_required_items( + state, EraType.ERA_MODERN, player, has_progressive_items, has_progressive_eras) + ) + + world.get_region(EraType.ERA_ATOMIC.value).connect( + world.get_region(EraType.ERA_INFORMATION.value), None, lambda state: has_required_items( + state, EraType.ERA_ATOMIC, player, has_progressive_items, has_progressive_eras) + ) + + world.get_region(EraType.ERA_INFORMATION.value).connect( + world.get_region(EraType.ERA_FUTURE.value), None, lambda state: has_required_items(state, EraType.ERA_INFORMATION, player, has_progressive_items, has_progressive_eras)) + + world.multiworld.completion_condition[player] = lambda state: state.can_reach( + EraType.ERA_FUTURE.value, "Region", player) + + if world.options.boostsanity.value and not world.options.exclude_missable_boosts.value: + _update_boost_locations_to_include_missable(world) + + +def _update_boost_locations_to_include_missable(world: 'CivVIWorld'): + """If the player has exclude missable boosts disabled, set them all to default if they are excluded""" + for loc_data in world.location_table.values(): + if loc_data.location_type == CivVICheckType.BOOST: + location = world.get_location(loc_data.name) + if location.progress_type == LocationProgressType.EXCLUDED: + location.progress_type = LocationProgressType.DEFAULT diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py new file mode 100644 index 000000000000..048a06241b9f --- /dev/null +++ b/worlds/civ_6/Rules.py @@ -0,0 +1,63 @@ +import typing + +from BaseClasses import CollectionState +from .Items import get_item_by_civ_name +from .Data import get_boosts_data +from .Enum import CivVICheckType +from .ProgressiveDistricts import convert_items_to_have_progression + +from worlds.generic.Rules import forbid_item, set_rule + + +if typing.TYPE_CHECKING: + from . import CivVIWorld + + +def generate_has_required_items_lambda(prereqs: typing.List[str], required_count: int, has_progressive_items: bool, player: int): + def has_required_items_lambda(state: CollectionState): + return has_required_items(state, prereqs, required_count, has_progressive_items, player) + return has_required_items_lambda + + +def create_boost_rules(world: 'CivVIWorld'): + boost_data_list = get_boosts_data() + boost_locations = [location for location in world.location_table.values() if location.location_type == CivVICheckType.BOOST] + for location in boost_locations: + boost_data = next((boost for boost in boost_data_list if boost.Type == location.name), None) + world_location = world.multiworld.get_location(location.name, world.player) + forbid_item(world_location, "Progressive Era", world.player) + if not boost_data or boost_data.PrereqRequiredCount == 0: + continue + + has_progressive_items = world.options.progression_style.current_key != "none" + set_rule(world_location, + generate_has_required_items_lambda(boost_data.Prereq, boost_data.PrereqRequiredCount, has_progressive_items, world.player) + ) + + +def has_required_items(state: CollectionState, prereqs: typing.List[str], required_count: int, has_progressive_items: bool, player: int): + if has_progressive_items: + items = [get_item_by_civ_name(item, state.multiworld.worlds[player].item_table).name for item in convert_items_to_have_progression(prereqs)] + progressive_items: typing.Dict[str, int] = {} + count = 0 + for item in items: + if "Progressive" in item: + if not progressive_items.get(item): + progressive_items[item] = 0 + progressive_items[item] += 1 + else: + if state.has(item, player): + count += 1 + + for item, required_progressive_item_count in progressive_items.items(): + if state.count(item, player) >= required_progressive_item_count: + count += required_progressive_item_count + if count > 0: + pass + return count >= required_count + else: + count = 0 + for prereq in prereqs: + if state.has(get_item_by_civ_name(prereq, state.multiworld.worlds[player].item_table).name, player): + count += 1 + return count >= required_count diff --git a/worlds/civ_6/TunerClient.py b/worlds/civ_6/TunerClient.py new file mode 100644 index 000000000000..8a6d3497af58 --- /dev/null +++ b/worlds/civ_6/TunerClient.py @@ -0,0 +1,108 @@ +import asyncio +from logging import Logger +import select +import socket + +ADDRESS = "127.0.0.1" +PORT = 4318 + +CLIENT_PREFIX = "APSTART:" +CLIENT_POSTFIX = ":APEND" + + +def decode_mixed_string(data): + return ''.join(chr(b) if 32 <= b < 127 else '?' for b in data) + + +class TunerException(Exception): + pass + + +class TunerTimeoutException(TunerException): + pass + + +class TunerErrorException(TunerException): + pass + + +class TunerConnectionException(TunerException): + pass + + +class TunerClient: + """Interfaces with Civilization via the tuner socket""" + logger: Logger + + def __init__(self, logger): + self.logger = logger + + def __parse_response(self, response: str) -> str: + """Parses the response from the tuner socket""" + split = response.split(CLIENT_PREFIX) + if len(split) > 1: + start = split[1] + end = start.split(CLIENT_POSTFIX)[0] + return end + elif "ERR:" in response: + raise TunerErrorException(response.replace("?", "")) + else: + return "" + + async def send_game_command(self, command_string: str, size: int = 64): + """Small helper that prefixes a command with GameCore.Game.""" + return await self.send_command("GameCore.Game." + command_string, size) + + async def send_command(self, command_string: str, size: int = 64): + """Send a raw commannd""" + self.logger.debug("Sending Command: " + command_string) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setblocking(False) + + b_command_string = command_string.encode('utf-8') + + # Send data to the server + command_prefix = b"CMD:0:" + delimiter = b"\x00" + full_command = b_command_string + message = command_prefix + full_command + delimiter + message_length = len(message).to_bytes(1, byteorder='little') + + # game expects this to be added before any command that is sent, indicates payload size + message_header = message_length + b"\x00\x00\x00\x03\x00\x00\x00" + data = message_header + command_prefix + full_command + delimiter + + server_address = (ADDRESS, PORT) + loop = asyncio.get_event_loop() + try: + await loop.sock_connect(sock, server_address) + await loop.sock_sendall(sock, data) + + # Add a delay before receiving data + await asyncio.sleep(.02) + + received_data = await self.async_recv(sock) + response = decode_mixed_string(received_data) + self.logger.debug('Received:') + self.logger.debug(response) + return self.__parse_response(response) + + except socket.timeout: + self.logger.debug('Timeout occurred while receiving data') + raise TunerTimeoutException() + except Exception as e: + self.logger.debug(f'Error occurred while receiving data: {str(e)}') + # check if No connection could be made is present in the error message + connection_errors = [ + "The remote computer refused the network connection", + ] + if any(error in str(e) for error in connection_errors): + raise TunerConnectionException(e) + else: + raise TunerErrorException(e) + finally: + sock.close() + + async def async_recv(self, sock, timeout=2.0, size=4096): + response = await asyncio.wait_for(asyncio.get_event_loop().sock_recv(sock, size), timeout) + return response diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py new file mode 100644 index 000000000000..45af2cdc2e52 --- /dev/null +++ b/worlds/civ_6/__init__.py @@ -0,0 +1,195 @@ +import math +import os +import random +from typing import Dict +import typing + +from .Data import get_boosts_data + +from .Rules import create_boost_rules +import Utils +from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql +from .Enum import CivVICheckType +from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity +from .Locations import CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table +from .Options import CivVIOptions +from .Regions import create_regions +from BaseClasses import Item, ItemClassification, MultiWorld, Tutorial +from worlds.AutoWorld import World, WebWorld +from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components, launch_subprocess + + +def run_client(): + print("Running Civ6 Client") + from .Civ6Client import main # lazy import + launch_subprocess(main, name="Civ6Client") + + +components.append( + Component("Civ6 Client", func=run_client, component_type=Type.CLIENT, + file_identifier=SuffixIdentifier(".apcivvi")) +) + + +class CivVIWeb(WebWorld): + tutorials = [Tutorial( + "Multiworld Setup Guide", + "A guide to setting up Civlization VI for MultiWorld.", + "English", + "setup_en.md", + "setup/en", + ["hesto2"] + )] + + +class CivVIWorld(World): + """ + Civilization VI is a turn-based strategy video game in which one or more players compete alongside computer-controlled AI opponents to grow their individual civilization from a small tribe to control the entire planet across several periods of development. + """ + + game: str = "Civilization VI" + topology_present = False + options_dataclass = CivVIOptions + options: CivVIOptions + + web = CivVIWeb() + + item_name_to_id = { + item.name: item.code for item in generate_item_table().values()} + location_name_to_id = { + location.name: location.code for location in generate_flat_location_table().values()} + + item_table: Dict[str, CivVIItemData] = {} + location_by_era: Dict[EraType, Dict[str, CivVILocationData]] + + data_version = 1 + required_client_version = (0, 4, 5) + + def __init__(self, multiworld: "MultiWorld", player: int): + super().__init__(multiworld, player) + self.location_by_era = generate_era_location_table() + + self.location_table: Dict[str, CivVILocationData] = {} + self.item_table = generate_item_table() + + for _era, locations in self.location_by_era.items(): + for _item_name, location in locations.items(): + self.location_table[location.name] = location + + def get_filler_item_name(self): + return get_random_filler_by_rarity(FillerItemRarity.COMMON, self.item_table).name + + def create_regions(self): + create_regions(self, self.options, self.player) + + def set_rules(self) -> None: + if self.options.boostsanity.value: + create_boost_rules(self) + + def create_item(self, name: str) -> Item: + item: CivVIItemData = self.item_table[name] + classification = item.classification + if self.options.boostsanity.value: + if item.civ_name in BOOSTSANITY_PROGRESSION_ITEMS: + classification = ItemClassification.progression + + return CivVIItem(item, self.player, classification) + + def create_items(self): + progressive_era_item = None + for item_name, data in self.item_table.items(): + # Don't add progressive items to the itempool here + if data.item_type == CivVICheckType.PROGRESSIVE_DISTRICT: + continue + if data.item_type == CivVICheckType.ERA: + # Don't add era items in this way + progressive_era_item = data + continue + if data.item_type == CivVICheckType.GOODY: + continue + + # If we're using progressive districts, we need to check if we need to create a different item instead + item_to_create = item_name + if self.options.progression_style.current_key != "none": + item: CivVIItemData = self.item_table[item_name] + if item.progression_name != None: + item_to_create = self.item_table[item.progression_name].name + + self.multiworld.itempool += [self.create_item( + item_to_create)] + + # Era items + if self.options.progression_style.current_key == "eras_and_districts": + # Add one less than the total number of eras (start in ancient, don't need to find it) + for era in EraType: + if era.value == "ERA_ANCIENT": + continue + self.multiworld.itempool += [self.create_item( + progressive_era_item.name)] + + num_filler_items = 0 + # Goody items, create 10 by default if options are enabled + if self.options.shuffle_goody_hut_rewards.value: + num_filler_items += 10 + + if self.options.boostsanity.value: + boost_data = get_boosts_data() + num_filler_items += len(boost_data) + + filler_count = {rarity: FILLER_DISTRIBUTION[rarity] * num_filler_items for rarity in FillerItemRarity.__reversed__()} + min_count = 1 + # Add filler items by rarity + total_created = 0 + for rarity, count in filler_count.items(): + for _ in range(max(min_count, math.ceil(count))): + if total_created >= num_filler_items: + break + self.multiworld.itempool += [self.create_item( + get_random_filler_by_rarity(rarity, self.item_table).name)] + total_created += 1 + + def post_fill(self): + if self.options.pre_hint_items.current_key == "none": + return + + show_flags = { + ItemClassification.progression: self.options.pre_hint_items.current_key != "none", + ItemClassification.useful: self.options.pre_hint_items.current_key == "no_junk" or self.options.pre_hint_items.current_key == "all", + ItemClassification.filler: self.options.pre_hint_items.current_key == "all", + } + + start_location_hints: typing.Set[str] = self.options.start_location_hints.value + for location_name, location_data in self.location_table.items(): + if location_data.location_type != CivVICheckType.CIVIC and location_data.location_type != CivVICheckType.TECH: + continue + + location: CivVILocation = self.multiworld.get_location(location_name, self.player) + + if not location.item or not show_flags.get(location.item.classification, False): + continue + + start_location_hints.add(location_name) + + def fill_slot_data(self): + return { + "progression_style": self.options.progression_style.current_key, + "death_link": self.options.death_link.value, + "research_cost_multiplier": self.options.research_cost_multiplier.value, + "death_link_effect": self.options.death_link_effect.value, + "death_link_effect_percent": self.options.death_link_effect_percent.value, + + } + + def generate_output(self, output_directory: str): + mod_name = f"AP-{self.multiworld.get_file_safe_player_name(self.player)}" + mod_dir = os.path.join( + output_directory, mod_name + "_" + Utils.__version__) + mod_files = { + f"NewItems.xml": generate_new_items(self), + f"InitOptions.lua": generate_setup_file(self), + f"GoodyHutOverride.sql": generate_goody_hut_sql(self), + f"UpdateExistingBoosts.sql": generate_update_boosts_sql(self), + } + mod = CivVIContainer(mod_files, mod_dir, output_directory, self.player, + self.multiworld.get_file_safe_player_name(self.player)) + mod.write() diff --git a/worlds/civ_6/data/boosts.json b/worlds/civ_6/data/boosts.json new file mode 100644 index 000000000000..e2658c0c4c69 --- /dev/null +++ b/worlds/civ_6/data/boosts.json @@ -0,0 +1,923 @@ +[ + { + "Type": "BOOST_TECH_SAILING", + "EraType": "ERA_ANCIENT", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_ASTROLOGY", + "EraType": "ERA_ANCIENT", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_IRRIGATION", + "EraType": "ERA_ANCIENT", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_ARCHERY", + "EraType": "ERA_ANCIENT", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_WRITING", + "EraType": "ERA_ANCIENT", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "PRIORITY" + }, + { + "Type": "BOOST_TECH_MASONRY", + "EraType": "ERA_ANCIENT", + "Prereq": ["TECH_MINING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_BRONZE_WORKING", + "EraType": "ERA_ANCIENT", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_THE_WHEEL", + "EraType": "ERA_ANCIENT", + "Prereq": ["TECH_MINING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_CELESTIAL_NAVIGATION", + "EraType": "ERA_CLASSICAL", + "Prereq": ["TECH_SAILING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_CURRENCY", + "EraType": "ERA_CLASSICAL", + "Prereq": ["CIVIC_FOREIGN_TRADE"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_HORSEBACK_RIDING", + "EraType": "ERA_CLASSICAL", + "Prereq": ["TECH_ANIMAL_HUSBANDRY"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_IRON_WORKING", + "EraType": "ERA_CLASSICAL", + "Prereq": ["TECH_MINING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_SHIPBUILDING", + "EraType": "ERA_CLASSICAL", + "Prereq": ["TECH_SAILING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_MATHEMATICS", + "EraType": "ERA_CLASSICAL", + "Prereq": [ + "TECH_CURRENCY", + "TECH_BRONZE_WORKING", + "TECH_CELESTIAL_NAVIGATION", + "TECH_WRITING", + "TECH_APPRENTICESHIP", + "TECH_FLIGHT", + "CIVIC_GAMES_RECREATION", + "CIVIC_DRAMA_POETRY" + ], + "PrereqRequiredCount": 3, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_CONSTRUCTION", + "EraType": "ERA_CLASSICAL", + "Prereq": ["TECH_THE_WHEEL"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_ENGINEERING", + "EraType": "ERA_CLASSICAL", + "Prereq": ["TECH_MASONRY"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_MILITARY_TACTICS", + "EraType": "ERA_MEDIEVAL", + "Prereq": ["TECH_BRONZE_WORKING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_APPRENTICESHIP", + "EraType": "ERA_MEDIEVAL", + "Prereq": ["TECH_MINING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_MACHINERY", + "EraType": "ERA_MEDIEVAL", + "Prereq": ["TECH_ARCHERY"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_EDUCATION", + "EraType": "ERA_MEDIEVAL", + "Prereq": ["TECH_WRITING"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_STIRRUPS", + "EraType": "ERA_MEDIEVAL", + "Prereq": ["CIVIC_FEUDALISM"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_MILITARY_ENGINEERING", + "EraType": "ERA_MEDIEVAL", + "Prereq": ["TECH_ENGINEERING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_CASTLES", + "EraType": "ERA_MEDIEVAL", + "Prereq": [ + "CIVIC_DIVINE_RIGHT", + "CIVIC_EXPLORATION", + "CIVIC_REFORMED_CHURCH", + "CIVIC_SUFFRAGE", + "CIVIC_TOTALITARIANISM", + "CIVIC_CLASS_STRUGGLE", + "CIVIC_DIGITAL_DEMOCRACY", + "CIVIC_CORPORATE_LIBERTARIANISM", + "CIVIC_SYNTHETIC_TECHNOCRACY" + ], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_CARTOGRAPHY", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_CELESTIAL_NAVIGATION"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_MASS_PRODUCTION", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_CONSTRUCTION"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_BANKING", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["CIVIC_GUILDS"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_GUNPOWDER", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_BRONZE_WORKING", "TECH_MILITARY_ENGINEERING"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_PRINTING", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_WRITING", "TECH_EDUCATION"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_SQUARE_RIGGING", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_GUNPOWDER"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_ASTRONOMY", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_EDUCATION"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_METAL_CASTING", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_MACHINERY"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_SIEGE_TACTICS", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_MILITARY_ENGINEERING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_INDUSTRIALIZATION", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["TECH_APPRENTICESHIP"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_SCIENTIFIC_THEORY", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["CIVIC_THE_ENLIGHTENMENT"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_BALLISTICS", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["TECH_SIEGE_TACTICS", "TECH_MILITARY_ENGINEERING"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_MILITARY_SCIENCE", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["TECH_STIRRUPS"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_STEAM_POWER", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["TECH_MASS_PRODUCTION"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_SANITATION", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["CIVIC_URBANIZATION"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_ECONOMICS", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["TECH_CURRENCY", "TECH_BANKING"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_RIFLING", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["TECH_MINING", "TECH_MILITARY_ENGINEERING"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_FLIGHT", + "EraType": "ERA_MODERN", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_REPLACEABLE_PARTS", + "EraType": "ERA_MODERN", + "Prereq": ["TECH_MILITARY_SCIENCE"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_STEEL", + "EraType": "ERA_MODERN", + "Prereq": ["TECH_MINING", "TECH_STEAM_POWER", "TECH_INDUSTRIALIZATION"], + "PrereqRequiredCount": 3, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_ELECTRICITY", + "EraType": "ERA_MODERN", + "Prereq": ["CIVIC_MERCANTILISM", "TECH_CELESTIAL_NAVIGATION"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_RADIO", + "EraType": "ERA_MODERN", + "Prereq": ["CIVIC_CONSERVATION"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_CHEMISTRY", + "EraType": "ERA_MODERN", + "Prereq": ["CIVIC_CIVIL_SERVICE"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_COMBUSTION", + "EraType": "ERA_MODERN", + "Prereq": ["CIVIC_NATURAL_HISTORY", "CIVIC_HUMANISM"], + "PrereqRequiredCount": 2, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_ADVANCED_FLIGHT", + "EraType": "ERA_ATOMIC", + "Prereq": ["TECH_FLIGHT"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_ROCKETRY", + "EraType": "ERA_ATOMIC", + "Prereq": ["CIVIC_DIPLOMATIC_SERVICE"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_ADVANCED_BALLISTICS", + "EraType": "ERA_ATOMIC", + "Prereq": [ + "TECH_ELECTRICITY", + "TECH_REFINING", + "TECH_APPRENTICESHIP", + "TECH_INDUSTRIALIZATION" + ], + "PrereqRequiredCount": 4, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_COMBINED_ARMS", + "EraType": "ERA_ATOMIC", + "Prereq": [ + "CIVIC_MOBILIZATION", + "CIVIC_NATIONALISM" + ], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_PLASTICS", + "EraType": "ERA_ATOMIC", + "Prereq": ["TECH_REFINING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_COMPUTERS", + "EraType": "ERA_ATOMIC", + "Prereq": [ + "CIVIC_SUFFRAGE", + "CIVIC_TOTALITARIANISM", + "CIVIC_CLASS_STRUGGLE", + "CIVIC_DIGITAL_DEMOCRACY", + "CIVIC_CORPORATE_LIBERTARIANISM", + "CIVIC_SYNTHETIC_TECHNOCRACY" + ], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_NUCLEAR_FISSION", + "EraType": "ERA_ATOMIC", + "Prereq": ["CIVIC_DIPLOMATIC_SERVICE"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_SYNTHETIC_MATERIALS", + "EraType": "ERA_ATOMIC", + "Prereq": ["TECH_FLIGHT"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_TELECOMMUNICATIONS", + "EraType": "ERA_INFORMATION", + "Prereq": ["CIVIC_DIPLOMATIC_SERVICE"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_SATELLITES", + "EraType": "ERA_INFORMATION", + "Prereq": ["CIVIC_DRAMA_POETRY", "CIVIC_HUMANISM", "TECH_RADIO"], + "PrereqRequiredCount": 3, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_GUIDANCE_SYSTEMS", + "EraType": "ERA_INFORMATION", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_LASERS", + "EraType": "ERA_INFORMATION", + "Prereq": ["TECH_COMPUTERS"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_COMPOSITES", + "EraType": "ERA_INFORMATION", + "Prereq": ["TECH_COMBUSTION"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_STEALTH_TECHNOLOGY", + "EraType": "ERA_INFORMATION", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_ROBOTICS", + "EraType": "ERA_INFORMATION", + "Prereq": ["CIVIC_GLOBALIZATION"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_NANOTECHNOLOGY", + "EraType": "ERA_INFORMATION", + "Prereq": ["TECH_MINING", "TECH_RADIO"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_NUCLEAR_FUSION", + "EraType": "ERA_INFORMATION", + "Prereq": [ + "TECH_APPRENTICESHIP", + "TECH_INDUSTRIALIZATION", + "TECH_ELECTRICITY", + "TECH_NUCLEAR_FISSION" + ], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_BUTTRESS", + "EraType": "ERA_MEDIEVAL", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_REFINING", + "EraType": "ERA_MODERN", + "Prereq": ["TECH_INDUSTRIALIZATION", "TECH_MINING", "TECH_APPRENTICESHIP"], + "PrereqRequiredCount": 3, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_TECH_SEASTEADS", + "EraType": "ERA_FUTURE", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_ADVANCED_AI", + "EraType": "ERA_FUTURE", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_ADVANCED_POWER_CELLS", + "EraType": "ERA_FUTURE", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_CYBERNETICS", + "EraType": "ERA_FUTURE", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_SMART_MATERIALS", + "EraType": "ERA_FUTURE", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_PREDICTIVE_SYSTEMS", + "EraType": "ERA_FUTURE", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_TECH_OFFWORLD_MISSION", + "EraType": "ERA_FUTURE", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_CRAFTSMANSHIP", + "EraType": "ERA_ANCIENT", + "Prereq": [ + "TECH_IRRIGATION", + "TECH_MINING", + "TECH_CONSTRUCTION", + "TECH_ANIMAL_HUSBANDRY", + "TECH_SAILING" + ], + "PrereqRequiredCount": 3, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_FOREIGN_TRADE", + "EraType": "ERA_ANCIENT", + "Prereq": ["TECH_CARTOGRAPHY"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_MILITARY_TRADITION", + "EraType": "ERA_ANCIENT", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_STATE_WORKFORCE", + "EraType": "ERA_ANCIENT", + "Prereq": [ + "TECH_CURRENCY", + "TECH_BRONZE_WORKING", + "TECH_CELESTIAL_NAVIGATION", + "TECH_WRITING", + "TECH_APPRENTICESHIP", + "TECH_FLIGHT", + "CIVIC_GAMES_RECREATION", + "CIVIC_DRAMA_POETRY" + ], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_EARLY_EMPIRE", + "EraType": "ERA_ANCIENT", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_MYSTICISM", + "EraType": "ERA_ANCIENT", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_GAMES_RECREATION", + "EraType": "ERA_CLASSICAL", + "Prereq": ["TECH_CONSTRUCTION"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_POLITICAL_PHILOSOPHY", + "EraType": "ERA_CLASSICAL", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_DRAMA_POETRY", + "EraType": "ERA_CLASSICAL", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_MILITARY_TRAINING", + "EraType": "ERA_CLASSICAL", + "Prereq": ["TECH_BRONZE_WORKING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_DEFENSIVE_TACTICS", + "EraType": "ERA_CLASSICAL", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_RECORDED_HISTORY", + "EraType": "ERA_CLASSICAL", + "Prereq": ["TECH_WRITING"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_THEOLOGY", + "EraType": "ERA_CLASSICAL", + "Prereq": ["TECH_ASTROLOGY"], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_NAVAL_TRADITION", + "EraType": "ERA_MEDIEVAL", + "Prereq": ["TECH_SHIPBUILDING"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_FEUDALISM", + "EraType": "ERA_MEDIEVAL", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_CIVIL_SERVICE", + "EraType": "ERA_MEDIEVAL", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_MERCENARIES", + "EraType": "ERA_MEDIEVAL", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_MEDIEVAL_FAIRES", + "EraType": "ERA_MEDIEVAL", + "Prereq": ["CIVIC_FOREIGN_TRADE", "TECH_CURRENCY"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_GUILDS", + "EraType": "ERA_MEDIEVAL", + "Prereq": ["TECH_CURRENCY"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_DIVINE_RIGHT", + "EraType": "ERA_MEDIEVAL", + "Prereq": ["CIVIC_THEOLOGY", "TECH_ASTROLOGY"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_EXPLORATION", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_CARTOGRAPHY", "TECH_CELESTIAL_NAVIGATION"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_HUMANISM", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["CIVIC_DRAMA_POETRY"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_DIPLOMATIC_SERVICE", + "EraType": "ERA_RENAISSANCE", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_REFORMED_CHURCH", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_ASTROLOGY"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_MERCANTILISM", + "EraType": "ERA_RENAISSANCE", + "Prereq": ["TECH_CURRENCY"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_THE_ENLIGHTENMENT", + "EraType": "ERA_RENAISSANCE", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_COLONIALISM", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["TECH_ASTRONOMY"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_CIVIL_ENGINEERING", + "EraType": "ERA_INDUSTRIAL", + "Prereq": [ + "TECH_CURRENCY", + "TECH_BRONZE_WORKING", + "TECH_CELESTIAL_NAVIGATION", + "TECH_WRITING", + "TECH_APPRENTICESHIP", + "TECH_FLIGHT", + "CIVIC_GAMES_RECREATION", + "CIVIC_DRAMA_POETRY" + ], + "PrereqRequiredCount": 8, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_NATIONALISM", + "EraType": "ERA_INDUSTRIAL", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_OPERA_BALLET", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["CIVIC_HUMANISM", "CIVIC_DRAMA_POETRY"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_NATURAL_HISTORY", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["CIVIC_HUMANISM", "CIVIC_DRAMA_POETRY"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_SCORCHED_EARTH", + "EraType": "ERA_INDUSTRIAL", + "Prereq": ["TECH_BALLISTICS"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_URBANIZATION", + "EraType": "ERA_INDUSTRIAL", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_CONSERVATION", + "EraType": "ERA_MODERN", + "Prereq": ["CIVIC_URBANIZATION"], + "PrereqRequiredCount": 1, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_CAPITALISM", + "EraType": "ERA_MODERN", + "Prereq": ["TECH_CURRENCY", "TECH_BANKING", "TECH_ECONOMICS"], + "PrereqRequiredCount": 3, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_NUCLEAR_PROGRAM", + "EraType": "ERA_MODERN", + "Prereq": ["TECH_WRITING", "TECH_EDUCATION", "TECH_CHEMISTRY"], + "PrereqRequiredCount": 3, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_MASS_MEDIA", + "EraType": "ERA_MODERN", + "Prereq": ["TECH_RADIO"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_MOBILIZATION", + "EraType": "ERA_MODERN", + "Prereq": ["CIVIC_NATIONALISM"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_SUFFRAGE", + "EraType": "ERA_MODERN", + "Prereq": ["TECH_SANITATION"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_TOTALITARIANISM", + "EraType": "ERA_MODERN", + "Prereq": [ + "TECH_BRONZE_WORKING", + "TECH_MILITARY_ENGINEERING", + "TECH_MILITARY_SCIENCE" + ], + "PrereqRequiredCount": 3, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_CLASS_STRUGGLE", + "EraType": "ERA_MODERN", + "Prereq": ["TECH_APPRENTICESHIP", "TECH_INDUSTRIALIZATION"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_COLD_WAR", + "EraType": "ERA_ATOMIC", + "Prereq": ["TECH_NUCLEAR_FISSION"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_PROFESSIONAL_SPORTS", + "EraType": "ERA_ATOMIC", + "Prereq": ["CIVIC_GAMES_RECREATION"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_CULTURAL_HERITAGE", + "EraType": "ERA_ATOMIC", + "Prereq": [], + "PrereqRequiredCount": 0, + "Classification": "EXCLUDED" + }, + { + "Type": "BOOST_CIVIC_RAPID_DEPLOYMENT", + "EraType": "ERA_ATOMIC", + "Prereq": ["TECH_FLIGHT", "TECH_CARTOGRAPHY", "TECH_SHIPBUILDING"], + "PrereqRequiredCount": 3, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_SPACE_RACE", + "EraType": "ERA_ATOMIC", + "Prereq": ["TECH_ROCKETRY"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_GLOBALIZATION", + "EraType": "ERA_INFORMATION", + "Prereq": ["TECH_FLIGHT", "TECH_ADVANCED_FLIGHT"], + "PrereqRequiredCount": 2, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_SOCIAL_MEDIA", + "EraType": "ERA_INFORMATION", + "Prereq": ["TECH_TELECOMMUNICATIONS"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + }, + { + "Type": "BOOST_CIVIC_ENVIRONMENTALISM", + "EraType": "ERA_INFORMATION", + "Prereq": ["TECH_SATELLITES"], + "PrereqRequiredCount": 1, + "Classification": "DEFAULT" + } +] diff --git a/worlds/civ_6/data/era_required_items.json b/worlds/civ_6/data/era_required_items.json new file mode 100644 index 000000000000..606028e85b7d --- /dev/null +++ b/worlds/civ_6/data/era_required_items.json @@ -0,0 +1,72 @@ +{ + "ERA_ANCIENT": [ + "TECH_MINING", + "TECH_BRONZE_WORKING", + "TECH_ASTROLOGY", + "TECH_WRITING", + "TECH_IRRIGATION", + "TECH_SAILING", + "TECH_ANIMAL_HUSBANDRY", + "CIVIC_STATE_WORKFORCE", + "CIVIC_FOREIGN_TRADE" + ], + "ERA_CLASSICAL": [ + "TECH_CELESTIAL_NAVIGATION", + "TECH_CURRENCY", + "TECH_MATHEMATICS", + "TECH_SHIPBUILDING", + "CIVIC_GAMES_RECREATION", + "CIVIC_POLITICAL_PHILOSOPHY", + "CIVIC_DRAMA_POETRY", + "CIVIC_THEOLOGY" + ], + "ERA_MEDIEVAL": [ + "TECH_APPRENTICESHIP", + "TECH_EDUCATION", + "TECH_MILITARY_ENGINEERING", + "CIVIC_DIVINE_RIGHT" + ], + "ERA_RENAISSANCE": [ + "TECH_MASS_PRODUCTION", + "TECH_BANKING", + "CIVIC_EXPLORATION", + "CIVIC_HUMANISM", + "CIVIC_REFORMED_CHURCH", + "CIVIC_DIPLOMATIC_SERVICE", + "TECH_CARTOGRAPHY" + ], + "ERA_INDUSTRIAL": [ + "TECH_INDUSTRIALIZATION", + "TECH_MILITARY_SCIENCE", + "TECH_ECONOMICS", + "CIVIC_NATIONALISM", + "CIVIC_NATURAL_HISTORY" + ], + "ERA_MODERN": [ + "TECH_FLIGHT", + "TECH_REFINING", + "TECH_ELECTRICITY", + "TECH_RADIO", + "TECH_CHEMISTRY", + "CIVIC_SUFFRAGE", + "CIVIC_TOTALITARIANISM", + "CIVIC_CLASS_STRUGGLE" + ], + "ERA_ATOMIC": [ + "TECH_ADVANCED_FLIGHT", + "TECH_ROCKETRY", + "TECH_COMBINED_ARMS", + "TECH_PLASTICS", + "TECH_NUCLEAR_FISSION", + "CIVIC_PROFESSIONAL_SPORTS" + ], + "ERA_INFORMATION": [ + "TECH_SATELLITES", + "TECH_NANOTECHNOLOGY", + "TECH_SMART_MATERIALS", + "CIVIC_CORPORATE_LIBERTARIANISM", + "CIVIC_DIGITAL_DEMOCRACY", + "CIVIC_SYNTHETIC_TECHNOCRACY" + ], + "ERA_FUTURE": [] +} diff --git a/worlds/civ_6/data/existing_civics.json b/worlds/civ_6/data/existing_civics.json new file mode 100644 index 000000000000..c4d871cfd39f --- /dev/null +++ b/worlds/civ_6/data/existing_civics.json @@ -0,0 +1,429 @@ +[ + { + "Type": "CIVIC_CODE_OF_LAWS", + "Name": "Code of Laws", + "Cost": 20, + "EraType": "ERA_ANCIENT", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_CRAFTSMANSHIP", + "Name": "Craftsmanship", + "Cost": 40, + "EraType": "ERA_ANCIENT", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_FOREIGN_TRADE", + "Name": "Foreign Trade", + "Cost": 40, + "EraType": "ERA_ANCIENT", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_MILITARY_TRADITION", + "Name": "Military Tradition", + "Cost": 50, + "EraType": "ERA_ANCIENT", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_STATE_WORKFORCE", + "Name": "State Workforce", + "Cost": 70, + "EraType": "ERA_ANCIENT", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_EARLY_EMPIRE", + "Name": "Early Empire", + "Cost": 70, + "EraType": "ERA_ANCIENT", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_MYSTICISM", + "Name": "Mysticism", + "Cost": 50, + "EraType": "ERA_ANCIENT", + "UITreeRow": 3 + }, + { + "Type": "CIVIC_GAMES_RECREATION", + "Name": "Games Recreation", + "Cost": 110, + "EraType": "ERA_CLASSICAL", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_POLITICAL_PHILOSOPHY", + "Name": "Political Philosophy", + "Cost": 110, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_DRAMA_POETRY", + "Name": "Drama and Poetry", + "Cost": 110, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_MILITARY_TRAINING", + "Name": "Military Training", + "Cost": 120, + "EraType": "ERA_CLASSICAL", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_DEFENSIVE_TACTICS", + "Name": "Defensive Tactics", + "Cost": 175, + "EraType": "ERA_CLASSICAL", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_RECORDED_HISTORY", + "Name": "Recorded History", + "Cost": 175, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_THEOLOGY", + "Name": "Theology", + "Cost": 120, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 3 + }, + { + "Type": "CIVIC_NAVAL_TRADITION", + "Name": "Naval Tradition", + "Cost": 220, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_FEUDALISM", + "Name": "Feudalism", + "Cost": 300, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_CIVIL_SERVICE", + "Name": "Civil Service", + "Cost": 300, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_MERCENARIES", + "Name": "Mercenaries", + "Cost": 340, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_MEDIEVAL_FAIRES", + "Name": "Medieval Faires", + "Cost": 420, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_GUILDS", + "Name": "Guilds", + "Cost": 420, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_DIVINE_RIGHT", + "Name": "Divine Right", + "Cost": 340, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": 3 + }, + { + "Type": "CIVIC_EXPLORATION", + "Name": "Exploration", + "Cost": 440, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_HUMANISM", + "Name": "Humanism", + "Cost": 600, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_DIPLOMATIC_SERVICE", + "Name": "Diplomatic Service", + "Cost": 600, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_REFORMED_CHURCH", + "Name": "Reformed Church", + "Cost": 440, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": 3 + }, + { + "Type": "CIVIC_MERCANTILISM", + "Name": "Mercantilism", + "Cost": 720, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_THE_ENLIGHTENMENT", + "Name": "The Enlightenment", + "Cost": 720, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_COLONIALISM", + "Name": "Colonialism", + "Cost": 800, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_CIVIL_ENGINEERING", + "Name": "Civil Engineering", + "Cost": 1010, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_NATIONALISM", + "Name": "Nationalism", + "Cost": 1010, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_OPERA_BALLET", + "Name": "Opera and Ballet", + "Cost": 800, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_NATURAL_HISTORY", + "Name": "Natural History", + "Cost": 1050, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_SCORCHED_EARTH", + "Name": "Scorched Earth", + "Cost": 1210, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_URBANIZATION", + "Name": "Urbanization", + "Cost": 1210, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_CONSERVATION", + "Name": "Conservation", + "Cost": 1540, + "EraType": "ERA_MODERN", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_CAPITALISM", + "Name": "Capitalism", + "Cost": 1580, + "EraType": "ERA_MODERN", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_NUCLEAR_PROGRAM", + "Name": "Nuclear Program", + "Cost": 1715, + "EraType": "ERA_MODERN", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_MASS_MEDIA", + "Name": "Mass Media", + "Cost": 1540, + "EraType": "ERA_MODERN", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_MOBILIZATION", + "Name": "Mobilization", + "Cost": 1540, + "EraType": "ERA_MODERN", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_IDEOLOGY", + "Name": "Ideology", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_SUFFRAGE", + "Name": "Suffrage", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_TOTALITARIANISM", + "Name": "Totalitarianism", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_CLASS_STRUGGLE", + "Name": "Class Struggle", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": 3 + }, + { + "Type": "CIVIC_COLD_WAR", + "Name": "Cold War", + "Cost": 2185, + "EraType": "ERA_ATOMIC", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_PROFESSIONAL_SPORTS", + "Name": "Professional Sports", + "Cost": 2185, + "EraType": "ERA_ATOMIC", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_CULTURAL_HERITAGE", + "Name": "Cultural Heritage", + "Cost": 1955, + "EraType": "ERA_ATOMIC", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_RAPID_DEPLOYMENT", + "Name": "Rapid Deployment", + "Cost": 2415, + "EraType": "ERA_ATOMIC", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_SPACE_RACE", + "Name": "Space Race", + "Cost": 2415, + "EraType": "ERA_ATOMIC", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_GLOBALIZATION", + "Name": "Globalization", + "Cost": 2880, + "EraType": "ERA_INFORMATION", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_SOCIAL_MEDIA", + "Name": "Social Media", + "Cost": 2880, + "EraType": "ERA_INFORMATION", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_FUTURE_CIVIC", + "Name": "Future Civic", + "Cost": 3500, + "EraType": "ERA_FUTURE", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_ENVIRONMENTALISM", + "Name": "Environmentalism", + "Cost": 2880, + "EraType": "ERA_INFORMATION", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_CORPORATE_LIBERTARIANISM", + "Name": "Corporate Libertarianism", + "Cost": 3000, + "EraType": "ERA_INFORMATION", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_DIGITAL_DEMOCRACY", + "Name": "Digital Democracy", + "Cost": 3000, + "EraType": "ERA_INFORMATION", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_SYNTHETIC_TECHNOCRACY", + "Name": "Synthetic Technocracy", + "Cost": 3000, + "EraType": "ERA_INFORMATION", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_NEAR_FUTURE_GOVERNANCE", + "Name": "Near Future Governance", + "Cost": 3100, + "EraType": "ERA_INFORMATION", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_GLOBAL_WARMING_MITIGATION", + "Name": "Global Warming Mitigation", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_SMART_POWER_DOCTRINE", + "Name": "Smart Power Doctrine", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_INFORMATION_WARFARE", + "Name": "Information Warfare", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_EXODUS_IMPERATIVE", + "Name": "Exodus Imperative", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_CULTURAL_HEGEMONY", + "Name": "Cultural Hegemony", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": 2 + } +] \ No newline at end of file diff --git a/worlds/civ_6/data/existing_tech.json b/worlds/civ_6/data/existing_tech.json new file mode 100644 index 000000000000..21869101d20c --- /dev/null +++ b/worlds/civ_6/data/existing_tech.json @@ -0,0 +1,541 @@ +[ + { + "Type": "TECH_POTTERY", + "Cost": 25, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT", + "Name": "Pottery" + }, + { + "Type": "TECH_ANIMAL_HUSBANDRY", + "Cost": 25, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT", + "Name": "Animal Husbandry" + }, + { + "Type": "TECH_MINING", + "Cost": 25, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT", + "Name": "Mining" + }, + { + "Type": "TECH_SAILING", + "Cost": 50, + "UITreeRow": -3, + "EraType": "ERA_ANCIENT", + "Name": "Sailing" + }, + { + "Type": "TECH_ASTROLOGY", + "Cost": 50, + "UITreeRow": -2, + "EraType": "ERA_ANCIENT", + "Name": "Astrology" + }, + { + "Type": "TECH_IRRIGATION", + "Cost": 50, + "UITreeRow": -1, + "EraType": "ERA_ANCIENT", + "Name": "Irrigation" + }, + { + "Type": "TECH_ARCHERY", + "Cost": 50, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT", + "Name": "Archery" + }, + { + "Type": "TECH_WRITING", + "Cost": 50, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT", + "Name": "Writing" + }, + { + "Type": "TECH_MASONRY", + "Cost": 80, + "UITreeRow": 2, + "EraType": "ERA_ANCIENT", + "Name": "Masonry" + }, + { + "Type": "TECH_BRONZE_WORKING", + "Cost": 80, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT", + "Name": "Bronze Working" + }, + { + "Type": "TECH_THE_WHEEL", + "Cost": 80, + "UITreeRow": 4, + "EraType": "ERA_ANCIENT", + "Name": "The Wheel" + }, + { + "Type": "TECH_CELESTIAL_NAVIGATION", + "Cost": 120, + "UITreeRow": -2, + "EraType": "ERA_CLASSICAL", + "Name": "Celestial Navigation" + }, + { + "Type": "TECH_CURRENCY", + "Cost": 120, + "UITreeRow": 0, + "EraType": "ERA_CLASSICAL", + "Name": "Currency" + }, + { + "Type": "TECH_HORSEBACK_RIDING", + "Cost": 120, + "UITreeRow": 1, + "EraType": "ERA_CLASSICAL", + "Name": "Horseback Riding" + }, + { + "Type": "TECH_IRON_WORKING", + "Cost": 120, + "UITreeRow": 3, + "EraType": "ERA_CLASSICAL", + "Name": "Iron Working" + }, + { + "Type": "TECH_SHIPBUILDING", + "Cost": 200, + "UITreeRow": -3, + "EraType": "ERA_CLASSICAL", + "Name": "Shipbuilding" + }, + { + "Type": "TECH_MATHEMATICS", + "Cost": 200, + "UITreeRow": -1, + "EraType": "ERA_CLASSICAL", + "Name": "Mathematics" + }, + { + "Type": "TECH_CONSTRUCTION", + "Cost": 200, + "UITreeRow": 2, + "EraType": "ERA_CLASSICAL", + "Name": "Construction" + }, + { + "Type": "TECH_ENGINEERING", + "Cost": 200, + "UITreeRow": 4, + "EraType": "ERA_CLASSICAL", + "Name": "Engineering" + }, + { + "Type": "TECH_MILITARY_TACTICS", + "Cost": 300, + "UITreeRow": -2, + "EraType": "ERA_MEDIEVAL", + "Name": "Military Tactics" + }, + { + "Type": "TECH_APPRENTICESHIP", + "Cost": 300, + "UITreeRow": 0, + "EraType": "ERA_MEDIEVAL", + "Name": "Apprenticeship" + }, + { + "Type": "TECH_MACHINERY", + "Cost": 300, + "UITreeRow": 4, + "EraType": "ERA_MEDIEVAL", + "Name": "Machinery" + }, + { + "Type": "TECH_EDUCATION", + "Cost": 390, + "UITreeRow": -1, + "EraType": "ERA_MEDIEVAL", + "Name": "Education" + }, + { + "Type": "TECH_STIRRUPS", + "Cost": 390, + "UITreeRow": 1, + "EraType": "ERA_MEDIEVAL", + "Name": "Stirrups" + }, + { + "Type": "TECH_MILITARY_ENGINEERING", + "Cost": 390, + "UITreeRow": 2, + "EraType": "ERA_MEDIEVAL", + "Name": "Military Engineering" + }, + { + "Type": "TECH_CASTLES", + "Cost": 390, + "UITreeRow": 3, + "EraType": "ERA_MEDIEVAL", + "Name": "Castles" + }, + { + "Type": "TECH_CARTOGRAPHY", + "Cost": 600, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE", + "Name": "Cartography" + }, + { + "Type": "TECH_MASS_PRODUCTION", + "Cost": 600, + "UITreeRow": -2, + "EraType": "ERA_RENAISSANCE", + "Name": "Mass Production" + }, + { + "Type": "TECH_BANKING", + "Cost": 600, + "UITreeRow": 0, + "EraType": "ERA_RENAISSANCE", + "Name": "Banking" + }, + { + "Type": "TECH_GUNPOWDER", + "Cost": 600, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE", + "Name": "Gunpowder" + }, + { + "Type": "TECH_PRINTING", + "Cost": 600, + "UITreeRow": 4, + "EraType": "ERA_RENAISSANCE", + "Name": "Printing" + }, + { + "Type": "TECH_SQUARE_RIGGING", + "Cost": 730, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE", + "Name": "Square Rigging" + }, + { + "Type": "TECH_ASTRONOMY", + "Cost": 730, + "UITreeRow": -1, + "EraType": "ERA_RENAISSANCE", + "Name": "Astronomy" + }, + { + "Type": "TECH_METAL_CASTING", + "Cost": 730, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE", + "Name": "Metal Casting" + }, + { + "Type": "TECH_SIEGE_TACTICS", + "Cost": 730, + "UITreeRow": 3, + "EraType": "ERA_RENAISSANCE", + "Name": "Siege Tactics" + }, + { + "Type": "TECH_INDUSTRIALIZATION", + "Cost": 930, + "UITreeRow": -2, + "EraType": "ERA_INDUSTRIAL", + "Name": "Industrialization" + }, + { + "Type": "TECH_SCIENTIFIC_THEORY", + "Cost": 930, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL", + "Name": "Scientific Theory" + }, + { + "Type": "TECH_BALLISTICS", + "Cost": 930, + "UITreeRow": 1, + "EraType": "ERA_INDUSTRIAL", + "Name": "Ballistics" + }, + { + "Type": "TECH_MILITARY_SCIENCE", + "Cost": 930, + "UITreeRow": 3, + "EraType": "ERA_INDUSTRIAL", + "Name": "Military Science" + }, + { + "Type": "TECH_STEAM_POWER", + "Cost": 1070, + "UITreeRow": -3, + "EraType": "ERA_INDUSTRIAL", + "Name": "Steam Power" + }, + { + "Type": "TECH_SANITATION", + "Cost": 1070, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL", + "Name": "Sanitation" + }, + { + "Type": "TECH_ECONOMICS", + "Cost": 1070, + "UITreeRow": 0, + "EraType": "ERA_INDUSTRIAL", + "Name": "Economics" + }, + { + "Type": "TECH_RIFLING", + "Cost": 1070, + "UITreeRow": 2, + "EraType": "ERA_INDUSTRIAL", + "Name": "Rifling" + }, + { + "Type": "TECH_FLIGHT", + "Cost": 1250, + "UITreeRow": -2, + "EraType": "ERA_MODERN", + "Name": "Flight" + }, + { + "Type": "TECH_REPLACEABLE_PARTS", + "Cost": 1250, + "UITreeRow": 0, + "EraType": "ERA_MODERN", + "Name": "Replaceable Parts" + }, + { + "Type": "TECH_STEEL", + "Cost": 1250, + "UITreeRow": 1, + "EraType": "ERA_MODERN", + "Name": "Steel" + }, + { + "Type": "TECH_ELECTRICITY", + "Cost": 1370, + "UITreeRow": -3, + "EraType": "ERA_MODERN", + "Name": "Electricity" + }, + { + "Type": "TECH_RADIO", + "Cost": 1370, + "UITreeRow": -2, + "EraType": "ERA_MODERN", + "Name": "Radio" + }, + { + "Type": "TECH_CHEMISTRY", + "Cost": 1370, + "UITreeRow": -1, + "EraType": "ERA_MODERN", + "Name": "Chemistry" + }, + { + "Type": "TECH_COMBUSTION", + "Cost": 1370, + "UITreeRow": 2, + "EraType": "ERA_MODERN", + "Name": "Combustion" + }, + { + "Type": "TECH_ADVANCED_FLIGHT", + "Cost": 1480, + "UITreeRow": -2, + "EraType": "ERA_ATOMIC", + "Name": "Advanced Flight" + }, + { + "Type": "TECH_ROCKETRY", + "Cost": 1480, + "UITreeRow": -1, + "EraType": "ERA_ATOMIC", + "Name": "Rocketry" + }, + { + "Type": "TECH_ADVANCED_BALLISTICS", + "Cost": 1480, + "UITreeRow": 0, + "EraType": "ERA_ATOMIC", + "Name": "Advanced Ballistics" + }, + { + "Type": "TECH_COMBINED_ARMS", + "Cost": 1480, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC", + "Name": "Combined Arms" + }, + { + "Type": "TECH_PLASTICS", + "Cost": 1480, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC", + "Name": "Plastics" + }, + { + "Type": "TECH_COMPUTERS", + "Cost": 1660, + "UITreeRow": -3, + "EraType": "ERA_ATOMIC", + "Name": "Computers" + }, + { + "Type": "TECH_NUCLEAR_FISSION", + "Cost": 1660, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC", + "Name": "Nuclear Fission" + }, + { + "Type": "TECH_SYNTHETIC_MATERIALS", + "Cost": 1660, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC", + "Name": "Synthetic Materials" + }, + { + "Type": "TECH_TELECOMMUNICATIONS", + "Cost": 1850, + "UITreeRow": -3, + "EraType": "ERA_INFORMATION", + "Name": "Telecommunications" + }, + { + "Type": "TECH_SATELLITES", + "Cost": 1850, + "UITreeRow": -1, + "EraType": "ERA_INFORMATION", + "Name": "Satellites" + }, + { + "Type": "TECH_GUIDANCE_SYSTEMS", + "Cost": 1850, + "UITreeRow": 0, + "EraType": "ERA_INFORMATION", + "Name": "Guidance Systems" + }, + { + "Type": "TECH_LASERS", + "Cost": 1850, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION", + "Name": "Lasers" + }, + { + "Type": "TECH_COMPOSITES", + "Cost": 1850, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION", + "Name": "Composites" + }, + { + "Type": "TECH_STEALTH_TECHNOLOGY", + "Cost": 1850, + "UITreeRow": 3, + "EraType": "ERA_INFORMATION", + "Name": "Stealth Technology" + }, + { + "Type": "TECH_ROBOTICS", + "Cost": 2155, + "UITreeRow": -2, + "EraType": "ERA_INFORMATION", + "Name": "Robotics" + }, + { + "Type": "TECH_NANOTECHNOLOGY", + "Cost": 2155, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION", + "Name": "Nanotechnology" + }, + { + "Type": "TECH_NUCLEAR_FUSION", + "Cost": 2155, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION", + "Name": "Nuclear Fusion" + }, + { + "Type": "TECH_BUTTRESS", + "Cost": 300, + "UITreeRow": -3, + "EraType": "ERA_MEDIEVAL", + "Name": "Buttress" + }, + { + "Type": "TECH_REFINING", + "Cost": 1250, + "UITreeRow": 3, + "EraType": "ERA_MODERN", + "Name": "Refining" + }, + { + "Type": "TECH_SEASTEADS", + "Cost": 2200, + "UITreeRow": -3, + "EraType": "ERA_FUTURE", + "Name": "Seasteads" + }, + { + "Type": "TECH_ADVANCED_AI", + "Cost": 2200, + "UITreeRow": -2, + "EraType": "ERA_FUTURE", + "Name": "Advanced AI" + }, + { + "Type": "TECH_ADVANCED_POWER_CELLS", + "Cost": 2200, + "UITreeRow": -1, + "EraType": "ERA_FUTURE", + "Name": "Advanced Power Cells" + }, + { + "Type": "TECH_CYBERNETICS", + "Cost": 2200, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + "Name": "Cybernetics" + }, + { + "Type": "TECH_SMART_MATERIALS", + "Cost": 2200, + "UITreeRow": 1, + "EraType": "ERA_FUTURE", + "Name": "Smart Materials" + }, + { + "Type": "TECH_PREDICTIVE_SYSTEMS", + "Cost": 2200, + "UITreeRow": 2, + "EraType": "ERA_FUTURE", + "Name": "Predictive Systems" + }, + { + "Type": "TECH_OFFWORLD_MISSION", + "Cost": 2500, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + "Name": "Offworld Mission" + }, + { + "Type": "TECH_FUTURE_TECH", + "Cost": 2600, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + "Name": "Future Tech" + } +] \ No newline at end of file diff --git a/worlds/civ_6/data/goody_hut_rewards.json b/worlds/civ_6/data/goody_hut_rewards.json new file mode 100644 index 000000000000..b2055efaf33c --- /dev/null +++ b/worlds/civ_6/data/goody_hut_rewards.json @@ -0,0 +1,77 @@ +[ + { + "Type": "GOODY_GOLD_SMALL_MODIFIER", + "Rarity": "COMMON", + "Name": "Gold: Small" + }, + { + "Type": "GOODY_GOLD_MEDIUM_MODIFIER", + "Rarity": "COMMON", + "Name": "Gold: Medium" + }, + { + "Type": "GOODY_GOLD_LARGE_MODIFIER", + "Rarity": "UNCOMMON", + "Name": "Gold: Large" + }, + { + "Type": "GOODY_FAITH_SMALL_MODIFIER", + "Rarity": "COMMON", + "Name": "Faith: Small" + }, + { + "Type": "GOODY_FAITH_MEDIUM_MODIFIER", + "Rarity": "COMMON", + "Name": "Faith: Medium" + }, + { + "Type": "GOODY_FAITH_LARGE_MODIFIER", + "Rarity": "UNCOMMON", + "Name": "Faith: Large" + }, + { + "Type": "GOODY_DIPLOMACY_GRANT_FAVOR", + "Rarity": "COMMON", + "Name": "Diplomatic Favor" + }, + { + "Type": "GOODY_DIPLOMACY_GRANT_GOVERNOR_TITLE", + "Rarity": "RARE", + "Name": "Governor Title" + }, + { + "Type": "GOODY_DIPLOMACY_GRANT_ENVOY", + "Rarity": "UNCOMMON", + "Name": "Envoy" + }, + { + "Type": "GOODY_CULTURE_GRANT_ONE_RELIC", + "Rarity": "RARE", + "Name": "Relic" + }, + { + "Type": "GOODY_MILITARY_GRANT_SCOUT", + "Rarity": "UNCOMMON", + "Name": "Scout" + }, + { + "Type": "GOODY_SURVIVORS_ADD_POPULATION", + "Rarity": "UNCOMMON", + "Name": "Additional Population" + }, + { + "Type": "GOODY_SURVIVORS_GRANT_BUILDER", + "Rarity": "UNCOMMON", + "Name": "Builder" + }, + { + "Type": "GOODY_SURVIVORS_GRANT_TRADER", + "Rarity": "UNCOMMON", + "Name": "Trader" + }, + { + "Type": "GOODY_SURVIVORS_GRANT_SETTLER", + "Rarity": "UNCOMMON", + "Name": "Settler" + } +] \ No newline at end of file diff --git a/worlds/civ_6/data/new_civic_prereqs.json b/worlds/civ_6/data/new_civic_prereqs.json new file mode 100644 index 000000000000..ca63d18c0edf --- /dev/null +++ b/worlds/civ_6/data/new_civic_prereqs.json @@ -0,0 +1,87 @@ +[ + { "Civic": "CIVIC_AP_ANCIENT_01", "PrereqCivic": "CIVIC_AP_ANCIENT_00" }, + { "Civic": "CIVIC_AP_ANCIENT_02", "PrereqCivic": "CIVIC_AP_ANCIENT_00" }, + { "Civic": "CIVIC_AP_ANCIENT_03", "PrereqCivic": "CIVIC_AP_ANCIENT_01" }, + { "Civic": "CIVIC_AP_ANCIENT_04", "PrereqCivic": "CIVIC_AP_ANCIENT_01" }, + { "Civic": "CIVIC_AP_ANCIENT_05", "PrereqCivic": "CIVIC_AP_ANCIENT_02" }, + { "Civic": "CIVIC_AP_ANCIENT_06", "PrereqCivic": "CIVIC_AP_ANCIENT_02" }, + { "Civic": "CIVIC_AP_CLASSICAL_07", "PrereqCivic": "CIVIC_AP_ANCIENT_04" }, + { "Civic": "CIVIC_AP_CLASSICAL_08", "PrereqCivic": "CIVIC_AP_ANCIENT_04" }, + { "Civic": "CIVIC_AP_CLASSICAL_08", "PrereqCivic": "CIVIC_AP_ANCIENT_05" }, + { "Civic": "CIVIC_AP_CLASSICAL_09", "PrereqCivic": "CIVIC_AP_ANCIENT_05" }, + { "Civic": "CIVIC_AP_CLASSICAL_10", "PrereqCivic": "CIVIC_AP_ANCIENT_03" }, + { "Civic": "CIVIC_AP_CLASSICAL_10", "PrereqCivic": "CIVIC_AP_CLASSICAL_07" }, + { "Civic": "CIVIC_AP_CLASSICAL_11", "PrereqCivic": "CIVIC_AP_CLASSICAL_07" }, + { "Civic": "CIVIC_AP_CLASSICAL_11", "PrereqCivic": "CIVIC_AP_CLASSICAL_08" }, + { "Civic": "CIVIC_AP_CLASSICAL_12", "PrereqCivic": "CIVIC_AP_CLASSICAL_08" }, + { "Civic": "CIVIC_AP_CLASSICAL_12", "PrereqCivic": "CIVIC_AP_CLASSICAL_09" }, + { "Civic": "CIVIC_AP_CLASSICAL_13", "PrereqCivic": "CIVIC_AP_CLASSICAL_09" }, + { "Civic": "CIVIC_AP_CLASSICAL_13", "PrereqCivic": "CIVIC_AP_ANCIENT_06" }, + { "Civic": "CIVIC_AP_MEDIEVAL_14", "PrereqCivic": "CIVIC_AP_CLASSICAL_11" }, + { "Civic": "CIVIC_AP_MEDIEVAL_15", "PrereqCivic": "CIVIC_AP_CLASSICAL_11" }, + { "Civic": "CIVIC_AP_MEDIEVAL_16", "PrereqCivic": "CIVIC_AP_CLASSICAL_11" }, + { "Civic": "CIVIC_AP_MEDIEVAL_16", "PrereqCivic": "CIVIC_AP_CLASSICAL_12" }, + { "Civic": "CIVIC_AP_MEDIEVAL_17", "PrereqCivic": "CIVIC_AP_CLASSICAL_10" }, + { "Civic": "CIVIC_AP_MEDIEVAL_17", "PrereqCivic": "CIVIC_AP_MEDIEVAL_15" }, + { "Civic": "CIVIC_AP_MEDIEVAL_18", "PrereqCivic": "CIVIC_AP_MEDIEVAL_15" }, + { "Civic": "CIVIC_AP_MEDIEVAL_19", "PrereqCivic": "CIVIC_AP_MEDIEVAL_15" }, + { "Civic": "CIVIC_AP_MEDIEVAL_19", "PrereqCivic": "CIVIC_AP_MEDIEVAL_16" }, + { "Civic": "CIVIC_AP_MEDIEVAL_20", "PrereqCivic": "CIVIC_AP_MEDIEVAL_16" }, + { "Civic": "CIVIC_AP_MEDIEVAL_20", "PrereqCivic": "CIVIC_AP_CLASSICAL_13" }, + { "Civic": "CIVIC_AP_RENAISSANCE_21", "PrereqCivic": "CIVIC_AP_MEDIEVAL_17" }, + { "Civic": "CIVIC_AP_RENAISSANCE_21", "PrereqCivic": "CIVIC_AP_MEDIEVAL_18" }, + { "Civic": "CIVIC_AP_RENAISSANCE_22", "PrereqCivic": "CIVIC_AP_MEDIEVAL_18" }, + { "Civic": "CIVIC_AP_RENAISSANCE_22", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19" }, + { "Civic": "CIVIC_AP_RENAISSANCE_23", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19" }, + { "Civic": "CIVIC_AP_RENAISSANCE_24", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19" }, + { "Civic": "CIVIC_AP_RENAISSANCE_24", "PrereqCivic": "CIVIC_AP_MEDIEVAL_20" }, + { "Civic": "CIVIC_AP_RENAISSANCE_25", "PrereqCivic": "CIVIC_AP_RENAISSANCE_22" }, + { "Civic": "CIVIC_AP_RENAISSANCE_26", "PrereqCivic": "CIVIC_AP_RENAISSANCE_22" }, + { "Civic": "CIVIC_AP_RENAISSANCE_26", "PrereqCivic": "CIVIC_AP_RENAISSANCE_23" }, + { "Civic": "CIVIC_AP_INDUSTRIAL_27", "PrereqCivic": "CIVIC_AP_RENAISSANCE_25" }, + { "Civic": "CIVIC_AP_INDUSTRIAL_28", "PrereqCivic": "CIVIC_AP_RENAISSANCE_25" }, + { "Civic": "CIVIC_AP_INDUSTRIAL_29", "PrereqCivic": "CIVIC_AP_RENAISSANCE_26" }, + { "Civic": "CIVIC_AP_INDUSTRIAL_30", "PrereqCivic": "CIVIC_AP_RENAISSANCE_26" }, + { "Civic": "CIVIC_AP_INDUSTRIAL_31", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_27" }, + { "Civic": "CIVIC_AP_INDUSTRIAL_32", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_29" }, + { "Civic": "CIVIC_AP_INDUSTRIAL_33", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_28" }, + { "Civic": "CIVIC_AP_INDUSTRIAL_33", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_29" }, + { "Civic": "CIVIC_AP_MODERN_34", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_31" }, + { "Civic": "CIVIC_AP_MODERN_37", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_31" }, + { "Civic": "CIVIC_AP_MODERN_37", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_33" }, + { "Civic": "CIVIC_AP_MODERN_35", "PrereqCivic": "CIVIC_AP_MODERN_37" }, + { "Civic": "CIVIC_AP_MODERN_38", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_33" }, + { "Civic": "CIVIC_AP_MODERN_39", "PrereqCivic": "CIVIC_AP_MODERN_37" }, + { "Civic": "CIVIC_AP_MODERN_39", "PrereqCivic": "CIVIC_AP_MODERN_38" }, + { "Civic": "CIVIC_AP_MODERN_36", "PrereqCivic": "CIVIC_AP_MODERN_39" }, + { "Civic": "CIVIC_AP_MODERN_40", "PrereqCivic": "CIVIC_AP_MODERN_39" }, + { "Civic": "CIVIC_AP_MODERN_41", "PrereqCivic": "CIVIC_AP_MODERN_39" }, + { "Civic": "CIVIC_AP_MODERN_42", "PrereqCivic": "CIVIC_AP_MODERN_39" }, + { "Civic": "CIVIC_AP_ATOMIC_43", "PrereqCivic": "CIVIC_AP_MODERN_39" }, + { "Civic": "CIVIC_AP_ATOMIC_44", "PrereqCivic": "CIVIC_AP_MODERN_39" }, + { "Civic": "CIVIC_AP_ATOMIC_45", "PrereqCivic": "CIVIC_AP_MODERN_34" }, + { "Civic": "CIVIC_AP_ATOMIC_46", "PrereqCivic": "CIVIC_AP_ATOMIC_43" }, + { "Civic": "CIVIC_AP_ATOMIC_47", "PrereqCivic": "CIVIC_AP_ATOMIC_43" }, + { "Civic": "CIVIC_AP_INFORMATION_48", "PrereqCivic": "CIVIC_AP_ATOMIC_46" }, + { "Civic": "CIVIC_AP_INFORMATION_48", "PrereqCivic": "CIVIC_AP_ATOMIC_47" }, + { "Civic": "CIVIC_AP_INFORMATION_49", "PrereqCivic": "CIVIC_AP_ATOMIC_47" }, + { "Civic": "CIVIC_AP_INFORMATION_49", "PrereqCivic": "CIVIC_AP_ATOMIC_44" }, + { "Civic": "CIVIC_AP_FUTURE_50", "PrereqCivic": "CIVIC_AP_INFORMATION_48" }, + { "Civic": "CIVIC_AP_FUTURE_50", "PrereqCivic": "CIVIC_AP_INFORMATION_49" }, + { "Civic": "CIVIC_AP_MODERN_38", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_32" }, + { "Civic": "CIVIC_AP_INFORMATION_51", "PrereqCivic": "CIVIC_AP_ATOMIC_45" }, + { "Civic": "CIVIC_AP_INFORMATION_51", "PrereqCivic": "CIVIC_AP_ATOMIC_46" }, + { "Civic": "CIVIC_AP_INFORMATION_52", "PrereqCivic": "CIVIC_AP_INFORMATION_48" }, + { "Civic": "CIVIC_AP_INFORMATION_52", "PrereqCivic": "CIVIC_AP_INFORMATION_49" }, + { "Civic": "CIVIC_AP_INFORMATION_53", "PrereqCivic": "CIVIC_AP_INFORMATION_48" }, + { "Civic": "CIVIC_AP_INFORMATION_53", "PrereqCivic": "CIVIC_AP_INFORMATION_49" }, + { "Civic": "CIVIC_AP_INFORMATION_54", "PrereqCivic": "CIVIC_AP_INFORMATION_48" }, + { "Civic": "CIVIC_AP_INFORMATION_54", "PrereqCivic": "CIVIC_AP_INFORMATION_49" }, + { "Civic": "CIVIC_AP_INFORMATION_55", "PrereqCivic": "CIVIC_AP_INFORMATION_51" }, + { "Civic": "CIVIC_AP_INFORMATION_55", "PrereqCivic": "CIVIC_AP_INFORMATION_48" }, + {"Civic": "CIVIC_AP_FUTURE_56", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, + {"Civic": "CIVIC_AP_FUTURE_57", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, + {"Civic": "CIVIC_AP_FUTURE_58", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, + {"Civic": "CIVIC_AP_FUTURE_59", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, + {"Civic": "CIVIC_AP_FUTURE_60", "PrereqCivic": "CIVIC_AP_FUTURE_50"} +] \ No newline at end of file diff --git a/worlds/civ_6/data/new_civics.json b/worlds/civ_6/data/new_civics.json new file mode 100644 index 000000000000..4ed6fe83dde5 --- /dev/null +++ b/worlds/civ_6/data/new_civics.json @@ -0,0 +1,63 @@ +[ +{"Type": "CIVIC_AP_ANCIENT_00", "Cost": 20, "UITreeRow": 0, "EraType": "ERA_ANCIENT"}, +{"Type": "CIVIC_AP_ANCIENT_01", "Cost": 40, "UITreeRow": -2, "EraType": "ERA_ANCIENT"}, +{"Type": "CIVIC_AP_ANCIENT_02", "Cost": 40, "UITreeRow": 2, "EraType": "ERA_ANCIENT"}, +{"Type": "CIVIC_AP_ANCIENT_03", "Cost": 50, "UITreeRow": -3, "EraType": "ERA_ANCIENT"}, +{"Type": "CIVIC_AP_ANCIENT_04", "Cost": 70, "UITreeRow": 0, "EraType": "ERA_ANCIENT"}, +{"Type": "CIVIC_AP_ANCIENT_05", "Cost": 70, "UITreeRow": 1, "EraType": "ERA_ANCIENT"}, +{"Type": "CIVIC_AP_ANCIENT_06", "Cost": 50, "UITreeRow": 3, "EraType": "ERA_ANCIENT"}, +{"Type": "CIVIC_AP_CLASSICAL_07", "Cost": 110, "UITreeRow": -2, "EraType": "ERA_CLASSICAL"}, +{"Type": "CIVIC_AP_CLASSICAL_08", "Cost": 110, "UITreeRow": 0, "EraType": "ERA_CLASSICAL"}, +{"Type": "CIVIC_AP_CLASSICAL_09", "Cost": 110, "UITreeRow": 2, "EraType": "ERA_CLASSICAL"}, +{"Type": "CIVIC_AP_CLASSICAL_10", "Cost": 120, "UITreeRow": -3, "EraType": "ERA_CLASSICAL"}, +{"Type": "CIVIC_AP_CLASSICAL_11", "Cost": 175, "UITreeRow": -1, "EraType": "ERA_CLASSICAL"}, +{"Type": "CIVIC_AP_CLASSICAL_12", "Cost": 175, "UITreeRow": 1, "EraType": "ERA_CLASSICAL"}, +{"Type": "CIVIC_AP_CLASSICAL_13", "Cost": 120, "UITreeRow": 3, "EraType": "ERA_CLASSICAL"}, +{"Type": "CIVIC_AP_MEDIEVAL_14", "Cost": 220, "UITreeRow": -2, "EraType": "ERA_MEDIEVAL"}, +{"Type": "CIVIC_AP_MEDIEVAL_15", "Cost": 300, "UITreeRow": -1, "EraType": "ERA_MEDIEVAL"}, +{"Type": "CIVIC_AP_MEDIEVAL_16", "Cost": 300, "UITreeRow": 1, "EraType": "ERA_MEDIEVAL"}, +{"Type": "CIVIC_AP_MEDIEVAL_17", "Cost": 340, "UITreeRow": -3, "EraType": "ERA_MEDIEVAL"}, +{"Type": "CIVIC_AP_MEDIEVAL_18", "Cost": 420, "UITreeRow": -1, "EraType": "ERA_MEDIEVAL"}, +{"Type": "CIVIC_AP_MEDIEVAL_19", "Cost": 420, "UITreeRow": 1, "EraType": "ERA_MEDIEVAL"}, +{"Type": "CIVIC_AP_MEDIEVAL_20", "Cost": 340, "UITreeRow": 3, "EraType": "ERA_MEDIEVAL"}, +{"Type": "CIVIC_AP_RENAISSANCE_21", "Cost": 440, "UITreeRow": -3, "EraType": "ERA_RENAISSANCE"}, +{"Type": "CIVIC_AP_RENAISSANCE_22", "Cost": 600, "UITreeRow": -1, "EraType": "ERA_RENAISSANCE"}, +{"Type": "CIVIC_AP_RENAISSANCE_23", "Cost": 600, "UITreeRow": 1, "EraType": "ERA_RENAISSANCE"}, +{"Type": "CIVIC_AP_RENAISSANCE_24", "Cost": 440, "UITreeRow": 3, "EraType": "ERA_RENAISSANCE"}, +{"Type": "CIVIC_AP_RENAISSANCE_25", "Cost": 720, "UITreeRow": -1, "EraType": "ERA_RENAISSANCE"}, +{"Type": "CIVIC_AP_RENAISSANCE_26", "Cost": 720, "UITreeRow": 1, "EraType": "ERA_RENAISSANCE"}, +{"Type": "CIVIC_AP_INDUSTRIAL_27", "Cost": 800, "UITreeRow": -3, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "CIVIC_AP_INDUSTRIAL_28", "Cost": 1010, "UITreeRow": -1, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "CIVIC_AP_INDUSTRIAL_29", "Cost": 1010, "UITreeRow": 0, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "CIVIC_AP_INDUSTRIAL_30", "Cost": 800, "UITreeRow": 2, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "CIVIC_AP_INDUSTRIAL_31", "Cost": 1050, "UITreeRow": -3, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "CIVIC_AP_INDUSTRIAL_32", "Cost": 1210, "UITreeRow": 2, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "CIVIC_AP_INDUSTRIAL_33", "Cost": 1210, "UITreeRow": -1, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "CIVIC_AP_MODERN_34", "Cost": 1540, "UITreeRow": -3, "EraType": "ERA_MODERN"}, +{"Type": "CIVIC_AP_MODERN_35", "Cost": 1580, "UITreeRow": -2, "EraType": "ERA_MODERN"}, +{"Type": "CIVIC_AP_MODERN_36", "Cost": 1715, "UITreeRow": -2, "EraType": "ERA_MODERN"}, +{"Type": "CIVIC_AP_MODERN_37", "Cost": 1540, "UITreeRow": -1, "EraType": "ERA_MODERN"}, +{"Type": "CIVIC_AP_MODERN_38", "Cost": 1540, "UITreeRow": 1, "EraType": "ERA_MODERN"}, +{"Type": "CIVIC_AP_MODERN_39", "Cost": 1640, "UITreeRow": -1, "EraType": "ERA_MODERN"}, +{"Type": "CIVIC_AP_MODERN_40", "Cost": 1640, "UITreeRow": 0, "EraType": "ERA_MODERN"}, +{"Type": "CIVIC_AP_MODERN_41", "Cost": 1640, "UITreeRow": 2, "EraType": "ERA_MODERN"}, +{"Type": "CIVIC_AP_MODERN_42", "Cost": 1640, "UITreeRow": 3, "EraType": "ERA_MODERN"}, +{"Type": "CIVIC_AP_ATOMIC_43", "Cost": 2185, "UITreeRow": -1, "EraType": "ERA_ATOMIC"}, +{"Type": "CIVIC_AP_ATOMIC_44", "Cost": 2185, "UITreeRow": 2, "EraType": "ERA_ATOMIC"}, +{"Type": "CIVIC_AP_ATOMIC_45", "Cost": 1955, "UITreeRow": -3, "EraType": "ERA_ATOMIC"}, +{"Type": "CIVIC_AP_ATOMIC_46", "Cost": 2415, "UITreeRow": -1, "EraType": "ERA_ATOMIC"}, +{"Type": "CIVIC_AP_ATOMIC_47", "Cost": 2415, "UITreeRow": 1, "EraType": "ERA_ATOMIC"}, +{"Type": "CIVIC_AP_INFORMATION_48", "Cost": 2880, "UITreeRow": 0, "EraType": "ERA_INFORMATION"}, +{"Type": "CIVIC_AP_INFORMATION_49", "Cost": 2880, "UITreeRow": 2, "EraType": "ERA_INFORMATION"}, +{"Type": "CIVIC_AP_FUTURE_50", "Cost": 3200, "UITreeRow": 3, "EraType": "ERA_FUTURE"}, +{"Type": "CIVIC_AP_INFORMATION_51", "Cost": 2880, "UITreeRow": -2, "EraType": "ERA_INFORMATION"}, +{"Type": "CIVIC_AP_INFORMATION_52", "Cost": 3000, "UITreeRow": 0, "EraType": "ERA_INFORMATION"}, +{"Type": "CIVIC_AP_INFORMATION_53", "Cost": 3000, "UITreeRow": 1, "EraType": "ERA_INFORMATION"}, +{"Type": "CIVIC_AP_INFORMATION_54", "Cost": 3000, "UITreeRow": 2, "EraType": "ERA_INFORMATION"}, +{"Type": "CIVIC_AP_INFORMATION_55", "Cost": 3100, "UITreeRow": -1, "EraType": "ERA_INFORMATION"}, +{"Type": "CIVIC_AP_FUTURE_56", "Cost": 3200, "UITreeRow": -2, "EraType": "ERA_FUTURE"}, +{"Type": "CIVIC_AP_FUTURE_57", "Cost": 3200, "UITreeRow": -1, "EraType": "ERA_FUTURE"}, +{"Type": "CIVIC_AP_FUTURE_58", "Cost": 3200, "UITreeRow": 0, "EraType": "ERA_FUTURE"}, +{"Type": "CIVIC_AP_FUTURE_59", "Cost": 3200, "UITreeRow": 1, "EraType": "ERA_FUTURE"}, +{"Type": "CIVIC_AP_FUTURE_60", "Cost": 3200, "UITreeRow": 2, "EraType": "ERA_FUTURE"} +] \ No newline at end of file diff --git a/worlds/civ_6/data/new_tech.json b/worlds/civ_6/data/new_tech.json new file mode 100644 index 000000000000..e5b3c9d83601 --- /dev/null +++ b/worlds/civ_6/data/new_tech.json @@ -0,0 +1,79 @@ +[ +{"Type": "TECH_AP_ANCIENT_00", "Cost": 25, "UITreeRow": 0, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_ANCIENT_01", "Cost": 25, "UITreeRow": 1, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_ANCIENT_02", "Cost": 25, "UITreeRow": 3, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_ANCIENT_03", "Cost": 50, "UITreeRow": -3, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_ANCIENT_04", "Cost": 50, "UITreeRow": -2, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_ANCIENT_05", "Cost": 50, "UITreeRow": -1, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_ANCIENT_06", "Cost": 50, "UITreeRow": 1, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_ANCIENT_07", "Cost": 50, "UITreeRow": 0, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_ANCIENT_08", "Cost": 80, "UITreeRow": 2, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_ANCIENT_09", "Cost": 80, "UITreeRow": 3, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_ANCIENT_10", "Cost": 80, "UITreeRow": 4, "EraType": "ERA_ANCIENT"}, +{"Type": "TECH_AP_CLASSICAL_11", "Cost": 120, "UITreeRow": -2, "EraType": "ERA_CLASSICAL"}, +{"Type": "TECH_AP_CLASSICAL_12", "Cost": 120, "UITreeRow": 0, "EraType": "ERA_CLASSICAL"}, +{"Type": "TECH_AP_CLASSICAL_13", "Cost": 120, "UITreeRow": 1, "EraType": "ERA_CLASSICAL"}, +{"Type": "TECH_AP_CLASSICAL_14", "Cost": 120, "UITreeRow": 3, "EraType": "ERA_CLASSICAL"}, +{"Type": "TECH_AP_CLASSICAL_15", "Cost": 200, "UITreeRow": -3, "EraType": "ERA_CLASSICAL"}, +{"Type": "TECH_AP_CLASSICAL_16", "Cost": 200, "UITreeRow": -1, "EraType": "ERA_CLASSICAL"}, +{"Type": "TECH_AP_CLASSICAL_17", "Cost": 200, "UITreeRow": 2, "EraType": "ERA_CLASSICAL"}, +{"Type": "TECH_AP_CLASSICAL_18", "Cost": 200, "UITreeRow": 4, "EraType": "ERA_CLASSICAL"}, +{"Type": "TECH_AP_MEDIEVAL_19", "Cost": 300, "UITreeRow": -2, "EraType": "ERA_MEDIEVAL"}, +{"Type": "TECH_AP_MEDIEVAL_20", "Cost": 300, "UITreeRow": 0, "EraType": "ERA_MEDIEVAL"}, +{"Type": "TECH_AP_MEDIEVAL_21", "Cost": 300, "UITreeRow": 4, "EraType": "ERA_MEDIEVAL"}, +{"Type": "TECH_AP_MEDIEVAL_22", "Cost": 390, "UITreeRow": -1, "EraType": "ERA_MEDIEVAL"}, +{"Type": "TECH_AP_MEDIEVAL_23", "Cost": 390, "UITreeRow": 1, "EraType": "ERA_MEDIEVAL"}, +{"Type": "TECH_AP_MEDIEVAL_24", "Cost": 390, "UITreeRow": 2, "EraType": "ERA_MEDIEVAL"}, +{"Type": "TECH_AP_MEDIEVAL_25", "Cost": 390, "UITreeRow": 3, "EraType": "ERA_MEDIEVAL"}, +{"Type": "TECH_AP_RENAISSANCE_26", "Cost": 600, "UITreeRow": -3, "EraType": "ERA_RENAISSANCE"}, +{"Type": "TECH_AP_RENAISSANCE_27", "Cost": 600, "UITreeRow": -2, "EraType": "ERA_RENAISSANCE"}, +{"Type": "TECH_AP_RENAISSANCE_28", "Cost": 600, "UITreeRow": 0, "EraType": "ERA_RENAISSANCE"}, +{"Type": "TECH_AP_RENAISSANCE_29", "Cost": 600, "UITreeRow": 1, "EraType": "ERA_RENAISSANCE"}, +{"Type": "TECH_AP_RENAISSANCE_30", "Cost": 600, "UITreeRow": 4, "EraType": "ERA_RENAISSANCE"}, +{"Type": "TECH_AP_RENAISSANCE_31", "Cost": 730, "UITreeRow": -3, "EraType": "ERA_RENAISSANCE"}, +{"Type": "TECH_AP_RENAISSANCE_32", "Cost": 730, "UITreeRow": -1, "EraType": "ERA_RENAISSANCE"}, +{"Type": "TECH_AP_RENAISSANCE_33", "Cost": 730, "UITreeRow": 1, "EraType": "ERA_RENAISSANCE"}, +{"Type": "TECH_AP_RENAISSANCE_34", "Cost": 730, "UITreeRow": 3, "EraType": "ERA_RENAISSANCE"}, +{"Type": "TECH_AP_INDUSTRIAL_35", "Cost": 930, "UITreeRow": -2, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "TECH_AP_INDUSTRIAL_36", "Cost": 930, "UITreeRow": -1, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "TECH_AP_INDUSTRIAL_37", "Cost": 930, "UITreeRow": 1, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "TECH_AP_INDUSTRIAL_38", "Cost": 930, "UITreeRow": 3, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "TECH_AP_INDUSTRIAL_39", "Cost": 1070, "UITreeRow": -3, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "TECH_AP_INDUSTRIAL_40", "Cost": 1070, "UITreeRow": -1, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "TECH_AP_INDUSTRIAL_41", "Cost": 1070, "UITreeRow": 0, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "TECH_AP_INDUSTRIAL_42", "Cost": 1070, "UITreeRow": 2, "EraType": "ERA_INDUSTRIAL"}, +{"Type": "TECH_AP_MODERN_43", "Cost": 1250, "UITreeRow": -2, "EraType": "ERA_MODERN"}, +{"Type": "TECH_AP_MODERN_44", "Cost": 1250, "UITreeRow": 0, "EraType": "ERA_MODERN"}, +{"Type": "TECH_AP_MODERN_45", "Cost": 1250, "UITreeRow": 1, "EraType": "ERA_MODERN"}, +{"Type": "TECH_AP_MODERN_46", "Cost": 1370, "UITreeRow": -3, "EraType": "ERA_MODERN"}, +{"Type": "TECH_AP_MODERN_47", "Cost": 1370, "UITreeRow": -2, "EraType": "ERA_MODERN"}, +{"Type": "TECH_AP_MODERN_48", "Cost": 1370, "UITreeRow": -1, "EraType": "ERA_MODERN"}, +{"Type": "TECH_AP_MODERN_49", "Cost": 1370, "UITreeRow": 2, "EraType": "ERA_MODERN"}, +{"Type": "TECH_AP_ATOMIC_50", "Cost": 1480, "UITreeRow": -2, "EraType": "ERA_ATOMIC"}, +{"Type": "TECH_AP_ATOMIC_51", "Cost": 1480, "UITreeRow": -1, "EraType": "ERA_ATOMIC"}, +{"Type": "TECH_AP_ATOMIC_52", "Cost": 1480, "UITreeRow": 0, "EraType": "ERA_ATOMIC"}, +{"Type": "TECH_AP_ATOMIC_53", "Cost": 1480, "UITreeRow": 1, "EraType": "ERA_ATOMIC"}, +{"Type": "TECH_AP_ATOMIC_54", "Cost": 1480, "UITreeRow": 2, "EraType": "ERA_ATOMIC"}, +{"Type": "TECH_AP_ATOMIC_55", "Cost": 1660, "UITreeRow": -3, "EraType": "ERA_ATOMIC"}, +{"Type": "TECH_AP_ATOMIC_56", "Cost": 1660, "UITreeRow": 1, "EraType": "ERA_ATOMIC"}, +{"Type": "TECH_AP_ATOMIC_57", "Cost": 1660, "UITreeRow": 2, "EraType": "ERA_ATOMIC"}, +{"Type": "TECH_AP_INFORMATION_58", "Cost": 1850, "UITreeRow": -3, "EraType": "ERA_INFORMATION"}, +{"Type": "TECH_AP_INFORMATION_59", "Cost": 1850, "UITreeRow": -1, "EraType": "ERA_INFORMATION"}, +{"Type": "TECH_AP_INFORMATION_60", "Cost": 1850, "UITreeRow": 0, "EraType": "ERA_INFORMATION"}, +{"Type": "TECH_AP_INFORMATION_61", "Cost": 1850, "UITreeRow": 1, "EraType": "ERA_INFORMATION"}, +{"Type": "TECH_AP_INFORMATION_62", "Cost": 1850, "UITreeRow": 2, "EraType": "ERA_INFORMATION"}, +{"Type": "TECH_AP_INFORMATION_63", "Cost": 1850, "UITreeRow": 3, "EraType": "ERA_INFORMATION"}, +{"Type": "TECH_AP_INFORMATION_64", "Cost": 2155, "UITreeRow": -2, "EraType": "ERA_INFORMATION"}, +{"Type": "TECH_AP_INFORMATION_65", "Cost": 2155, "UITreeRow": 2, "EraType": "ERA_INFORMATION"}, +{"Type": "TECH_AP_INFORMATION_66", "Cost": 2155, "UITreeRow": 1, "EraType": "ERA_INFORMATION"}, +{"Type": "TECH_AP_MEDIEVAL_67", "Cost": 300, "UITreeRow": -3, "EraType": "ERA_MEDIEVAL"}, +{"Type": "TECH_AP_MODERN_68", "Cost": 1250, "UITreeRow": 3, "EraType": "ERA_MODERN"}, +{"Type": "TECH_AP_FUTURE_69", "Cost": 2200, "UITreeRow": -3, "EraType": "ERA_FUTURE"}, +{"Type": "TECH_AP_FUTURE_70", "Cost": 2200, "UITreeRow": -2, "EraType": "ERA_FUTURE"}, +{"Type": "TECH_AP_FUTURE_71", "Cost": 2200, "UITreeRow": -1, "EraType": "ERA_FUTURE"}, +{"Type": "TECH_AP_FUTURE_72", "Cost": 2200, "UITreeRow": 0, "EraType": "ERA_FUTURE"}, +{"Type": "TECH_AP_FUTURE_73", "Cost": 2200, "UITreeRow": 1, "EraType": "ERA_FUTURE"}, +{"Type": "TECH_AP_FUTURE_74", "Cost": 2200, "UITreeRow": 2, "EraType": "ERA_FUTURE"}, +{"Type": "TECH_AP_FUTURE_75", "Cost": 2500, "UITreeRow": 0, "EraType": "ERA_FUTURE"}, +{"Type": "TECH_AP_FUTURE_76", "Cost": 2600, "UITreeRow": 0, "EraType": "ERA_FUTURE"} +] \ No newline at end of file diff --git a/worlds/civ_6/data/new_tech_prereqs.json b/worlds/civ_6/data/new_tech_prereqs.json new file mode 100644 index 000000000000..2674292bc11e --- /dev/null +++ b/worlds/civ_6/data/new_tech_prereqs.json @@ -0,0 +1,104 @@ +[ +{"Technology": "TECH_AP_ANCIENT_06", "PrereqTech": "TECH_AP_ANCIENT_01"}, +{"Technology": "TECH_AP_ANCIENT_07", "PrereqTech": "TECH_AP_ANCIENT_00"}, +{"Technology": "TECH_AP_ANCIENT_05", "PrereqTech": "TECH_AP_ANCIENT_00"}, +{"Technology": "TECH_AP_ANCIENT_08", "PrereqTech": "TECH_AP_ANCIENT_02"}, +{"Technology": "TECH_AP_ANCIENT_09", "PrereqTech": "TECH_AP_ANCIENT_02"}, +{"Technology": "TECH_AP_ANCIENT_10", "PrereqTech": "TECH_AP_ANCIENT_02"}, +{"Technology": "TECH_AP_CLASSICAL_15", "PrereqTech": "TECH_AP_ANCIENT_03"}, +{"Technology": "TECH_AP_CLASSICAL_11", "PrereqTech": "TECH_AP_ANCIENT_03"}, +{"Technology": "TECH_AP_CLASSICAL_11", "PrereqTech": "TECH_AP_ANCIENT_04"}, +{"Technology": "TECH_AP_CLASSICAL_12", "PrereqTech": "TECH_AP_ANCIENT_07"}, +{"Technology": "TECH_AP_CLASSICAL_13", "PrereqTech": "TECH_AP_ANCIENT_06"}, +{"Technology": "TECH_AP_CLASSICAL_14", "PrereqTech": "TECH_AP_ANCIENT_09"}, +{"Technology": "TECH_AP_CLASSICAL_16", "PrereqTech": "TECH_AP_CLASSICAL_12"}, +{"Technology": "TECH_AP_CLASSICAL_17", "PrereqTech": "TECH_AP_ANCIENT_08"}, +{"Technology": "TECH_AP_CLASSICAL_17", "PrereqTech": "TECH_AP_CLASSICAL_13"}, +{"Technology": "TECH_AP_CLASSICAL_18", "PrereqTech": "TECH_AP_ANCIENT_10"}, +{"Technology": "TECH_AP_MEDIEVAL_19", "PrereqTech": "TECH_AP_CLASSICAL_16"}, +{"Technology": "TECH_AP_MEDIEVAL_20", "PrereqTech": "TECH_AP_CLASSICAL_12"}, +{"Technology": "TECH_AP_MEDIEVAL_20", "PrereqTech": "TECH_AP_CLASSICAL_13"}, +{"Technology": "TECH_AP_MEDIEVAL_23", "PrereqTech": "TECH_AP_CLASSICAL_13"}, +{"Technology": "TECH_AP_MEDIEVAL_21", "PrereqTech": "TECH_AP_CLASSICAL_14"}, +{"Technology": "TECH_AP_MEDIEVAL_21", "PrereqTech": "TECH_AP_CLASSICAL_18"}, +{"Technology": "TECH_AP_MEDIEVAL_22", "PrereqTech": "TECH_AP_CLASSICAL_16"}, +{"Technology": "TECH_AP_MEDIEVAL_22", "PrereqTech": "TECH_AP_MEDIEVAL_20"}, +{"Technology": "TECH_AP_MEDIEVAL_25", "PrereqTech": "TECH_AP_CLASSICAL_17"}, +{"Technology": "TECH_AP_MEDIEVAL_24", "PrereqTech": "TECH_AP_CLASSICAL_17"}, +{"Technology": "TECH_AP_RENAISSANCE_27", "PrereqTech": "TECH_AP_MEDIEVAL_22"}, +{"Technology": "TECH_AP_RENAISSANCE_28", "PrereqTech": "TECH_AP_MEDIEVAL_22"}, +{"Technology": "TECH_AP_RENAISSANCE_28", "PrereqTech": "TECH_AP_MEDIEVAL_23"}, +{"Technology": "TECH_AP_RENAISSANCE_29", "PrereqTech": "TECH_AP_MEDIEVAL_20"}, +{"Technology": "TECH_AP_RENAISSANCE_29", "PrereqTech": "TECH_AP_MEDIEVAL_23"}, +{"Technology": "TECH_AP_RENAISSANCE_29", "PrereqTech": "TECH_AP_MEDIEVAL_24"}, +{"Technology": "TECH_AP_RENAISSANCE_30", "PrereqTech": "TECH_AP_MEDIEVAL_21"}, +{"Technology": "TECH_AP_RENAISSANCE_31", "PrereqTech": "TECH_AP_RENAISSANCE_26"}, +{"Technology": "TECH_AP_RENAISSANCE_32", "PrereqTech": "TECH_AP_MEDIEVAL_22"}, +{"Technology": "TECH_AP_RENAISSANCE_33", "PrereqTech": "TECH_AP_RENAISSANCE_29"}, +{"Technology": "TECH_AP_RENAISSANCE_34", "PrereqTech": "TECH_AP_MEDIEVAL_25"}, +{"Technology": "TECH_AP_INDUSTRIAL_35", "PrereqTech": "TECH_AP_RENAISSANCE_31"}, +{"Technology": "TECH_AP_INDUSTRIAL_35", "PrereqTech": "TECH_AP_RENAISSANCE_27"}, +{"Technology": "TECH_AP_INDUSTRIAL_36", "PrereqTech": "TECH_AP_RENAISSANCE_32"}, +{"Technology": "TECH_AP_INDUSTRIAL_36", "PrereqTech": "TECH_AP_RENAISSANCE_28"}, +{"Technology": "TECH_AP_INDUSTRIAL_41", "PrereqTech": "TECH_AP_INDUSTRIAL_36"}, +{"Technology": "TECH_AP_INDUSTRIAL_41", "PrereqTech": "TECH_AP_RENAISSANCE_33"}, +{"Technology": "TECH_AP_INDUSTRIAL_38", "PrereqTech": "TECH_AP_RENAISSANCE_34"}, +{"Technology": "TECH_AP_INDUSTRIAL_38", "PrereqTech": "TECH_AP_RENAISSANCE_30"}, +{"Technology": "TECH_AP_INDUSTRIAL_39", "PrereqTech": "TECH_AP_INDUSTRIAL_35"}, +{"Technology": "TECH_AP_INDUSTRIAL_40", "PrereqTech": "TECH_AP_INDUSTRIAL_36"}, +{"Technology": "TECH_AP_INDUSTRIAL_37", "PrereqTech": "TECH_AP_RENAISSANCE_33"}, +{"Technology": "TECH_AP_INDUSTRIAL_42", "PrereqTech": "TECH_AP_INDUSTRIAL_37"}, +{"Technology": "TECH_AP_INDUSTRIAL_42", "PrereqTech": "TECH_AP_INDUSTRIAL_38"}, +{"Technology": "TECH_AP_MODERN_43", "PrereqTech": "TECH_AP_INDUSTRIAL_35"}, +{"Technology": "TECH_AP_MODERN_43", "PrereqTech": "TECH_AP_INDUSTRIAL_36"}, +{"Technology": "TECH_AP_MODERN_44", "PrereqTech": "TECH_AP_INDUSTRIAL_41"}, +{"Technology": "TECH_AP_MODERN_45", "PrereqTech": "TECH_AP_INDUSTRIAL_42"}, +{"Technology": "TECH_AP_MODERN_46", "PrereqTech": "TECH_AP_INDUSTRIAL_39"}, +{"Technology": "TECH_AP_MODERN_47", "PrereqTech": "TECH_AP_INDUSTRIAL_39"}, +{"Technology": "TECH_AP_MODERN_47", "PrereqTech": "TECH_AP_MODERN_43"}, +{"Technology": "TECH_AP_MODERN_48", "PrereqTech": "TECH_AP_INDUSTRIAL_40"}, +{"Technology": "TECH_AP_MODERN_49", "PrereqTech": "TECH_AP_MODERN_45"}, +{"Technology": "TECH_AP_ATOMIC_55", "PrereqTech": "TECH_AP_MODERN_46"}, +{"Technology": "TECH_AP_ATOMIC_55", "PrereqTech": "TECH_AP_MODERN_47"}, +{"Technology": "TECH_AP_ATOMIC_50", "PrereqTech": "TECH_AP_MODERN_47"}, +{"Technology": "TECH_AP_ATOMIC_51", "PrereqTech": "TECH_AP_MODERN_47"}, +{"Technology": "TECH_AP_ATOMIC_51", "PrereqTech": "TECH_AP_MODERN_48"}, +{"Technology": "TECH_AP_ATOMIC_52", "PrereqTech": "TECH_AP_MODERN_44"}, +{"Technology": "TECH_AP_ATOMIC_52", "PrereqTech": "TECH_AP_MODERN_45"}, +{"Technology": "TECH_AP_ATOMIC_53", "PrereqTech": "TECH_AP_MODERN_45"}, +{"Technology": "TECH_AP_ATOMIC_53", "PrereqTech": "TECH_AP_MODERN_49"}, +{"Technology": "TECH_AP_ATOMIC_56", "PrereqTech": "TECH_AP_ATOMIC_52"}, +{"Technology": "TECH_AP_ATOMIC_56", "PrereqTech": "TECH_AP_ATOMIC_53"}, +{"Technology": "TECH_AP_ATOMIC_54", "PrereqTech": "TECH_AP_MODERN_49"}, +{"Technology": "TECH_AP_ATOMIC_57", "PrereqTech": "TECH_AP_ATOMIC_54"}, +{"Technology": "TECH_AP_INFORMATION_58", "PrereqTech": "TECH_AP_ATOMIC_55"}, +{"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_ATOMIC_55"}, +{"Technology": "TECH_AP_INFORMATION_59", "PrereqTech": "TECH_AP_ATOMIC_50"}, +{"Technology": "TECH_AP_INFORMATION_59", "PrereqTech": "TECH_AP_ATOMIC_51"}, +{"Technology": "TECH_AP_INFORMATION_60", "PrereqTech": "TECH_AP_ATOMIC_51"}, +{"Technology": "TECH_AP_INFORMATION_60", "PrereqTech": "TECH_AP_ATOMIC_52"}, +{"Technology": "TECH_AP_INFORMATION_61", "PrereqTech": "TECH_AP_ATOMIC_56"}, +{"Technology": "TECH_AP_INFORMATION_62", "PrereqTech": "TECH_AP_ATOMIC_57"}, +{"Technology": "TECH_AP_INFORMATION_63", "PrereqTech": "TECH_AP_ATOMIC_57"}, +{"Technology": "TECH_AP_INFORMATION_65", "PrereqTech": "TECH_AP_INFORMATION_62"}, +{"Technology": "TECH_AP_INFORMATION_66", "PrereqTech": "TECH_AP_INFORMATION_61"}, +{"Technology": "TECH_AP_MEDIEVAL_67", "PrereqTech": "TECH_AP_CLASSICAL_15"}, +{"Technology": "TECH_AP_MEDIEVAL_67", "PrereqTech": "TECH_AP_CLASSICAL_16"}, +{"Technology": "TECH_AP_MEDIEVAL_23", "PrereqTech": "TECH_AP_MEDIEVAL_20"}, +{"Technology": "TECH_AP_MODERN_68", "PrereqTech": "TECH_AP_INDUSTRIAL_42"}, +{"Technology": "TECH_AP_MODERN_49", "PrereqTech": "TECH_AP_MODERN_68"}, +{"Technology": "TECH_AP_RENAISSANCE_26", "PrereqTech": "TECH_AP_MEDIEVAL_67"}, +{"Technology": "TECH_AP_RENAISSANCE_27", "PrereqTech": "TECH_AP_MEDIEVAL_67"}, +{"Technology": "TECH_AP_RENAISSANCE_27", "PrereqTech": "TECH_AP_MEDIEVAL_19"}, +{"Technology": "TECH_AP_MODERN_48", "PrereqTech": "TECH_AP_MODERN_44"}, +{"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_INFORMATION_59"}, +{"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_INFORMATION_60"}, +{"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_INFORMATION_61"}, +{"Technology": "TECH_AP_FUTURE_69", "PrereqTech": "TECH_AP_AP60"}, +{"Technology": "TECH_AP_FUTURE_70", "PrereqTech": "TECH_AP_AP60"}, +{"Technology": "TECH_AP_FUTURE_71", "PrereqTech": "TECH_AP_AP60"}, +{"Technology": "TECH_AP_FUTURE_72", "PrereqTech": "TECH_AP_AP60"}, +{"Technology": "TECH_AP_FUTURE_73", "PrereqTech": "TECH_AP_AP60"}, +{"Technology": "TECH_AP_FUTURE_74", "PrereqTech": "TECH_AP_AP60"}, +{"Technology": "TECH_AP_FUTURE_75", "PrereqTech": "TECH_AP_AP60"}, +{"Technology": "TECH_AP_FUTURE_76", "PrereqTech": "TECH_AP_AP60"}] \ No newline at end of file diff --git a/worlds/civ_6/data/progressive_districts.json b/worlds/civ_6/data/progressive_districts.json new file mode 100644 index 000000000000..1de4a185b4d6 --- /dev/null +++ b/worlds/civ_6/data/progressive_districts.json @@ -0,0 +1,64 @@ +{ + "PROGRESSIVE_CAMPUS": ["TECH_WRITING", "TECH_EDUCATION", "TECH_CHEMISTRY"], + + "PROGRESSIVE_THEATER": [ + "CIVIC_DRAMA_POETRY", + "CIVIC_HUMANISM", + "TECH_RADIO" + ], + + "PROGRESSIVE_HOLY_SITE": ["TECH_ASTROLOGY", "CIVIC_THEOLOGY"], + + "PROGRESSIVE_ENCAMPMENT": [ + "TECH_BRONZE_WORKING", + "TECH_MILITARY_ENGINEERING", + "TECH_MILITARY_SCIENCE" + ], + + "PROGRESSIVE_COMMERCIAL_HUB": [ + "TECH_CURRENCY", + "TECH_BANKING", + "TECH_ECONOMICS" + ], + + "PROGRESSIVE_HARBOR": [ + "TECH_CELESTIAL_NAVIGATION", + "TECH_MASS_PRODUCTION" + ], + + "PROGRESSIVE_INDUSTRIAL_ZONE": [ + "TECH_APPRENTICESHIP", + "TECH_INDUSTRIALIZATION", + "TECH_ELECTRICITY", + "TECH_NUCLEAR_FISSION" + ], + + "PROGRESSIVE_PRESERVE": ["CIVIC_MYSTICISM", "CIVIC_CONSERVATION"], + + "PROGRESSIVE_ENTERTAINMENT_COMPLEX": [ + "CIVIC_GAMES_RECREATION", + "CIVIC_NATURAL_HISTORY", + "CIVIC_PROFESSIONAL_SPORTS" + ], + + "PROGRESSIVE_NEIGHBORHOOD": [ + "CIVIC_URBANIZATION", + "TECH_REPLACEABLE_PARTS", + "CIVIC_CAPITALISM" + ], + + "PROGRESSIVE_AERODROME": ["TECH_FLIGHT", "TECH_ADVANCED_FLIGHT"], + + "PROGRESSIVE_DIPLOMATIC_QUARTER": [ + "TECH_MATHEMATICS", + "CIVIC_DIPLOMATIC_SERVICE" + ], + + "PROGRESSIVE_SPACE_PORT": [ + "TECH_ROCKETRY", + "TECH_SATELLITES", + "TECH_NANOTECHNOLOGY", + "TECH_SMART_MATERIALS", + "TECH_OFFWORLD_MISSION" + ] +} diff --git a/worlds/civ_6/docs/boostsanity.md b/worlds/civ_6/docs/boostsanity.md new file mode 100644 index 000000000000..666b8ef49149 --- /dev/null +++ b/worlds/civ_6/docs/boostsanity.md @@ -0,0 +1,15 @@ +## Boostsanity +Boostsanity takes all of the Eureka & Inspiration events and makes them location checks. This feature is the one to changeup the way Civilization is played in an AP multiworld/randomizer to date. What normally are mundane tasks that are passively collected now become a novel and interesting bucket list that you need to pay attention to in order to unlock items for yourself and others! +Boosts have logic associated with them in order to verify you can always reach the ones you need to, when you need to. One side effect of this is that when boostsanity is enabled, previously some "Useful" items are now flagged as "Progression" (Urbanization, Pottery, The Wheel, to name a few). + +### FAQs +- Someone sent me a tech/civic and I'm worried I won't be able to boost it anymore! + - Fear not! The mod has been updated, and through a lot of wizardry 🧙‍♂️ you will be able to boost civics/techs that have already been received. Additionally the UI has been updated to show you whether they have been boosted or not after receiving them still. +- I need to kill a unit with a slinger/archer/musketman or some other obsolete unit I can't build anymore, how can I do this?? + - Don't forget you can go into the Tech Tree and click on a Vanilla tech you've received in order to toggle it on/off. This is necessary in order to pursue some of the boosts if you receive techs in certain orders. +- Something happened and I'm not able to unlock the boost due to game rules! + - A few scenarios you may worry about: "Found a religion", "Make an alliance with another player", "Develop an alliance to level 2", "Build a wonder from X Era", to name a few. Any boost that is "miss-able" has been flagged as an "Excluded" location and will not ever receive a progression item. For a list of how each boost is flagged, take a look [here](../data/boosts.json). +- I'm worried that my `PROGRESSIVE_ERA` item is going to be stuck in a boost I won't have time to complete before my maximum unlocked era ends! + - I, too, have shared this fear. Due to the unpredictable timing of boosts and unlocking them, this could lead to a hard lock in certain scenarios. As a result, `PROGRESSIVE_ERA` items will never be located at a boost check. +- There's too many boosts, how will I know which one's I should focus on?! + - In order to give a little more focus to all the boosts rather than just arbitrarily picking them at random, items in both of the vanilla trees will now have an advisor icon on them if it's associated boost contains a progression item. \ No newline at end of file diff --git a/worlds/civ_6/docs/en_Civilization VI.md b/worlds/civ_6/docs/en_Civilization VI.md new file mode 100644 index 000000000000..f6392ec0c313 --- /dev/null +++ b/worlds/civ_6/docs/en_Civilization VI.md @@ -0,0 +1,53 @@ +# Civlization 6 Archipelago + +## Setup Guide +For setup instructions go [here](./docs/setup_en.md). + +## What does randomization do to this game? + +In Civilization VI, the tech and civic trees are both shuffled. This presents some interesting ways to play the game in a non-standard way. If you are feeling adventurous, you can enable the `boostsanity` option in order to really change up the way you normally would play a Civ game. Details on the option can be found [here](./docs/boostsanity.md) + +There are a few changes that the Archipelago mod introduces in order to make this playable/fun. These are detailed in the __FAQ__ section below. + +## What is the goal of Civilization VI when randomized? +The goal of randomized Civlization VI remains the same. Pursue any victory type you have enabled in your game settings, the one you normally go for may or may not be feasible based on how things have been changed up! + +## Which items can be in another player's world? +All technologies and civics can be found in another player's world. + +## What does another world's item look like in Civilization VI? +Each item from another world is represented as a researchable tech/civic in your normal tech/civic trees. + +## When the player receives an item, what happens? +A short period after receiving an item, you will get a notification indicating you have discovered the relevant tech/civic. You will also get the regular popup that details what the given item has unlocked for you. + +## FAQs +- Do I need the DLC to play this? + - Yes, you need both Rise & Fall and Gathering Storm. If there is enough interest then I can eventually add support for Archipellago runs that don't require both expansions. + +- Does this work with Multiplayer? + - It does not and, despite my best efforts, probably won't until there's a new way for external programs to be able to interact with the game. + +- Does my mod that reskins Barbarians as various Pro Wrestlers work with this?? + - Only one way to find out! Any mods that modify techs/civics will most likely cause issues, though. + +- "Help! I can't see any of the items that have been sent to me!" + - Both trees by default will show you the researchable Archipelago locations. To view the normal tree, you can click "Toggle Archipelago Tree" on the top left corner of the tree view. + +- "Oh no! I received the Machinery tech and now instead of getting an Archer next turn, I have to wait an additional 10 turns to get a Crossbowman!" + - Vanilla prevents you from building units of the same class from an earlier tech level after you have researched a later variant. For example, this could be problematic if someone unlocks Crossbowmen for you right out the gate since you won't be able to make Archers (which have a much lower production cost). + + - Solution: You can now go in to the tech tree, click "Toggle Archipelago Tree" to view your unlocked techs, and then can click any tech you have unlocked to toggle whether it is currently active or not. __NOTE__: This is an experimental feature and may yield some unexpected behaviors. Please DM `@Hesto2` on Discord if you run into any issues. + +- I enabled `progressive districts` but I have no idea techs/civics what items are locked behind progression now! + - Any technology or civic that grants you a new building in a district (or grants you the district itself) is now locked behind a progressive item. For example, `PROGRESSIVE_CAMPUS` would give you these items in the following order: + 1. `TECH_WRITING` + 2. `TECH_EDUCATION` + 3. `TECH_CHEMISTRY` + - If you want to see the details around each item, you can review [this file](./data/progressive_districts.json) + +- "How does DeathLink work? Am I going to have to start a new game every time one of my friends dies??" + - Heavens no, my fellow Archipelago appreciator. When configuring your Archipelago options for Civilization on the options page, there are several choices available for you to fine tune the way you'd like to be punished for the follies of your friends. These include: Having a random unit destroyed, losing a percentage of gold or faith, or even losing a point on your era score. If you can't make up your mind, you can elect to have any of them be selected every time a death link is sent your way. + + - In the event you lose one of your units in combat (this means captured units don't count), then you will send a death link event to the rest of your friends. + diff --git a/worlds/civ_6/docs/setup_en.md b/worlds/civ_6/docs/setup_en.md new file mode 100644 index 000000000000..a7c177605d94 --- /dev/null +++ b/worlds/civ_6/docs/setup_en.md @@ -0,0 +1,111 @@ +# Setup Guide for Civilization VI Archipelago + +This guide is meant to help you get up and running with Civlization VI in your Archipelago run. Note that this requires you to have both Rise & Fall as well as Gathering Storm installed. This will not work unless both of those DLCs are enabled. + +## Requirements + +The following are required in order to play Civ VI in Archipelago + +- Windows OS (Firaxis does not support the necessary tooling for Mac, Linux is yet to bet verified) + +- Installed [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) v0.4.5 or higher.\ + **Make sure to install the Generator if you intend to generate multiworlds.** +- The latest version of the [Civ VI apworld](https://github.com/hesto2/civilization_vi_apworld/releases/latest). + +- The latest version of the [Civ VI AP Mod](https://github.com/hesto2/civilization_archipelago_mod). + +- Tuner setting enabled so the archipelago client can communicate with the game + +## Enabling the tuner +Depending on how you installed Civ 6 you will have to navigate to one of the following: +- `YOUR_USER/Documents/My Games/Sid Meier's Civilization VI/AppOptions.txt` +- `YOUR_USER/AppData/Local/Firaxis Games/Sid Meier's Civilization VI/AppOptions.txt` + +Once you have located your `AppOptions.txt`, do a search for `Enable FireTuner`. Set `EnableTuner` to `1` instead of `0`. __NOTE__: While this is active, achievments will be disabled. + +## Mod Installation + +1. Download and unzip the latest release of the mod from [github](https://github.com/hesto2/civilization_archipelago_mod/releases). + +2. Copy the folder containing the mod files to your Civ VI mods folder. On Windows, this is usually located at `C:\Users\YOUR_USER\Documents\My Games\Sid Meier's Civilization VI\Mods` + +3. After the Archipelago host generates a game, you should be given another zip file titled `AP-{playername}....zip`. Unzip this and copy all of its contents into your mod folder. + +4. Your finished mod folder should look something like this: +- Civ VI Mods Directory + - civilization_archipelago_mod + - NewItems.xml + - InitOptions.lua + - Archipelago.modinfo + - All the other mod files, etc. + +## AP World Installation + +1. Unzip the downloaded Civ VI apworld zip file +2. Place the `civ6.apworld` file in your Archipelago installation's `lib/worlds` folder (Windows default to: + `%programdata%/Archipelago`). + +- If you have a `civ6.apworld` file from a previous version of the apworld, you **must** delete it, as it is no longer + supported. Additionally, if there is a `civ6` folder in that folder, you **must** also delete it. Keeping + these around will cause issues, even if multiworlds are successfully generated. + +## Setting Up a YAML + +All players playing Civ VI must provide the room host with a YAML file containing the settings for their world. +A sample YAML file for Civ VI is supplied in the Civ VI apworld download. Refer to the comments in that file for +details about what each setting does. + +Once complete, provide the room host with your YAML file. + +## Generating a Multiworld + +If you're generating a multiworld game that includes Civ VI, you'll need to run it locally since the online +generator does not yet support it. Follow these steps to generate a multiworld: + +1. Gather all player's YAMLs. Place these YAMLs into the `Players` folder of your Archipelago installation. If the + folder does not exist, then it must be created manually. The files here should not be compressed. +2. Modify any local host settings for generation, as desired. +3. Run `ArchipelagoGenerate.exe` (without `.exe` on Linux) or click `Generate` in the launcher. The generation output + is placed in the `output` folder (usually named something like `AP_XXXXX.zip`). \* Please note that if any player in the game you want to generate plays a game that needs a ROM file to generate, + you will need the corresponding ROM files. +4. Unzip the `AP_XXXXX.zip` file. It should include a zip file for each player in the room playing Civ VI. Distribute each file to the appropriate player. +5. **Delete the distributed zip files and re-zip the remaining files**. In the next section, use this archive file to + host a room or provide it to the room host. \* If you plan to host the room on a local machine, skip this step and use the original zip file (`AP_XXXX.zip`) instead. + +## Hosting a Room + +If you're generating the multiworld, follow the instructions in the previous section. Once you have the zip file +corresponding to your multiworld, follow +[these steps](https://archipelago.gg/tutorial/Archipelago/setup/en#hosting-an-archipelago-server) to host a room. Follow +the instructions for hosting on the website from a locally generated game or on a local machine. + +## Connecting to a Room + +You should have the zip file provided to you by the multiworld generator. You should also have the room's server +name and port number from the room's host. + +Once you do, follow these steps to connect to the room: + +1. Unzip the folder given to you and copy its contents (should include `NewItems.xml` an `archipelago.json`, and more) into the installed mod folder. +2. Start `ArchipelagoLauncher.exe` and choose `Civ6 Client`, which will open the text client. +3. Connect to the room by entering the server name and port number at the top and pressing `Connect`. For rooms hosted + on the website, this will be `archipelago.gg:`, where `` is the port number. If a game is hosted from the + `ArchipelagoServer.exe` (without `.exe` on Linux), this will default to `38281` but may be changed in the `host.yaml`. +4. Once you successfully configure and launch a game, the client should let you know it is connected and you will be ready to play! + +## Configuring your game + +When configuring your game, make sure to start the game in the Ancient Era and leave all settings related to starting technologies and civics as the defaults. Other than that, configure difficulty, AI, etc. as you normally would. + +## Troubleshooting + +- If you do not see the client in the launcher, ensure you have placed the `civ6.apworld` in the correct folder (the + `lib/worlds` folder of your Archipelago installation). + +- If you are getting an error: `The remote computer refused the network connection`, or something else related to the client (or tuner) not being able to connect, it likely indicates the tuner is not actually enabled. One simple way to verify that it is enabled is, after completing the setup steps, to go Main Menu -> Options -> Look for an option named "Tuner" and verify it is set to "Enabled" + +- If your game gets in a state where someone has sent you items or you have sent locations but these are not correctly replicated to the multiworld, you can run `/resync` from the Civ 6 client. This may take up to a minute depending on how many items there are. + + +## Feedback +In the offical [Archipelago Discord](https://discord.com/invite/8Z65BR2) under the `future-game-design` channel there is a `civilization-vi` [thread](https://discord.com/channels/731205301247803413/1235473969487024189/1235473969487024189). Feel free to ping `@hesto2` with any bugs/thoughts/complaints/wishes/jokes you may have! \ No newline at end of file diff --git a/worlds/civ_6/requirements.txt b/worlds/civ_6/requirements.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/worlds/civ_6/test/TestBoostsanity.py b/worlds/civ_6/test/TestBoostsanity.py new file mode 100644 index 000000000000..430f2b58e3d0 --- /dev/null +++ b/worlds/civ_6/test/TestBoostsanity.py @@ -0,0 +1,50 @@ +from Fill import distribute_items_restrictive +from ..Data import get_boosts_data +from . import CivVITestBase + + +class TestBoostsanityIncluded(CivVITestBase): + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "boostsanity": "true", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "shuffle_goody_hut_rewards": "false", + "pre_hint_items": "all", + } + + def test_boosts_get_included(self) -> None: + self.world_setup() + distribute_items_restrictive(self.multiworld) + locations = self.multiworld.get_locations(self.player) + found_locations = 0 + for location in locations: + if "BOOST" in location.name != -1: + found_locations += 1 + num_boost_locations = len(get_boosts_data()) + self.assertEqual(found_locations, num_boost_locations) + + +class TestBoostsanityExcluded(CivVITestBase): + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "boostsanity": "false", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "shuffle_goody_hut_rewards": "false", + "pre_hint_items": "all", + } + + def test_boosts_are_not_included(self) -> None: + self.world_setup() + distribute_items_restrictive(self.multiworld) + locations = self.multiworld.get_locations(self.player) + found_locations = 0 + for location in locations: + if "BOOST" in location.name != -1: + found_locations += 1 + self.assertEqual(found_locations, 0) diff --git a/worlds/civ_6/test/TestGoodyHuts.py b/worlds/civ_6/test/TestGoodyHuts.py new file mode 100644 index 000000000000..06db16a5f778 --- /dev/null +++ b/worlds/civ_6/test/TestGoodyHuts.py @@ -0,0 +1,125 @@ +from typing import Dict +from BaseClasses import ItemClassification +from Fill import distribute_items_restrictive +from ..Items import FILLER_DISTRIBUTION, FillerItemRarity, get_filler_item_data +from . import CivVITestBase + + +class TestGoodyHutsIncluded(CivVITestBase): + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "shuffle_goody_hut_rewards": "true", + "pre_hint_items": "all", + } + + def test_goody_huts_get_included(self) -> None: + self.world_setup() + distribute_items_restrictive + distribute_items_restrictive(self.multiworld) + expected_goody_huts = 10 + found = 0 + for i in range(expected_goody_huts): + location = self.multiworld.get_location(f"GOODY_HUT_{i + 1}", self.player) + self.assertEqual(location.item.classification, ItemClassification.filler) + found += 1 + self.assertEqual(found, expected_goody_huts) + + +class TestGoodyHutsExcluded(CivVITestBase): + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "shuffle_goody_hut_rewards": "false", + "pre_hint_items": "all", + } + + def test_goody_huts_are_not_included(self) -> None: + self.world_setup() + distribute_items_restrictive + found_goody_huts = 0 + for location in self.multiworld.get_locations(self.player): + if location.name.startswith("GOODY_HUT_"): + found_goody_huts += 1 + self.assertEqual(found_goody_huts, 0) + + +class TestFillerItemsIncludedByRarity(CivVITestBase): + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "shuffle_goody_hut_rewards": "true", + "pre_hint_items": "all", + "boostsanity": "true" + } + + def test_filler_items_are_included_by_rarity(self) -> None: + self.world_setup() + distribute_items_restrictive + rarity_counts: Dict[FillerItemRarity, int] = { + FillerItemRarity.COMMON: 0, + FillerItemRarity.UNCOMMON: 0, + FillerItemRarity.RARE: 0, + } + total_filler_items = 0 + for item in self.multiworld.itempool: + if item.classification == ItemClassification.filler: + rarity = get_filler_item_data()[item.name].rarity + rarity_counts[rarity] += 1 + total_filler_items += 1 + + expected_counts = { + FillerItemRarity.COMMON: 102, + FillerItemRarity.UNCOMMON: 26, + FillerItemRarity.RARE: 3, + } + + for rarity, expected in expected_counts.items(): + self.assertEqual(rarity_counts[rarity], expected, f"Expected {expected} {rarity} items, found {rarity_counts[rarity]}") + + + +class TestFillerItemsIncludedByRarityWithoutBoostsanity(CivVITestBase): + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "shuffle_goody_hut_rewards": "true", + "pre_hint_items": "all", + "boostsanity": "false" + } + + def test_filler_items_are_included_by_rarity_without_boostsanity(self) -> None: + self.world_setup() + distribute_items_restrictive + rarity_counts: Dict[FillerItemRarity, int] = { + FillerItemRarity.COMMON: 0, + FillerItemRarity.UNCOMMON: 0, + FillerItemRarity.RARE: 0, + } + total_filler_items = 0 + for item in self.multiworld.itempool: + if item.classification == ItemClassification.filler: + rarity = get_filler_item_data()[item.name].rarity + rarity_counts[rarity] += 1 + total_filler_items += 1 + + expected_counts = { + FillerItemRarity.COMMON: 7, + FillerItemRarity.UNCOMMON: 2, + FillerItemRarity.RARE: 1, + } + + for rarity, expected in expected_counts.items(): + self.assertEqual(rarity_counts[rarity], expected, f"Expected {expected} {rarity} items, found {rarity_counts[rarity]}") diff --git a/worlds/civ_6/test/TestRegionRequirements.py b/worlds/civ_6/test/TestRegionRequirements.py new file mode 100644 index 000000000000..01453fa2e5d5 --- /dev/null +++ b/worlds/civ_6/test/TestRegionRequirements.py @@ -0,0 +1,238 @@ +from typing import List + +from BaseClasses import CollectionState +from ..Data import get_era_required_items_data +from ..Enum import EraType +from ..ProgressiveDistricts import convert_items_to_have_progression +from ..Items import get_item_by_civ_name +from . import CivVITestBase + + +def collect_items_for_era(test: CivVITestBase, era: EraType) -> None: + era_required_items = get_era_required_items_data() + items = [get_item_by_civ_name(item, test.world.item_table).name for item in era_required_items[era.value]] + test.collect_by_name(items) + + +def collect_items_for_era_progressive(test: CivVITestBase, era: EraType) -> None: + era_progression_items = get_era_required_items_data() + progressive_items = convert_items_to_have_progression( + era_progression_items[era.value]) + items = [get_item_by_civ_name(item, test.world.item_table).name for item in progressive_items] + test.collect_by_name(items) + + +def verify_eras_accessible(test: CivVITestBase, state: CollectionState, collect_func): + for era in EraType: + if era == EraType.ERA_ANCIENT: + test.assertTrue(state.can_reach( + era.value, "Region", test.player)) + else: + test.assertFalse(state.can_reach( + era.value, "Region", test.player)) + + collect_func(test, EraType.ERA_ANCIENT) + test.assertTrue(state.can_reach( + EraType.ERA_CLASSICAL.value, "Region", test.player)) + + collect_func(test, EraType.ERA_CLASSICAL) + test.assertTrue(state.can_reach( + EraType.ERA_MEDIEVAL.value, "Region", test.player)) + + collect_func(test, EraType.ERA_MEDIEVAL) + test.assertTrue(state.can_reach( + EraType.ERA_RENAISSANCE.value, "Region", test.player)) + + collect_func(test, EraType.ERA_RENAISSANCE) + test.assertTrue(state.can_reach( + EraType.ERA_INDUSTRIAL.value, "Region", test.player)) + + collect_func(test, EraType.ERA_INDUSTRIAL) + test.assertTrue(state.can_reach( + EraType.ERA_MODERN.value, "Region", test.player)) + + collect_func(test, EraType.ERA_MODERN) + test.assertTrue(state.can_reach( + EraType.ERA_ATOMIC.value, "Region", test.player)) + + collect_func(test, EraType.ERA_ATOMIC) + test.assertTrue(state.can_reach( + EraType.ERA_INFORMATION.value, "Region", test.player)) + + collect_func(test, EraType.ERA_INFORMATION) + test.assertTrue(state.can_reach( + EraType.ERA_FUTURE.value, "Region", test.player)) + + +class TestNonProgressiveRegionRequirements(CivVITestBase): + options = { + "pre_hint_items": "all", + "progression_style": "none", + "death_link": "false", + "death_link_effect": "unit_killed", + "boostsanity": "false", + } + + def test_eras_are_accessible_without_progressive_districts(self) -> None: + state = self.multiworld.state + verify_eras_accessible(self, state, collect_items_for_era) + + +class TestNonProgressiveRegionRequirementsWithBoostsanity(CivVITestBase): + options = { + "pre_hint_items": "all", + "progression_style": "none", + "death_link": "false", + "death_link_effect": "unit_killed", + "boostsanity": "true", + } + + def test_eras_are_accessible_without_progressive_districts(self) -> None: + state = self.multiworld.state + verify_eras_accessible(self, state, collect_items_for_era) + + +class TestProgressiveDistrictRequirementsWithBoostsanity(CivVITestBase): + options = { + "pre_hint_items": "all", + "progression_style": "districts_only", + "death_link": "false", + "death_link_effect": "unit_killed", + "boostsanity": "true", + } + + def test_eras_are_accessible_with_progressive_districts(self) -> None: + state = self.multiworld.state + verify_eras_accessible(self, state, collect_items_for_era_progressive) + + +class TestProgressiveDistrictRequirements(CivVITestBase): + options = { + "pre_hint_items": "all", + "progression_style": "districts_only", + "death_link": "false", + "death_link_effect": "unit_killed", + "boostsanity": "false", + } + + def test_eras_are_accessible_with_progressive_districts(self) -> None: + state = self.multiworld.state + verify_eras_accessible(self, state, collect_items_for_era_progressive) + + +class TestProgressiveEraRequirements(CivVITestBase): + options = { + "pre_hint_items": "all", + "progression_style": "eras_and_districts", + "death_link": "false", + "death_link_effect": "unit_killed" + } + + def test_eras_are_accessible_with_progressive_eras(self) -> None: + state = self.multiworld.state + self.collect_all_but(["Progressive Era"]) + + def check_eras_accessible(eras: List[EraType]): + for era in EraType: + if era in eras: + self.assertTrue(state.can_reach( + era.value, "Region", self.player)) + else: + self.assertFalse(state.can_reach( + era.value, "Region", self.player)) + + progresive_era_item = self.get_item_by_name("Progressive Era") + accessible_eras = [EraType.ERA_ANCIENT] + check_eras_accessible(accessible_eras) + + # Classical era requires 2 progressive era items + self.collect(progresive_era_item) + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_CLASSICAL] + check_eras_accessible(accessible_eras) + + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_MEDIEVAL] + check_eras_accessible(accessible_eras) + + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_RENAISSANCE] + check_eras_accessible(accessible_eras) + + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_INDUSTRIAL] + check_eras_accessible(accessible_eras) + + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_MODERN] + check_eras_accessible(accessible_eras) + + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_ATOMIC] + check_eras_accessible(accessible_eras) + + # Since we collect 2 in the ancient era, information and future era have same logic requirement + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_INFORMATION] + accessible_eras += [EraType.ERA_FUTURE] + check_eras_accessible(accessible_eras) + + +class TestProgressiveEraRequirementsWithBoostsanity(CivVITestBase): + options = { + "pre_hint_items": "all", + "progression_style": "eras_and_districts", + "death_link": "false", + "death_link_effect": "unit_killed", + "boostsanity": "true", + } + + def test_eras_are_accessible_with_progressive_eras(self) -> None: + state = self.multiworld.state + self.collect_all_but(["Progressive Era"]) + + def check_eras_accessible(eras: List[EraType]): + for era in EraType: + if era in eras: + self.assertTrue(state.can_reach( + era.value, "Region", self.player)) + else: + self.assertFalse(state.can_reach( + era.value, "Region", self.player)) + + progresive_era_item = self.get_item_by_name("Progressive Era") + accessible_eras = [EraType.ERA_ANCIENT] + check_eras_accessible(accessible_eras) + + # Classical era requires 2 progressive era items + self.collect(progresive_era_item) + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_CLASSICAL] + check_eras_accessible(accessible_eras) + + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_MEDIEVAL] + check_eras_accessible(accessible_eras) + + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_RENAISSANCE] + check_eras_accessible(accessible_eras) + + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_INDUSTRIAL] + check_eras_accessible(accessible_eras) + + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_MODERN] + check_eras_accessible(accessible_eras) + + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_ATOMIC] + check_eras_accessible(accessible_eras) + + # Since we collect 2 in the ancient era, information and future era have same logic requirement + self.collect(progresive_era_item) + accessible_eras += [EraType.ERA_INFORMATION] + accessible_eras += [EraType.ERA_FUTURE] + check_eras_accessible(accessible_eras) + diff --git a/worlds/civ_6/test/TestStartingHints.py b/worlds/civ_6/test/TestStartingHints.py new file mode 100644 index 000000000000..d7a94cdc066f --- /dev/null +++ b/worlds/civ_6/test/TestStartingHints.py @@ -0,0 +1,90 @@ +from BaseClasses import ItemClassification +from Fill import distribute_items_restrictive +from ..Enum import CivVICheckType +from . import CivVITestBase + + +class TestStartingHints(CivVITestBase): + run_default_tests = False + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "pre_hint_items": "all", + } + + def test_all_tech_civic_items_are_hinted_default(self) -> None: + self.world_setup() + distribute_items_restrictive(self.multiworld) + self.world.post_fill() + start_location_hints = self.world.options.start_location_hints.value + for location_name, location_data in self.world.location_table.items(): + if location_data.location_type == CivVICheckType.CIVIC or location_data.location_type == CivVICheckType.TECH: + self.assertIn(location_name, start_location_hints) + else: + self.assertNotIn(location_name, start_location_hints) + + +class TestOnlyProgressionItemsHinted(CivVITestBase): + run_default_tests = False + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "pre_hint_items": "progression_items", + } + + def test_only_progression_items_are_hinted(self) -> None: + self.world_setup() + distribute_items_restrictive(self.multiworld) + self.world.post_fill() + start_location_hints = self.world.options.start_location_hints.value + self.assertTrue(len(start_location_hints) > 0) + for hint in start_location_hints: + location_data = self.world.get_location(hint) + self.assertTrue(location_data.item.classification == ItemClassification.progression) + + +class TestNoJunkItemsHinted(CivVITestBase): + run_default_tests = False + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "pre_hint_items": "no_junk", + } + + def test_no_junk_items_are_hinted(self) -> None: + self.world_setup() + distribute_items_restrictive(self.multiworld) + self.world.post_fill() + start_location_hints = self.world.options.start_location_hints.value + self.assertTrue(len(start_location_hints) > 0) + for hint in start_location_hints: + location_data = self.world.get_location(hint) + self.assertTrue(location_data.item.classification == ItemClassification.progression or location_data.item.classification == ItemClassification.useful) + + +class TestNoItemsHinted(CivVITestBase): + run_default_tests = False + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "pre_hint_items": "none", + } + + def test_no_items_are_hinted(self) -> None: + self.world_setup() + distribute_items_restrictive(self.multiworld) + self.world.post_fill() + start_location_hints = self.world.options.start_location_hints.value + self.assertEqual(len(start_location_hints), 0) diff --git a/worlds/civ_6/test/__init__.py b/worlds/civ_6/test/__init__.py new file mode 100644 index 000000000000..597b52711ae9 --- /dev/null +++ b/worlds/civ_6/test/__init__.py @@ -0,0 +1,8 @@ +from typing import ClassVar + +from test.bases import WorldTestBase + + +class CivVITestBase(WorldTestBase): + game = "Civilization VI" + player: ClassVar[int] = 1 From 2d77757bfdf70a5d867402f92058052753086940 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 30 Jul 2024 22:37:45 -0600 Subject: [PATCH 04/69] Update docs --- worlds/civ_6/docs/boostsanity.md | 15 ------ worlds/civ_6/docs/en_Civilization VI.md | 56 ++++++++++++---------- worlds/civ_6/docs/setup_en.md | 64 +------------------------ 3 files changed, 32 insertions(+), 103 deletions(-) delete mode 100644 worlds/civ_6/docs/boostsanity.md diff --git a/worlds/civ_6/docs/boostsanity.md b/worlds/civ_6/docs/boostsanity.md deleted file mode 100644 index 666b8ef49149..000000000000 --- a/worlds/civ_6/docs/boostsanity.md +++ /dev/null @@ -1,15 +0,0 @@ -## Boostsanity -Boostsanity takes all of the Eureka & Inspiration events and makes them location checks. This feature is the one to changeup the way Civilization is played in an AP multiworld/randomizer to date. What normally are mundane tasks that are passively collected now become a novel and interesting bucket list that you need to pay attention to in order to unlock items for yourself and others! -Boosts have logic associated with them in order to verify you can always reach the ones you need to, when you need to. One side effect of this is that when boostsanity is enabled, previously some "Useful" items are now flagged as "Progression" (Urbanization, Pottery, The Wheel, to name a few). - -### FAQs -- Someone sent me a tech/civic and I'm worried I won't be able to boost it anymore! - - Fear not! The mod has been updated, and through a lot of wizardry 🧙‍♂️ you will be able to boost civics/techs that have already been received. Additionally the UI has been updated to show you whether they have been boosted or not after receiving them still. -- I need to kill a unit with a slinger/archer/musketman or some other obsolete unit I can't build anymore, how can I do this?? - - Don't forget you can go into the Tech Tree and click on a Vanilla tech you've received in order to toggle it on/off. This is necessary in order to pursue some of the boosts if you receive techs in certain orders. -- Something happened and I'm not able to unlock the boost due to game rules! - - A few scenarios you may worry about: "Found a religion", "Make an alliance with another player", "Develop an alliance to level 2", "Build a wonder from X Era", to name a few. Any boost that is "miss-able" has been flagged as an "Excluded" location and will not ever receive a progression item. For a list of how each boost is flagged, take a look [here](../data/boosts.json). -- I'm worried that my `PROGRESSIVE_ERA` item is going to be stuck in a boost I won't have time to complete before my maximum unlocked era ends! - - I, too, have shared this fear. Due to the unpredictable timing of boosts and unlocking them, this could lead to a hard lock in certain scenarios. As a result, `PROGRESSIVE_ERA` items will never be located at a boost check. -- There's too many boosts, how will I know which one's I should focus on?! - - In order to give a little more focus to all the boosts rather than just arbitrarily picking them at random, items in both of the vanilla trees will now have an advisor icon on them if it's associated boost contains a progression item. \ No newline at end of file diff --git a/worlds/civ_6/docs/en_Civilization VI.md b/worlds/civ_6/docs/en_Civilization VI.md index f6392ec0c313..8b738b41338e 100644 --- a/worlds/civ_6/docs/en_Civilization VI.md +++ b/worlds/civ_6/docs/en_Civilization VI.md @@ -1,11 +1,8 @@ # Civlization 6 Archipelago -## Setup Guide -For setup instructions go [here](./docs/setup_en.md). - ## What does randomization do to this game? -In Civilization VI, the tech and civic trees are both shuffled. This presents some interesting ways to play the game in a non-standard way. If you are feeling adventurous, you can enable the `boostsanity` option in order to really change up the way you normally would play a Civ game. Details on the option can be found [here](./docs/boostsanity.md) +In Civilization VI, the tech and civic trees are both shuffled. This presents some interesting ways to play the game in a non-standard way. If you are feeling adventurous, you can enable the `boostsanity` option in order to really change up the way you normally would play a Civ game. Details on the option can be found in the "Boostsanity" section below. There are a few changes that the Archipelago mod introduces in order to make this playable/fun. These are detailed in the __FAQ__ section below. @@ -23,31 +20,40 @@ A short period after receiving an item, you will get a notification indicating y ## FAQs - Do I need the DLC to play this? - - Yes, you need both Rise & Fall and Gathering Storm. If there is enough interest then I can eventually add support for Archipellago runs that don't require both expansions. - + - Yes, you need both Rise & Fall and Gathering Storm. If there is enough interest then I can eventually add support for Archipellago runs that don't require both expansions. - Does this work with Multiplayer? - - It does not and, despite my best efforts, probably won't until there's a new way for external programs to be able to interact with the game. - + - It does not and, despite my best efforts, probably won't until there's a new way for external programs to be able to interact with the game. - Does my mod that reskins Barbarians as various Pro Wrestlers work with this?? - - Only one way to find out! Any mods that modify techs/civics will most likely cause issues, though. - + - Only one way to find out! Any mods that modify techs/civics will most likely cause issues, though. - "Help! I can't see any of the items that have been sent to me!" - - Both trees by default will show you the researchable Archipelago locations. To view the normal tree, you can click "Toggle Archipelago Tree" on the top left corner of the tree view. - + - Both trees by default will show you the researchable Archipelago locations. To view the normal tree, you can click "Toggle Archipelago Tree" on the top left corner of the tree view. - "Oh no! I received the Machinery tech and now instead of getting an Archer next turn, I have to wait an additional 10 turns to get a Crossbowman!" - - Vanilla prevents you from building units of the same class from an earlier tech level after you have researched a later variant. For example, this could be problematic if someone unlocks Crossbowmen for you right out the gate since you won't be able to make Archers (which have a much lower production cost). - - - Solution: You can now go in to the tech tree, click "Toggle Archipelago Tree" to view your unlocked techs, and then can click any tech you have unlocked to toggle whether it is currently active or not. __NOTE__: This is an experimental feature and may yield some unexpected behaviors. Please DM `@Hesto2` on Discord if you run into any issues. - -- I enabled `progressive districts` but I have no idea techs/civics what items are locked behind progression now! - - Any technology or civic that grants you a new building in a district (or grants you the district itself) is now locked behind a progressive item. For example, `PROGRESSIVE_CAMPUS` would give you these items in the following order: - 1. `TECH_WRITING` - 2. `TECH_EDUCATION` - 3. `TECH_CHEMISTRY` - - If you want to see the details around each item, you can review [this file](./data/progressive_districts.json) - + - Vanilla prevents you from building units of the same class from an earlier tech level after you have researched a later variant. For example, this could be problematic if someone unlocks Crossbowmen for you right out the gate since you won't be able to make Archers (which have a much lower production cost). +Solution: You can now go in to the tech tree, click "Toggle Archipelago Tree" to view your unlocked techs, and then can click any tech you have unlocked to toggle whether it is currently active or not. - "How does DeathLink work? Am I going to have to start a new game every time one of my friends dies??" - - Heavens no, my fellow Archipelago appreciator. When configuring your Archipelago options for Civilization on the options page, there are several choices available for you to fine tune the way you'd like to be punished for the follies of your friends. These include: Having a random unit destroyed, losing a percentage of gold or faith, or even losing a point on your era score. If you can't make up your mind, you can elect to have any of them be selected every time a death link is sent your way. + - Heavens no, my fellow Archipelago appreciator. When configuring your Archipelago options for Civilization on the options page, there are several choices available for you to fine tune the way you'd like to be punished for the follies of your friends. These include: Having a random unit destroyed, losing a percentage of gold or faith, or even losing a point on your era score. If you can't make up your mind, you can elect to have any of them be selected every time a death link is sent your way. + In the event you lose one of your units in combat (this means captured units don't count), then you will send a death link event to the rest of your friends. - - In the event you lose one of your units in combat (this means captured units don't count), then you will send a death link event to the rest of your friends. +- I enabled `progressive districts` but I have no idea techs/civics what items are locked behind progression now! + - Any technology or civic that grants you a new building in a district (or grants you the district itself) is now locked behind a progressive item. For example, `PROGRESSIVE_CAMPUS` would give you these items in the following order: + 1. `TECH_WRITING` + 2. `TECH_EDUCATION` + 3. `TECH_CHEMISTRY` + - If you want to see the details around each item, you can review [this file](https://github.com/hesto2/civilization_vi_apworld/blob/main/data/progressive_districts.json) + +## Boostsanity +Boostsanity takes all of the Eureka & Inspiration events and makes them location checks. This feature is the one to changeup the way Civilization is played in an AP multiworld/randomizer to date. What normally are mundane tasks that are passively collected now become a novel and interesting bucket list that you need to pay attention to in order to unlock items for yourself and others! +Boosts have logic associated with them in order to verify you can always reach the ones you need to, when you need to. One side effect of this is that when boostsanity is enabled, previously some "Useful" items are now flagged as "Progression" (Urbanization, Pottery, The Wheel, to name a few). + +### Boostsanity FAQs +- Someone sent me a tech/civic and I'm worried I won't be able to boost it anymore! + - Fear not! The mod has been updated, and through a lot of wizardry 🧙‍♂️ you will be able to boost civics/techs that have already been received. Additionally the UI has been updated to show you whether they have been boosted or not after receiving them still. +- I need to kill a unit with a slinger/archer/musketman or some other obsolete unit I can't build anymore, how can I do this?? + - Don't forget you can go into the Tech Tree and click on a Vanilla tech you've received in order to toggle it on/off. This is necessary in order to pursue some of the boosts if you receive techs in certain orders. +- Something happened and I'm not able to unlock the boost due to game rules! + - A few scenarios you may worry about: "Found a religion", "Make an alliance with another player", "Develop an alliance to level 2", "Build a wonder from X Era", to name a few. Any boost that is "miss-able" has been flagged as an "Excluded" location and will not ever receive a progression item. For a list of how each boost is flagged, take a look [here](https://github.com/hesto2/civilization_vi_apworld/blob/main/data/boosts.json). +- I'm worried that my `PROGRESSIVE_ERA` item is going to be stuck in a boost I won't have time to complete before my maximum unlocked era ends! + - Due to the unpredictable timing of boosts and unlocking them, this could lead to a hard lock in certain scenarios. As a result, `PROGRESSIVE_ERA` items will never be located at a boost check. +- There's too many boosts, how will I know which one's I should focus on?! + - In order to give a little more focus to all the boosts rather than just arbitrarily picking them at random, items in both of the vanilla trees will now have an advisor icon on them if it's associated boost contains a progression item. diff --git a/worlds/civ_6/docs/setup_en.md b/worlds/civ_6/docs/setup_en.md index a7c177605d94..cd419fc66f24 100644 --- a/worlds/civ_6/docs/setup_en.md +++ b/worlds/civ_6/docs/setup_en.md @@ -10,7 +10,6 @@ The following are required in order to play Civ VI in Archipelago - Installed [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) v0.4.5 or higher.\ **Make sure to install the Generator if you intend to generate multiworlds.** -- The latest version of the [Civ VI apworld](https://github.com/hesto2/civilization_vi_apworld/releases/latest). - The latest version of the [Civ VI AP Mod](https://github.com/hesto2/civilization_archipelago_mod). @@ -39,73 +38,12 @@ Once you have located your `AppOptions.txt`, do a search for `Enable FireTuner`. - Archipelago.modinfo - All the other mod files, etc. -## AP World Installation - -1. Unzip the downloaded Civ VI apworld zip file -2. Place the `civ6.apworld` file in your Archipelago installation's `lib/worlds` folder (Windows default to: - `%programdata%/Archipelago`). - -- If you have a `civ6.apworld` file from a previous version of the apworld, you **must** delete it, as it is no longer - supported. Additionally, if there is a `civ6` folder in that folder, you **must** also delete it. Keeping - these around will cause issues, even if multiworlds are successfully generated. - -## Setting Up a YAML - -All players playing Civ VI must provide the room host with a YAML file containing the settings for their world. -A sample YAML file for Civ VI is supplied in the Civ VI apworld download. Refer to the comments in that file for -details about what each setting does. - -Once complete, provide the room host with your YAML file. - -## Generating a Multiworld - -If you're generating a multiworld game that includes Civ VI, you'll need to run it locally since the online -generator does not yet support it. Follow these steps to generate a multiworld: - -1. Gather all player's YAMLs. Place these YAMLs into the `Players` folder of your Archipelago installation. If the - folder does not exist, then it must be created manually. The files here should not be compressed. -2. Modify any local host settings for generation, as desired. -3. Run `ArchipelagoGenerate.exe` (without `.exe` on Linux) or click `Generate` in the launcher. The generation output - is placed in the `output` folder (usually named something like `AP_XXXXX.zip`). \* Please note that if any player in the game you want to generate plays a game that needs a ROM file to generate, - you will need the corresponding ROM files. -4. Unzip the `AP_XXXXX.zip` file. It should include a zip file for each player in the room playing Civ VI. Distribute each file to the appropriate player. -5. **Delete the distributed zip files and re-zip the remaining files**. In the next section, use this archive file to - host a room or provide it to the room host. \* If you plan to host the room on a local machine, skip this step and use the original zip file (`AP_XXXX.zip`) instead. - -## Hosting a Room - -If you're generating the multiworld, follow the instructions in the previous section. Once you have the zip file -corresponding to your multiworld, follow -[these steps](https://archipelago.gg/tutorial/Archipelago/setup/en#hosting-an-archipelago-server) to host a room. Follow -the instructions for hosting on the website from a locally generated game or on a local machine. - -## Connecting to a Room - -You should have the zip file provided to you by the multiworld generator. You should also have the room's server -name and port number from the room's host. - -Once you do, follow these steps to connect to the room: - -1. Unzip the folder given to you and copy its contents (should include `NewItems.xml` an `archipelago.json`, and more) into the installed mod folder. -2. Start `ArchipelagoLauncher.exe` and choose `Civ6 Client`, which will open the text client. -3. Connect to the room by entering the server name and port number at the top and pressing `Connect`. For rooms hosted - on the website, this will be `archipelago.gg:`, where `` is the port number. If a game is hosted from the - `ArchipelagoServer.exe` (without `.exe` on Linux), this will default to `38281` but may be changed in the `host.yaml`. -4. Once you successfully configure and launch a game, the client should let you know it is connected and you will be ready to play! - ## Configuring your game When configuring your game, make sure to start the game in the Ancient Era and leave all settings related to starting technologies and civics as the defaults. Other than that, configure difficulty, AI, etc. as you normally would. ## Troubleshooting -- If you do not see the client in the launcher, ensure you have placed the `civ6.apworld` in the correct folder (the - `lib/worlds` folder of your Archipelago installation). - - If you are getting an error: `The remote computer refused the network connection`, or something else related to the client (or tuner) not being able to connect, it likely indicates the tuner is not actually enabled. One simple way to verify that it is enabled is, after completing the setup steps, to go Main Menu -> Options -> Look for an option named "Tuner" and verify it is set to "Enabled" -- If your game gets in a state where someone has sent you items or you have sent locations but these are not correctly replicated to the multiworld, you can run `/resync` from the Civ 6 client. This may take up to a minute depending on how many items there are. - - -## Feedback -In the offical [Archipelago Discord](https://discord.com/invite/8Z65BR2) under the `future-game-design` channel there is a `civilization-vi` [thread](https://discord.com/channels/731205301247803413/1235473969487024189/1235473969487024189). Feel free to ping `@hesto2` with any bugs/thoughts/complaints/wishes/jokes you may have! \ No newline at end of file +- If your game gets in a state where someone has sent you items or you have sent locations but these are not correctly replicated to the multiworld, you can run `/resync` from the Civ 6 client. This may take up to a minute depending on how many items there are. \ No newline at end of file From 76a7c36fd08b79ec4f7df746321d0740103ffa16 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 30 Jul 2024 23:08:25 -0600 Subject: [PATCH 05/69] Fix tests --- worlds/civ_6/Civilization VI.yaml | 58 ------------------------------ worlds/civ_6/Options.py | 32 ++++++++--------- worlds/civ_6/test/TestGoodyHuts.py | 6 ++-- 3 files changed, 19 insertions(+), 77 deletions(-) delete mode 100644 worlds/civ_6/Civilization VI.yaml diff --git a/worlds/civ_6/Civilization VI.yaml b/worlds/civ_6/Civilization VI.yaml deleted file mode 100644 index 5540f0223066..000000000000 --- a/worlds/civ_6/Civilization VI.yaml +++ /dev/null @@ -1,58 +0,0 @@ -Civilization VI: - progression_balancing: 50 - accessibility: items - - # Determines what progressive items (if any) should be included. - # districts_only: Each tech/civic that would normally unlock a district or district building now has a logical progression. Example: TECH_BRONZE_WORKING is now PROGRESSIVE_ENCAMPMENT - # eras_and_districts: Players will be defeated if they play until the world era advances beyond the currently unlocked maximum era. A notification will be shown as the end of the era approaches letting the player know if they don't have enough progressive era items. Unlocked eras can be seen in both the tech and civic trees. Includes all progressive districts. - # none: No progressive items will be included. This means you can get district upgrades that won't be usable until the relevant district is unlocked. - progression_style: "districts_only" - - # Shuffles the goody hut rewards. Goody huts will only contain junk items and location checks are received sequentially (GOODY_HUT_1, GOODY_HUT_2, etc). - # true: goody hut rewards are shuffled in - # false: goody hut rewards are not shuffled in and will behave as normal - shuffle_goody_hut_rewards: true - - # Boosts for Civics/Techs are location checks. Boosts can now be triggered even if - # the item has already been researched. If it is dependent upon a unit that is now - # obsolete, you can click toggle on/off the relevant tech in the tech tree. - boostsanity: false - - # Controls if/what items in the tech/civics trees are pre hinted for the multiworld. - # all: All items in the tech & civics trees are pre hinted. - # progression_items: Only locations in the trees containing progression items are pre hinted. - # no_junk: Pre hint the progression and useful items. - # none: No items are pre hinted. - pre_hint_items: "progression_items" - - # Controls the cost of techs and civics. - # cheap: Techs and civics cost 50% of the normal cost. - # default: Techs and civics cost the normal amount. - # expensive: Techs and civics cost 150% of the normal cost. - research_cost_multiplier: cheap - - # If boostsanity is enabled, this will prevent any boosts that are 'missable' from having progression items. Disabling this will potentially require multiple playthroughs to complete the seed. - exclude_missable_boosts: "true" - - # Each Tech and Civic Location will have a title of 'Unrevealed' until its prereqs have been researched. Note that hints will still be pre collected if that option is enabled. - hide_item_names: "false" - - # If enabled, an advisor icon will be added to any location that contains a progression item - advisor_show_progression_items: "true" - - # 'true' or 'false' - death_link: "false" - - # Determines the effect that is applied when a player dies. - # gold: The player's gold is reduced by the amount specified in death_link_effect_percent. - # faith: The player's faith is reduced by the amount specified in death_link_effect_percent. - # era_score: The player's era score is reduced by 1. - death_link_effect: unit_killed - - # The percentage of the effect that is applied for gold and faith. Era score decreases by 1 each time. - # 0 - 100 - death_link_effect_percent: 20 - -description: "Generated by https://archipelago.gg." -game: Civilization VI -name: PlayerName diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 40a22a5cd4ee..b2197254e807 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -11,10 +11,10 @@ class ProgressionStyle(Choice): None: No progressive items will be included. This means you can get district upgrades that won't be usable until the relevant district is unlocked. """ display_name = "Progression Style" - option_districts_only = "Districts Only" - option_eras_and_districts = "Eras and Districts" - option_none = "None" - default = "districts_only" + option_districts_only = 0 + option_eras_and_districts = 1 + option_none = 2 + default = option_districts_only class ShuffleGoodyHuts(DefaultOnToggle): @@ -50,11 +50,11 @@ class PreHintItems(Choice): None: No items are pre hinted. """ display_name = "Tech/Civic Tree Pre Hinted Items" - option_all = "All Tree Locations" - option_progression_items = "Progression Tree Locations" - option_no_junk = "No Junk" - option_none = "None" - default = "progression_items" + option_all = 0 + option_progression_items = 1 + option_no_junk = 2 + option_none = 3 + default = option_progression_items class HideItemNames(Toggle): @@ -73,13 +73,13 @@ class DeathLinkEffect(Choice): Era score is decrased by 1.\n Any will select any of these options any time a death link is received.""" display_name = "Death Link Effect" - option_unit_killed = "Unit Killed" - option_era_score = "Era Score" - option_gold = "Gold" - option_faith = "Faith" - option_any = "Any" - option_any_except_era_score = "Any Except Era Score" - default = "unit_killed" + option_unit_killed = 0 + option_era_score = 1 + option_gold = 2 + option_faith = 3 + option_any = 4 + option_any_except_era_score = 5 + default = option_unit_killed class DeathLinkEffectPercent(Range): diff --git a/worlds/civ_6/test/TestGoodyHuts.py b/worlds/civ_6/test/TestGoodyHuts.py index 06db16a5f778..cc39d10a7284 100644 --- a/worlds/civ_6/test/TestGoodyHuts.py +++ b/worlds/civ_6/test/TestGoodyHuts.py @@ -78,9 +78,9 @@ def test_filler_items_are_included_by_rarity(self) -> None: total_filler_items += 1 expected_counts = { - FillerItemRarity.COMMON: 102, - FillerItemRarity.UNCOMMON: 26, - FillerItemRarity.RARE: 3, + FillerItemRarity.COMMON: 101, + FillerItemRarity.UNCOMMON: 27, + FillerItemRarity.RARE: 4, } for rarity, expected in expected_counts.items(): From 0cc1b876dfc12c296f4e94f8944822eaf3373f2f Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Mon, 5 Aug 2024 21:29:53 -0600 Subject: [PATCH 06/69] Update to use apcivvi --- .gitignore | 1 + worlds/civ_6/Civ6Client.py | 28 +++++++--- worlds/civ_6/Container.py | 2 +- worlds/civ_6/TunerClient.py | 7 +-- worlds/civ_6/__init__.py | 6 +-- worlds/civ_6/docs/setup_en.md | 98 ++++++++++++++++++----------------- 6 files changed, 78 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index 791f7b1bb7fe..5da42dc1e0b9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *_Spoiler.txt *.bmbp *.apbp +*.apcivvi *.apl2ac *.apm3 *.apmc diff --git a/worlds/civ_6/Civ6Client.py b/worlds/civ_6/Civ6Client.py index 973a794a4f45..f1053a989b5d 100644 --- a/worlds/civ_6/Civ6Client.py +++ b/worlds/civ_6/Civ6Client.py @@ -1,7 +1,9 @@ import asyncio import logging +import os import traceback from typing import Dict, List +import zipfile from CommonClient import ClientCommandProcessor, CommonContext, get_base_parser, logger, server_loop, gui_enabled from .Data import get_progressive_districts_data @@ -68,11 +70,12 @@ class CivVIContext(CommonContext): item.name: item.code for item in generate_item_table().values()} connection_state = ConnectionState.DISCONNECTED - def __init__(self, server_address, password): + def __init__(self, server_address, password, apcivvi_file=None): super().__init__(server_address, password) self.game_interface = CivVIInterface(logger) location_by_era = generate_era_location_table() self.item_table = generate_item_table() + self.apcivvi_file = apcivvi_file # Get tables formatted in a way that is easier to use here for era, locations in location_by_era.items(): @@ -151,7 +154,6 @@ async def tuner_sync_task(ctx: CivVIContext): else: try: if ctx.processing_multiple_items == True: - logger.debug("Waiting for items to finish processing") await asyncio.sleep(3) else: state = await ctx.game_interface.is_in_game() @@ -203,7 +205,6 @@ async def handle_receive_items(ctx: CivVIContext, last_received_index_override: try: last_received_index = last_received_index_override or await ctx.game_interface.get_last_received_index() if len(ctx.items_received) - last_received_index > 1: - logger.debug("Multiple items received") ctx.processing_multiple_items = True progressive_districts: List[CivVIItemData] = [] @@ -244,8 +245,6 @@ async def handle_receive_items(ctx: CivVIContext, last_received_index_override: elif item.item_type == CivVICheckType.ERA: progressive_eras.append(item) - if ctx.processing_multiple_items: - logger.debug("DONE") ctx.processing_multiple_items = False finally: # If something errors out, then unblock item processing @@ -253,7 +252,6 @@ async def handle_receive_items(ctx: CivVIContext, last_received_index_override: async def handle_check_goal_complete(ctx: CivVIContext): - # logger.debug("Sending Goal Complete") result = await ctx.game_interface.check_victory() if result: logger.info("Sending Victory to server!") @@ -285,7 +283,22 @@ def main(connect=None, password=None, name=None): Utils.init_logging("Civilization VI Client") async def _main(connect, password, name): - ctx = CivVIContext(connect, password) + parser = get_base_parser() + parser.add_argument('apcivvi_file', default="", type=str, nargs='?', help="Path to apcivvi file") + args = parser.parse_args() + ctx = CivVIContext(connect, password, args.apcivvi_file) + + if args.apcivvi_file: + parent_dir = os.path.dirname(args.apcivvi_file) + target_name = os.path.basename(args.apcivvi_file).replace(".apcivvi", "-MOD-FILES") + target_path = os.path.join(parent_dir, target_name) + if not os.path.exists(target_path): + os.makedirs(target_path, exist_ok=True) + logger.info("Extracting mod files to %s", target_path) + with zipfile.ZipFile(args.apcivvi_file, 'r') as zip_ref: + for member in zip_ref.namelist(): + zip_ref.extract(member, target_path) + ctx.auth = name ctx.server_task = asyncio.create_task( server_loop(ctx), name="ServerLoop") @@ -314,6 +327,7 @@ async def _main(connect, password, name): def debug_main(): parser = get_base_parser() + parser.add_argument('apcivvi_file', default="", type=str, nargs='?', help="Path to apcivvi file") parser.add_argument('--name', default=None, help="Slot Name to connect as.") parser.add_argument('--debug', default=None, diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py index 5d324eea5c6c..93db3647e157 100644 --- a/worlds/civ_6/Container.py +++ b/worlds/civ_6/Container.py @@ -36,7 +36,7 @@ def __init__(self, patch_data: dict, base_path: str, output_directory: str, player=None, player_name: str = "", server: str = ""): self.patch_data = patch_data self.file_path = base_path - container_path = os.path.join(output_directory, base_path + ".zip") + container_path = os.path.join(output_directory, base_path + ".apcivvi") super().__init__(container_path, player, player_name, server) def write_contents(self, opened_zipfile: zipfile.ZipFile) -> None: diff --git a/worlds/civ_6/TunerClient.py b/worlds/civ_6/TunerClient.py index 8a6d3497af58..7478e02e8b03 100644 --- a/worlds/civ_6/TunerClient.py +++ b/worlds/civ_6/TunerClient.py @@ -55,7 +55,6 @@ async def send_game_command(self, command_string: str, size: int = 64): async def send_command(self, command_string: str, size: int = 64): """Send a raw commannd""" - self.logger.debug("Sending Command: " + command_string) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setblocking(False) @@ -83,8 +82,6 @@ async def send_command(self, command_string: str, size: int = 64): received_data = await self.async_recv(sock) response = decode_mixed_string(received_data) - self.logger.debug('Received:') - self.logger.debug(response) return self.__parse_response(response) except socket.timeout: @@ -97,9 +94,9 @@ async def send_command(self, command_string: str, size: int = 64): "The remote computer refused the network connection", ] if any(error in str(e) for error in connection_errors): - raise TunerConnectionException(e) + raise TunerConnectionException(e) else: - raise TunerErrorException(e) + raise TunerErrorException(e) finally: sock.close() diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 45af2cdc2e52..b8d61247a830 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -1,7 +1,7 @@ import math import os import random -from typing import Dict +from typing import Dict, Optional import typing from .Data import get_boosts_data @@ -19,7 +19,7 @@ from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components, launch_subprocess -def run_client(): +def run_client(url: Optional[str] = None): print("Running Civ6 Client") from .Civ6Client import main # lazy import launch_subprocess(main, name="Civ6Client") @@ -183,7 +183,7 @@ def fill_slot_data(self): def generate_output(self, output_directory: str): mod_name = f"AP-{self.multiworld.get_file_safe_player_name(self.player)}" mod_dir = os.path.join( - output_directory, mod_name + "_" + Utils.__version__) + output_directory, mod_name + "_" + self.multiworld.seed_name) mod_files = { f"NewItems.xml": generate_new_items(self), f"InitOptions.lua": generate_setup_file(self), diff --git a/worlds/civ_6/docs/setup_en.md b/worlds/civ_6/docs/setup_en.md index cd419fc66f24..7dc44150f989 100644 --- a/worlds/civ_6/docs/setup_en.md +++ b/worlds/civ_6/docs/setup_en.md @@ -1,49 +1,51 @@ -# Setup Guide for Civilization VI Archipelago - -This guide is meant to help you get up and running with Civlization VI in your Archipelago run. Note that this requires you to have both Rise & Fall as well as Gathering Storm installed. This will not work unless both of those DLCs are enabled. - -## Requirements - -The following are required in order to play Civ VI in Archipelago - -- Windows OS (Firaxis does not support the necessary tooling for Mac, Linux is yet to bet verified) - -- Installed [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) v0.4.5 or higher.\ - **Make sure to install the Generator if you intend to generate multiworlds.** - -- The latest version of the [Civ VI AP Mod](https://github.com/hesto2/civilization_archipelago_mod). - -- Tuner setting enabled so the archipelago client can communicate with the game - -## Enabling the tuner -Depending on how you installed Civ 6 you will have to navigate to one of the following: -- `YOUR_USER/Documents/My Games/Sid Meier's Civilization VI/AppOptions.txt` -- `YOUR_USER/AppData/Local/Firaxis Games/Sid Meier's Civilization VI/AppOptions.txt` - -Once you have located your `AppOptions.txt`, do a search for `Enable FireTuner`. Set `EnableTuner` to `1` instead of `0`. __NOTE__: While this is active, achievments will be disabled. - -## Mod Installation - -1. Download and unzip the latest release of the mod from [github](https://github.com/hesto2/civilization_archipelago_mod/releases). - -2. Copy the folder containing the mod files to your Civ VI mods folder. On Windows, this is usually located at `C:\Users\YOUR_USER\Documents\My Games\Sid Meier's Civilization VI\Mods` - -3. After the Archipelago host generates a game, you should be given another zip file titled `AP-{playername}....zip`. Unzip this and copy all of its contents into your mod folder. - -4. Your finished mod folder should look something like this: -- Civ VI Mods Directory - - civilization_archipelago_mod - - NewItems.xml - - InitOptions.lua - - Archipelago.modinfo - - All the other mod files, etc. - -## Configuring your game - -When configuring your game, make sure to start the game in the Ancient Era and leave all settings related to starting technologies and civics as the defaults. Other than that, configure difficulty, AI, etc. as you normally would. - -## Troubleshooting - -- If you are getting an error: `The remote computer refused the network connection`, or something else related to the client (or tuner) not being able to connect, it likely indicates the tuner is not actually enabled. One simple way to verify that it is enabled is, after completing the setup steps, to go Main Menu -> Options -> Look for an option named "Tuner" and verify it is set to "Enabled" - +# Setup Guide for Civilization VI Archipelago + +This guide is meant to help you get up and running with Civlization VI in your Archipelago run. Note that this requires you to have both Rise & Fall as well as Gathering Storm installed. This will not work unless both of those DLCs are enabled. + +## Requirements + +The following are required in order to play Civ VI in Archipelago + +- Windows OS (Firaxis does not support the necessary tooling for Mac, Linux is yet to bet verified) + +- Installed [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) v0.4.5 or higher.\ + **Make sure to install the Generator if you intend to generate multiworlds.** + +- The latest version of the [Civ VI AP Mod](https://github.com/hesto2/civilization_archipelago_mod). + +- Tuner setting enabled so the archipelago client can communicate with the game + +## Enabling the tuner +Depending on how you installed Civ 6 you will have to navigate to one of the following: +- `YOUR_USER/Documents/My Games/Sid Meier's Civilization VI/AppOptions.txt` +- `YOUR_USER/AppData/Local/Firaxis Games/Sid Meier's Civilization VI/AppOptions.txt` + +Once you have located your `AppOptions.txt`, do a search for `Enable FireTuner`. Set `EnableTuner` to `1` instead of `0`. __NOTE__: While this is active, achievments will be disabled. + +## Mod Installation + +1. Download and unzip the latest release of the mod from [github](https://github.com/hesto2/civilization_archipelago_mod/releases). + +2. Copy the folder containing the mod files to your Civ VI mods folder. On Windows, this is usually located at `C:\Users\YOUR_USER\Documents\My Games\Sid Meier's Civilization VI\Mods` + +3. After the Archipelago host generates a game, you should be given a `.apcivvi` file. Associate the file with the Archipelago Launcher and double click it. + +4. Copy the contents of the new folder it generates (it will have the same name as the `.apcivvi` file) into your Civilization VI Archipelago Mod folder. + +5. Your finished mod folder should look something like this: +- Civ VI Mods Directory + - civilization_archipelago_mod + - NewItems.xml + - InitOptions.lua + - Archipelago.modinfo + - All the other mod files, etc. + +## Configuring your game + +When configuring your game, make sure to start the game in the Ancient Era and leave all settings related to starting technologies and civics as the defaults. Other than that, configure difficulty, AI, etc. as you normally would. + +## Troubleshooting + +- If you are getting an error: `The remote computer refused the network connection`, or something else related to the client (or tuner) not being able to connect, it likely indicates the tuner is not actually enabled. One simple way to verify that it is enabled is, after completing the setup steps, to go Main Menu -> Options -> Look for an option named "Tuner" and verify it is set to "Enabled" + - If your game gets in a state where someone has sent you items or you have sent locations but these are not correctly replicated to the multiworld, you can run `/resync` from the Civ 6 client. This may take up to a minute depending on how many items there are. \ No newline at end of file From 52b64d4f27f7aef1063476e45db8c2cbcb6f19f1 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Mon, 5 Aug 2024 22:12:48 -0600 Subject: [PATCH 07/69] Update Readme and codeowners --- README.md | 1 + docs/CODEOWNERS | 3 +++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index cebd4f7e7529..8460350b3a5c 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Currently, the following games are supported: * Aquaria * Yu-Gi-Oh! Ultimate Masters: World Championship Tournament 2006 * A Hat in Time +* Civilization VI For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/). Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index ab841e65ee4c..f4ed074569a7 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -42,6 +42,9 @@ # ChecksFinder /worlds/checksfinder/ @SunCatMC +# Civilization VI +/worlds/civ6/ @hesto2 + # Clique /worlds/clique/ @ThePhar From 5cbbe9a0b1cfcc4f01b55f29b770536f0bcd26a2 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 6 Aug 2024 14:11:42 -0600 Subject: [PATCH 08/69] Minor changes --- worlds/civ_6/Items.py | 19 ++++++++----------- worlds/civ_6/Locations.py | 9 +++------ worlds/civ_6/Regions.py | 24 ++++++++++++------------ worlds/civ_6/Rules.py | 11 +++++------ worlds/civ_6/TunerClient.py | 1 - worlds/civ_6/__init__.py | 11 ++++------- worlds/civ_6/test/TestGoodyHuts.py | 2 +- 7 files changed, 33 insertions(+), 44 deletions(-) diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index c521e8eed2d7..b7d801fa1b04 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -1,14 +1,11 @@ from enum import Enum -import json -import os -import pkgutil -import random -from typing import Dict, List, Optional -import typing +from typing import Dict, List, Optional, TYPE_CHECKING, List from BaseClasses import Item, ItemClassification from .Data import get_era_required_items_data, get_existing_civics_data, get_existing_techs_data, get_goody_hut_rewards_data, get_progressive_districts_data -from .Enum import CivVICheckType, EraType +from .Enum import CivVICheckType from .ProgressiveDistricts import get_flat_progressive_districts +if TYPE_CHECKING: + from . import CivVIWorld CIV_VI_AP_ITEM_ID_BASE = 5041000 NON_PROGRESSION_DISTRICTS = [ @@ -98,7 +95,7 @@ class CivVIItemData: progression_name: Optional[str] civ_name: Optional[str] - def __init__(self, name, civ_vi_id: int, cost: int, item_type: CivVICheckType, id_offset: int, classification: ItemClassification, progression_name: Optional[str], civ_name: Optional[str] = None): + def __init__(self, name, civ_vi_id: int, cost: int, item_type: CivVICheckType, id_offset: int, classification: ItemClassification, progression_name: Optional[str], civ_name: Optional[str] = None): self.classification = classification self.civ_vi_id = civ_vi_id self.name = name @@ -125,7 +122,7 @@ def format_item_name(name: str) -> str: return " ".join([part.capitalize() for part in name_parts]) -def get_item_by_civ_name(item_name: typing.List[str], item_table: typing.Dict[str, 'CivVIItemData']) -> 'CivVIItemData': +def get_item_by_civ_name(item_name: List[str], item_table: Dict[str, 'CivVIItemData']) -> 'CivVIItemData': """Gets the names of the items in the item_table""" for item in item_table.values(): if item_name == item.civ_name: @@ -291,9 +288,9 @@ def get_items_by_type(item_type: CivVICheckType, item_table: Dict[str, CivVIItem return [item for item in item_table.values() if item.item_type == item_type] -def get_random_filler_by_rarity(rarity: FillerItemRarity, item_table: Dict[str, CivVIItemData]) -> CivVIItemData: +def get_random_filler_by_rarity(world: 'CivVIWorld', rarity: FillerItemRarity, item_table: Dict[str, CivVIItemData]) -> CivVIItemData: """ Returns a random filler item by rarity """ items = [item for item in get_filler_item_data().values() if item.rarity == rarity] - return items[random.randint(0, len(items) - 1)] + return items[world.random.randint(0, len(items) - 1)] diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index 3d08c37e66ee..1c53a2da87fa 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -1,5 +1,4 @@ -from dataclasses import dataclass -from typing import Any, List, Optional, Dict +from typing import List, Optional, Dict from BaseClasses import Location, LocationProgressType, Region from .Data import get_boosts_data, get_new_civic_prereqs_data, get_new_civics_data, get_new_tech_prereqs_data, get_new_techs_data @@ -106,8 +105,6 @@ def __init__(self, player: int, name: str = '', address: Optional[int] = None, p self.progress_type = LocationProgressType.EXCLUDED - - def generate_flat_location_table() -> Dict[str, CivVILocationData]: """ Generates a flat location table in the following format: @@ -182,8 +179,8 @@ def generate_era_location_table() -> Dict[EraType, Dict[str, CivVILocationData]] id_base += 1 # Goody Huts, defaults to 10 goody huts as location checks (rarely will a player get more than this) for i in range(10): - era_locations[EraType.ERA_ANCIENT.value]["GOODY_HUT_" + str(i+1)] = CivVILocationData( - "GOODY_HUT_" + str(i+1), 0, 0, id_base, EraType.ERA_ANCIENT, CivVICheckType.GOODY) + era_locations[EraType.ERA_ANCIENT.value]["GOODY_HUT_" + str(i + 1)] = CivVILocationData( + "GOODY_HUT_" + str(i + 1), 0, 0, id_base, EraType.ERA_ANCIENT, CivVICheckType.GOODY) id_base += 1 # Boosts boosts = get_boosts_data() diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 0fae53dcc766..1d711d0b2d80 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -1,4 +1,4 @@ -import typing +from typing import TYPE_CHECKING, Dict, List from BaseClasses import CollectionState, LocationProgressType, Region from .Data import get_era_required_items_data, get_progressive_districts_data from .Items import format_item_name, get_item_by_civ_name @@ -7,7 +7,7 @@ from .ProgressiveDistricts import get_flat_progressive_districts from .Options import CivVIOptions -if typing.TYPE_CHECKING: +if TYPE_CHECKING: from . import CivVIWorld from Items import CivVIItemData @@ -19,7 +19,7 @@ def get_required_items_for_era(era: EraType): return era_required_items[era.value] -def get_cumulative_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool = True, item_table: typing.Dict[str, 'CivVIItemData'] = None) -> typing.List['CivVIItemData']: +def get_cumulative_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool = True, item_table: Dict[str, 'CivVIItemData'] = None) -> List['CivVIItemData']: """Gets the specific techs/civics that are required for the specified era as well as all previous eras""" cumulative_prereqs = [] era_required_items = {} @@ -58,7 +58,7 @@ def has_required_progressive_districts(state: CollectionState, era: EraType, pla # Verify we have the correct amount of progressive items all_previous_items = get_cumulative_prereqs_for_era( era, False, item_table) - required_counts: typing.Dict[str, int] = {} + required_counts: Dict[str, int] = {} for key, value in progressive_districts.items(): required_counts[key] = 0 @@ -109,7 +109,7 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): has_goody_huts = options.shuffle_goody_hut_rewards.value has_boosts = options.boostsanity.value - regions: typing.List[Region] = [] + regions: List[Region] = [] for era in EraType: era_region = Region(era.value, player, world.multiworld) era_locations = {location.name: location.code for key, @@ -137,37 +137,37 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): ) world.get_region(EraType.ERA_CLASSICAL.value).connect( - world.get_region(EraType.ERA_MEDIEVAL.value), None, lambda state: has_required_items( + world.get_region(EraType.ERA_MEDIEVAL.value), None, lambda state: has_required_items( state, EraType.ERA_CLASSICAL, player, has_progressive_items, has_progressive_eras) ) world.get_region(EraType.ERA_MEDIEVAL.value).connect( - world.get_region(EraType.ERA_RENAISSANCE.value), None, lambda state: has_required_items( + world.get_region(EraType.ERA_RENAISSANCE.value), None, lambda state: has_required_items( state, EraType.ERA_MEDIEVAL, player, has_progressive_items, has_progressive_eras) ) world.get_region(EraType.ERA_RENAISSANCE.value).connect( - world.get_region(EraType.ERA_INDUSTRIAL.value), None, lambda state: has_required_items( + world.get_region(EraType.ERA_INDUSTRIAL.value), None, lambda state: has_required_items( state, EraType.ERA_RENAISSANCE, player, has_progressive_items, has_progressive_eras) ) world.get_region(EraType.ERA_INDUSTRIAL.value).connect( - world.get_region(EraType.ERA_MODERN.value), None, lambda state: has_required_items( + world.get_region(EraType.ERA_MODERN.value), None, lambda state: has_required_items( state, EraType.ERA_INDUSTRIAL, player, has_progressive_items, has_progressive_eras) ) world.get_region(EraType.ERA_MODERN.value).connect( - world.get_region(EraType.ERA_ATOMIC.value), None, lambda state: has_required_items( + world.get_region(EraType.ERA_ATOMIC.value), None, lambda state: has_required_items( state, EraType.ERA_MODERN, player, has_progressive_items, has_progressive_eras) ) world.get_region(EraType.ERA_ATOMIC.value).connect( - world.get_region(EraType.ERA_INFORMATION.value), None, lambda state: has_required_items( + world.get_region(EraType.ERA_INFORMATION.value), None, lambda state: has_required_items( state, EraType.ERA_ATOMIC, player, has_progressive_items, has_progressive_eras) ) world.get_region(EraType.ERA_INFORMATION.value).connect( - world.get_region(EraType.ERA_FUTURE.value), None, lambda state: has_required_items(state, EraType.ERA_INFORMATION, player, has_progressive_items, has_progressive_eras)) + world.get_region(EraType.ERA_FUTURE.value), None, lambda state: has_required_items(state, EraType.ERA_INFORMATION, player, has_progressive_items, has_progressive_eras)) world.multiworld.completion_condition[player] = lambda state: state.can_reach( EraType.ERA_FUTURE.value, "Region", player) diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py index 048a06241b9f..1c1e9056f3c6 100644 --- a/worlds/civ_6/Rules.py +++ b/worlds/civ_6/Rules.py @@ -1,5 +1,4 @@ -import typing - +from typing import TYPE_CHECKING, List, Dict from BaseClasses import CollectionState from .Items import get_item_by_civ_name from .Data import get_boosts_data @@ -9,11 +8,11 @@ from worlds.generic.Rules import forbid_item, set_rule -if typing.TYPE_CHECKING: +if TYPE_CHECKING: from . import CivVIWorld -def generate_has_required_items_lambda(prereqs: typing.List[str], required_count: int, has_progressive_items: bool, player: int): +def generate_has_required_items_lambda(prereqs: List[str], required_count: int, has_progressive_items: bool, player: int): def has_required_items_lambda(state: CollectionState): return has_required_items(state, prereqs, required_count, has_progressive_items, player) return has_required_items_lambda @@ -35,10 +34,10 @@ def create_boost_rules(world: 'CivVIWorld'): ) -def has_required_items(state: CollectionState, prereqs: typing.List[str], required_count: int, has_progressive_items: bool, player: int): +def has_required_items(state: CollectionState, prereqs: List[str], required_count: int, has_progressive_items: bool, player: int): if has_progressive_items: items = [get_item_by_civ_name(item, state.multiworld.worlds[player].item_table).name for item in convert_items_to_have_progression(prereqs)] - progressive_items: typing.Dict[str, int] = {} + progressive_items: Dict[str, int] = {} count = 0 for item in items: if "Progressive" in item: diff --git a/worlds/civ_6/TunerClient.py b/worlds/civ_6/TunerClient.py index 7478e02e8b03..c75d95c0f5c9 100644 --- a/worlds/civ_6/TunerClient.py +++ b/worlds/civ_6/TunerClient.py @@ -1,6 +1,5 @@ import asyncio from logging import Logger -import select import socket ADDRESS = "127.0.0.1" diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index b8d61247a830..9874b6daa9cd 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -1,13 +1,10 @@ import math import os -import random -from typing import Dict, Optional -import typing +from typing import Dict, Optional, Set from .Data import get_boosts_data from .Rules import create_boost_rules -import Utils from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql from .Enum import CivVICheckType from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity @@ -77,7 +74,7 @@ def __init__(self, multiworld: "MultiWorld", player: int): self.location_table[location.name] = location def get_filler_item_name(self): - return get_random_filler_by_rarity(FillerItemRarity.COMMON, self.item_table).name + return get_random_filler_by_rarity(self, FillerItemRarity.COMMON, self.item_table).name def create_regions(self): create_regions(self, self.options, self.player) @@ -145,7 +142,7 @@ def create_items(self): if total_created >= num_filler_items: break self.multiworld.itempool += [self.create_item( - get_random_filler_by_rarity(rarity, self.item_table).name)] + get_random_filler_by_rarity(self, rarity, self.item_table).name)] total_created += 1 def post_fill(self): @@ -158,7 +155,7 @@ def post_fill(self): ItemClassification.filler: self.options.pre_hint_items.current_key == "all", } - start_location_hints: typing.Set[str] = self.options.start_location_hints.value + start_location_hints: Set[str] = self.options.start_location_hints.value for location_name, location_data in self.location_table.items(): if location_data.location_type != CivVICheckType.CIVIC and location_data.location_type != CivVICheckType.TECH: continue diff --git a/worlds/civ_6/test/TestGoodyHuts.py b/worlds/civ_6/test/TestGoodyHuts.py index cc39d10a7284..8c978dc7ffe5 100644 --- a/worlds/civ_6/test/TestGoodyHuts.py +++ b/worlds/civ_6/test/TestGoodyHuts.py @@ -1,7 +1,7 @@ from typing import Dict from BaseClasses import ItemClassification from Fill import distribute_items_restrictive -from ..Items import FILLER_DISTRIBUTION, FillerItemRarity, get_filler_item_data +from ..Items import FillerItemRarity, get_filler_item_data from . import CivVITestBase From 0980a5d33e617a2bc7338dd9b095382da6c0f9f5 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 6 Aug 2024 14:22:06 -0600 Subject: [PATCH 09/69] Remove .value from options (except starting hint) --- worlds/civ_6/Container.py | 12 ++++++------ worlds/civ_6/Regions.py | 6 +++--- worlds/civ_6/__init__.py | 16 ++++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py index 93db3647e157..466ea8bc60ed 100644 --- a/worlds/civ_6/Container.py +++ b/worlds/civ_6/Container.py @@ -65,7 +65,7 @@ def get_formatted_player_name(world, player) -> str: def get_advisor_type(world: 'CivVIWorld', location: Location) -> str: - if world.options.advisor_show_progression_items.value and location.item.classification == ItemClassification.progression: + if world.options.advisor_show_progression_items and location.item.classification == ItemClassification.progression: return "ADVISOR_PROGRESSIVE" else: return "ADVISOR_GENERIC" @@ -87,13 +87,13 @@ def generate_new_items(world: 'CivVIWorld') -> str: hidden_techs = [] hidden_civics = [] - if world.options.boostsanity.value: + if world.options.boostsanity: boost_techs = [location for location in locations if location.location_type == CivVICheckType.BOOST and location.name.split("_")[1] == "TECH"] boost_civics = [location for location in locations if location.location_type == CivVICheckType.BOOST and location.name.split("_")[1] == "CIVIC"] techs += boost_techs civics += boost_civics - if world.options.hide_item_names.value: + if world.options.hide_item_names: hidden_techs = [tech.name for tech in techs] hidden_civics = [civic.name for civic in civics] @@ -166,7 +166,7 @@ def generate_setup_file(world) -> str: end """ - if world.options.boostsanity.value: + if world.options.boostsanity: setup += f""" -- Init Boosts if Game.GetProperty("BoostsAsChecks") == nil then @@ -182,7 +182,7 @@ def generate_goody_hut_sql(world) -> str: Generates the SQL for the goody huts or an empty string if they are disabled since the mod expects the file to be there """ - if world.options.shuffle_goody_hut_rewards.value: + if world.options.shuffle_goody_hut_rewards: return f""" UPDATE GoodyHutSubTypes SET Description = NULL WHERE GoodyHut NOT IN ('METEOR_GOODIES', 'GOODYHUT_SAILOR_WONDROUS', 'DUMMY_GOODY_BUILDIER') AND Weight > 0; @@ -218,7 +218,7 @@ def generate_update_boosts_sql(world) -> str: Generates the SQL for existing boosts in boostsanity or an empty string if they are disabled since the mod expects the file to be there """ - if world.options.boostsanity.value: + if world.options.boostsanity: return f""" UPDATE Boosts SET TechnologyType = 'BOOST_' || TechnologyType diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 1d711d0b2d80..b85d8f80b64e 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -106,8 +106,8 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): has_progressive_items = options.progression_style.current_key != "none" has_progressive_eras = options.progression_style.current_key == "eras_and_districts" - has_goody_huts = options.shuffle_goody_hut_rewards.value - has_boosts = options.boostsanity.value + has_goody_huts = options.shuffle_goody_hut_rewards + has_boosts = options.boostsanity regions: List[Region] = [] for era in EraType: @@ -172,7 +172,7 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): world.multiworld.completion_condition[player] = lambda state: state.can_reach( EraType.ERA_FUTURE.value, "Region", player) - if world.options.boostsanity.value and not world.options.exclude_missable_boosts.value: + if world.options.boostsanity and not world.options.exclude_missable_boosts: _update_boost_locations_to_include_missable(world) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 9874b6daa9cd..00729838c551 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -80,13 +80,13 @@ def create_regions(self): create_regions(self, self.options, self.player) def set_rules(self) -> None: - if self.options.boostsanity.value: + if self.options.boostsanity: create_boost_rules(self) def create_item(self, name: str) -> Item: item: CivVIItemData = self.item_table[name] classification = item.classification - if self.options.boostsanity.value: + if self.options.boostsanity: if item.civ_name in BOOSTSANITY_PROGRESSION_ITEMS: classification = ItemClassification.progression @@ -126,10 +126,10 @@ def create_items(self): num_filler_items = 0 # Goody items, create 10 by default if options are enabled - if self.options.shuffle_goody_hut_rewards.value: + if self.options.shuffle_goody_hut_rewards: num_filler_items += 10 - if self.options.boostsanity.value: + if self.options.boostsanity: boost_data = get_boosts_data() num_filler_items += len(boost_data) @@ -170,10 +170,10 @@ def post_fill(self): def fill_slot_data(self): return { "progression_style": self.options.progression_style.current_key, - "death_link": self.options.death_link.value, - "research_cost_multiplier": self.options.research_cost_multiplier.value, - "death_link_effect": self.options.death_link_effect.value, - "death_link_effect_percent": self.options.death_link_effect_percent.value, + "death_link": self.options.death_link, + "research_cost_multiplier": self.options.research_cost_multiplier, + "death_link_effect": self.options.death_link_effect, + "death_link_effect_percent": self.options.death_link_effect_percent, } From 3b7cc06a8bbafde89fb21279443d569beaf947e8 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 6 Aug 2024 14:32:06 -0600 Subject: [PATCH 10/69] Minor updates --- worlds/civ_6/Options.py | 11 +++++------ worlds/civ_6/__init__.py | 14 +++++++------- worlds/civ_6/docs/setup_en.md | 12 ++++++------ 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index b2197254e807..3e6d730a7fe1 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -20,17 +20,17 @@ class ProgressionStyle(Choice): class ShuffleGoodyHuts(DefaultOnToggle): """Shuffles the goody hut rewards. Goody huts will only contain junk items and location checks are received sequentially (GOODY_HUT_1, GOODY_HUT_2, etc).""" display_name = "Shuffle Goody Hut Rewards" - default = True class BoostSanity(Toggle): """Boosts for Civics/Techs are location checks. Boosts can now be triggered even if the item has already been researched. If it is dependent upon a unit that is now obsolete, you can click toggle on/off the relevant tech in the tech tree.""" - default = False + display_name = "Boostsanity" + class ExcludeMissableBoosts(Toggle): """If boostsanity is enabled, this will prevent any boosts that are 'missable' from having progression items. Disabling this will potentially require multiple playthroughs to complete the seed.""" - default = True + display_name = "Exclude Missable Boosts" class ResearchCostMultiplier(Choice): @@ -59,13 +59,12 @@ class PreHintItems(Choice): class HideItemNames(Toggle): """Each Tech and Civic Location will have a title of 'Unrevealed' until its prereqs have been researched. Note that hints will still be pre collected if that option is enabled.""" - default = False + display_name = "Hide Item Names" class InGameFlagProgressionItems(DefaultOnToggle): """If enabled, an advisor icon will be added to any location that contains a progression item""" - default = True - + display_name = "Advisor Indicates Progression Items" class DeathLinkEffect(Choice): """What happens when a unit dies. Default is Unit Killed.\n diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 00729838c551..fd49516d782a 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -107,7 +107,7 @@ def create_items(self): # If we're using progressive districts, we need to check if we need to create a different item instead item_to_create = item_name - if self.options.progression_style.current_key != "none": + if self.options.progression_style != "none": item: CivVIItemData = self.item_table[item_name] if item.progression_name != None: item_to_create = self.item_table[item.progression_name].name @@ -116,7 +116,7 @@ def create_items(self): item_to_create)] # Era items - if self.options.progression_style.current_key == "eras_and_districts": + if self.options.progression_style == "eras_and_districts": # Add one less than the total number of eras (start in ancient, don't need to find it) for era in EraType: if era.value == "ERA_ANCIENT": @@ -146,13 +146,13 @@ def create_items(self): total_created += 1 def post_fill(self): - if self.options.pre_hint_items.current_key == "none": + if self.options.pre_hint_items == "none": return show_flags = { - ItemClassification.progression: self.options.pre_hint_items.current_key != "none", - ItemClassification.useful: self.options.pre_hint_items.current_key == "no_junk" or self.options.pre_hint_items.current_key == "all", - ItemClassification.filler: self.options.pre_hint_items.current_key == "all", + ItemClassification.progression: self.options.pre_hint_items != "none", + ItemClassification.useful: self.options.pre_hint_items == "no_junk" or self.options.pre_hint_items == "all", + ItemClassification.filler: self.options.pre_hint_items == "all", } start_location_hints: Set[str] = self.options.start_location_hints.value @@ -169,7 +169,7 @@ def post_fill(self): def fill_slot_data(self): return { - "progression_style": self.options.progression_style.current_key, + "progression_style": self.options.progression_style, "death_link": self.options.death_link, "research_cost_multiplier": self.options.research_cost_multiplier, "death_link_effect": self.options.death_link_effect, diff --git a/worlds/civ_6/docs/setup_en.md b/worlds/civ_6/docs/setup_en.md index 7dc44150f989..6e1e236287ff 100644 --- a/worlds/civ_6/docs/setup_en.md +++ b/worlds/civ_6/docs/setup_en.md @@ -6,21 +6,20 @@ This guide is meant to help you get up and running with Civlization VI in your A The following are required in order to play Civ VI in Archipelago -- Windows OS (Firaxis does not support the necessary tooling for Mac, Linux is yet to bet verified) +- Windows OS (Firaxis does not support the necessary tooling for Mac, or Linux) - Installed [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) v0.4.5 or higher.\ - **Make sure to install the Generator if you intend to generate multiworlds.** - The latest version of the [Civ VI AP Mod](https://github.com/hesto2/civilization_archipelago_mod). -- Tuner setting enabled so the archipelago client can communicate with the game - ## Enabling the tuner + Depending on how you installed Civ 6 you will have to navigate to one of the following: + - `YOUR_USER/Documents/My Games/Sid Meier's Civilization VI/AppOptions.txt` - `YOUR_USER/AppData/Local/Firaxis Games/Sid Meier's Civilization VI/AppOptions.txt` -Once you have located your `AppOptions.txt`, do a search for `Enable FireTuner`. Set `EnableTuner` to `1` instead of `0`. __NOTE__: While this is active, achievments will be disabled. +Once you have located your `AppOptions.txt`, do a search for `Enable FireTuner`. Set `EnableTuner` to `1` instead of `0`. **NOTE**: While this is active, achievments will be disabled. ## Mod Installation @@ -33,6 +32,7 @@ Once you have located your `AppOptions.txt`, do a search for `Enable FireTuner`. 4. Copy the contents of the new folder it generates (it will have the same name as the `.apcivvi` file) into your Civilization VI Archipelago Mod folder. 5. Your finished mod folder should look something like this: + - Civ VI Mods Directory - civilization_archipelago_mod - NewItems.xml @@ -48,4 +48,4 @@ When configuring your game, make sure to start the game in the Ancient Era and l - If you are getting an error: `The remote computer refused the network connection`, or something else related to the client (or tuner) not being able to connect, it likely indicates the tuner is not actually enabled. One simple way to verify that it is enabled is, after completing the setup steps, to go Main Menu -> Options -> Look for an option named "Tuner" and verify it is set to "Enabled" -- If your game gets in a state where someone has sent you items or you have sent locations but these are not correctly replicated to the multiworld, you can run `/resync` from the Civ 6 client. This may take up to a minute depending on how many items there are. \ No newline at end of file +- If your game gets in a state where someone has sent you items or you have sent locations but these are not correctly replicated to the multiworld, you can run `/resync` from the Civ 6 client. This may take up to a minute depending on how many items there are. From 185df3d897ac1e3bf2c94425c5ff5204a6948cf4 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 6 Aug 2024 14:46:42 -0600 Subject: [PATCH 11/69] remove unnecessary property --- worlds/civ_6/Container.py | 2 +- worlds/civ_6/Enum.py | 2 +- worlds/civ_6/Regions.py | 4 ++-- worlds/civ_6/Rules.py | 2 +- worlds/civ_6/__init__.py | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py index 466ea8bc60ed..268ef993b666 100644 --- a/worlds/civ_6/Container.py +++ b/worlds/civ_6/Container.py @@ -157,7 +157,7 @@ def generate_setup_file(world) -> str: Generates the Lua for the setup file. This sets initial variables and state that affect gameplay around Progressive Eras """ setup = "-- Setup" - if world.options.progression_style.current_key == "eras_and_districts": + if world.options.progression_style == "eras_and_districts": setup += f""" -- Init Progressive Era Value if it hasn't been set already if Game.GetProperty("MaxAllowedEra") == nil then diff --git a/worlds/civ_6/Enum.py b/worlds/civ_6/Enum.py index b57a5944ab8d..d814e05a12ec 100644 --- a/worlds/civ_6/Enum.py +++ b/worlds/civ_6/Enum.py @@ -16,4 +16,4 @@ class CivVICheckType(Enum): PROGRESSIVE_DISTRICT = "PROGRESSIVE_DISTRICT" ERA = "ERA" GOODY = "GOODY" - BOOST = "BOOST" \ No newline at end of file + BOOST = "BOOST" diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index b85d8f80b64e..c5eec1808390 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -104,8 +104,8 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): menu = Region("Menu", player, world.multiworld) world.multiworld.regions.append(menu) - has_progressive_items = options.progression_style.current_key != "none" - has_progressive_eras = options.progression_style.current_key == "eras_and_districts" + has_progressive_items = options.progression_style != "none" + has_progressive_eras = options.progression_style == "eras_and_districts" has_goody_huts = options.shuffle_goody_hut_rewards has_boosts = options.boostsanity diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py index 1c1e9056f3c6..abd0fde502ba 100644 --- a/worlds/civ_6/Rules.py +++ b/worlds/civ_6/Rules.py @@ -28,7 +28,7 @@ def create_boost_rules(world: 'CivVIWorld'): if not boost_data or boost_data.PrereqRequiredCount == 0: continue - has_progressive_items = world.options.progression_style.current_key != "none" + has_progressive_items = world.options.progression_style != "none" set_rule(world_location, generate_has_required_items_lambda(boost_data.Prereq, boost_data.PrereqRequiredCount, has_progressive_items, world.player) ) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index fd49516d782a..222895b2931d 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -133,12 +133,12 @@ def create_items(self): boost_data = get_boosts_data() num_filler_items += len(boost_data) - filler_count = {rarity: FILLER_DISTRIBUTION[rarity] * num_filler_items for rarity in FillerItemRarity.__reversed__()} + filler_count = {rarity: math.ceil(FILLER_DISTRIBUTION[rarity] * num_filler_items) for rarity in FillerItemRarity.__reversed__()} min_count = 1 # Add filler items by rarity total_created = 0 for rarity, count in filler_count.items(): - for _ in range(max(min_count, math.ceil(count))): + for _ in range(max(min_count, count)): if total_created >= num_filler_items: break self.multiworld.itempool += [self.create_item( From 878185173ea8d66056f1b5047d8e2ed562d8bb91 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 6 Aug 2024 15:33:24 -0600 Subject: [PATCH 12/69] Cleanup Rules and Region --- worlds/civ_6/Regions.py | 47 +++++++++++++++++----------------------- worlds/civ_6/Rules.py | 31 +++++++++++++------------- worlds/civ_6/__init__.py | 2 +- 3 files changed, 36 insertions(+), 44 deletions(-) diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index c5eec1808390..49a3fd9dbc84 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -12,13 +12,6 @@ from Items import CivVIItemData -def get_required_items_for_era(era: EraType): - """Gets the specific techs/civics that are required for the specified era""" - era_required_items = {} - era_required_items = get_era_required_items_data() - return era_required_items[era.value] - - def get_cumulative_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool = True, item_table: Dict[str, 'CivVIItemData'] = None) -> List['CivVIItemData']: """Gets the specific techs/civics that are required for the specified era as well as all previous eras""" cumulative_prereqs = [] @@ -44,7 +37,7 @@ def get_cumulative_prereqs_for_era(end_era: EraType, exclude_progressive_items: return [get_item_by_civ_name(prereq, item_table) for prereq in cumulative_prereqs] -def has_required_progressive_districts(state: CollectionState, era: EraType, player: int): +def has_required_progressive_districts(state: CollectionState, era: EraType, player: int) -> bool: """ If player has progressive items enabled, it will count how many progressive techs it should have, otherwise return the default array""" progressive_districts = get_progressive_districts_data() @@ -73,7 +66,7 @@ def has_required_progressive_districts(state: CollectionState, era: EraType, pla return True -def has_required_progressive_eras(state: CollectionState, era: EraType, player: int): +def has_required_progressive_eras(state: CollectionState, era: EraType, player: int) -> bool: """Checks, for the given era, how many are required to proceed to the next era. Ancient = 1, Classical = 2, etc. Assumes 2 required for classical and starts from there so as to decrease odds of hard locking without the turns to get the items""" if era == EraType.ERA_FUTURE or era == EraType.ERA_INFORMATION: return True @@ -83,19 +76,21 @@ def has_required_progressive_eras(state: CollectionState, era: EraType, player: return state.has(format_item_name("PROGRESSIVE_ERA"), player, era_index + 2) -def has_required_items(state: CollectionState, era: EraType, player: int, has_progressive_districts: bool, has_progressive_eras: bool): - required_items = False - required_eras = False +def has_required_items(state: CollectionState, era: EraType, world: 'CivVIWorld') -> bool: + player = world.player + has_progressive_districts = world.options.progression_style != "none" + has_progressive_eras = world.options.progression_style == "eras_and_districts" + if has_progressive_districts: required_items = has_required_progressive_districts(state, era, player) else: - era_required_items = [get_item_by_civ_name(item, state.multiworld.worlds[player].item_table).name for item in get_era_required_items_data()[era.value]] + era_required_items = [get_item_by_civ_name(item, world.item_table).name for item in get_era_required_items_data()[era.value]] required_items = state.has_all(era_required_items, player) - if has_progressive_eras: - required_eras = has_required_progressive_eras(state, era, player) - else: - required_eras = True + if not required_items: + return False + + required_eras = has_required_progressive_eras(state, era, player) if has_progressive_eras else True return required_items and required_eras @@ -104,7 +99,6 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): menu = Region("Menu", player, world.multiworld) world.multiworld.regions.append(menu) - has_progressive_items = options.progression_style != "none" has_progressive_eras = options.progression_style == "eras_and_districts" has_goody_huts = options.shuffle_goody_hut_rewards has_boosts = options.boostsanity @@ -115,7 +109,6 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): era_locations = {location.name: location.code for key, location in world.location_by_era[era.value].items()} - # If progressive_eras is not enabled, then era check types from the era_locations if not has_progressive_eras: era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "ERA"} if not has_goody_huts: @@ -133,41 +126,41 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): world.get_region(EraType.ERA_ANCIENT.value).connect( world.get_region(EraType.ERA_CLASSICAL.value), None, lambda state: has_required_items( - state, EraType.ERA_ANCIENT, player, has_progressive_items, has_progressive_eras) + state, EraType.ERA_ANCIENT, world) ) world.get_region(EraType.ERA_CLASSICAL.value).connect( world.get_region(EraType.ERA_MEDIEVAL.value), None, lambda state: has_required_items( - state, EraType.ERA_CLASSICAL, player, has_progressive_items, has_progressive_eras) + state, EraType.ERA_CLASSICAL, world) ) world.get_region(EraType.ERA_MEDIEVAL.value).connect( world.get_region(EraType.ERA_RENAISSANCE.value), None, lambda state: has_required_items( - state, EraType.ERA_MEDIEVAL, player, has_progressive_items, has_progressive_eras) + state, EraType.ERA_MEDIEVAL, world) ) world.get_region(EraType.ERA_RENAISSANCE.value).connect( world.get_region(EraType.ERA_INDUSTRIAL.value), None, lambda state: has_required_items( - state, EraType.ERA_RENAISSANCE, player, has_progressive_items, has_progressive_eras) + state, EraType.ERA_RENAISSANCE, world) ) world.get_region(EraType.ERA_INDUSTRIAL.value).connect( world.get_region(EraType.ERA_MODERN.value), None, lambda state: has_required_items( - state, EraType.ERA_INDUSTRIAL, player, has_progressive_items, has_progressive_eras) + state, EraType.ERA_INDUSTRIAL, world) ) world.get_region(EraType.ERA_MODERN.value).connect( world.get_region(EraType.ERA_ATOMIC.value), None, lambda state: has_required_items( - state, EraType.ERA_MODERN, player, has_progressive_items, has_progressive_eras) + state, EraType.ERA_MODERN, world) ) world.get_region(EraType.ERA_ATOMIC.value).connect( world.get_region(EraType.ERA_INFORMATION.value), None, lambda state: has_required_items( - state, EraType.ERA_ATOMIC, player, has_progressive_items, has_progressive_eras) + state, EraType.ERA_ATOMIC, world) ) world.get_region(EraType.ERA_INFORMATION.value).connect( - world.get_region(EraType.ERA_FUTURE.value), None, lambda state: has_required_items(state, EraType.ERA_INFORMATION, player, has_progressive_items, has_progressive_eras)) + world.get_region(EraType.ERA_FUTURE.value), None, lambda state: has_required_items(state, EraType.ERA_INFORMATION, world)) world.multiworld.completion_condition[player] = lambda state: state.can_reach( EraType.ERA_FUTURE.value, "Region", player) diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py index abd0fde502ba..bdcd688d8446 100644 --- a/worlds/civ_6/Rules.py +++ b/worlds/civ_6/Rules.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, List, Dict +from typing import TYPE_CHECKING, Callable, List, Dict from BaseClasses import CollectionState from .Items import get_item_by_civ_name from .Data import get_boosts_data @@ -12,9 +12,9 @@ from . import CivVIWorld -def generate_has_required_items_lambda(prereqs: List[str], required_count: int, has_progressive_items: bool, player: int): +def generate_has_required_items_lambda(prereqs: List[str], required_count: int, player: int) -> Callable[[CollectionState, int], bool]: def has_required_items_lambda(state: CollectionState): - return has_required_items(state, prereqs, required_count, has_progressive_items, player) + return has_required_items(state, prereqs, required_count, player) return has_required_items_lambda @@ -28,17 +28,18 @@ def create_boost_rules(world: 'CivVIWorld'): if not boost_data or boost_data.PrereqRequiredCount == 0: continue - has_progressive_items = world.options.progression_style != "none" set_rule(world_location, - generate_has_required_items_lambda(boost_data.Prereq, boost_data.PrereqRequiredCount, has_progressive_items, world.player) + generate_has_required_items_lambda(boost_data.Prereq, boost_data.PrereqRequiredCount, world.player) ) -def has_required_items(state: CollectionState, prereqs: List[str], required_count: int, has_progressive_items: bool, player: int): +def has_required_items(state: CollectionState, prereqs: List[str], required_count: int, player: int) -> bool: + world: 'CivVIWorld' = state.multiworld.worlds[player] + has_progressive_items = world.options.progression_style != "none" if has_progressive_items: - items = [get_item_by_civ_name(item, state.multiworld.worlds[player].item_table).name for item in convert_items_to_have_progression(prereqs)] - progressive_items: Dict[str, int] = {} count = 0 + items = [get_item_by_civ_name(item, world.item_table).name for item in convert_items_to_have_progression(prereqs)] + progressive_items: Dict[str, int] = {} for item in items: if "Progressive" in item: if not progressive_items.get(item): @@ -49,14 +50,12 @@ def has_required_items(state: CollectionState, prereqs: List[str], required_coun count += 1 for item, required_progressive_item_count in progressive_items.items(): - if state.count(item, player) >= required_progressive_item_count: + if state.has(item, player, required_progressive_item_count): count += required_progressive_item_count - if count > 0: - pass return count >= required_count else: - count = 0 - for prereq in prereqs: - if state.has(get_item_by_civ_name(prereq, state.multiworld.worlds[player].item_table).name, player): - count += 1 - return count >= required_count + total = state.count_from_list_unique( + [ + get_item_by_civ_name(prereq, state.multiworld.worlds[player].item_table).name for prereq in prereqs + ], player) + return total >= required_count diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 222895b2931d..17e08b31f238 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -73,7 +73,7 @@ def __init__(self, multiworld: "MultiWorld", player: int): for _item_name, location in locations.items(): self.location_table[location.name] = location - def get_filler_item_name(self): + def get_filler_item_name(self) -> str: return get_random_filler_by_rarity(self, FillerItemRarity.COMMON, self.item_table).name def create_regions(self): From a670097cd50f3cc88e328b005440ae2de9b7aa2d Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 20 Aug 2024 20:09:12 -0600 Subject: [PATCH 13/69] Fix output file generation --- worlds/civ_6/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 17e08b31f238..4a6b366d7b20 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -178,9 +178,9 @@ def fill_slot_data(self): } def generate_output(self, output_directory: str): - mod_name = f"AP-{self.multiworld.get_file_safe_player_name(self.player)}" + mod_name = self.multiworld.get_out_file_name_base(self.player) mod_dir = os.path.join( - output_directory, mod_name + "_" + self.multiworld.seed_name) + output_directory, mod_name) mod_files = { f"NewItems.xml": generate_new_items(self), f"InitOptions.lua": generate_setup_file(self), From 2c490ef6375c21876541a9cb83d561c1541055d6 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 20 Aug 2024 20:54:12 -0600 Subject: [PATCH 14/69] Implement feedback --- worlds/civ_6/Civ6Client.py | 14 +++++++------- worlds/civ_6/CivVIInterface.py | 6 +++--- worlds/civ_6/Data.py | 10 +++++----- worlds/civ_6/Locations.py | 14 +++++++------- worlds/civ_6/Options.py | 15 +++++++++++++-- worlds/civ_6/Rules.py | 10 +--------- worlds/civ_6/TunerClient.py | 10 +++++----- worlds/civ_6/__init__.py | 10 +++++----- 8 files changed, 46 insertions(+), 43 deletions(-) diff --git a/worlds/civ_6/Civ6Client.py b/worlds/civ_6/Civ6Client.py index f1053a989b5d..cb222c2869dc 100644 --- a/worlds/civ_6/Civ6Client.py +++ b/worlds/civ_6/Civ6Client.py @@ -29,7 +29,7 @@ def _cmd_deathlink(self): Utils.async_start(self.ctx.update_death_link( self.ctx.death_link_enabled), name="Update Deathlink") self.ctx.logger.info( - f"Deathlink is now {'enabled' if self.ctx.death_link_enabled else 'disabled'}") + f"Deathlink is now {"enabled" if self.ctx.death_link_enabled else "disabled"}") def _cmd_resync(self): """Resends all items to client, and has client resend all locations to server. This can take up to a minute if the player has received a lot of items""" @@ -101,7 +101,7 @@ def on_deathlink(self, data: Utils.Dict[str, Utils.Any]) -> None: if text: message = text else: - message = f"Received from {data['source']}" + message = f"Received from {data["source"]}" self.death_link_message = message self.received_death_link = True @@ -284,7 +284,7 @@ def main(connect=None, password=None, name=None): async def _main(connect, password, name): parser = get_base_parser() - parser.add_argument('apcivvi_file', default="", type=str, nargs='?', help="Path to apcivvi file") + parser.add_argument("apcivvi_file", default="", type=str, nargs="?", help="Path to apcivvi file") args = parser.parse_args() ctx = CivVIContext(connect, password, args.apcivvi_file) @@ -295,7 +295,7 @@ async def _main(connect, password, name): if not os.path.exists(target_path): os.makedirs(target_path, exist_ok=True) logger.info("Extracting mod files to %s", target_path) - with zipfile.ZipFile(args.apcivvi_file, 'r') as zip_ref: + with zipfile.ZipFile(args.apcivvi_file, "r") as zip_ref: for member in zip_ref.namelist(): zip_ref.extract(member, target_path) @@ -327,10 +327,10 @@ async def _main(connect, password, name): def debug_main(): parser = get_base_parser() - parser.add_argument('apcivvi_file', default="", type=str, nargs='?', help="Path to apcivvi file") - parser.add_argument('--name', default=None, + parser.add_argument("apcivvi_file", default="", type=str, nargs="?", help="Path to apcivvi file") + parser.add_argument("--name", default=None, help="Slot Name to connect as.") - parser.add_argument('--debug', default=None, + parser.add_argument("--debug", default=None, help="debug mode, additional logging") args = parser.parse_args() if args.debug: diff --git a/worlds/civ_6/CivVIInterface.py b/worlds/civ_6/CivVIInterface.py index 45cf15d09bbc..aba4032bf083 100644 --- a/worlds/civ_6/CivVIInterface.py +++ b/worlds/civ_6/CivVIInterface.py @@ -5,13 +5,13 @@ from .Items import CivVIItemData from .TunerClient import TunerClient, TunerConnectionException, TunerTimeoutException + class ConnectionState(Enum): DISCONNECTED = 0 IN_GAME = 1 IN_MENU = 2 - class CivVIInterface: logger: Logger tuner: TunerClient @@ -28,7 +28,7 @@ async def is_in_game(self) -> ConnectionState: if result == "false": return ConnectionState.IN_MENU self.last_error = None - return ConnectionState.IN_GAME + return ConnectionState.IN_GAME except TunerTimeoutException: self.print_connection_error( "Not connected to game, waiting for connection to be available") @@ -55,7 +55,7 @@ async def give_item_to_player(self, item: CivVIItemData, sender: str = "", amoun if isinstance(item.civ_vi_id, str): item_id = f'"{item.civ_vi_id}"' else: - item_id = item.civ_vi_id + item_id = item.civ_vi_id command = f"HandleReceiveItem({item_id}, \"{item.name}\", \"{item.item_type.value}\", \"{sender}\", {amount})" await self.tuner.send_game_command(command) diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index a27893193301..8e3eb40f5e27 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -33,11 +33,11 @@ def get_boosts_data() -> List[CivVIBoostData]: boosts = [] for boost in boosts_json: boosts.append(CivVIBoostData( - Type=boost['Type'], - EraType=boost['EraType'], - Prereq=boost['Prereq'], - PrereqRequiredCount=boost['PrereqRequiredCount'], - Classification=boost['Classification'] + Type=boost["Type"], + EraType=boost["EraType"], + Prereq=boost["Prereq"], + PrereqRequiredCount=boost["PrereqRequiredCount"], + Classification=boost["Classification"] )) return boosts diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index 1c53a2da87fa..2063bb342375 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -78,7 +78,7 @@ class CivVILocation(Location): game: str = "Civilization VI" location_type: CivVICheckType - def __init__(self, player: int, name: str = '', address: Optional[int] = None, parent: Optional[Region] = None): + def __init__(self, player: int, name: str = "", address: Optional[int] = None, parent: Optional[Region] = None): super().__init__(player, name, address, parent) if name.split("_")[0] == "TECH": self.location_type = CivVICheckType.TECH @@ -142,28 +142,28 @@ def generate_era_location_table() -> Dict[EraType, Dict[str, CivVILocationData]] id_base = 0 # Techs for data in new_techs: - era_type = data['EraType'] + era_type = data["EraType"] if era_type not in era_locations: era_locations[era_type] = {} prereq_data = [ - item for item in new_tech_prereqs if item['Technology'] == data['Type']] + item for item in new_tech_prereqs if item["Technology"] == data["Type"]] era_locations[era_type][data["Type"]] = CivVILocationData( - data["Type"], data['Cost'], data['UITreeRow'], id_base, era_type, CivVICheckType.TECH, prereq_data) + data["Type"], data["Cost"], data["UITreeRow"], id_base, era_type, CivVICheckType.TECH, prereq_data) id_base += 1 # Civics new_civic_prereqs = get_new_civic_prereqs_data() new_civics = get_new_civics_data() for data in new_civics: - era_type = data['EraType'] + era_type = data["EraType"] if era_type not in era_locations: era_locations[era_type] = {} prereq_data = [ - item for item in new_civic_prereqs if item['Civic'] == data['Type']] + item for item in new_civic_prereqs if item["Civic"] == data["Type"]] era_locations[era_type][data["Type"]] = CivVILocationData( - data["Type"], data['Cost'], data['UITreeRow'], id_base, era_type, CivVICheckType.CIVIC, prereq_data) + data["Type"], data["Cost"], data["UITreeRow"], id_base, era_type, CivVICheckType.CIVIC, prereq_data) id_base += 1 # Eras diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 3e6d730a7fe1..2789bd664137 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from Options import Choice, DeathLink, DefaultOnToggle, PerGameCommonOptions, Range, Toggle +from Options import Choice, DeathLink, DefaultOnToggle, PerGameCommonOptions, Range, StartInventory, Toggle class ProgressionStyle(Choice): @@ -27,7 +27,6 @@ class BoostSanity(Toggle): display_name = "Boostsanity" - class ExcludeMissableBoosts(Toggle): """If boostsanity is enabled, this will prevent any boosts that are 'missable' from having progression items. Disabling this will potentially require multiple playthroughs to complete the seed.""" display_name = "Exclude Missable Boosts" @@ -66,6 +65,7 @@ class InGameFlagProgressionItems(DefaultOnToggle): """If enabled, an advisor icon will be added to any location that contains a progression item""" display_name = "Advisor Indicates Progression Items" + class DeathLinkEffect(Choice): """What happens when a unit dies. Default is Unit Killed.\n Faith, and Gold will be decreased by the amount specified in 'Death Link Effect Percent'. \n @@ -89,8 +89,19 @@ class DeathLinkEffectPercent(Range): range_end = 100 +class StartInventoryPool(StartInventory): + """Start with these items and don't place them in the world. + + The game decides what the replacement items will be. + """ + verify_item_name = True + display_name = "Start Inventory from Pool" + rich_text_doc = True + + @dataclass class CivVIOptions(PerGameCommonOptions): + start_inventory_from_pool: StartInventoryPool progression_style: ProgressionStyle shuffle_goody_hut_rewards: ShuffleGoodyHuts boostsanity: BoostSanity diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py index bdcd688d8446..b51e18776987 100644 --- a/worlds/civ_6/Rules.py +++ b/worlds/civ_6/Rules.py @@ -12,12 +12,6 @@ from . import CivVIWorld -def generate_has_required_items_lambda(prereqs: List[str], required_count: int, player: int) -> Callable[[CollectionState, int], bool]: - def has_required_items_lambda(state: CollectionState): - return has_required_items(state, prereqs, required_count, player) - return has_required_items_lambda - - def create_boost_rules(world: 'CivVIWorld'): boost_data_list = get_boosts_data() boost_locations = [location for location in world.location_table.values() if location.location_type == CivVICheckType.BOOST] @@ -28,9 +22,7 @@ def create_boost_rules(world: 'CivVIWorld'): if not boost_data or boost_data.PrereqRequiredCount == 0: continue - set_rule(world_location, - generate_has_required_items_lambda(boost_data.Prereq, boost_data.PrereqRequiredCount, world.player) - ) + set_rule(world_location, lambda state, prereqs=boost_data.Prereq, required_count=boost_data.PrereqRequiredCount: has_required_items(state, prereqs, required_count, world.player)) def has_required_items(state: CollectionState, prereqs: List[str], required_count: int, player: int) -> bool: diff --git a/worlds/civ_6/TunerClient.py b/worlds/civ_6/TunerClient.py index c75d95c0f5c9..bf08d1c9aeba 100644 --- a/worlds/civ_6/TunerClient.py +++ b/worlds/civ_6/TunerClient.py @@ -10,7 +10,7 @@ def decode_mixed_string(data): - return ''.join(chr(b) if 32 <= b < 127 else '?' for b in data) + return "".join(chr(b) if 32 <= b < 127 else "?" for b in data) class TunerException(Exception): @@ -57,14 +57,14 @@ async def send_command(self, command_string: str, size: int = 64): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setblocking(False) - b_command_string = command_string.encode('utf-8') + b_command_string = command_string.encode("utf-8") # Send data to the server command_prefix = b"CMD:0:" delimiter = b"\x00" full_command = b_command_string message = command_prefix + full_command + delimiter - message_length = len(message).to_bytes(1, byteorder='little') + message_length = len(message).to_bytes(1, byteorder="little") # game expects this to be added before any command that is sent, indicates payload size message_header = message_length + b"\x00\x00\x00\x03\x00\x00\x00" @@ -84,10 +84,10 @@ async def send_command(self, command_string: str, size: int = 64): return self.__parse_response(response) except socket.timeout: - self.logger.debug('Timeout occurred while receiving data') + self.logger.debug("Timeout occurred while receiving data") raise TunerTimeoutException() except Exception as e: - self.logger.debug(f'Error occurred while receiving data: {str(e)}') + self.logger.debug(f"Error occurred while receiving data: {str(e)}") # check if No connection could be made is present in the error message connection_errors = [ "The remote computer refused the network connection", diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 4a6b366d7b20..a9cb2dd5cd66 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -1,6 +1,6 @@ import math import os -from typing import Dict, Optional, Set +from typing import Any, Dict, Optional, Set from .Data import get_boosts_data @@ -76,7 +76,7 @@ def __init__(self, multiworld: "MultiWorld", player: int): def get_filler_item_name(self) -> str: return get_random_filler_by_rarity(self, FillerItemRarity.COMMON, self.item_table).name - def create_regions(self): + def create_regions(self) -> None: create_regions(self, self.options, self.player) def set_rules(self) -> None: @@ -92,7 +92,7 @@ def create_item(self, name: str) -> Item: return CivVIItem(item, self.player, classification) - def create_items(self): + def create_items(self) -> None: progressive_era_item = None for item_name, data in self.item_table.items(): # Don't add progressive items to the itempool here @@ -145,7 +145,7 @@ def create_items(self): get_random_filler_by_rarity(self, rarity, self.item_table).name)] total_created += 1 - def post_fill(self): + def post_fill(self) -> None: if self.options.pre_hint_items == "none": return @@ -167,7 +167,7 @@ def post_fill(self): start_location_hints.add(location_name) - def fill_slot_data(self): + def fill_slot_data(self) -> Dict[str, Any]: return { "progression_style": self.options.progression_style, "death_link": self.options.death_link, From 2aeb463a538199ba9889f2b5b3483ec08eeeb54d Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 27 Aug 2024 23:11:41 -0600 Subject: [PATCH 15/69] Remove 'AP' tag and fix issue with format strings and using same quotes --- worlds/civ_6/Civ6Client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/worlds/civ_6/Civ6Client.py b/worlds/civ_6/Civ6Client.py index cb222c2869dc..a84095bf2549 100644 --- a/worlds/civ_6/Civ6Client.py +++ b/worlds/civ_6/Civ6Client.py @@ -28,8 +28,7 @@ def _cmd_deathlink(self): self.ctx.death_link_just_changed = True Utils.async_start(self.ctx.update_death_link( self.ctx.death_link_enabled), name="Update Deathlink") - self.ctx.logger.info( - f"Deathlink is now {"enabled" if self.ctx.death_link_enabled else "disabled"}") + self.ctx.logger.info(f"Deathlink is now {'enabled' if self.ctx.death_link_enabled else 'disabled'}") def _cmd_resync(self): """Resends all items to client, and has client resend all locations to server. This can take up to a minute if the player has received a lot of items""" @@ -101,7 +100,7 @@ def on_deathlink(self, data: Utils.Dict[str, Utils.Any]) -> None: if text: message = text else: - message = f"Received from {data["source"]}" + message = f"Received from {data['source']}" self.death_link_message = message self.received_death_link = True @@ -109,6 +108,7 @@ async def server_auth(self, password_requested: bool = False): if password_requested and not self.password: await super(CivVIContext, self).server_auth(password_requested) await self.get_username() + self.tags = set() await self.send_connect() def run_gui(self): From 0f8aee8e054206a6ca45fe1b242691add25df82e Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 3 Sep 2024 16:09:31 -0600 Subject: [PATCH 16/69] Update worlds/civ_6/__init__.py Co-authored-by: Scipio Wright --- worlds/civ_6/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index a9cb2dd5cd66..e953b74fb8e2 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -41,7 +41,7 @@ class CivVIWeb(WebWorld): class CivVIWorld(World): """ - Civilization VI is a turn-based strategy video game in which one or more players compete alongside computer-controlled AI opponents to grow their individual civilization from a small tribe to control the entire planet across several periods of development. + Civilization VI is a turn-based strategy video game in which one or more players compete alongside computer-controlled opponents to grow their individual civilization from a small tribe to control the entire planet across several periods of development. """ game: str = "Civilization VI" From 37e8413360dabd690f616f5673d2563a989757f2 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 3 Sep 2024 16:13:58 -0600 Subject: [PATCH 17/69] Minor docs changes --- worlds/civ_6/Options.py | 16 +++------------- worlds/civ_6/requirements.txt | 0 2 files changed, 3 insertions(+), 13 deletions(-) delete mode 100644 worlds/civ_6/requirements.txt diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 2789bd664137..7f802ed2b2a0 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from Options import Choice, DeathLink, DefaultOnToggle, PerGameCommonOptions, Range, StartInventory, Toggle +from Options import Choice, DeathLink, DefaultOnToggle, PerGameCommonOptions, Range, StartInventory, StartInventoryPool, Toggle class ProgressionStyle(Choice): @@ -28,7 +28,7 @@ class BoostSanity(Toggle): class ExcludeMissableBoosts(Toggle): - """If boostsanity is enabled, this will prevent any boosts that are 'missable' from having progression items. Disabling this will potentially require multiple playthroughs to complete the seed.""" + """If Boostsanity is enabled, this will prevent any boosts that are 'missable' from having progression items. Disabling this will potentially require multiple playthroughs to complete the seed.""" display_name = "Exclude Missable Boosts" @@ -69,7 +69,7 @@ class InGameFlagProgressionItems(DefaultOnToggle): class DeathLinkEffect(Choice): """What happens when a unit dies. Default is Unit Killed.\n Faith, and Gold will be decreased by the amount specified in 'Death Link Effect Percent'. \n - Era score is decrased by 1.\n + Era score is decreased by 1.\n Any will select any of these options any time a death link is received.""" display_name = "Death Link Effect" option_unit_killed = 0 @@ -89,16 +89,6 @@ class DeathLinkEffectPercent(Range): range_end = 100 -class StartInventoryPool(StartInventory): - """Start with these items and don't place them in the world. - - The game decides what the replacement items will be. - """ - verify_item_name = True - display_name = "Start Inventory from Pool" - rich_text_doc = True - - @dataclass class CivVIOptions(PerGameCommonOptions): start_inventory_from_pool: StartInventoryPool diff --git a/worlds/civ_6/requirements.txt b/worlds/civ_6/requirements.txt deleted file mode 100644 index e69de29bb2d1..000000000000 From 28d71796311489ffe38eb3f89a0c5feaa5f9cad5 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 3 Sep 2024 19:20:36 -0600 Subject: [PATCH 18/69] minor updates --- worlds/civ_6/Options.py | 14 +++++------ worlds/civ_6/README.md | 53 ---------------------------------------- worlds/civ_6/__init__.py | 4 +-- 3 files changed, 9 insertions(+), 62 deletions(-) delete mode 100644 worlds/civ_6/README.md diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 7f802ed2b2a0..b0423ba77a25 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -42,13 +42,13 @@ class ResearchCostMultiplier(Choice): class PreHintItems(Choice): - """Controls if/what items in the tech/civics trees are pre hinted for the multiworld.\n - All : All items in the tech & civics trees are pre hinted.\n - Progression items: Only locations in the trees containing progression items are pre hinted.\n - No Junk: Pre hint the progression and useful items.\n - None: No items are pre hinted. + """Controls if/what items in the tech/civics trees are pre-hinted for the multiworld.\n + All : All items in the tech & civics trees are pre-hinted.\n + Progression items: Only locations in the trees containing progression items are pre-hinted.\n + No Junk: pre-hint the progression and useful items.\n + None: No items are pre-hinted. """ - display_name = "Tech/Civic Tree Pre Hinted Items" + display_name = "Tech/Civic Tree pre-hinted Items" option_all = 0 option_progression_items = 1 option_no_junk = 2 @@ -57,7 +57,7 @@ class PreHintItems(Choice): class HideItemNames(Toggle): - """Each Tech and Civic Location will have a title of 'Unrevealed' until its prereqs have been researched. Note that hints will still be pre collected if that option is enabled.""" + """Each Tech and Civic Location will have a title of 'Unrevealed' until its prereqs have been researched. Note that hints will still be precollected if that option is enabled.""" display_name = "Hide Item Names" diff --git a/worlds/civ_6/README.md b/worlds/civ_6/README.md deleted file mode 100644 index d510583d70e0..000000000000 --- a/worlds/civ_6/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# Civlization 6 Archipelago - -## Setup Guide -For setup instructions go [here](./docs/setup_en.md). - -## What does randomization do to this game? - -In Civilization VI, the tech and civic trees are both shuffled. This presents some interesting ways to play the game in a non-standard way. If you are feeling adventurous, you can enable the `boostsanity` option in order to really change up the way you normally would play a Civ game. Details on the option can be found [here](./docs/boostsanity.md) - -There are a few changes that the Archipelago mod introduces in order to make this playable/fun. These are detailed in the __FAQ__ section below. - -## What is the goal of Civilization VI when randomized? -The goal of randomized Civlization VI remains the same. Pursue any victory type you have enabled in your game settings, the one you normally go for may or may not be feasible based on how things have been changed up! - -## Which items can be in another player's world? -All technologies and civics can be found in another player's world. - -## What does another world's item look like in Civilization VI? -Each item from another world is represented as a researchable tech/civic in your normal tech/civic trees. - -## When the player receives an item, what happens? -A short period after receiving an item, you will get a notification indicating you have discovered the relevant tech/civic. You will also get the regular popup that details what the given item has unlocked for you. - -## FAQs -- Do I need the DLC to play this? - - Yes, you need both Rise & Fall and Gathering Storm. If there is enough interest then I can eventually add support for Archipellago runs that don't require both expansions. - -- Does this work with Multiplayer? - - It does not and, despite my best efforts, probably won't until there's a new way for external programs to be able to interact with the game. - -- Does my mod that reskins Barbarians as various Pro Wrestlers work with this?? - - Only one way to find out! Any mods that modify techs/civics will most likely cause issues, though. - -- "Help! I can't see any of the items that have been sent to me!" - - Both trees by default will show you the researchable Archipelago locations. To view the normal tree, you can click "Toggle Archipelago Tree" on the top left corner of the tree view. - -- "Oh no! I received the Machinery tech and now instead of getting an Archer next turn, I have to wait an additional 10 turns to get a Crossbowman!" - - Vanilla prevents you from building units of the same class from an earlier tech level after you have researched a later variant. For example, this could be problematic if someone unlocks Crossbowmen for you right out the gate since you won't be able to make Archers (which have a much lower production cost). - - - Solution: You can now go in to the tech tree, click "Toggle Archipelago Tree" to view your unlocked techs, and then can click any tech you have unlocked to toggle whether it is currently active or not. __NOTE__: This is an experimental feature and may yield some unexpected behaviors. Please DM `@Hesto2` on Discord if you run into any issues. - -- I enabled `progressive districts` but I have no idea techs/civics what items are locked behind progression now! - - Any technology or civic that grants you a new building in a district (or grants you the district itself) is now locked behind a progressive item. For example, `PROGRESSIVE_CAMPUS` would give you these items in the following order: - 1. `TECH_WRITING` - 2. `TECH_EDUCATION` - 3. `TECH_CHEMISTRY` - - If you want to see the details around each item, you can review [this file](./data/progressive_districts.json) - -- "How does DeathLink work? Am I going to have to start a new game every time one of my friends dies??" - - Heavens no, my fellow Archipelago appreciator. When configuring your Archipelago options for Civilization on the options page, there are several choices available for you to fine tune the way you'd like to be punished for the follies of your friends. These include: Having a random unit destroyed, losing a percentage of gold or faith, or even losing a point on your era score. If you can't make up your mind, you can elect to have any of them be selected every time a death link is sent your way. - - - In the event you lose one of your units in combat (this means captured units don't count), then you will send a death link event to the rest of your friends. - diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index e953b74fb8e2..3586fec4b5dd 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -37,6 +37,7 @@ class CivVIWeb(WebWorld): "setup/en", ["hesto2"] )] + theme = "ocean" class CivVIWorld(World): @@ -44,7 +45,7 @@ class CivVIWorld(World): Civilization VI is a turn-based strategy video game in which one or more players compete alongside computer-controlled opponents to grow their individual civilization from a small tribe to control the entire planet across several periods of development. """ - game: str = "Civilization VI" + game = "Civilization VI" topology_present = False options_dataclass = CivVIOptions options: CivVIOptions @@ -59,7 +60,6 @@ class CivVIWorld(World): item_table: Dict[str, CivVIItemData] = {} location_by_era: Dict[EraType, Dict[str, CivVILocationData]] - data_version = 1 required_client_version = (0, 4, 5) def __init__(self, multiworld: "MultiWorld", player: int): From 821d8fc650e3f6904b5d13d84342d8642d6b3fd5 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 3 Sep 2024 19:29:53 -0600 Subject: [PATCH 19/69] Small rework of create items --- worlds/civ_6/__init__.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 3586fec4b5dd..2a464710ea4f 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -95,17 +95,11 @@ def create_item(self, name: str) -> Item: def create_items(self) -> None: progressive_era_item = None for item_name, data in self.item_table.items(): - # Don't add progressive items to the itempool here - if data.item_type == CivVICheckType.PROGRESSIVE_DISTRICT: - continue - if data.item_type == CivVICheckType.ERA: - # Don't add era items in this way - progressive_era_item = data - continue - if data.item_type == CivVICheckType.GOODY: + # These item types are handled individually + if data.item_type in [CivVICheckType.PROGRESSIVE_DISTRICT, CivVICheckType.ERA, CivVICheckType.GOODY]: continue - # If we're using progressive districts, we need to check if we need to create a different item instead + # If we're using progressive districts, we need to check if we need to create a different item instead item_to_create = item_name if self.options.progression_style != "none": item: CivVIItemData = self.item_table[item_name] @@ -121,8 +115,7 @@ def create_items(self) -> None: for era in EraType: if era.value == "ERA_ANCIENT": continue - self.multiworld.itempool += [self.create_item( - progressive_era_item.name)] + self.multiworld.itempool += [self.create_item(self.item_table.get("Progressive Era").name)] num_filler_items = 0 # Goody items, create 10 by default if options are enabled From 01d6400e253e99255fe86832220508ca75196afd Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 3 Sep 2024 20:20:09 -0600 Subject: [PATCH 20/69] Minor updates --- worlds/civ_6/Data.py | 2 -- worlds/civ_6/Items.py | 18 +++++++++--------- worlds/civ_6/Regions.py | 3 +-- worlds/civ_6/Rules.py | 6 +++--- worlds/civ_6/__init__.py | 9 ++++----- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index 8e3eb40f5e27..ccc41428206c 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -17,8 +17,6 @@ def _get_data(key: str): return _cache[key] -def get_boosts_data(): - return _get_data("boosts") @dataclass class CivVIBoostData(): Type: str diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index b7d801fa1b04..9de0ea7b8fb2 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Dict, List, Optional, TYPE_CHECKING, List +from typing import Dict, Optional, TYPE_CHECKING, List from BaseClasses import Item, ItemClassification from .Data import get_era_required_items_data, get_existing_civics_data, get_existing_techs_data, get_goody_hut_rewards_data, get_progressive_districts_data from .Enum import CivVICheckType @@ -122,7 +122,7 @@ def format_item_name(name: str) -> str: return " ".join([part.capitalize() for part in name_parts]) -def get_item_by_civ_name(item_name: List[str], item_table: Dict[str, 'CivVIItemData']) -> 'CivVIItemData': +def get_item_by_civ_name(item_name: str, item_table: Dict[str, 'CivVIItemData']) -> 'CivVIItemData': """Gets the names of the items in the item_table""" for item in item_table.values(): if item_name == item.civ_name: @@ -131,7 +131,7 @@ def get_item_by_civ_name(item_name: List[str], item_table: Dict[str, 'CivVIItemD raise Exception(f"Item {item_name} not found in item_table") -def _generate_tech_items(id_base: int, required_items: List[str], progressive_items: Dict[str, str]) -> List[CivVIItemData]: +def _generate_tech_items(id_base: int, required_items: List[str], progressive_items: Dict[str, str]) -> Dict[str, CivVIItemData]: # Generate Techs existing_techs = get_existing_techs_data() tech_table = {} @@ -164,7 +164,7 @@ def _generate_tech_items(id_base: int, required_items: List[str], progressive_it return tech_table -def _generate_civics_items(id_base: int, required_items: List[str], progressive_items: Dict[str, str]) -> List[CivVIItemData]: +def _generate_civics_items(id_base: int, required_items: List[str], progressive_items: Dict[str, str]) -> Dict[str, CivVIItemData]: civic_id = 0 civic_table = {} existing_civics = get_existing_civics_data() @@ -198,7 +198,7 @@ def _generate_civics_items(id_base: int, required_items: List[str], progressive_ return civic_table -def _generate_progressive_district_items(id_base: int) -> List[CivVIItemData]: +def _generate_progressive_district_items(id_base: int) -> Dict[str, CivVIItemData]: progressive_table = {} progressive_id_base = 0 progressive_items = get_progressive_districts_data() @@ -221,7 +221,7 @@ def _generate_progressive_district_items(id_base: int) -> List[CivVIItemData]: return progressive_table -def _generate_progressive_era_items(id_base: int) -> List[CivVIItemData]: +def _generate_progressive_era_items(id_base: int) -> Dict[str, CivVIItemData]: """Generates the single progressive district item""" era_table = {} # Generate progressive eras @@ -239,7 +239,7 @@ def _generate_progressive_era_items(id_base: int) -> List[CivVIItemData]: return era_table -def _generate_goody_hut_items(id_base: int) -> List[CivVIItemData]: +def _generate_goody_hut_items(id_base: int) -> Dict[str, CivVIItemData]: # Generate goody hut items goody_huts = get_filler_item_data() goody_table = {} @@ -281,14 +281,14 @@ def get_id_base(): return item_table -def get_items_by_type(item_type: CivVICheckType, item_table: Dict[str, CivVIItemData]) -> List[CivVIItemData]: +def get_items_by_type(item_type: CivVICheckType, item_table: Dict[str, CivVIItemData]) -> Dict[str, CivVIItemData]: """ Returns a list of items that match the given item type """ return [item for item in item_table.values() if item.item_type == item_type] -def get_random_filler_by_rarity(world: 'CivVIWorld', rarity: FillerItemRarity, item_table: Dict[str, CivVIItemData]) -> CivVIItemData: +def get_random_filler_by_rarity(world: 'CivVIWorld', rarity: FillerItemRarity) -> FillerItemData: """ Returns a random filler item by rarity """ diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 49a3fd9dbc84..c2c9a70e1325 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -60,8 +60,7 @@ def has_required_progressive_districts(state: CollectionState, era: EraType, pla required_counts[key] += 1 for key, value in required_counts.items(): - has_amount = state.has(format_item_name(key), player, required_counts[key]) - if not has_amount: + if not state.has(format_item_name(key), player, required_counts[key]): return False return True diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py index b51e18776987..60fee5e914d9 100644 --- a/worlds/civ_6/Rules.py +++ b/worlds/civ_6/Rules.py @@ -22,11 +22,11 @@ def create_boost_rules(world: 'CivVIWorld'): if not boost_data or boost_data.PrereqRequiredCount == 0: continue - set_rule(world_location, lambda state, prereqs=boost_data.Prereq, required_count=boost_data.PrereqRequiredCount: has_required_items(state, prereqs, required_count, world.player)) + set_rule(world_location, lambda state, prereqs=boost_data.Prereq, required_count=boost_data.PrereqRequiredCount: has_required_items(state, prereqs, required_count, world)) -def has_required_items(state: CollectionState, prereqs: List[str], required_count: int, player: int) -> bool: - world: 'CivVIWorld' = state.multiworld.worlds[player] +def has_required_items(state: CollectionState, prereqs: List[str], required_count: int, world: 'CivVIWorld') -> bool: + player = world.player has_progressive_items = world.options.progression_style != "none" if has_progressive_items: count = 0 diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 2a464710ea4f..6bd5a7223021 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -74,7 +74,7 @@ def __init__(self, multiworld: "MultiWorld", player: int): self.location_table[location.name] = location def get_filler_item_name(self) -> str: - return get_random_filler_by_rarity(self, FillerItemRarity.COMMON, self.item_table).name + return get_random_filler_by_rarity(self, FillerItemRarity.COMMON).name def create_regions(self) -> None: create_regions(self, self.options, self.player) @@ -103,7 +103,7 @@ def create_items(self) -> None: item_to_create = item_name if self.options.progression_style != "none": item: CivVIItemData = self.item_table[item_name] - if item.progression_name != None: + if item.progression_name: item_to_create = self.item_table[item.progression_name].name self.multiworld.itempool += [self.create_item( @@ -123,8 +123,7 @@ def create_items(self) -> None: num_filler_items += 10 if self.options.boostsanity: - boost_data = get_boosts_data() - num_filler_items += len(boost_data) + num_filler_items += len(get_boosts_data()) filler_count = {rarity: math.ceil(FILLER_DISTRIBUTION[rarity] * num_filler_items) for rarity in FillerItemRarity.__reversed__()} min_count = 1 @@ -135,7 +134,7 @@ def create_items(self) -> None: if total_created >= num_filler_items: break self.multiworld.itempool += [self.create_item( - get_random_filler_by_rarity(self, rarity, self.item_table).name)] + get_random_filler_by_rarity(self, rarity).name)] total_created += 1 def post_fill(self) -> None: From ea196363b94b5ddda54df3db961a690ffc1fc86e Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Wed, 4 Sep 2024 17:01:09 -0600 Subject: [PATCH 21/69] Remove unused variable --- worlds/civ_6/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 6bd5a7223021..7b9e99aacc64 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -93,7 +93,6 @@ def create_item(self, name: str) -> Item: return CivVIItem(item, self.player, classification) def create_items(self) -> None: - progressive_era_item = None for item_name, data in self.item_table.items(): # These item types are handled individually if data.item_type in [CivVICheckType.PROGRESSIVE_DISTRICT, CivVICheckType.ERA, CivVICheckType.GOODY]: From f9fd5df9fdf19eaf4f1de54e21e3c33a74f02364 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Thu, 5 Sep 2024 19:47:58 -0600 Subject: [PATCH 22/69] Move client to Launcher Components with rest of similar clients --- worlds/civ_6/Civ6Client.py => Civ6Client.py | 18 +++++++++++------- worlds/LauncherComponents.py | 7 +++++-- worlds/civ_6/__init__.py | 13 ------------- 3 files changed, 16 insertions(+), 22 deletions(-) rename worlds/civ_6/Civ6Client.py => Civ6Client.py (94%) diff --git a/worlds/civ_6/Civ6Client.py b/Civ6Client.py similarity index 94% rename from worlds/civ_6/Civ6Client.py rename to Civ6Client.py index a84095bf2549..1fcdce0534eb 100644 --- a/worlds/civ_6/Civ6Client.py +++ b/Civ6Client.py @@ -6,15 +6,15 @@ import zipfile from CommonClient import ClientCommandProcessor, CommonContext, get_base_parser, logger, server_loop, gui_enabled -from .Data import get_progressive_districts_data -from .DeathLink import handle_check_deathlink +from worlds.civ_6.Data import get_progressive_districts_data +from worlds.civ_6.DeathLink import handle_check_deathlink from NetUtils import ClientStatus import Utils -from .CivVIInterface import CivVIInterface, ConnectionState -from .Enum import CivVICheckType -from .Items import CivVIItemData, generate_item_table, get_item_by_civ_name -from .Locations import generate_era_location_table -from .TunerClient import TunerErrorException, TunerTimeoutException +from worlds.civ_6.CivVIInterface import CivVIInterface, ConnectionState +from worlds.civ_6.Enum import CivVICheckType +from worlds.civ_6.Items import CivVIItemData, generate_item_table, get_item_by_civ_name +from worlds.civ_6.Locations import generate_era_location_table +from worlds.civ_6.TunerClient import TunerErrorException, TunerTimeoutException class CivVICommandProcessor(ClientCommandProcessor): @@ -336,3 +336,7 @@ def debug_main(): if args.debug: logger.setLevel(logging.DEBUG) main(args.connect, args.password, args.name) + + +if __name__ == '__main__': + main() diff --git a/worlds/LauncherComponents.py b/worlds/LauncherComponents.py index d127bbea36ed..3a7b264a70e7 100644 --- a/worlds/LauncherComponents.py +++ b/worlds/LauncherComponents.py @@ -191,8 +191,11 @@ def install_apworld(apworld_path: str = "") -> None: Component('Zillion Client', 'ZillionClient', file_identifier=SuffixIdentifier('.apzl')), - #MegaMan Battle Network 3 - Component('MMBN3 Client', 'MMBN3Client', file_identifier=SuffixIdentifier('.apbn3')) + # MegaMan Battle Network 3 + Component('MMBN3 Client', 'MMBN3Client', file_identifier=SuffixIdentifier('.apbn3')), + + # Civilization 6 + Component("Civilization VI Client", "Civ6Client", file_identifier=SuffixIdentifier(".apcivvi")) ] diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 7b9e99aacc64..f426a7758294 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -13,19 +13,6 @@ from .Regions import create_regions from BaseClasses import Item, ItemClassification, MultiWorld, Tutorial from worlds.AutoWorld import World, WebWorld -from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components, launch_subprocess - - -def run_client(url: Optional[str] = None): - print("Running Civ6 Client") - from .Civ6Client import main # lazy import - launch_subprocess(main, name="Civ6Client") - - -components.append( - Component("Civ6 Client", func=run_client, component_type=Type.CLIENT, - file_identifier=SuffixIdentifier(".apcivvi")) -) class CivVIWeb(WebWorld): From 6a3340fad4d39749fbb857d29c32ee5b64ecedea Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Sun, 8 Sep 2024 14:03:46 -0600 Subject: [PATCH 23/69] Revert "Move client to Launcher Components with rest of similar clients" This reverts commit f9fd5df9fdf19eaf4f1de54e21e3c33a74f02364. --- worlds/LauncherComponents.py | 7 ++----- Civ6Client.py => worlds/civ_6/Civ6Client.py | 18 +++++++----------- worlds/civ_6/__init__.py | 13 +++++++++++++ 3 files changed, 22 insertions(+), 16 deletions(-) rename Civ6Client.py => worlds/civ_6/Civ6Client.py (94%) diff --git a/worlds/LauncherComponents.py b/worlds/LauncherComponents.py index 3a7b264a70e7..d127bbea36ed 100644 --- a/worlds/LauncherComponents.py +++ b/worlds/LauncherComponents.py @@ -191,11 +191,8 @@ def install_apworld(apworld_path: str = "") -> None: Component('Zillion Client', 'ZillionClient', file_identifier=SuffixIdentifier('.apzl')), - # MegaMan Battle Network 3 - Component('MMBN3 Client', 'MMBN3Client', file_identifier=SuffixIdentifier('.apbn3')), - - # Civilization 6 - Component("Civilization VI Client", "Civ6Client", file_identifier=SuffixIdentifier(".apcivvi")) + #MegaMan Battle Network 3 + Component('MMBN3 Client', 'MMBN3Client', file_identifier=SuffixIdentifier('.apbn3')) ] diff --git a/Civ6Client.py b/worlds/civ_6/Civ6Client.py similarity index 94% rename from Civ6Client.py rename to worlds/civ_6/Civ6Client.py index 1fcdce0534eb..a84095bf2549 100644 --- a/Civ6Client.py +++ b/worlds/civ_6/Civ6Client.py @@ -6,15 +6,15 @@ import zipfile from CommonClient import ClientCommandProcessor, CommonContext, get_base_parser, logger, server_loop, gui_enabled -from worlds.civ_6.Data import get_progressive_districts_data -from worlds.civ_6.DeathLink import handle_check_deathlink +from .Data import get_progressive_districts_data +from .DeathLink import handle_check_deathlink from NetUtils import ClientStatus import Utils -from worlds.civ_6.CivVIInterface import CivVIInterface, ConnectionState -from worlds.civ_6.Enum import CivVICheckType -from worlds.civ_6.Items import CivVIItemData, generate_item_table, get_item_by_civ_name -from worlds.civ_6.Locations import generate_era_location_table -from worlds.civ_6.TunerClient import TunerErrorException, TunerTimeoutException +from .CivVIInterface import CivVIInterface, ConnectionState +from .Enum import CivVICheckType +from .Items import CivVIItemData, generate_item_table, get_item_by_civ_name +from .Locations import generate_era_location_table +from .TunerClient import TunerErrorException, TunerTimeoutException class CivVICommandProcessor(ClientCommandProcessor): @@ -336,7 +336,3 @@ def debug_main(): if args.debug: logger.setLevel(logging.DEBUG) main(args.connect, args.password, args.name) - - -if __name__ == '__main__': - main() diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index f426a7758294..7b9e99aacc64 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -13,6 +13,19 @@ from .Regions import create_regions from BaseClasses import Item, ItemClassification, MultiWorld, Tutorial from worlds.AutoWorld import World, WebWorld +from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components, launch_subprocess + + +def run_client(url: Optional[str] = None): + print("Running Civ6 Client") + from .Civ6Client import main # lazy import + launch_subprocess(main, name="Civ6Client") + + +components.append( + Component("Civ6 Client", func=run_client, component_type=Type.CLIENT, + file_identifier=SuffixIdentifier(".apcivvi")) +) class CivVIWeb(WebWorld): From 4e71e59db8f7fbd216d30d011c8c865e5125c413 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Sun, 8 Sep 2024 14:09:44 -0600 Subject: [PATCH 24/69] modify component --- worlds/civ_6/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 7b9e99aacc64..9931d0629a98 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -16,7 +16,7 @@ from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components, launch_subprocess -def run_client(url: Optional[str] = None): +def run_client(*args): print("Running Civ6 Client") from .Civ6Client import main # lazy import launch_subprocess(main, name="Civ6Client") From 05e51c04e446a381bac90c07f5b808d519413558 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Thu, 12 Sep 2024 12:04:47 -0600 Subject: [PATCH 25/69] Fix generation issues --- worlds/civ_6/Data.py | 2 +- worlds/civ_6/Items.py | 12 ++++++++---- worlds/civ_6/Locations.py | 2 +- worlds/civ_6/Regions.py | 24 +++++++++--------------- worlds/civ_6/Rules.py | 16 ++++++++++------ worlds/civ_6/__init__.py | 24 ++++++++++++++++++++---- 6 files changed, 49 insertions(+), 31 deletions(-) diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index ccc41428206c..70899a652431 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -41,7 +41,7 @@ def get_boosts_data() -> List[CivVIBoostData]: return boosts -def get_era_required_items_data(): +def get_era_required_items_data() -> List[str]: return _get_data("era_required_items") diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index 9de0ea7b8fb2..fe612069fe9f 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -2,7 +2,7 @@ from typing import Dict, Optional, TYPE_CHECKING, List from BaseClasses import Item, ItemClassification from .Data import get_era_required_items_data, get_existing_civics_data, get_existing_techs_data, get_goody_hut_rewards_data, get_progressive_districts_data -from .Enum import CivVICheckType +from .Enum import CivVICheckType, EraType from .ProgressiveDistricts import get_flat_progressive_districts if TYPE_CHECKING: from . import CivVIWorld @@ -94,8 +94,9 @@ class CivVIItemData: item_type: CivVICheckType progression_name: Optional[str] civ_name: Optional[str] + era: Optional[EraType] - def __init__(self, name, civ_vi_id: int, cost: int, item_type: CivVICheckType, id_offset: int, classification: ItemClassification, progression_name: Optional[str], civ_name: Optional[str] = None): + def __init__(self, name, civ_vi_id: int, cost: int, item_type: CivVICheckType, id_offset: int, classification: ItemClassification, progression_name: Optional[str], civ_name: Optional[str] = None, era: Optional[EraType] = None): self.classification = classification self.civ_vi_id = civ_vi_id self.name = name @@ -104,6 +105,7 @@ def __init__(self, name, civ_vi_id: int, cost: int, item_type: CivVICheckType, i self.item_type = item_type self.progression_name = progression_name self.civ_name = civ_name + self.era = era class CivVIItem(Item): @@ -156,7 +158,8 @@ def _generate_tech_items(id_base: int, required_items: List[str], progressive_it id_offset=id_base, classification=classification, progression_name=progression_name, - civ_name=civ_name + civ_name=civ_name, + era=EraType(tech["EraType"]) ) tech_id += 1 @@ -190,7 +193,8 @@ def _generate_civics_items(id_base: int, required_items: List[str], progressive_ id_offset=id_base, classification=classification, progression_name=progression_name, - civ_name=civ_name + civ_name=civ_name, + era=EraType(civic["EraType"]) ) civic_id += 1 diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index 2063bb342375..14b458e45b80 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -180,7 +180,7 @@ def generate_era_location_table() -> Dict[EraType, Dict[str, CivVILocationData]] # Goody Huts, defaults to 10 goody huts as location checks (rarely will a player get more than this) for i in range(10): era_locations[EraType.ERA_ANCIENT.value]["GOODY_HUT_" + str(i + 1)] = CivVILocationData( - "GOODY_HUT_" + str(i + 1), 0, 0, id_base, EraType.ERA_ANCIENT, CivVICheckType.GOODY) + "GOODY_HUT_" + str(i + 1), 0, 0, id_base, EraType.ERA_ANCIENT.value, CivVICheckType.GOODY) id_base += 1 # Boosts boosts = get_boosts_data() diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index c2c9a70e1325..7d8332310edf 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -12,21 +12,15 @@ from Items import CivVIItemData -def get_cumulative_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool = True, item_table: Dict[str, 'CivVIItemData'] = None) -> List['CivVIItemData']: - """Gets the specific techs/civics that are required for the specified era as well as all previous eras""" - cumulative_prereqs = [] - era_required_items = {} - era_required_items = get_era_required_items_data() +def get_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool = True, item_table: Dict[str, 'CivVIItemData'] = None) -> List['CivVIItemData']: + """Gets the specific techs/civics that are required for the specified era""" + era_required_items = get_era_required_items_data()[end_era.value].copy() - for era in EraType: - cumulative_prereqs += era_required_items[era.value] - if era == end_era: - break # If we are excluding progressive items, we need to remove them from the list of expected items (TECH_BRONZE_WORKING won't be here since it will be PROGRESSIVE_ENCAMPMENT) if exclude_progressive_items: flat_progressive_items = get_flat_progressive_districts() prereqs_without_progressive_items = [] - for item in cumulative_prereqs: + for item in era_required_items: if item in flat_progressive_items: continue else: @@ -34,7 +28,7 @@ def get_cumulative_prereqs_for_era(end_era: EraType, exclude_progressive_items: return [get_item_by_civ_name(prereq, item_table) for prereq in prereqs_without_progressive_items] - return [get_item_by_civ_name(prereq, item_table) for prereq in cumulative_prereqs] + return [get_item_by_civ_name(prereq, item_table) for prereq in era_required_items] def has_required_progressive_districts(state: CollectionState, era: EraType, player: int) -> bool: @@ -43,13 +37,13 @@ def has_required_progressive_districts(state: CollectionState, era: EraType, pla item_table = state.multiworld.worlds[player].item_table # Verify we can still reach non progressive items - all_previous_items_no_progression = get_cumulative_prereqs_for_era( + all_previous_items_no_progression = get_prereqs_for_era( era, True, item_table) if not state.has_all([item.name for item in all_previous_items_no_progression], player): return False # Verify we have the correct amount of progressive items - all_previous_items = get_cumulative_prereqs_for_era( + all_previous_items = get_prereqs_for_era( era, False, item_table) required_counts: Dict[str, int] = {} @@ -66,13 +60,13 @@ def has_required_progressive_districts(state: CollectionState, era: EraType, pla def has_required_progressive_eras(state: CollectionState, era: EraType, player: int) -> bool: - """Checks, for the given era, how many are required to proceed to the next era. Ancient = 1, Classical = 2, etc. Assumes 2 required for classical and starts from there so as to decrease odds of hard locking without the turns to get the items""" + """Checks, for the given era, how many are required to proceed to the next era. Ancient = 0, Classical = 1, etc.""" if era == EraType.ERA_FUTURE or era == EraType.ERA_INFORMATION: return True eras = [e.value for e in EraType] era_index = eras.index(era.value) - return state.has(format_item_name("PROGRESSIVE_ERA"), player, era_index + 2) + return state.has(format_item_name("PROGRESSIVE_ERA"), player, era_index + 1) def has_required_items(state: CollectionState, era: EraType, world: 'CivVIWorld') -> bool: diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py index 60fee5e914d9..1678bfafbff7 100644 --- a/worlds/civ_6/Rules.py +++ b/worlds/civ_6/Rules.py @@ -40,14 +40,18 @@ def has_required_items(state: CollectionState, prereqs: List[str], required_coun else: if state.has(item, player): count += 1 - + # early out if we've already gotten enough + if count >= required_count: + return True for item, required_progressive_item_count in progressive_items.items(): if state.has(item, player, required_progressive_item_count): count += required_progressive_item_count - return count >= required_count + # early out if we've already gotten enough + if count >= required_count: + return True + return False else: - total = state.count_from_list_unique( + return state.has_from_list_unique( [ - get_item_by_civ_name(prereq, state.multiworld.worlds[player].item_table).name for prereq in prereqs - ], player) - return total >= required_count + get_item_by_civ_name(prereq, world.item_table).name for prereq in prereqs + ], player, required_count) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 9931d0629a98..9c3ab37ac707 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -1,13 +1,16 @@ import math import os -from typing import Any, Dict, Optional, Set +from typing import Any, Dict, Set -from .Data import get_boosts_data +from worlds.generic.Rules import forbid_item + + +from .Data import get_boosts_data, get_era_required_items_data from .Rules import create_boost_rules from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql from .Enum import CivVICheckType -from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity +from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIItemData, FillerItemRarity, format_item_name, generate_item_table, CivVIItem, get_random_filler_by_rarity from .Locations import CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table from .Options import CivVIOptions from .Regions import create_regions @@ -59,7 +62,6 @@ class CivVIWorld(World): item_table: Dict[str, CivVIItemData] = {} location_by_era: Dict[EraType, Dict[str, CivVILocationData]] - required_client_version = (0, 4, 5) def __init__(self, multiworld: "MultiWorld", player: int): @@ -93,6 +95,9 @@ def create_item(self, name: str) -> Item: return CivVIItem(item, self.player, classification) def create_items(self) -> None: + data = get_era_required_items_data() + early_items = data[EraType.ERA_ANCIENT.value] + early_locations = [location for location in self.location_table.values() if location.era_type == EraType.ERA_ANCIENT.value] for item_name, data in self.item_table.items(): # These item types are handled individually if data.item_type in [CivVICheckType.PROGRESSIVE_DISTRICT, CivVICheckType.ERA, CivVICheckType.GOODY]: @@ -107,6 +112,16 @@ def create_items(self) -> None: self.multiworld.itempool += [self.create_item( item_to_create)] + if item.civ_name in early_items: + self.multiworld.early_items[self.player][item_to_create] = 1 + elif self.item_table[item_name].era in [EraType.ERA_ATOMIC, EraType.ERA_INFORMATION, EraType.ERA_FUTURE]: + for location in early_locations: + found_location = None + try: + found_location = self.multiworld.get_location(location.name, self.player) + forbid_item(found_location, item_to_create, self.player) + except KeyError: + pass # Era items if self.options.progression_style == "eras_and_districts": @@ -115,6 +130,7 @@ def create_items(self) -> None: if era.value == "ERA_ANCIENT": continue self.multiworld.itempool += [self.create_item(self.item_table.get("Progressive Era").name)] + self.multiworld.early_items[self.player]["Progressive Era"] = 2 num_filler_items = 0 # Goody items, create 10 by default if options are enabled From 8cf3806d2ae1b247d96d5f64ddc2696e6d8a0e2c Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Thu, 12 Sep 2024 17:59:20 -0600 Subject: [PATCH 26/69] Fix tests --- worlds/civ_6/__init__.py | 2 +- worlds/civ_6/test/TestRegionRequirements.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 9c3ab37ac707..b9b7f3c96a16 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -105,8 +105,8 @@ def create_items(self) -> None: # If we're using progressive districts, we need to check if we need to create a different item instead item_to_create = item_name + item: CivVIItemData = self.item_table[item_name] if self.options.progression_style != "none": - item: CivVIItemData = self.item_table[item_name] if item.progression_name: item_to_create = self.item_table[item.progression_name].name diff --git a/worlds/civ_6/test/TestRegionRequirements.py b/worlds/civ_6/test/TestRegionRequirements.py index 01453fa2e5d5..cc1cc9fdf1fc 100644 --- a/worlds/civ_6/test/TestRegionRequirements.py +++ b/worlds/civ_6/test/TestRegionRequirements.py @@ -147,7 +147,6 @@ def check_eras_accessible(eras: List[EraType]): # Classical era requires 2 progressive era items self.collect(progresive_era_item) - self.collect(progresive_era_item) accessible_eras += [EraType.ERA_CLASSICAL] check_eras_accessible(accessible_eras) @@ -204,8 +203,6 @@ def check_eras_accessible(eras: List[EraType]): accessible_eras = [EraType.ERA_ANCIENT] check_eras_accessible(accessible_eras) - # Classical era requires 2 progressive era items - self.collect(progresive_era_item) self.collect(progresive_era_item) accessible_eras += [EraType.ERA_CLASSICAL] check_eras_accessible(accessible_eras) From 2f447264bdf305ff48490d1d7ad8351066b72092 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Thu, 12 Sep 2024 20:44:07 -0600 Subject: [PATCH 27/69] Minor change --- worlds/civ_6/ProgressiveDistricts.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/worlds/civ_6/ProgressiveDistricts.py b/worlds/civ_6/ProgressiveDistricts.py index b22f7be7f581..b81ebba4b13f 100644 --- a/worlds/civ_6/ProgressiveDistricts.py +++ b/worlds/civ_6/ProgressiveDistricts.py @@ -2,6 +2,7 @@ from .Data import get_progressive_districts_data + def get_flat_progressive_districts() -> Dict[str, str]: """Returns a dictionary of all items that are associated with a progressive item. Key is the item name ("TECH_WRITING") and the value is the associated progressive @@ -18,10 +19,4 @@ def convert_items_to_have_progression(items: List[str]): """ converts a list of items to instead be their associated progressive item if they have one. ["TECH_MINING", "TECH_WRITING"] -> ["TECH_MINING", "PROGRESSIVE_CAMPUS]""" flat_progressive_techs = get_flat_progressive_districts() - new_list = [] - for item in items: - if item in flat_progressive_techs.keys(): - new_list.append(flat_progressive_techs[item]) - else: - new_list.append(item) - return new_list + return [flat_progressive_techs.get(item, item) for item in items] From 38717b29c8453ec04dcf6450374517aab94ced11 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Fri, 13 Sep 2024 22:25:36 -0600 Subject: [PATCH 28/69] Add improvement and test case --- worlds/civ_6/Regions.py | 5 +- worlds/civ_6/test/TestRegionRequirements.py | 65 +++++++++------------ 2 files changed, 29 insertions(+), 41 deletions(-) diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 7d8332310edf..8e4a57301c5c 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -53,10 +53,7 @@ def has_required_progressive_districts(state: CollectionState, era: EraType, pla if item.civ_name in value: required_counts[key] += 1 - for key, value in required_counts.items(): - if not state.has(format_item_name(key), player, required_counts[key]): - return False - return True + return state.has_all_counts({format_item_name(key): value for key, value in required_counts.items()}, player) def has_required_progressive_eras(state: CollectionState, era: EraType, player: int) -> bool: diff --git a/worlds/civ_6/test/TestRegionRequirements.py b/worlds/civ_6/test/TestRegionRequirements.py index cc1cc9fdf1fc..b1a9b0f656d4 100644 --- a/worlds/civ_6/test/TestRegionRequirements.py +++ b/worlds/civ_6/test/TestRegionRequirements.py @@ -23,45 +23,30 @@ def collect_items_for_era_progressive(test: CivVITestBase, era: EraType) -> None def verify_eras_accessible(test: CivVITestBase, state: CollectionState, collect_func): + """Collect for an era, then check if the next era is accessible and the one after that is not""" for era in EraType: if era == EraType.ERA_ANCIENT: - test.assertTrue(state.can_reach( - era.value, "Region", test.player)) + test.assertTrue(state.can_reach(era.value, "Region", test.player)) else: - test.assertFalse(state.can_reach( - era.value, "Region", test.player)) - - collect_func(test, EraType.ERA_ANCIENT) - test.assertTrue(state.can_reach( - EraType.ERA_CLASSICAL.value, "Region", test.player)) - - collect_func(test, EraType.ERA_CLASSICAL) - test.assertTrue(state.can_reach( - EraType.ERA_MEDIEVAL.value, "Region", test.player)) - - collect_func(test, EraType.ERA_MEDIEVAL) - test.assertTrue(state.can_reach( - EraType.ERA_RENAISSANCE.value, "Region", test.player)) - - collect_func(test, EraType.ERA_RENAISSANCE) - test.assertTrue(state.can_reach( - EraType.ERA_INDUSTRIAL.value, "Region", test.player)) - - collect_func(test, EraType.ERA_INDUSTRIAL) - test.assertTrue(state.can_reach( - EraType.ERA_MODERN.value, "Region", test.player)) - - collect_func(test, EraType.ERA_MODERN) - test.assertTrue(state.can_reach( - EraType.ERA_ATOMIC.value, "Region", test.player)) - - collect_func(test, EraType.ERA_ATOMIC) - test.assertTrue(state.can_reach( - EraType.ERA_INFORMATION.value, "Region", test.player)) - - collect_func(test, EraType.ERA_INFORMATION) - test.assertTrue(state.can_reach( - EraType.ERA_FUTURE.value, "Region", test.player)) + test.assertFalse(state.can_reach(era.value, "Region", test.player)) + + eras = [ + EraType.ERA_ANCIENT, + EraType.ERA_CLASSICAL, + EraType.ERA_MEDIEVAL, + EraType.ERA_RENAISSANCE, + EraType.ERA_INDUSTRIAL, + EraType.ERA_MODERN, + EraType.ERA_ATOMIC, + EraType.ERA_INFORMATION, + EraType.ERA_FUTURE + ] + + for i in range(len(eras) - 1): + collect_func(test, eras[i]) + test.assertTrue(state.can_reach(eras[i + 1].value, "Region", test.player)) + if i + 2 < len(eras): + test.assertFalse(state.can_reach(eras[i + 2].value, "Region", test.player)) class TestNonProgressiveRegionRequirements(CivVITestBase): @@ -119,6 +104,13 @@ def test_eras_are_accessible_with_progressive_districts(self) -> None: state = self.multiworld.state verify_eras_accessible(self, state, collect_items_for_era_progressive) + def test_progressive_districts_are_required(self) -> None: + state = self.multiworld.state + self.collect_all_but(["Progressive Holy Site"]) + self.assertFalse(state.can_reach("ERA_CLASSICAL", "Region", self.player)) + self.collect_by_name(["Progressive Holy Site"]) + self.assertTrue(state.can_reach("ERA_CLASSICAL", "Region", self.player)) + class TestProgressiveEraRequirements(CivVITestBase): options = { @@ -232,4 +224,3 @@ def check_eras_accessible(eras: List[EraType]): accessible_eras += [EraType.ERA_INFORMATION] accessible_eras += [EraType.ERA_FUTURE] check_eras_accessible(accessible_eras) - From 2fd31dd8e903533d8c0cb4d2a89633f78d1042a9 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Fri, 13 Sep 2024 23:20:40 -0600 Subject: [PATCH 29/69] Minor options changes --- worlds/civ_6/Options.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index b0423ba77a25..930ce229f469 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -3,11 +3,11 @@ class ProgressionStyle(Choice): - """Determines what progressive items (if any) should be included.\n\n - Districts Only: Each tech/civic that would normally unlock a district or district building now has a logical progression. Example: TECH_BRONZE_WORKING is now PROGRESSIVE_ENCAMPMENT\n\n + """Determines what progressive items (if any) should be included. + Districts Only: Each tech/civic that would normally unlock a district or district building now has a logical progression. Example: TECH_BRONZE_WORKING is now PROGRESSIVE_ENCAMPMENT Eras and Districts: Players will be defeated if they play until the world era advances beyond the currently unlocked maximum era. A notification will be shown as the end of the era approaches letting the player know if they don't have enough progressive era items. - Unlocked eras can be seen in both the tech and civic trees. Includes all progressive districts.\n\n + Unlocked eras can be seen in both the tech and civic trees. Includes all progressive districts. None: No progressive items will be included. This means you can get district upgrades that won't be usable until the relevant district is unlocked. """ display_name = "Progression Style" @@ -42,10 +42,10 @@ class ResearchCostMultiplier(Choice): class PreHintItems(Choice): - """Controls if/what items in the tech/civics trees are pre-hinted for the multiworld.\n - All : All items in the tech & civics trees are pre-hinted.\n - Progression items: Only locations in the trees containing progression items are pre-hinted.\n - No Junk: pre-hint the progression and useful items.\n + """Controls if/what items in the tech/civics trees are pre-hinted for the multiworld. + All : All items in the tech & civics trees are pre-hinted. + Progression items: Only locations in the trees containing progression items are pre-hinted. + No Junk: pre-hint the progression and useful items. None: No items are pre-hinted. """ display_name = "Tech/Civic Tree pre-hinted Items" @@ -67,9 +67,9 @@ class InGameFlagProgressionItems(DefaultOnToggle): class DeathLinkEffect(Choice): - """What happens when a unit dies. Default is Unit Killed.\n - Faith, and Gold will be decreased by the amount specified in 'Death Link Effect Percent'. \n - Era score is decreased by 1.\n + """What happens when a unit dies. Default is Unit Killed. + Faith, and Gold will be decreased by the amount specified in 'Death Link Effect Percent'. + Era score is decreased by 1. Any will select any of these options any time a death link is received.""" display_name = "Death Link Effect" option_unit_killed = 0 @@ -100,6 +100,6 @@ class CivVIOptions(PerGameCommonOptions): pre_hint_items: PreHintItems hide_item_names: HideItemNames advisor_show_progression_items: InGameFlagProgressionItems + death_link: DeathLink death_link_effect: DeathLinkEffect death_link_effect_percent: DeathLinkEffectPercent - death_link: DeathLink From 37c869f16ff6c1aaef82e256dc86057587bcdcb5 Mon Sep 17 00:00:00 2001 From: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:11:52 -0400 Subject: [PATCH 30/69] . From bc8448c5b5967be8872a7a29dd3af51242a1e215 Mon Sep 17 00:00:00 2001 From: Exempt-Medic Date: Tue, 15 Oct 2024 13:45:22 -0400 Subject: [PATCH 31/69] Preliminary Review --- worlds/civ_6/Civ6Client.py | 14 +++++----- worlds/civ_6/CivVIInterface.py | 4 +-- worlds/civ_6/Container.py | 3 +-- worlds/civ_6/Data.py | 6 ++--- worlds/civ_6/DeathLink.py | 2 +- worlds/civ_6/Enum.py | 3 +++ worlds/civ_6/Items.py | 20 +++++++-------- worlds/civ_6/Locations.py | 6 ++--- worlds/civ_6/Options.py | 18 ++++++------- worlds/civ_6/Regions.py | 7 +++-- worlds/civ_6/Rules.py | 4 +-- worlds/civ_6/__init__.py | 8 +++--- worlds/civ_6/docs/en_Civilization VI.md | 34 ++++++++++++------------- worlds/civ_6/docs/setup_en.md | 14 +++++----- 14 files changed, 72 insertions(+), 71 deletions(-) diff --git a/worlds/civ_6/Civ6Client.py b/worlds/civ_6/Civ6Client.py index a84095bf2549..5d8b79de5088 100644 --- a/worlds/civ_6/Civ6Client.py +++ b/worlds/civ_6/Civ6Client.py @@ -2,7 +2,7 @@ import logging import os import traceback -from typing import Dict, List +from typing import Dict, List, Optional import zipfile from CommonClient import ClientCommandProcessor, CommonContext, get_base_parser, logger, server_loop, gui_enabled @@ -13,7 +13,7 @@ from .CivVIInterface import CivVIInterface, ConnectionState from .Enum import CivVICheckType from .Items import CivVIItemData, generate_item_table, get_item_by_civ_name -from .Locations import generate_era_location_table +from .Locations import CivVILocationData, generate_era_location_table from .TunerClient import TunerErrorException, TunerTimeoutException @@ -51,8 +51,8 @@ class CivVIContext(CommonContext): items_handling = 0b111 tuner_sync_task = None game_interface: CivVIInterface - location_name_to_civ_location = {} - location_name_to_id = {} + location_name_to_civ_location: Dict[str, CivVILocationData] = {} + location_name_to_id: Dict[str, int] = {} item_id_to_civ_item: Dict[int, CivVIItemData] = {} item_table: Dict[str, CivVIItemData] = {} processing_multiple_items = False @@ -118,7 +118,7 @@ class CivVIManager(GameManager): logging_pairs = [ ("Client", "Archipelago") ] - base_title = "Archipelago Civlization VI Client" + base_title = "Archipelago Civilization VI Client" self.ui = CivVIManager(self) self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") @@ -153,7 +153,7 @@ async def tuner_sync_task(ctx: CivVIContext): continue else: try: - if ctx.processing_multiple_items == True: + if ctx.processing_multiple_items: await asyncio.sleep(3) else: state = await ctx.game_interface.is_in_game() @@ -201,7 +201,7 @@ async def handle_checked_location(ctx: CivVIContext): await ctx.send_msgs([{"cmd": "LocationChecks", "locations": checked_location_ids}]) -async def handle_receive_items(ctx: CivVIContext, last_received_index_override: int = None): +async def handle_receive_items(ctx: CivVIContext, last_received_index_override: Optional[int] = None): try: last_received_index = last_received_index_override or await ctx.game_interface.get_last_received_index() if len(ctx.items_received) - last_received_index > 1: diff --git a/worlds/civ_6/CivVIInterface.py b/worlds/civ_6/CivVIInterface.py index aba4032bf083..51c1914a5ac8 100644 --- a/worlds/civ_6/CivVIInterface.py +++ b/worlds/civ_6/CivVIInterface.py @@ -1,6 +1,6 @@ from enum import Enum from logging import Logger -from typing import List +from typing import List, Optional from .Items import CivVIItemData from .TunerClient import TunerClient, TunerConnectionException, TunerTimeoutException @@ -15,7 +15,7 @@ class ConnectionState(Enum): class CivVIInterface: logger: Logger tuner: TunerClient - last_error: str = None + last_error: Optional[str] = None def __init__(self, logger: Logger): self.logger = logger diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py index 268ef993b666..d2ea665ed4b5 100644 --- a/worlds/civ_6/Container.py +++ b/worlds/civ_6/Container.py @@ -75,8 +75,7 @@ def generate_new_items(world: 'CivVIWorld') -> str: """ Generates the XML for the new techs/civics as well as the blockers used to prevent players from researching their own items """ - locations: List[CivVILocation] = world.multiworld.get_locations( - world.player) + locations: List[CivVILocation] = world.multiworld.get_filled_locations(world.player) techs = [location for location in locations if location.location_type == CivVICheckType.TECH] civics = [location for location in locations if location.location_type == diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index 70899a652431..90d04c79b50b 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -2,7 +2,7 @@ import json import os import pkgutil -from typing import List +from typing import Dict, List _cache = {} @@ -18,7 +18,7 @@ def _get_data(key: str): @dataclass -class CivVIBoostData(): +class CivVIBoostData: Type: str EraType: str Prereq: List[str] @@ -41,7 +41,7 @@ def get_boosts_data() -> List[CivVIBoostData]: return boosts -def get_era_required_items_data() -> List[str]: +def get_era_required_items_data() -> Dict[str, List[str]]: return _get_data("era_required_items") diff --git a/worlds/civ_6/DeathLink.py b/worlds/civ_6/DeathLink.py index 3bf4ceef1d35..22ee579cfbd9 100644 --- a/worlds/civ_6/DeathLink.py +++ b/worlds/civ_6/DeathLink.py @@ -68,7 +68,7 @@ async def handle_check_deathlink(ctx: CommonContext): f"was tired of sitting in BK and decided to fight a {result} instead", f"purposely lost to a {result} as a cry for help", f"is wanting to remind everyone that they are here to have fun and not to win", - f"is reconisdering their pursuit of a domination victory", + f"is reconsidering their pursuit of a domination victory", f"had their plans toppled by a {result}", ] player = ctx.player_names[ctx.slot] diff --git a/worlds/civ_6/Enum.py b/worlds/civ_6/Enum.py index d814e05a12ec..498e9f29efab 100644 --- a/worlds/civ_6/Enum.py +++ b/worlds/civ_6/Enum.py @@ -1,4 +1,6 @@ from enum import Enum + + class EraType(Enum): ERA_ANCIENT = "ERA_ANCIENT" ERA_CLASSICAL = "ERA_CLASSICAL" @@ -10,6 +12,7 @@ class EraType(Enum): ERA_INFORMATION = "ERA_INFORMATION" ERA_FUTURE = "ERA_FUTURE" + class CivVICheckType(Enum): TECH = "TECH" CIVIC = "CIVIC" diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index fe612069fe9f..0667b19f1ef8 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -57,7 +57,7 @@ class FillerItemRarity(Enum): FILLER_DISTRIBUTION: Dict[FillerItemRarity, float] = { FillerItemRarity.RARE: 0.025, - FillerItemRarity.UNCOMMON: .2, + FillerItemRarity.UNCOMMON: 0.2, FillerItemRarity.COMMON: 0.775, } @@ -113,7 +113,7 @@ class CivVIItem(Item): civ_vi_id: int item_type: CivVICheckType - def __init__(self, item: CivVIItemData, player: int, classification: ItemClassification = None): + def __init__(self, item: CivVIItemData, player: int, classification: Optional[ItemClassification] = None): super().__init__(item.name, classification or item.classification, item.code, player) self.civ_vi_id = item.civ_vi_id self.item_type = item.item_type @@ -271,21 +271,21 @@ def generate_item_table() -> Dict[str, CivVIItemData]: progressive_items = get_flat_progressive_districts() - item_table = {} + item_table: Dict[str, CivVIItemData] = {} def get_id_base(): return len(item_table.keys()) - item_table = {**item_table, **_generate_tech_items(get_id_base(), required_items, progressive_items)} - item_table = {**item_table, **_generate_civics_items(get_id_base(), required_items, progressive_items)} - item_table = {**item_table, **_generate_progressive_district_items(get_id_base())} - item_table = {**item_table, **_generate_progressive_era_items(get_id_base())} - item_table = {**item_table, **_generate_goody_hut_items(get_id_base())} + item_table.update(**_generate_tech_items(get_id_base(), required_items, progressive_items)) + item_table.update(**_generate_civics_items(get_id_base(), required_items, progressive_items)) + item_table.update(**_generate_progressive_district_items(get_id_base())) + item_table.update(**_generate_progressive_era_items(get_id_base())) + item_table.update(**_generate_goody_hut_items(get_id_base())) return item_table -def get_items_by_type(item_type: CivVICheckType, item_table: Dict[str, CivVIItemData]) -> Dict[str, CivVIItemData]: +def get_items_by_type(item_type: CivVICheckType, item_table: Dict[str, CivVIItemData]) -> List[CivVIItemData]: """ Returns a list of items that match the given item type """ @@ -297,4 +297,4 @@ def get_random_filler_by_rarity(world: 'CivVIWorld', rarity: FillerItemRarity) - Returns a random filler item by rarity """ items = [item for item in get_filler_item_data().values() if item.rarity == rarity] - return items[world.random.randint(0, len(items) - 1)] + return world.random.choice(items) diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index 14b458e45b80..e8fc29691580 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -9,7 +9,7 @@ # Locs that should have progression items (keypoint techs/civics, ~1 per era) PRIORITY_LOCATIONS = [ - "TECH_ANCEINT_09", + "TECH_ANCIENT_09", "TECH_CLASSICAL_15", "TECH_MEDIEVAL_20", "TECH_RENAISSANCE_33", @@ -52,7 +52,7 @@ ] -class CivVILocationData(): +class CivVILocationData: game: str = "Civilization VI" name: str cost: int @@ -61,7 +61,7 @@ class CivVILocationData(): code: int era_type: EraType location_type: CivVICheckType - pre_reqs: List[str] + pre_reqs: Optional[List[str]] def __init__(self, name: str, cost: int, uiTreeRow: int, id: int, era_type: EraType, location_type: CivVICheckType, pre_reqs: Optional[List[str]] = None): self.name = name diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 930ce229f469..9dc87801eb24 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from Options import Choice, DeathLink, DefaultOnToggle, PerGameCommonOptions, Range, StartInventory, StartInventoryPool, Toggle +from Options import Choice, DeathLink, DefaultOnToggle, PerGameCommonOptions, Range, StartInventoryPool, Toggle class ProgressionStyle(Choice): @@ -18,12 +18,12 @@ class ProgressionStyle(Choice): class ShuffleGoodyHuts(DefaultOnToggle): - """Shuffles the goody hut rewards. Goody huts will only contain junk items and location checks are received sequentially (GOODY_HUT_1, GOODY_HUT_2, etc).""" + """Shuffles the goody hut rewards. Goody huts will only contain junk items and location checks are received sequentially (GOODY_HUT_1, GOODY_HUT_2, etc.).""" display_name = "Shuffle Goody Hut Rewards" class BoostSanity(Toggle): - """Boosts for Civics/Techs are location checks. Boosts can now be triggered even if the item has already been researched. If it is dependent upon a unit that is now obsolete, you can click toggle on/off the relevant tech in the tech tree.""" + """Boosts for Civics/Techs are location checks. Boosts can now be triggered even if the item has already been researched. If it is dependent upon a unit that is now obsolete, you can click to toggle on/off the relevant tech in the tech tree.""" display_name = "Boostsanity" @@ -33,7 +33,7 @@ class ExcludeMissableBoosts(Toggle): class ResearchCostMultiplier(Choice): - """Multiplier for research cost of techs and civics, higher values make research more expensive. Cheap = 0.5x, Expensive = 1.5x. Default is 1. """ + """Multiplier for research cost of techs and civics, higher values make research more expensive. Cheap = 0.5x, Expensive = 1.5x.""" display_name = "Tech/Civic Cost Multiplier" option_cheap = 0.5 option_default = 1 @@ -43,9 +43,9 @@ class ResearchCostMultiplier(Choice): class PreHintItems(Choice): """Controls if/what items in the tech/civics trees are pre-hinted for the multiworld. - All : All items in the tech & civics trees are pre-hinted. + All: All items in the tech & civics trees are pre-hinted. Progression items: Only locations in the trees containing progression items are pre-hinted. - No Junk: pre-hint the progression and useful items. + No Junk: Pre-hint the progression and useful items. None: No items are pre-hinted. """ display_name = "Tech/Civic Tree pre-hinted Items" @@ -62,12 +62,12 @@ class HideItemNames(Toggle): class InGameFlagProgressionItems(DefaultOnToggle): - """If enabled, an advisor icon will be added to any location that contains a progression item""" + """If enabled, an advisor icon will be added to any location that contains a progression item.""" display_name = "Advisor Indicates Progression Items" class DeathLinkEffect(Choice): - """What happens when a unit dies. Default is Unit Killed. + """What happens when a unit dies. Faith, and Gold will be decreased by the amount specified in 'Death Link Effect Percent'. Era score is decreased by 1. Any will select any of these options any time a death link is received.""" @@ -82,7 +82,7 @@ class DeathLinkEffect(Choice): class DeathLinkEffectPercent(Range): - """The percentage of the effect that will be applied. Only applicable for Gold and Faith effects. Default is 20%""" + """The percentage of the effect that will be applied. Only applicable for Gold and Faith effects.""" display_name = "Death Link Effect Percent" default = 20 range_start = 1 diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 8e4a57301c5c..406a96aece1f 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -1,7 +1,7 @@ -from typing import TYPE_CHECKING, Dict, List +from typing import TYPE_CHECKING, Dict, List, Optional from BaseClasses import CollectionState, LocationProgressType, Region from .Data import get_era_required_items_data, get_progressive_districts_data -from .Items import format_item_name, get_item_by_civ_name +from .Items import CivVIItemData, format_item_name, get_item_by_civ_name from .Enum import CivVICheckType, EraType from .Locations import CivVILocation from .ProgressiveDistricts import get_flat_progressive_districts @@ -9,10 +9,9 @@ if TYPE_CHECKING: from . import CivVIWorld - from Items import CivVIItemData -def get_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool = True, item_table: Dict[str, 'CivVIItemData'] = None) -> List['CivVIItemData']: +def get_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool = True, item_table: Optional[Dict[str, CivVIItemData]] = None) -> List[CivVIItemData]: """Gets the specific techs/civics that are required for the specified era""" era_required_items = get_era_required_items_data()[end_era.value].copy() diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py index 1678bfafbff7..b546cf35ed72 100644 --- a/worlds/civ_6/Rules.py +++ b/worlds/civ_6/Rules.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Callable, List, Dict +from typing import TYPE_CHECKING, List, Dict from BaseClasses import CollectionState from .Items import get_item_by_civ_name from .Data import get_boosts_data @@ -17,7 +17,7 @@ def create_boost_rules(world: 'CivVIWorld'): boost_locations = [location for location in world.location_table.values() if location.location_type == CivVICheckType.BOOST] for location in boost_locations: boost_data = next((boost for boost in boost_data_list if boost.Type == location.name), None) - world_location = world.multiworld.get_location(location.name, world.player) + world_location = world.get_location(location.name) forbid_item(world_location, "Progressive Era", world.player) if not boost_data or boost_data.PrereqRequiredCount == 0: continue diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index b9b7f3c96a16..a81772c9038b 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -98,9 +98,9 @@ def create_items(self) -> None: data = get_era_required_items_data() early_items = data[EraType.ERA_ANCIENT.value] early_locations = [location for location in self.location_table.values() if location.era_type == EraType.ERA_ANCIENT.value] - for item_name, data in self.item_table.items(): + for item_name, item_data in self.item_table.items(): # These item types are handled individually - if data.item_type in [CivVICheckType.PROGRESSIVE_DISTRICT, CivVICheckType.ERA, CivVICheckType.GOODY]: + if item_data.item_type in [CivVICheckType.PROGRESSIVE_DISTRICT, CivVICheckType.ERA, CivVICheckType.GOODY]: continue # If we're using progressive districts, we need to check if we need to create a different item instead @@ -118,7 +118,7 @@ def create_items(self) -> None: for location in early_locations: found_location = None try: - found_location = self.multiworld.get_location(location.name, self.player) + found_location = self.get_location(location.name) forbid_item(found_location, item_to_create, self.player) except KeyError: pass @@ -167,7 +167,7 @@ def post_fill(self) -> None: if location_data.location_type != CivVICheckType.CIVIC and location_data.location_type != CivVICheckType.TECH: continue - location: CivVILocation = self.multiworld.get_location(location_name, self.player) + location: CivVILocation = self.get_location(location_name) if not location.item or not show_flags.get(location.item.classification, False): continue diff --git a/worlds/civ_6/docs/en_Civilization VI.md b/worlds/civ_6/docs/en_Civilization VI.md index 8b738b41338e..d4664e70ea7b 100644 --- a/worlds/civ_6/docs/en_Civilization VI.md +++ b/worlds/civ_6/docs/en_Civilization VI.md @@ -1,13 +1,13 @@ -# Civlization 6 Archipelago +# Civilization 6 Archipelago ## What does randomization do to this game? -In Civilization VI, the tech and civic trees are both shuffled. This presents some interesting ways to play the game in a non-standard way. If you are feeling adventurous, you can enable the `boostsanity` option in order to really change up the way you normally would play a Civ game. Details on the option can be found in the "Boostsanity" section below. +In Civilization VI, the tech and civic trees are both shuffled. This presents some interesting ways to play the game in a non-standard way. If you are feeling adventurous, you can enable the "boostsanity" option in order to really change up the way you normally would play a Civ game. Details on the option can be found in the [Boostsanity](#boostsanity) section below. -There are a few changes that the Archipelago mod introduces in order to make this playable/fun. These are detailed in the __FAQ__ section below. +There are a few changes that the Archipelago mod introduces in order to make this playable/fun. These are detailed in the [__FAQ__](#faqs) section below. ## What is the goal of Civilization VI when randomized? -The goal of randomized Civlization VI remains the same. Pursue any victory type you have enabled in your game settings, the one you normally go for may or may not be feasible based on how things have been changed up! +The goal of randomized Civilization VI remains the same. Pursue any victory type you have enabled in your game settings, the one you normally go for may or may not be feasible based on how things have been changed up! ## Which items can be in another player's world? All technologies and civics can be found in another player's world. @@ -20,13 +20,13 @@ A short period after receiving an item, you will get a notification indicating y ## FAQs - Do I need the DLC to play this? - - Yes, you need both Rise & Fall and Gathering Storm. If there is enough interest then I can eventually add support for Archipellago runs that don't require both expansions. + - Yes, you need both Rise & Fall and Gathering Storm. If there is enough interest, then I can eventually add support for Archipelago runs that don't require both expansions. - Does this work with Multiplayer? - It does not and, despite my best efforts, probably won't until there's a new way for external programs to be able to interact with the game. -- Does my mod that reskins Barbarians as various Pro Wrestlers work with this?? +- Does my mod that reskins Barbarians as various Pro Wrestlers work with this? - Only one way to find out! Any mods that modify techs/civics will most likely cause issues, though. - "Help! I can't see any of the items that have been sent to me!" - - Both trees by default will show you the researchable Archipelago locations. To view the normal tree, you can click "Toggle Archipelago Tree" on the top left corner of the tree view. + - Both trees by default will show you the researchable Archipelago locations. To view the normal tree, you can click "Toggle Archipelago Tree" in the top-left corner of the tree view. - "Oh no! I received the Machinery tech and now instead of getting an Archer next turn, I have to wait an additional 10 turns to get a Crossbowman!" - Vanilla prevents you from building units of the same class from an earlier tech level after you have researched a later variant. For example, this could be problematic if someone unlocks Crossbowmen for you right out the gate since you won't be able to make Archers (which have a much lower production cost). Solution: You can now go in to the tech tree, click "Toggle Archipelago Tree" to view your unlocked techs, and then can click any tech you have unlocked to toggle whether it is currently active or not. @@ -35,25 +35,25 @@ Solution: You can now go in to the tech tree, click "Toggle Archipelago Tree" to In the event you lose one of your units in combat (this means captured units don't count), then you will send a death link event to the rest of your friends. - I enabled `progressive districts` but I have no idea techs/civics what items are locked behind progression now! - - Any technology or civic that grants you a new building in a district (or grants you the district itself) is now locked behind a progressive item. For example, `PROGRESSIVE_CAMPUS` would give you these items in the following order: - 1. `TECH_WRITING` - 2. `TECH_EDUCATION` + - Any technology or civic that grants you a new building in a district (or grants you the district itself) is now locked behind a progressive item. For example, `PROGRESSIVE_CAMPUS` would give you these items in the following order: + 1. `TECH_WRITING` + 2. `TECH_EDUCATION` 3. `TECH_CHEMISTRY` - - If you want to see the details around each item, you can review [this file](https://github.com/hesto2/civilization_vi_apworld/blob/main/data/progressive_districts.json) + - If you want to see the details around each item, you can review [this file](https://github.com/hesto2/civilization_vi_apworld/blob/main/data/progressive_districts.json). ## Boostsanity -Boostsanity takes all of the Eureka & Inspiration events and makes them location checks. This feature is the one to changeup the way Civilization is played in an AP multiworld/randomizer to date. What normally are mundane tasks that are passively collected now become a novel and interesting bucket list that you need to pay attention to in order to unlock items for yourself and others! -Boosts have logic associated with them in order to verify you can always reach the ones you need to, when you need to. One side effect of this is that when boostsanity is enabled, previously some "Useful" items are now flagged as "Progression" (Urbanization, Pottery, The Wheel, to name a few). +Boostsanity takes all of the Eureka & Inspiration events and makes them location checks. This feature is the one to change up the way Civilization is played in an AP multiworld/randomizer. What normally are mundane tasks that are passively collected now become a novel and interesting bucket list that you need to pay attention to in order to unlock items for yourself and others! +Boosts have logic associated with them in order to verify you can always reach the ones you need to, when you need to. One side effect of this is that when boostsanity is enabled, some previously "Useful" items are now flagged as "Progression" (Urbanization, Pottery, The Wheel, to name a few). ### Boostsanity FAQs -- Someone sent me a tech/civic and I'm worried I won't be able to boost it anymore! - - Fear not! The mod has been updated, and through a lot of wizardry 🧙‍♂️ you will be able to boost civics/techs that have already been received. Additionally the UI has been updated to show you whether they have been boosted or not after receiving them still. +- Someone sent me a tech/civic, and I'm worried I won't be able to boost it anymore! + - Fear not! Through a lot of wizardry 🧙‍♂️ you can boost civics/techs that have already been received. Additionally, the UI has been updated to show you whether they have been boosted or not after receiving them. - I need to kill a unit with a slinger/archer/musketman or some other obsolete unit I can't build anymore, how can I do this?? - Don't forget you can go into the Tech Tree and click on a Vanilla tech you've received in order to toggle it on/off. This is necessary in order to pursue some of the boosts if you receive techs in certain orders. -- Something happened and I'm not able to unlock the boost due to game rules! +- Something happened, and I'm not able to unlock the boost due to game rules! - A few scenarios you may worry about: "Found a religion", "Make an alliance with another player", "Develop an alliance to level 2", "Build a wonder from X Era", to name a few. Any boost that is "miss-able" has been flagged as an "Excluded" location and will not ever receive a progression item. For a list of how each boost is flagged, take a look [here](https://github.com/hesto2/civilization_vi_apworld/blob/main/data/boosts.json). - I'm worried that my `PROGRESSIVE_ERA` item is going to be stuck in a boost I won't have time to complete before my maximum unlocked era ends! - Due to the unpredictable timing of boosts and unlocking them, this could lead to a hard lock in certain scenarios. As a result, `PROGRESSIVE_ERA` items will never be located at a boost check. - There's too many boosts, how will I know which one's I should focus on?! - - In order to give a little more focus to all the boosts rather than just arbitrarily picking them at random, items in both of the vanilla trees will now have an advisor icon on them if it's associated boost contains a progression item. + - In order to give a little more focus to all the boosts rather than just arbitrarily picking them at random, items in both of the vanilla trees will now have an advisor icon on them if its associated boost contains a progression item. diff --git a/worlds/civ_6/docs/setup_en.md b/worlds/civ_6/docs/setup_en.md index 6e1e236287ff..f437b58fd956 100644 --- a/worlds/civ_6/docs/setup_en.md +++ b/worlds/civ_6/docs/setup_en.md @@ -1,16 +1,16 @@ # Setup Guide for Civilization VI Archipelago -This guide is meant to help you get up and running with Civlization VI in your Archipelago run. Note that this requires you to have both Rise & Fall as well as Gathering Storm installed. This will not work unless both of those DLCs are enabled. +This guide is meant to help you get up and running with Civilization VI in Archipelago. Note that this requires you to have both Rise & Fall and Gathering Storm installed. This will not work unless both of those DLCs are enabled. ## Requirements -The following are required in order to play Civ VI in Archipelago +The following are required in order to play Civ VI in Archipelago: - Windows OS (Firaxis does not support the necessary tooling for Mac, or Linux) -- Installed [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) v0.4.5 or higher.\ +- Installed [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) v0.4.5 or higher. -- The latest version of the [Civ VI AP Mod](https://github.com/hesto2/civilization_archipelago_mod). +- The latest version of the [Civ VI AP Mod](https://github.com/hesto2/civilization_archipelago_mod/releases/latest). ## Enabling the tuner @@ -19,11 +19,11 @@ Depending on how you installed Civ 6 you will have to navigate to one of the fol - `YOUR_USER/Documents/My Games/Sid Meier's Civilization VI/AppOptions.txt` - `YOUR_USER/AppData/Local/Firaxis Games/Sid Meier's Civilization VI/AppOptions.txt` -Once you have located your `AppOptions.txt`, do a search for `Enable FireTuner`. Set `EnableTuner` to `1` instead of `0`. **NOTE**: While this is active, achievments will be disabled. +Once you have located your `AppOptions.txt`, do a search for `Enable FireTuner`. Set `EnableTuner` to `1` instead of `0`. **NOTE**: While this is active, achievements will be disabled. ## Mod Installation -1. Download and unzip the latest release of the mod from [github](https://github.com/hesto2/civilization_archipelago_mod/releases). +1. Download and unzip the latest release of the mod from [GitHub](https://github.com/hesto2/civilization_archipelago_mod/releases/latest). 2. Copy the folder containing the mod files to your Civ VI mods folder. On Windows, this is usually located at `C:\Users\YOUR_USER\Documents\My Games\Sid Meier's Civilization VI\Mods` @@ -46,6 +46,6 @@ When configuring your game, make sure to start the game in the Ancient Era and l ## Troubleshooting -- If you are getting an error: `The remote computer refused the network connection`, or something else related to the client (or tuner) not being able to connect, it likely indicates the tuner is not actually enabled. One simple way to verify that it is enabled is, after completing the setup steps, to go Main Menu -> Options -> Look for an option named "Tuner" and verify it is set to "Enabled" +- If you are getting an error: "The remote computer refused the network connection", or something else related to the client (or tuner) not being able to connect, it likely indicates the tuner is not actually enabled. One simple way to verify that it is enabled is, after completing the setup steps, go to Main Menu → Options → Look for an option named "Tuner" and verify it is set to "Enabled" - If your game gets in a state where someone has sent you items or you have sent locations but these are not correctly replicated to the multiworld, you can run `/resync` from the Civ 6 client. This may take up to a minute depending on how many items there are. From 99c54328b73fe97610df16ef014d80fdad71c5ec Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 15 Oct 2024 18:51:10 -0600 Subject: [PATCH 32/69] Fix failing test due to slot data serialization --- worlds/civ_6/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index a81772c9038b..bcd166af981b 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -176,11 +176,11 @@ def post_fill(self) -> None: def fill_slot_data(self) -> Dict[str, Any]: return { - "progression_style": self.options.progression_style, - "death_link": self.options.death_link, - "research_cost_multiplier": self.options.research_cost_multiplier, - "death_link_effect": self.options.death_link_effect, - "death_link_effect_percent": self.options.death_link_effect_percent, + "progression_style": self.options.progression_style.value, + "death_link": self.options.death_link.value, + "research_cost_multiplier": self.options.research_cost_multiplier.value, + "death_link_effect": self.options.death_link_effect.value, + "death_link_effect_percent": self.options.death_link_effect_percent.value, } From a6eba682ce4e6d59840d7dab31ab909a9b7573b6 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 15 Oct 2024 19:11:39 -0600 Subject: [PATCH 33/69] Format json --- worlds/civ_6/data/boosts.json | 5 +- worlds/civ_6/data/existing_civics.json | 856 +++++++------- worlds/civ_6/data/existing_tech.json | 1080 +++++++++--------- worlds/civ_6/data/goody_hut_rewards.json | 152 +-- worlds/civ_6/data/new_civic_prereqs.json | 107 +- worlds/civ_6/data/new_civics.json | 429 ++++++- worlds/civ_6/data/new_tech.json | 541 +++++++-- worlds/civ_6/data/new_tech_prereqs.json | 309 +++-- worlds/civ_6/data/progressive_districts.json | 11 +- 9 files changed, 2165 insertions(+), 1325 deletions(-) diff --git a/worlds/civ_6/data/boosts.json b/worlds/civ_6/data/boosts.json index e2658c0c4c69..0662e0e9c657 100644 --- a/worlds/civ_6/data/boosts.json +++ b/worlds/civ_6/data/boosts.json @@ -376,10 +376,7 @@ { "Type": "BOOST_TECH_COMBINED_ARMS", "EraType": "ERA_ATOMIC", - "Prereq": [ - "CIVIC_MOBILIZATION", - "CIVIC_NATIONALISM" - ], + "Prereq": ["CIVIC_MOBILIZATION", "CIVIC_NATIONALISM"], "PrereqRequiredCount": 2, "Classification": "DEFAULT" }, diff --git a/worlds/civ_6/data/existing_civics.json b/worlds/civ_6/data/existing_civics.json index c4d871cfd39f..d63cc24e86de 100644 --- a/worlds/civ_6/data/existing_civics.json +++ b/worlds/civ_6/data/existing_civics.json @@ -1,429 +1,429 @@ [ - { - "Type": "CIVIC_CODE_OF_LAWS", - "Name": "Code of Laws", - "Cost": 20, - "EraType": "ERA_ANCIENT", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_CRAFTSMANSHIP", - "Name": "Craftsmanship", - "Cost": 40, - "EraType": "ERA_ANCIENT", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_FOREIGN_TRADE", - "Name": "Foreign Trade", - "Cost": 40, - "EraType": "ERA_ANCIENT", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_MILITARY_TRADITION", - "Name": "Military Tradition", - "Cost": 50, - "EraType": "ERA_ANCIENT", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_STATE_WORKFORCE", - "Name": "State Workforce", - "Cost": 70, - "EraType": "ERA_ANCIENT", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_EARLY_EMPIRE", - "Name": "Early Empire", - "Cost": 70, - "EraType": "ERA_ANCIENT", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_MYSTICISM", - "Name": "Mysticism", - "Cost": 50, - "EraType": "ERA_ANCIENT", - "UITreeRow": 3 - }, - { - "Type": "CIVIC_GAMES_RECREATION", - "Name": "Games Recreation", - "Cost": 110, - "EraType": "ERA_CLASSICAL", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_POLITICAL_PHILOSOPHY", - "Name": "Political Philosophy", - "Cost": 110, - "EraType": "ERA_CLASSICAL", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_DRAMA_POETRY", - "Name": "Drama and Poetry", - "Cost": 110, - "EraType": "ERA_CLASSICAL", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_MILITARY_TRAINING", - "Name": "Military Training", - "Cost": 120, - "EraType": "ERA_CLASSICAL", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_DEFENSIVE_TACTICS", - "Name": "Defensive Tactics", - "Cost": 175, - "EraType": "ERA_CLASSICAL", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_RECORDED_HISTORY", - "Name": "Recorded History", - "Cost": 175, - "EraType": "ERA_CLASSICAL", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_THEOLOGY", - "Name": "Theology", - "Cost": 120, - "EraType": "ERA_CLASSICAL", - "UITreeRow": 3 - }, - { - "Type": "CIVIC_NAVAL_TRADITION", - "Name": "Naval Tradition", - "Cost": 220, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_FEUDALISM", - "Name": "Feudalism", - "Cost": 300, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_CIVIL_SERVICE", - "Name": "Civil Service", - "Cost": 300, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_MERCENARIES", - "Name": "Mercenaries", - "Cost": 340, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_MEDIEVAL_FAIRES", - "Name": "Medieval Faires", - "Cost": 420, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_GUILDS", - "Name": "Guilds", - "Cost": 420, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_DIVINE_RIGHT", - "Name": "Divine Right", - "Cost": 340, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": 3 - }, - { - "Type": "CIVIC_EXPLORATION", - "Name": "Exploration", - "Cost": 440, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_HUMANISM", - "Name": "Humanism", - "Cost": 600, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_DIPLOMATIC_SERVICE", - "Name": "Diplomatic Service", - "Cost": 600, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_REFORMED_CHURCH", - "Name": "Reformed Church", - "Cost": 440, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": 3 - }, - { - "Type": "CIVIC_MERCANTILISM", - "Name": "Mercantilism", - "Cost": 720, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_THE_ENLIGHTENMENT", - "Name": "The Enlightenment", - "Cost": 720, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_COLONIALISM", - "Name": "Colonialism", - "Cost": 800, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_CIVIL_ENGINEERING", - "Name": "Civil Engineering", - "Cost": 1010, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_NATIONALISM", - "Name": "Nationalism", - "Cost": 1010, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_OPERA_BALLET", - "Name": "Opera and Ballet", - "Cost": 800, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_NATURAL_HISTORY", - "Name": "Natural History", - "Cost": 1050, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_SCORCHED_EARTH", - "Name": "Scorched Earth", - "Cost": 1210, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_URBANIZATION", - "Name": "Urbanization", - "Cost": 1210, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_CONSERVATION", - "Name": "Conservation", - "Cost": 1540, - "EraType": "ERA_MODERN", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_CAPITALISM", - "Name": "Capitalism", - "Cost": 1580, - "EraType": "ERA_MODERN", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_NUCLEAR_PROGRAM", - "Name": "Nuclear Program", - "Cost": 1715, - "EraType": "ERA_MODERN", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_MASS_MEDIA", - "Name": "Mass Media", - "Cost": 1540, - "EraType": "ERA_MODERN", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_MOBILIZATION", - "Name": "Mobilization", - "Cost": 1540, - "EraType": "ERA_MODERN", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_IDEOLOGY", - "Name": "Ideology", - "Cost": 1640, - "EraType": "ERA_MODERN", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_SUFFRAGE", - "Name": "Suffrage", - "Cost": 1640, - "EraType": "ERA_MODERN", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_TOTALITARIANISM", - "Name": "Totalitarianism", - "Cost": 1640, - "EraType": "ERA_MODERN", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_CLASS_STRUGGLE", - "Name": "Class Struggle", - "Cost": 1640, - "EraType": "ERA_MODERN", - "UITreeRow": 3 - }, - { - "Type": "CIVIC_COLD_WAR", - "Name": "Cold War", - "Cost": 2185, - "EraType": "ERA_ATOMIC", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_PROFESSIONAL_SPORTS", - "Name": "Professional Sports", - "Cost": 2185, - "EraType": "ERA_ATOMIC", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_CULTURAL_HERITAGE", - "Name": "Cultural Heritage", - "Cost": 1955, - "EraType": "ERA_ATOMIC", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_RAPID_DEPLOYMENT", - "Name": "Rapid Deployment", - "Cost": 2415, - "EraType": "ERA_ATOMIC", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_SPACE_RACE", - "Name": "Space Race", - "Cost": 2415, - "EraType": "ERA_ATOMIC", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_GLOBALIZATION", - "Name": "Globalization", - "Cost": 2880, - "EraType": "ERA_INFORMATION", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_SOCIAL_MEDIA", - "Name": "Social Media", - "Cost": 2880, - "EraType": "ERA_INFORMATION", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_FUTURE_CIVIC", - "Name": "Future Civic", - "Cost": 3500, - "EraType": "ERA_FUTURE", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_ENVIRONMENTALISM", - "Name": "Environmentalism", - "Cost": 2880, - "EraType": "ERA_INFORMATION", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_CORPORATE_LIBERTARIANISM", - "Name": "Corporate Libertarianism", - "Cost": 3000, - "EraType": "ERA_INFORMATION", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_DIGITAL_DEMOCRACY", - "Name": "Digital Democracy", - "Cost": 3000, - "EraType": "ERA_INFORMATION", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_SYNTHETIC_TECHNOCRACY", - "Name": "Synthetic Technocracy", - "Cost": 3000, - "EraType": "ERA_INFORMATION", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_NEAR_FUTURE_GOVERNANCE", - "Name": "Near Future Governance", - "Cost": 3100, - "EraType": "ERA_INFORMATION", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_GLOBAL_WARMING_MITIGATION", - "Name": "Global Warming Mitigation", - "Cost": 3200, - "EraType": "ERA_FUTURE", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_SMART_POWER_DOCTRINE", - "Name": "Smart Power Doctrine", - "Cost": 3200, - "EraType": "ERA_FUTURE", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_INFORMATION_WARFARE", - "Name": "Information Warfare", - "Cost": 3200, - "EraType": "ERA_FUTURE", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_EXODUS_IMPERATIVE", - "Name": "Exodus Imperative", - "Cost": 3200, - "EraType": "ERA_FUTURE", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_CULTURAL_HEGEMONY", - "Name": "Cultural Hegemony", - "Cost": 3200, - "EraType": "ERA_FUTURE", - "UITreeRow": 2 - } -] \ No newline at end of file + { + "Type": "CIVIC_CODE_OF_LAWS", + "Name": "Code of Laws", + "Cost": 20, + "EraType": "ERA_ANCIENT", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_CRAFTSMANSHIP", + "Name": "Craftsmanship", + "Cost": 40, + "EraType": "ERA_ANCIENT", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_FOREIGN_TRADE", + "Name": "Foreign Trade", + "Cost": 40, + "EraType": "ERA_ANCIENT", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_MILITARY_TRADITION", + "Name": "Military Tradition", + "Cost": 50, + "EraType": "ERA_ANCIENT", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_STATE_WORKFORCE", + "Name": "State Workforce", + "Cost": 70, + "EraType": "ERA_ANCIENT", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_EARLY_EMPIRE", + "Name": "Early Empire", + "Cost": 70, + "EraType": "ERA_ANCIENT", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_MYSTICISM", + "Name": "Mysticism", + "Cost": 50, + "EraType": "ERA_ANCIENT", + "UITreeRow": 3 + }, + { + "Type": "CIVIC_GAMES_RECREATION", + "Name": "Games Recreation", + "Cost": 110, + "EraType": "ERA_CLASSICAL", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_POLITICAL_PHILOSOPHY", + "Name": "Political Philosophy", + "Cost": 110, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_DRAMA_POETRY", + "Name": "Drama and Poetry", + "Cost": 110, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_MILITARY_TRAINING", + "Name": "Military Training", + "Cost": 120, + "EraType": "ERA_CLASSICAL", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_DEFENSIVE_TACTICS", + "Name": "Defensive Tactics", + "Cost": 175, + "EraType": "ERA_CLASSICAL", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_RECORDED_HISTORY", + "Name": "Recorded History", + "Cost": 175, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_THEOLOGY", + "Name": "Theology", + "Cost": 120, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 3 + }, + { + "Type": "CIVIC_NAVAL_TRADITION", + "Name": "Naval Tradition", + "Cost": 220, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_FEUDALISM", + "Name": "Feudalism", + "Cost": 300, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_CIVIL_SERVICE", + "Name": "Civil Service", + "Cost": 300, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_MERCENARIES", + "Name": "Mercenaries", + "Cost": 340, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_MEDIEVAL_FAIRES", + "Name": "Medieval Faires", + "Cost": 420, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_GUILDS", + "Name": "Guilds", + "Cost": 420, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_DIVINE_RIGHT", + "Name": "Divine Right", + "Cost": 340, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": 3 + }, + { + "Type": "CIVIC_EXPLORATION", + "Name": "Exploration", + "Cost": 440, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_HUMANISM", + "Name": "Humanism", + "Cost": 600, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_DIPLOMATIC_SERVICE", + "Name": "Diplomatic Service", + "Cost": 600, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_REFORMED_CHURCH", + "Name": "Reformed Church", + "Cost": 440, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": 3 + }, + { + "Type": "CIVIC_MERCANTILISM", + "Name": "Mercantilism", + "Cost": 720, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_THE_ENLIGHTENMENT", + "Name": "The Enlightenment", + "Cost": 720, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_COLONIALISM", + "Name": "Colonialism", + "Cost": 800, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_CIVIL_ENGINEERING", + "Name": "Civil Engineering", + "Cost": 1010, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_NATIONALISM", + "Name": "Nationalism", + "Cost": 1010, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_OPERA_BALLET", + "Name": "Opera and Ballet", + "Cost": 800, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_NATURAL_HISTORY", + "Name": "Natural History", + "Cost": 1050, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_SCORCHED_EARTH", + "Name": "Scorched Earth", + "Cost": 1210, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_URBANIZATION", + "Name": "Urbanization", + "Cost": 1210, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_CONSERVATION", + "Name": "Conservation", + "Cost": 1540, + "EraType": "ERA_MODERN", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_CAPITALISM", + "Name": "Capitalism", + "Cost": 1580, + "EraType": "ERA_MODERN", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_NUCLEAR_PROGRAM", + "Name": "Nuclear Program", + "Cost": 1715, + "EraType": "ERA_MODERN", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_MASS_MEDIA", + "Name": "Mass Media", + "Cost": 1540, + "EraType": "ERA_MODERN", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_MOBILIZATION", + "Name": "Mobilization", + "Cost": 1540, + "EraType": "ERA_MODERN", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_IDEOLOGY", + "Name": "Ideology", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_SUFFRAGE", + "Name": "Suffrage", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_TOTALITARIANISM", + "Name": "Totalitarianism", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_CLASS_STRUGGLE", + "Name": "Class Struggle", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": 3 + }, + { + "Type": "CIVIC_COLD_WAR", + "Name": "Cold War", + "Cost": 2185, + "EraType": "ERA_ATOMIC", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_PROFESSIONAL_SPORTS", + "Name": "Professional Sports", + "Cost": 2185, + "EraType": "ERA_ATOMIC", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_CULTURAL_HERITAGE", + "Name": "Cultural Heritage", + "Cost": 1955, + "EraType": "ERA_ATOMIC", + "UITreeRow": -3 + }, + { + "Type": "CIVIC_RAPID_DEPLOYMENT", + "Name": "Rapid Deployment", + "Cost": 2415, + "EraType": "ERA_ATOMIC", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_SPACE_RACE", + "Name": "Space Race", + "Cost": 2415, + "EraType": "ERA_ATOMIC", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_GLOBALIZATION", + "Name": "Globalization", + "Cost": 2880, + "EraType": "ERA_INFORMATION", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_SOCIAL_MEDIA", + "Name": "Social Media", + "Cost": 2880, + "EraType": "ERA_INFORMATION", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_FUTURE_CIVIC", + "Name": "Future Civic", + "Cost": 3500, + "EraType": "ERA_FUTURE", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_ENVIRONMENTALISM", + "Name": "Environmentalism", + "Cost": 2880, + "EraType": "ERA_INFORMATION", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_CORPORATE_LIBERTARIANISM", + "Name": "Corporate Libertarianism", + "Cost": 3000, + "EraType": "ERA_INFORMATION", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_DIGITAL_DEMOCRACY", + "Name": "Digital Democracy", + "Cost": 3000, + "EraType": "ERA_INFORMATION", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_SYNTHETIC_TECHNOCRACY", + "Name": "Synthetic Technocracy", + "Cost": 3000, + "EraType": "ERA_INFORMATION", + "UITreeRow": 2 + }, + { + "Type": "CIVIC_NEAR_FUTURE_GOVERNANCE", + "Name": "Near Future Governance", + "Cost": 3100, + "EraType": "ERA_INFORMATION", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_GLOBAL_WARMING_MITIGATION", + "Name": "Global Warming Mitigation", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": -2 + }, + { + "Type": "CIVIC_SMART_POWER_DOCTRINE", + "Name": "Smart Power Doctrine", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": -1 + }, + { + "Type": "CIVIC_INFORMATION_WARFARE", + "Name": "Information Warfare", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": 0 + }, + { + "Type": "CIVIC_EXODUS_IMPERATIVE", + "Name": "Exodus Imperative", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": 1 + }, + { + "Type": "CIVIC_CULTURAL_HEGEMONY", + "Name": "Cultural Hegemony", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": 2 + } +] diff --git a/worlds/civ_6/data/existing_tech.json b/worlds/civ_6/data/existing_tech.json index 21869101d20c..109df28fadd8 100644 --- a/worlds/civ_6/data/existing_tech.json +++ b/worlds/civ_6/data/existing_tech.json @@ -1,541 +1,541 @@ [ - { - "Type": "TECH_POTTERY", - "Cost": 25, - "UITreeRow": 0, - "EraType": "ERA_ANCIENT", - "Name": "Pottery" - }, - { - "Type": "TECH_ANIMAL_HUSBANDRY", - "Cost": 25, - "UITreeRow": 1, - "EraType": "ERA_ANCIENT", - "Name": "Animal Husbandry" - }, - { - "Type": "TECH_MINING", - "Cost": 25, - "UITreeRow": 3, - "EraType": "ERA_ANCIENT", - "Name": "Mining" - }, - { - "Type": "TECH_SAILING", - "Cost": 50, - "UITreeRow": -3, - "EraType": "ERA_ANCIENT", - "Name": "Sailing" - }, - { - "Type": "TECH_ASTROLOGY", - "Cost": 50, - "UITreeRow": -2, - "EraType": "ERA_ANCIENT", - "Name": "Astrology" - }, - { - "Type": "TECH_IRRIGATION", - "Cost": 50, - "UITreeRow": -1, - "EraType": "ERA_ANCIENT", - "Name": "Irrigation" - }, - { - "Type": "TECH_ARCHERY", - "Cost": 50, - "UITreeRow": 1, - "EraType": "ERA_ANCIENT", - "Name": "Archery" - }, - { - "Type": "TECH_WRITING", - "Cost": 50, - "UITreeRow": 0, - "EraType": "ERA_ANCIENT", - "Name": "Writing" - }, - { - "Type": "TECH_MASONRY", - "Cost": 80, - "UITreeRow": 2, - "EraType": "ERA_ANCIENT", - "Name": "Masonry" - }, - { - "Type": "TECH_BRONZE_WORKING", - "Cost": 80, - "UITreeRow": 3, - "EraType": "ERA_ANCIENT", - "Name": "Bronze Working" - }, - { - "Type": "TECH_THE_WHEEL", - "Cost": 80, - "UITreeRow": 4, - "EraType": "ERA_ANCIENT", - "Name": "The Wheel" - }, - { - "Type": "TECH_CELESTIAL_NAVIGATION", - "Cost": 120, - "UITreeRow": -2, - "EraType": "ERA_CLASSICAL", - "Name": "Celestial Navigation" - }, - { - "Type": "TECH_CURRENCY", - "Cost": 120, - "UITreeRow": 0, - "EraType": "ERA_CLASSICAL", - "Name": "Currency" - }, - { - "Type": "TECH_HORSEBACK_RIDING", - "Cost": 120, - "UITreeRow": 1, - "EraType": "ERA_CLASSICAL", - "Name": "Horseback Riding" - }, - { - "Type": "TECH_IRON_WORKING", - "Cost": 120, - "UITreeRow": 3, - "EraType": "ERA_CLASSICAL", - "Name": "Iron Working" - }, - { - "Type": "TECH_SHIPBUILDING", - "Cost": 200, - "UITreeRow": -3, - "EraType": "ERA_CLASSICAL", - "Name": "Shipbuilding" - }, - { - "Type": "TECH_MATHEMATICS", - "Cost": 200, - "UITreeRow": -1, - "EraType": "ERA_CLASSICAL", - "Name": "Mathematics" - }, - { - "Type": "TECH_CONSTRUCTION", - "Cost": 200, - "UITreeRow": 2, - "EraType": "ERA_CLASSICAL", - "Name": "Construction" - }, - { - "Type": "TECH_ENGINEERING", - "Cost": 200, - "UITreeRow": 4, - "EraType": "ERA_CLASSICAL", - "Name": "Engineering" - }, - { - "Type": "TECH_MILITARY_TACTICS", - "Cost": 300, - "UITreeRow": -2, - "EraType": "ERA_MEDIEVAL", - "Name": "Military Tactics" - }, - { - "Type": "TECH_APPRENTICESHIP", - "Cost": 300, - "UITreeRow": 0, - "EraType": "ERA_MEDIEVAL", - "Name": "Apprenticeship" - }, - { - "Type": "TECH_MACHINERY", - "Cost": 300, - "UITreeRow": 4, - "EraType": "ERA_MEDIEVAL", - "Name": "Machinery" - }, - { - "Type": "TECH_EDUCATION", - "Cost": 390, - "UITreeRow": -1, - "EraType": "ERA_MEDIEVAL", - "Name": "Education" - }, - { - "Type": "TECH_STIRRUPS", - "Cost": 390, - "UITreeRow": 1, - "EraType": "ERA_MEDIEVAL", - "Name": "Stirrups" - }, - { - "Type": "TECH_MILITARY_ENGINEERING", - "Cost": 390, - "UITreeRow": 2, - "EraType": "ERA_MEDIEVAL", - "Name": "Military Engineering" - }, - { - "Type": "TECH_CASTLES", - "Cost": 390, - "UITreeRow": 3, - "EraType": "ERA_MEDIEVAL", - "Name": "Castles" - }, - { - "Type": "TECH_CARTOGRAPHY", - "Cost": 600, - "UITreeRow": -3, - "EraType": "ERA_RENAISSANCE", - "Name": "Cartography" - }, - { - "Type": "TECH_MASS_PRODUCTION", - "Cost": 600, - "UITreeRow": -2, - "EraType": "ERA_RENAISSANCE", - "Name": "Mass Production" - }, - { - "Type": "TECH_BANKING", - "Cost": 600, - "UITreeRow": 0, - "EraType": "ERA_RENAISSANCE", - "Name": "Banking" - }, - { - "Type": "TECH_GUNPOWDER", - "Cost": 600, - "UITreeRow": 1, - "EraType": "ERA_RENAISSANCE", - "Name": "Gunpowder" - }, - { - "Type": "TECH_PRINTING", - "Cost": 600, - "UITreeRow": 4, - "EraType": "ERA_RENAISSANCE", - "Name": "Printing" - }, - { - "Type": "TECH_SQUARE_RIGGING", - "Cost": 730, - "UITreeRow": -3, - "EraType": "ERA_RENAISSANCE", - "Name": "Square Rigging" - }, - { - "Type": "TECH_ASTRONOMY", - "Cost": 730, - "UITreeRow": -1, - "EraType": "ERA_RENAISSANCE", - "Name": "Astronomy" - }, - { - "Type": "TECH_METAL_CASTING", - "Cost": 730, - "UITreeRow": 1, - "EraType": "ERA_RENAISSANCE", - "Name": "Metal Casting" - }, - { - "Type": "TECH_SIEGE_TACTICS", - "Cost": 730, - "UITreeRow": 3, - "EraType": "ERA_RENAISSANCE", - "Name": "Siege Tactics" - }, - { - "Type": "TECH_INDUSTRIALIZATION", - "Cost": 930, - "UITreeRow": -2, - "EraType": "ERA_INDUSTRIAL", - "Name": "Industrialization" - }, - { - "Type": "TECH_SCIENTIFIC_THEORY", - "Cost": 930, - "UITreeRow": -1, - "EraType": "ERA_INDUSTRIAL", - "Name": "Scientific Theory" - }, - { - "Type": "TECH_BALLISTICS", - "Cost": 930, - "UITreeRow": 1, - "EraType": "ERA_INDUSTRIAL", - "Name": "Ballistics" - }, - { - "Type": "TECH_MILITARY_SCIENCE", - "Cost": 930, - "UITreeRow": 3, - "EraType": "ERA_INDUSTRIAL", - "Name": "Military Science" - }, - { - "Type": "TECH_STEAM_POWER", - "Cost": 1070, - "UITreeRow": -3, - "EraType": "ERA_INDUSTRIAL", - "Name": "Steam Power" - }, - { - "Type": "TECH_SANITATION", - "Cost": 1070, - "UITreeRow": -1, - "EraType": "ERA_INDUSTRIAL", - "Name": "Sanitation" - }, - { - "Type": "TECH_ECONOMICS", - "Cost": 1070, - "UITreeRow": 0, - "EraType": "ERA_INDUSTRIAL", - "Name": "Economics" - }, - { - "Type": "TECH_RIFLING", - "Cost": 1070, - "UITreeRow": 2, - "EraType": "ERA_INDUSTRIAL", - "Name": "Rifling" - }, - { - "Type": "TECH_FLIGHT", - "Cost": 1250, - "UITreeRow": -2, - "EraType": "ERA_MODERN", - "Name": "Flight" - }, - { - "Type": "TECH_REPLACEABLE_PARTS", - "Cost": 1250, - "UITreeRow": 0, - "EraType": "ERA_MODERN", - "Name": "Replaceable Parts" - }, - { - "Type": "TECH_STEEL", - "Cost": 1250, - "UITreeRow": 1, - "EraType": "ERA_MODERN", - "Name": "Steel" - }, - { - "Type": "TECH_ELECTRICITY", - "Cost": 1370, - "UITreeRow": -3, - "EraType": "ERA_MODERN", - "Name": "Electricity" - }, - { - "Type": "TECH_RADIO", - "Cost": 1370, - "UITreeRow": -2, - "EraType": "ERA_MODERN", - "Name": "Radio" - }, - { - "Type": "TECH_CHEMISTRY", - "Cost": 1370, - "UITreeRow": -1, - "EraType": "ERA_MODERN", - "Name": "Chemistry" - }, - { - "Type": "TECH_COMBUSTION", - "Cost": 1370, - "UITreeRow": 2, - "EraType": "ERA_MODERN", - "Name": "Combustion" - }, - { - "Type": "TECH_ADVANCED_FLIGHT", - "Cost": 1480, - "UITreeRow": -2, - "EraType": "ERA_ATOMIC", - "Name": "Advanced Flight" - }, - { - "Type": "TECH_ROCKETRY", - "Cost": 1480, - "UITreeRow": -1, - "EraType": "ERA_ATOMIC", - "Name": "Rocketry" - }, - { - "Type": "TECH_ADVANCED_BALLISTICS", - "Cost": 1480, - "UITreeRow": 0, - "EraType": "ERA_ATOMIC", - "Name": "Advanced Ballistics" - }, - { - "Type": "TECH_COMBINED_ARMS", - "Cost": 1480, - "UITreeRow": 1, - "EraType": "ERA_ATOMIC", - "Name": "Combined Arms" - }, - { - "Type": "TECH_PLASTICS", - "Cost": 1480, - "UITreeRow": 2, - "EraType": "ERA_ATOMIC", - "Name": "Plastics" - }, - { - "Type": "TECH_COMPUTERS", - "Cost": 1660, - "UITreeRow": -3, - "EraType": "ERA_ATOMIC", - "Name": "Computers" - }, - { - "Type": "TECH_NUCLEAR_FISSION", - "Cost": 1660, - "UITreeRow": 1, - "EraType": "ERA_ATOMIC", - "Name": "Nuclear Fission" - }, - { - "Type": "TECH_SYNTHETIC_MATERIALS", - "Cost": 1660, - "UITreeRow": 2, - "EraType": "ERA_ATOMIC", - "Name": "Synthetic Materials" - }, - { - "Type": "TECH_TELECOMMUNICATIONS", - "Cost": 1850, - "UITreeRow": -3, - "EraType": "ERA_INFORMATION", - "Name": "Telecommunications" - }, - { - "Type": "TECH_SATELLITES", - "Cost": 1850, - "UITreeRow": -1, - "EraType": "ERA_INFORMATION", - "Name": "Satellites" - }, - { - "Type": "TECH_GUIDANCE_SYSTEMS", - "Cost": 1850, - "UITreeRow": 0, - "EraType": "ERA_INFORMATION", - "Name": "Guidance Systems" - }, - { - "Type": "TECH_LASERS", - "Cost": 1850, - "UITreeRow": 1, - "EraType": "ERA_INFORMATION", - "Name": "Lasers" - }, - { - "Type": "TECH_COMPOSITES", - "Cost": 1850, - "UITreeRow": 2, - "EraType": "ERA_INFORMATION", - "Name": "Composites" - }, - { - "Type": "TECH_STEALTH_TECHNOLOGY", - "Cost": 1850, - "UITreeRow": 3, - "EraType": "ERA_INFORMATION", - "Name": "Stealth Technology" - }, - { - "Type": "TECH_ROBOTICS", - "Cost": 2155, - "UITreeRow": -2, - "EraType": "ERA_INFORMATION", - "Name": "Robotics" - }, - { - "Type": "TECH_NANOTECHNOLOGY", - "Cost": 2155, - "UITreeRow": 2, - "EraType": "ERA_INFORMATION", - "Name": "Nanotechnology" - }, - { - "Type": "TECH_NUCLEAR_FUSION", - "Cost": 2155, - "UITreeRow": 1, - "EraType": "ERA_INFORMATION", - "Name": "Nuclear Fusion" - }, - { - "Type": "TECH_BUTTRESS", - "Cost": 300, - "UITreeRow": -3, - "EraType": "ERA_MEDIEVAL", - "Name": "Buttress" - }, - { - "Type": "TECH_REFINING", - "Cost": 1250, - "UITreeRow": 3, - "EraType": "ERA_MODERN", - "Name": "Refining" - }, - { - "Type": "TECH_SEASTEADS", - "Cost": 2200, - "UITreeRow": -3, - "EraType": "ERA_FUTURE", - "Name": "Seasteads" - }, - { - "Type": "TECH_ADVANCED_AI", - "Cost": 2200, - "UITreeRow": -2, - "EraType": "ERA_FUTURE", - "Name": "Advanced AI" - }, - { - "Type": "TECH_ADVANCED_POWER_CELLS", - "Cost": 2200, - "UITreeRow": -1, - "EraType": "ERA_FUTURE", - "Name": "Advanced Power Cells" - }, - { - "Type": "TECH_CYBERNETICS", - "Cost": 2200, - "UITreeRow": 0, - "EraType": "ERA_FUTURE", - "Name": "Cybernetics" - }, - { - "Type": "TECH_SMART_MATERIALS", - "Cost": 2200, - "UITreeRow": 1, - "EraType": "ERA_FUTURE", - "Name": "Smart Materials" - }, - { - "Type": "TECH_PREDICTIVE_SYSTEMS", - "Cost": 2200, - "UITreeRow": 2, - "EraType": "ERA_FUTURE", - "Name": "Predictive Systems" - }, - { - "Type": "TECH_OFFWORLD_MISSION", - "Cost": 2500, - "UITreeRow": 0, - "EraType": "ERA_FUTURE", - "Name": "Offworld Mission" - }, - { - "Type": "TECH_FUTURE_TECH", - "Cost": 2600, - "UITreeRow": 0, - "EraType": "ERA_FUTURE", - "Name": "Future Tech" - } -] \ No newline at end of file + { + "Type": "TECH_POTTERY", + "Cost": 25, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT", + "Name": "Pottery" + }, + { + "Type": "TECH_ANIMAL_HUSBANDRY", + "Cost": 25, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT", + "Name": "Animal Husbandry" + }, + { + "Type": "TECH_MINING", + "Cost": 25, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT", + "Name": "Mining" + }, + { + "Type": "TECH_SAILING", + "Cost": 50, + "UITreeRow": -3, + "EraType": "ERA_ANCIENT", + "Name": "Sailing" + }, + { + "Type": "TECH_ASTROLOGY", + "Cost": 50, + "UITreeRow": -2, + "EraType": "ERA_ANCIENT", + "Name": "Astrology" + }, + { + "Type": "TECH_IRRIGATION", + "Cost": 50, + "UITreeRow": -1, + "EraType": "ERA_ANCIENT", + "Name": "Irrigation" + }, + { + "Type": "TECH_ARCHERY", + "Cost": 50, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT", + "Name": "Archery" + }, + { + "Type": "TECH_WRITING", + "Cost": 50, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT", + "Name": "Writing" + }, + { + "Type": "TECH_MASONRY", + "Cost": 80, + "UITreeRow": 2, + "EraType": "ERA_ANCIENT", + "Name": "Masonry" + }, + { + "Type": "TECH_BRONZE_WORKING", + "Cost": 80, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT", + "Name": "Bronze Working" + }, + { + "Type": "TECH_THE_WHEEL", + "Cost": 80, + "UITreeRow": 4, + "EraType": "ERA_ANCIENT", + "Name": "The Wheel" + }, + { + "Type": "TECH_CELESTIAL_NAVIGATION", + "Cost": 120, + "UITreeRow": -2, + "EraType": "ERA_CLASSICAL", + "Name": "Celestial Navigation" + }, + { + "Type": "TECH_CURRENCY", + "Cost": 120, + "UITreeRow": 0, + "EraType": "ERA_CLASSICAL", + "Name": "Currency" + }, + { + "Type": "TECH_HORSEBACK_RIDING", + "Cost": 120, + "UITreeRow": 1, + "EraType": "ERA_CLASSICAL", + "Name": "Horseback Riding" + }, + { + "Type": "TECH_IRON_WORKING", + "Cost": 120, + "UITreeRow": 3, + "EraType": "ERA_CLASSICAL", + "Name": "Iron Working" + }, + { + "Type": "TECH_SHIPBUILDING", + "Cost": 200, + "UITreeRow": -3, + "EraType": "ERA_CLASSICAL", + "Name": "Shipbuilding" + }, + { + "Type": "TECH_MATHEMATICS", + "Cost": 200, + "UITreeRow": -1, + "EraType": "ERA_CLASSICAL", + "Name": "Mathematics" + }, + { + "Type": "TECH_CONSTRUCTION", + "Cost": 200, + "UITreeRow": 2, + "EraType": "ERA_CLASSICAL", + "Name": "Construction" + }, + { + "Type": "TECH_ENGINEERING", + "Cost": 200, + "UITreeRow": 4, + "EraType": "ERA_CLASSICAL", + "Name": "Engineering" + }, + { + "Type": "TECH_MILITARY_TACTICS", + "Cost": 300, + "UITreeRow": -2, + "EraType": "ERA_MEDIEVAL", + "Name": "Military Tactics" + }, + { + "Type": "TECH_APPRENTICESHIP", + "Cost": 300, + "UITreeRow": 0, + "EraType": "ERA_MEDIEVAL", + "Name": "Apprenticeship" + }, + { + "Type": "TECH_MACHINERY", + "Cost": 300, + "UITreeRow": 4, + "EraType": "ERA_MEDIEVAL", + "Name": "Machinery" + }, + { + "Type": "TECH_EDUCATION", + "Cost": 390, + "UITreeRow": -1, + "EraType": "ERA_MEDIEVAL", + "Name": "Education" + }, + { + "Type": "TECH_STIRRUPS", + "Cost": 390, + "UITreeRow": 1, + "EraType": "ERA_MEDIEVAL", + "Name": "Stirrups" + }, + { + "Type": "TECH_MILITARY_ENGINEERING", + "Cost": 390, + "UITreeRow": 2, + "EraType": "ERA_MEDIEVAL", + "Name": "Military Engineering" + }, + { + "Type": "TECH_CASTLES", + "Cost": 390, + "UITreeRow": 3, + "EraType": "ERA_MEDIEVAL", + "Name": "Castles" + }, + { + "Type": "TECH_CARTOGRAPHY", + "Cost": 600, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE", + "Name": "Cartography" + }, + { + "Type": "TECH_MASS_PRODUCTION", + "Cost": 600, + "UITreeRow": -2, + "EraType": "ERA_RENAISSANCE", + "Name": "Mass Production" + }, + { + "Type": "TECH_BANKING", + "Cost": 600, + "UITreeRow": 0, + "EraType": "ERA_RENAISSANCE", + "Name": "Banking" + }, + { + "Type": "TECH_GUNPOWDER", + "Cost": 600, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE", + "Name": "Gunpowder" + }, + { + "Type": "TECH_PRINTING", + "Cost": 600, + "UITreeRow": 4, + "EraType": "ERA_RENAISSANCE", + "Name": "Printing" + }, + { + "Type": "TECH_SQUARE_RIGGING", + "Cost": 730, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE", + "Name": "Square Rigging" + }, + { + "Type": "TECH_ASTRONOMY", + "Cost": 730, + "UITreeRow": -1, + "EraType": "ERA_RENAISSANCE", + "Name": "Astronomy" + }, + { + "Type": "TECH_METAL_CASTING", + "Cost": 730, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE", + "Name": "Metal Casting" + }, + { + "Type": "TECH_SIEGE_TACTICS", + "Cost": 730, + "UITreeRow": 3, + "EraType": "ERA_RENAISSANCE", + "Name": "Siege Tactics" + }, + { + "Type": "TECH_INDUSTRIALIZATION", + "Cost": 930, + "UITreeRow": -2, + "EraType": "ERA_INDUSTRIAL", + "Name": "Industrialization" + }, + { + "Type": "TECH_SCIENTIFIC_THEORY", + "Cost": 930, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL", + "Name": "Scientific Theory" + }, + { + "Type": "TECH_BALLISTICS", + "Cost": 930, + "UITreeRow": 1, + "EraType": "ERA_INDUSTRIAL", + "Name": "Ballistics" + }, + { + "Type": "TECH_MILITARY_SCIENCE", + "Cost": 930, + "UITreeRow": 3, + "EraType": "ERA_INDUSTRIAL", + "Name": "Military Science" + }, + { + "Type": "TECH_STEAM_POWER", + "Cost": 1070, + "UITreeRow": -3, + "EraType": "ERA_INDUSTRIAL", + "Name": "Steam Power" + }, + { + "Type": "TECH_SANITATION", + "Cost": 1070, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL", + "Name": "Sanitation" + }, + { + "Type": "TECH_ECONOMICS", + "Cost": 1070, + "UITreeRow": 0, + "EraType": "ERA_INDUSTRIAL", + "Name": "Economics" + }, + { + "Type": "TECH_RIFLING", + "Cost": 1070, + "UITreeRow": 2, + "EraType": "ERA_INDUSTRIAL", + "Name": "Rifling" + }, + { + "Type": "TECH_FLIGHT", + "Cost": 1250, + "UITreeRow": -2, + "EraType": "ERA_MODERN", + "Name": "Flight" + }, + { + "Type": "TECH_REPLACEABLE_PARTS", + "Cost": 1250, + "UITreeRow": 0, + "EraType": "ERA_MODERN", + "Name": "Replaceable Parts" + }, + { + "Type": "TECH_STEEL", + "Cost": 1250, + "UITreeRow": 1, + "EraType": "ERA_MODERN", + "Name": "Steel" + }, + { + "Type": "TECH_ELECTRICITY", + "Cost": 1370, + "UITreeRow": -3, + "EraType": "ERA_MODERN", + "Name": "Electricity" + }, + { + "Type": "TECH_RADIO", + "Cost": 1370, + "UITreeRow": -2, + "EraType": "ERA_MODERN", + "Name": "Radio" + }, + { + "Type": "TECH_CHEMISTRY", + "Cost": 1370, + "UITreeRow": -1, + "EraType": "ERA_MODERN", + "Name": "Chemistry" + }, + { + "Type": "TECH_COMBUSTION", + "Cost": 1370, + "UITreeRow": 2, + "EraType": "ERA_MODERN", + "Name": "Combustion" + }, + { + "Type": "TECH_ADVANCED_FLIGHT", + "Cost": 1480, + "UITreeRow": -2, + "EraType": "ERA_ATOMIC", + "Name": "Advanced Flight" + }, + { + "Type": "TECH_ROCKETRY", + "Cost": 1480, + "UITreeRow": -1, + "EraType": "ERA_ATOMIC", + "Name": "Rocketry" + }, + { + "Type": "TECH_ADVANCED_BALLISTICS", + "Cost": 1480, + "UITreeRow": 0, + "EraType": "ERA_ATOMIC", + "Name": "Advanced Ballistics" + }, + { + "Type": "TECH_COMBINED_ARMS", + "Cost": 1480, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC", + "Name": "Combined Arms" + }, + { + "Type": "TECH_PLASTICS", + "Cost": 1480, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC", + "Name": "Plastics" + }, + { + "Type": "TECH_COMPUTERS", + "Cost": 1660, + "UITreeRow": -3, + "EraType": "ERA_ATOMIC", + "Name": "Computers" + }, + { + "Type": "TECH_NUCLEAR_FISSION", + "Cost": 1660, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC", + "Name": "Nuclear Fission" + }, + { + "Type": "TECH_SYNTHETIC_MATERIALS", + "Cost": 1660, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC", + "Name": "Synthetic Materials" + }, + { + "Type": "TECH_TELECOMMUNICATIONS", + "Cost": 1850, + "UITreeRow": -3, + "EraType": "ERA_INFORMATION", + "Name": "Telecommunications" + }, + { + "Type": "TECH_SATELLITES", + "Cost": 1850, + "UITreeRow": -1, + "EraType": "ERA_INFORMATION", + "Name": "Satellites" + }, + { + "Type": "TECH_GUIDANCE_SYSTEMS", + "Cost": 1850, + "UITreeRow": 0, + "EraType": "ERA_INFORMATION", + "Name": "Guidance Systems" + }, + { + "Type": "TECH_LASERS", + "Cost": 1850, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION", + "Name": "Lasers" + }, + { + "Type": "TECH_COMPOSITES", + "Cost": 1850, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION", + "Name": "Composites" + }, + { + "Type": "TECH_STEALTH_TECHNOLOGY", + "Cost": 1850, + "UITreeRow": 3, + "EraType": "ERA_INFORMATION", + "Name": "Stealth Technology" + }, + { + "Type": "TECH_ROBOTICS", + "Cost": 2155, + "UITreeRow": -2, + "EraType": "ERA_INFORMATION", + "Name": "Robotics" + }, + { + "Type": "TECH_NANOTECHNOLOGY", + "Cost": 2155, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION", + "Name": "Nanotechnology" + }, + { + "Type": "TECH_NUCLEAR_FUSION", + "Cost": 2155, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION", + "Name": "Nuclear Fusion" + }, + { + "Type": "TECH_BUTTRESS", + "Cost": 300, + "UITreeRow": -3, + "EraType": "ERA_MEDIEVAL", + "Name": "Buttress" + }, + { + "Type": "TECH_REFINING", + "Cost": 1250, + "UITreeRow": 3, + "EraType": "ERA_MODERN", + "Name": "Refining" + }, + { + "Type": "TECH_SEASTEADS", + "Cost": 2200, + "UITreeRow": -3, + "EraType": "ERA_FUTURE", + "Name": "Seasteads" + }, + { + "Type": "TECH_ADVANCED_AI", + "Cost": 2200, + "UITreeRow": -2, + "EraType": "ERA_FUTURE", + "Name": "Advanced AI" + }, + { + "Type": "TECH_ADVANCED_POWER_CELLS", + "Cost": 2200, + "UITreeRow": -1, + "EraType": "ERA_FUTURE", + "Name": "Advanced Power Cells" + }, + { + "Type": "TECH_CYBERNETICS", + "Cost": 2200, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + "Name": "Cybernetics" + }, + { + "Type": "TECH_SMART_MATERIALS", + "Cost": 2200, + "UITreeRow": 1, + "EraType": "ERA_FUTURE", + "Name": "Smart Materials" + }, + { + "Type": "TECH_PREDICTIVE_SYSTEMS", + "Cost": 2200, + "UITreeRow": 2, + "EraType": "ERA_FUTURE", + "Name": "Predictive Systems" + }, + { + "Type": "TECH_OFFWORLD_MISSION", + "Cost": 2500, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + "Name": "Offworld Mission" + }, + { + "Type": "TECH_FUTURE_TECH", + "Cost": 2600, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + "Name": "Future Tech" + } +] diff --git a/worlds/civ_6/data/goody_hut_rewards.json b/worlds/civ_6/data/goody_hut_rewards.json index b2055efaf33c..8315642fdfa5 100644 --- a/worlds/civ_6/data/goody_hut_rewards.json +++ b/worlds/civ_6/data/goody_hut_rewards.json @@ -1,77 +1,77 @@ [ - { - "Type": "GOODY_GOLD_SMALL_MODIFIER", - "Rarity": "COMMON", - "Name": "Gold: Small" - }, - { - "Type": "GOODY_GOLD_MEDIUM_MODIFIER", - "Rarity": "COMMON", - "Name": "Gold: Medium" - }, - { - "Type": "GOODY_GOLD_LARGE_MODIFIER", - "Rarity": "UNCOMMON", - "Name": "Gold: Large" - }, - { - "Type": "GOODY_FAITH_SMALL_MODIFIER", - "Rarity": "COMMON", - "Name": "Faith: Small" - }, - { - "Type": "GOODY_FAITH_MEDIUM_MODIFIER", - "Rarity": "COMMON", - "Name": "Faith: Medium" - }, - { - "Type": "GOODY_FAITH_LARGE_MODIFIER", - "Rarity": "UNCOMMON", - "Name": "Faith: Large" - }, - { - "Type": "GOODY_DIPLOMACY_GRANT_FAVOR", - "Rarity": "COMMON", - "Name": "Diplomatic Favor" - }, - { - "Type": "GOODY_DIPLOMACY_GRANT_GOVERNOR_TITLE", - "Rarity": "RARE", - "Name": "Governor Title" - }, - { - "Type": "GOODY_DIPLOMACY_GRANT_ENVOY", - "Rarity": "UNCOMMON", - "Name": "Envoy" - }, - { - "Type": "GOODY_CULTURE_GRANT_ONE_RELIC", - "Rarity": "RARE", - "Name": "Relic" - }, - { - "Type": "GOODY_MILITARY_GRANT_SCOUT", - "Rarity": "UNCOMMON", - "Name": "Scout" - }, - { - "Type": "GOODY_SURVIVORS_ADD_POPULATION", - "Rarity": "UNCOMMON", - "Name": "Additional Population" - }, - { - "Type": "GOODY_SURVIVORS_GRANT_BUILDER", - "Rarity": "UNCOMMON", - "Name": "Builder" - }, - { - "Type": "GOODY_SURVIVORS_GRANT_TRADER", - "Rarity": "UNCOMMON", - "Name": "Trader" - }, - { - "Type": "GOODY_SURVIVORS_GRANT_SETTLER", - "Rarity": "UNCOMMON", - "Name": "Settler" - } -] \ No newline at end of file + { + "Type": "GOODY_GOLD_SMALL_MODIFIER", + "Rarity": "COMMON", + "Name": "Gold: Small" + }, + { + "Type": "GOODY_GOLD_MEDIUM_MODIFIER", + "Rarity": "COMMON", + "Name": "Gold: Medium" + }, + { + "Type": "GOODY_GOLD_LARGE_MODIFIER", + "Rarity": "UNCOMMON", + "Name": "Gold: Large" + }, + { + "Type": "GOODY_FAITH_SMALL_MODIFIER", + "Rarity": "COMMON", + "Name": "Faith: Small" + }, + { + "Type": "GOODY_FAITH_MEDIUM_MODIFIER", + "Rarity": "COMMON", + "Name": "Faith: Medium" + }, + { + "Type": "GOODY_FAITH_LARGE_MODIFIER", + "Rarity": "UNCOMMON", + "Name": "Faith: Large" + }, + { + "Type": "GOODY_DIPLOMACY_GRANT_FAVOR", + "Rarity": "COMMON", + "Name": "Diplomatic Favor" + }, + { + "Type": "GOODY_DIPLOMACY_GRANT_GOVERNOR_TITLE", + "Rarity": "RARE", + "Name": "Governor Title" + }, + { + "Type": "GOODY_DIPLOMACY_GRANT_ENVOY", + "Rarity": "UNCOMMON", + "Name": "Envoy" + }, + { + "Type": "GOODY_CULTURE_GRANT_ONE_RELIC", + "Rarity": "RARE", + "Name": "Relic" + }, + { + "Type": "GOODY_MILITARY_GRANT_SCOUT", + "Rarity": "UNCOMMON", + "Name": "Scout" + }, + { + "Type": "GOODY_SURVIVORS_ADD_POPULATION", + "Rarity": "UNCOMMON", + "Name": "Additional Population" + }, + { + "Type": "GOODY_SURVIVORS_GRANT_BUILDER", + "Rarity": "UNCOMMON", + "Name": "Builder" + }, + { + "Type": "GOODY_SURVIVORS_GRANT_TRADER", + "Rarity": "UNCOMMON", + "Name": "Trader" + }, + { + "Type": "GOODY_SURVIVORS_GRANT_SETTLER", + "Rarity": "UNCOMMON", + "Name": "Settler" + } +] diff --git a/worlds/civ_6/data/new_civic_prereqs.json b/worlds/civ_6/data/new_civic_prereqs.json index ca63d18c0edf..90da567e873f 100644 --- a/worlds/civ_6/data/new_civic_prereqs.json +++ b/worlds/civ_6/data/new_civic_prereqs.json @@ -35,17 +35,50 @@ { "Civic": "CIVIC_AP_RENAISSANCE_23", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19" }, { "Civic": "CIVIC_AP_RENAISSANCE_24", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19" }, { "Civic": "CIVIC_AP_RENAISSANCE_24", "PrereqCivic": "CIVIC_AP_MEDIEVAL_20" }, - { "Civic": "CIVIC_AP_RENAISSANCE_25", "PrereqCivic": "CIVIC_AP_RENAISSANCE_22" }, - { "Civic": "CIVIC_AP_RENAISSANCE_26", "PrereqCivic": "CIVIC_AP_RENAISSANCE_22" }, - { "Civic": "CIVIC_AP_RENAISSANCE_26", "PrereqCivic": "CIVIC_AP_RENAISSANCE_23" }, - { "Civic": "CIVIC_AP_INDUSTRIAL_27", "PrereqCivic": "CIVIC_AP_RENAISSANCE_25" }, - { "Civic": "CIVIC_AP_INDUSTRIAL_28", "PrereqCivic": "CIVIC_AP_RENAISSANCE_25" }, - { "Civic": "CIVIC_AP_INDUSTRIAL_29", "PrereqCivic": "CIVIC_AP_RENAISSANCE_26" }, - { "Civic": "CIVIC_AP_INDUSTRIAL_30", "PrereqCivic": "CIVIC_AP_RENAISSANCE_26" }, - { "Civic": "CIVIC_AP_INDUSTRIAL_31", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_27" }, - { "Civic": "CIVIC_AP_INDUSTRIAL_32", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_29" }, - { "Civic": "CIVIC_AP_INDUSTRIAL_33", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_28" }, - { "Civic": "CIVIC_AP_INDUSTRIAL_33", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_29" }, + { + "Civic": "CIVIC_AP_RENAISSANCE_25", + "PrereqCivic": "CIVIC_AP_RENAISSANCE_22" + }, + { + "Civic": "CIVIC_AP_RENAISSANCE_26", + "PrereqCivic": "CIVIC_AP_RENAISSANCE_22" + }, + { + "Civic": "CIVIC_AP_RENAISSANCE_26", + "PrereqCivic": "CIVIC_AP_RENAISSANCE_23" + }, + { + "Civic": "CIVIC_AP_INDUSTRIAL_27", + "PrereqCivic": "CIVIC_AP_RENAISSANCE_25" + }, + { + "Civic": "CIVIC_AP_INDUSTRIAL_28", + "PrereqCivic": "CIVIC_AP_RENAISSANCE_25" + }, + { + "Civic": "CIVIC_AP_INDUSTRIAL_29", + "PrereqCivic": "CIVIC_AP_RENAISSANCE_26" + }, + { + "Civic": "CIVIC_AP_INDUSTRIAL_30", + "PrereqCivic": "CIVIC_AP_RENAISSANCE_26" + }, + { + "Civic": "CIVIC_AP_INDUSTRIAL_31", + "PrereqCivic": "CIVIC_AP_INDUSTRIAL_27" + }, + { + "Civic": "CIVIC_AP_INDUSTRIAL_32", + "PrereqCivic": "CIVIC_AP_INDUSTRIAL_29" + }, + { + "Civic": "CIVIC_AP_INDUSTRIAL_33", + "PrereqCivic": "CIVIC_AP_INDUSTRIAL_28" + }, + { + "Civic": "CIVIC_AP_INDUSTRIAL_33", + "PrereqCivic": "CIVIC_AP_INDUSTRIAL_29" + }, { "Civic": "CIVIC_AP_MODERN_34", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_31" }, { "Civic": "CIVIC_AP_MODERN_37", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_31" }, { "Civic": "CIVIC_AP_MODERN_37", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_33" }, @@ -71,17 +104,41 @@ { "Civic": "CIVIC_AP_MODERN_38", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_32" }, { "Civic": "CIVIC_AP_INFORMATION_51", "PrereqCivic": "CIVIC_AP_ATOMIC_45" }, { "Civic": "CIVIC_AP_INFORMATION_51", "PrereqCivic": "CIVIC_AP_ATOMIC_46" }, - { "Civic": "CIVIC_AP_INFORMATION_52", "PrereqCivic": "CIVIC_AP_INFORMATION_48" }, - { "Civic": "CIVIC_AP_INFORMATION_52", "PrereqCivic": "CIVIC_AP_INFORMATION_49" }, - { "Civic": "CIVIC_AP_INFORMATION_53", "PrereqCivic": "CIVIC_AP_INFORMATION_48" }, - { "Civic": "CIVIC_AP_INFORMATION_53", "PrereqCivic": "CIVIC_AP_INFORMATION_49" }, - { "Civic": "CIVIC_AP_INFORMATION_54", "PrereqCivic": "CIVIC_AP_INFORMATION_48" }, - { "Civic": "CIVIC_AP_INFORMATION_54", "PrereqCivic": "CIVIC_AP_INFORMATION_49" }, - { "Civic": "CIVIC_AP_INFORMATION_55", "PrereqCivic": "CIVIC_AP_INFORMATION_51" }, - { "Civic": "CIVIC_AP_INFORMATION_55", "PrereqCivic": "CIVIC_AP_INFORMATION_48" }, - {"Civic": "CIVIC_AP_FUTURE_56", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, - {"Civic": "CIVIC_AP_FUTURE_57", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, - {"Civic": "CIVIC_AP_FUTURE_58", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, - {"Civic": "CIVIC_AP_FUTURE_59", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, - {"Civic": "CIVIC_AP_FUTURE_60", "PrereqCivic": "CIVIC_AP_FUTURE_50"} -] \ No newline at end of file + { + "Civic": "CIVIC_AP_INFORMATION_52", + "PrereqCivic": "CIVIC_AP_INFORMATION_48" + }, + { + "Civic": "CIVIC_AP_INFORMATION_52", + "PrereqCivic": "CIVIC_AP_INFORMATION_49" + }, + { + "Civic": "CIVIC_AP_INFORMATION_53", + "PrereqCivic": "CIVIC_AP_INFORMATION_48" + }, + { + "Civic": "CIVIC_AP_INFORMATION_53", + "PrereqCivic": "CIVIC_AP_INFORMATION_49" + }, + { + "Civic": "CIVIC_AP_INFORMATION_54", + "PrereqCivic": "CIVIC_AP_INFORMATION_48" + }, + { + "Civic": "CIVIC_AP_INFORMATION_54", + "PrereqCivic": "CIVIC_AP_INFORMATION_49" + }, + { + "Civic": "CIVIC_AP_INFORMATION_55", + "PrereqCivic": "CIVIC_AP_INFORMATION_51" + }, + { + "Civic": "CIVIC_AP_INFORMATION_55", + "PrereqCivic": "CIVIC_AP_INFORMATION_48" + }, + { "Civic": "CIVIC_AP_FUTURE_56", "PrereqCivic": "CIVIC_AP_FUTURE_50" }, + { "Civic": "CIVIC_AP_FUTURE_57", "PrereqCivic": "CIVIC_AP_FUTURE_50" }, + { "Civic": "CIVIC_AP_FUTURE_58", "PrereqCivic": "CIVIC_AP_FUTURE_50" }, + { "Civic": "CIVIC_AP_FUTURE_59", "PrereqCivic": "CIVIC_AP_FUTURE_50" }, + { "Civic": "CIVIC_AP_FUTURE_60", "PrereqCivic": "CIVIC_AP_FUTURE_50" } +] diff --git a/worlds/civ_6/data/new_civics.json b/worlds/civ_6/data/new_civics.json index 4ed6fe83dde5..e52478101df0 100644 --- a/worlds/civ_6/data/new_civics.json +++ b/worlds/civ_6/data/new_civics.json @@ -1,63 +1,368 @@ [ -{"Type": "CIVIC_AP_ANCIENT_00", "Cost": 20, "UITreeRow": 0, "EraType": "ERA_ANCIENT"}, -{"Type": "CIVIC_AP_ANCIENT_01", "Cost": 40, "UITreeRow": -2, "EraType": "ERA_ANCIENT"}, -{"Type": "CIVIC_AP_ANCIENT_02", "Cost": 40, "UITreeRow": 2, "EraType": "ERA_ANCIENT"}, -{"Type": "CIVIC_AP_ANCIENT_03", "Cost": 50, "UITreeRow": -3, "EraType": "ERA_ANCIENT"}, -{"Type": "CIVIC_AP_ANCIENT_04", "Cost": 70, "UITreeRow": 0, "EraType": "ERA_ANCIENT"}, -{"Type": "CIVIC_AP_ANCIENT_05", "Cost": 70, "UITreeRow": 1, "EraType": "ERA_ANCIENT"}, -{"Type": "CIVIC_AP_ANCIENT_06", "Cost": 50, "UITreeRow": 3, "EraType": "ERA_ANCIENT"}, -{"Type": "CIVIC_AP_CLASSICAL_07", "Cost": 110, "UITreeRow": -2, "EraType": "ERA_CLASSICAL"}, -{"Type": "CIVIC_AP_CLASSICAL_08", "Cost": 110, "UITreeRow": 0, "EraType": "ERA_CLASSICAL"}, -{"Type": "CIVIC_AP_CLASSICAL_09", "Cost": 110, "UITreeRow": 2, "EraType": "ERA_CLASSICAL"}, -{"Type": "CIVIC_AP_CLASSICAL_10", "Cost": 120, "UITreeRow": -3, "EraType": "ERA_CLASSICAL"}, -{"Type": "CIVIC_AP_CLASSICAL_11", "Cost": 175, "UITreeRow": -1, "EraType": "ERA_CLASSICAL"}, -{"Type": "CIVIC_AP_CLASSICAL_12", "Cost": 175, "UITreeRow": 1, "EraType": "ERA_CLASSICAL"}, -{"Type": "CIVIC_AP_CLASSICAL_13", "Cost": 120, "UITreeRow": 3, "EraType": "ERA_CLASSICAL"}, -{"Type": "CIVIC_AP_MEDIEVAL_14", "Cost": 220, "UITreeRow": -2, "EraType": "ERA_MEDIEVAL"}, -{"Type": "CIVIC_AP_MEDIEVAL_15", "Cost": 300, "UITreeRow": -1, "EraType": "ERA_MEDIEVAL"}, -{"Type": "CIVIC_AP_MEDIEVAL_16", "Cost": 300, "UITreeRow": 1, "EraType": "ERA_MEDIEVAL"}, -{"Type": "CIVIC_AP_MEDIEVAL_17", "Cost": 340, "UITreeRow": -3, "EraType": "ERA_MEDIEVAL"}, -{"Type": "CIVIC_AP_MEDIEVAL_18", "Cost": 420, "UITreeRow": -1, "EraType": "ERA_MEDIEVAL"}, -{"Type": "CIVIC_AP_MEDIEVAL_19", "Cost": 420, "UITreeRow": 1, "EraType": "ERA_MEDIEVAL"}, -{"Type": "CIVIC_AP_MEDIEVAL_20", "Cost": 340, "UITreeRow": 3, "EraType": "ERA_MEDIEVAL"}, -{"Type": "CIVIC_AP_RENAISSANCE_21", "Cost": 440, "UITreeRow": -3, "EraType": "ERA_RENAISSANCE"}, -{"Type": "CIVIC_AP_RENAISSANCE_22", "Cost": 600, "UITreeRow": -1, "EraType": "ERA_RENAISSANCE"}, -{"Type": "CIVIC_AP_RENAISSANCE_23", "Cost": 600, "UITreeRow": 1, "EraType": "ERA_RENAISSANCE"}, -{"Type": "CIVIC_AP_RENAISSANCE_24", "Cost": 440, "UITreeRow": 3, "EraType": "ERA_RENAISSANCE"}, -{"Type": "CIVIC_AP_RENAISSANCE_25", "Cost": 720, "UITreeRow": -1, "EraType": "ERA_RENAISSANCE"}, -{"Type": "CIVIC_AP_RENAISSANCE_26", "Cost": 720, "UITreeRow": 1, "EraType": "ERA_RENAISSANCE"}, -{"Type": "CIVIC_AP_INDUSTRIAL_27", "Cost": 800, "UITreeRow": -3, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "CIVIC_AP_INDUSTRIAL_28", "Cost": 1010, "UITreeRow": -1, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "CIVIC_AP_INDUSTRIAL_29", "Cost": 1010, "UITreeRow": 0, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "CIVIC_AP_INDUSTRIAL_30", "Cost": 800, "UITreeRow": 2, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "CIVIC_AP_INDUSTRIAL_31", "Cost": 1050, "UITreeRow": -3, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "CIVIC_AP_INDUSTRIAL_32", "Cost": 1210, "UITreeRow": 2, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "CIVIC_AP_INDUSTRIAL_33", "Cost": 1210, "UITreeRow": -1, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "CIVIC_AP_MODERN_34", "Cost": 1540, "UITreeRow": -3, "EraType": "ERA_MODERN"}, -{"Type": "CIVIC_AP_MODERN_35", "Cost": 1580, "UITreeRow": -2, "EraType": "ERA_MODERN"}, -{"Type": "CIVIC_AP_MODERN_36", "Cost": 1715, "UITreeRow": -2, "EraType": "ERA_MODERN"}, -{"Type": "CIVIC_AP_MODERN_37", "Cost": 1540, "UITreeRow": -1, "EraType": "ERA_MODERN"}, -{"Type": "CIVIC_AP_MODERN_38", "Cost": 1540, "UITreeRow": 1, "EraType": "ERA_MODERN"}, -{"Type": "CIVIC_AP_MODERN_39", "Cost": 1640, "UITreeRow": -1, "EraType": "ERA_MODERN"}, -{"Type": "CIVIC_AP_MODERN_40", "Cost": 1640, "UITreeRow": 0, "EraType": "ERA_MODERN"}, -{"Type": "CIVIC_AP_MODERN_41", "Cost": 1640, "UITreeRow": 2, "EraType": "ERA_MODERN"}, -{"Type": "CIVIC_AP_MODERN_42", "Cost": 1640, "UITreeRow": 3, "EraType": "ERA_MODERN"}, -{"Type": "CIVIC_AP_ATOMIC_43", "Cost": 2185, "UITreeRow": -1, "EraType": "ERA_ATOMIC"}, -{"Type": "CIVIC_AP_ATOMIC_44", "Cost": 2185, "UITreeRow": 2, "EraType": "ERA_ATOMIC"}, -{"Type": "CIVIC_AP_ATOMIC_45", "Cost": 1955, "UITreeRow": -3, "EraType": "ERA_ATOMIC"}, -{"Type": "CIVIC_AP_ATOMIC_46", "Cost": 2415, "UITreeRow": -1, "EraType": "ERA_ATOMIC"}, -{"Type": "CIVIC_AP_ATOMIC_47", "Cost": 2415, "UITreeRow": 1, "EraType": "ERA_ATOMIC"}, -{"Type": "CIVIC_AP_INFORMATION_48", "Cost": 2880, "UITreeRow": 0, "EraType": "ERA_INFORMATION"}, -{"Type": "CIVIC_AP_INFORMATION_49", "Cost": 2880, "UITreeRow": 2, "EraType": "ERA_INFORMATION"}, -{"Type": "CIVIC_AP_FUTURE_50", "Cost": 3200, "UITreeRow": 3, "EraType": "ERA_FUTURE"}, -{"Type": "CIVIC_AP_INFORMATION_51", "Cost": 2880, "UITreeRow": -2, "EraType": "ERA_INFORMATION"}, -{"Type": "CIVIC_AP_INFORMATION_52", "Cost": 3000, "UITreeRow": 0, "EraType": "ERA_INFORMATION"}, -{"Type": "CIVIC_AP_INFORMATION_53", "Cost": 3000, "UITreeRow": 1, "EraType": "ERA_INFORMATION"}, -{"Type": "CIVIC_AP_INFORMATION_54", "Cost": 3000, "UITreeRow": 2, "EraType": "ERA_INFORMATION"}, -{"Type": "CIVIC_AP_INFORMATION_55", "Cost": 3100, "UITreeRow": -1, "EraType": "ERA_INFORMATION"}, -{"Type": "CIVIC_AP_FUTURE_56", "Cost": 3200, "UITreeRow": -2, "EraType": "ERA_FUTURE"}, -{"Type": "CIVIC_AP_FUTURE_57", "Cost": 3200, "UITreeRow": -1, "EraType": "ERA_FUTURE"}, -{"Type": "CIVIC_AP_FUTURE_58", "Cost": 3200, "UITreeRow": 0, "EraType": "ERA_FUTURE"}, -{"Type": "CIVIC_AP_FUTURE_59", "Cost": 3200, "UITreeRow": 1, "EraType": "ERA_FUTURE"}, -{"Type": "CIVIC_AP_FUTURE_60", "Cost": 3200, "UITreeRow": 2, "EraType": "ERA_FUTURE"} -] \ No newline at end of file + { + "Type": "CIVIC_AP_ANCIENT_00", + "Cost": 20, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "CIVIC_AP_ANCIENT_01", + "Cost": 40, + "UITreeRow": -2, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "CIVIC_AP_ANCIENT_02", + "Cost": 40, + "UITreeRow": 2, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "CIVIC_AP_ANCIENT_03", + "Cost": 50, + "UITreeRow": -3, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "CIVIC_AP_ANCIENT_04", + "Cost": 70, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "CIVIC_AP_ANCIENT_05", + "Cost": 70, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "CIVIC_AP_ANCIENT_06", + "Cost": 50, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "CIVIC_AP_CLASSICAL_07", + "Cost": 110, + "UITreeRow": -2, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "CIVIC_AP_CLASSICAL_08", + "Cost": 110, + "UITreeRow": 0, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "CIVIC_AP_CLASSICAL_09", + "Cost": 110, + "UITreeRow": 2, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "CIVIC_AP_CLASSICAL_10", + "Cost": 120, + "UITreeRow": -3, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "CIVIC_AP_CLASSICAL_11", + "Cost": 175, + "UITreeRow": -1, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "CIVIC_AP_CLASSICAL_12", + "Cost": 175, + "UITreeRow": 1, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "CIVIC_AP_CLASSICAL_13", + "Cost": 120, + "UITreeRow": 3, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "CIVIC_AP_MEDIEVAL_14", + "Cost": 220, + "UITreeRow": -2, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "CIVIC_AP_MEDIEVAL_15", + "Cost": 300, + "UITreeRow": -1, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "CIVIC_AP_MEDIEVAL_16", + "Cost": 300, + "UITreeRow": 1, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "CIVIC_AP_MEDIEVAL_17", + "Cost": 340, + "UITreeRow": -3, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "CIVIC_AP_MEDIEVAL_18", + "Cost": 420, + "UITreeRow": -1, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "CIVIC_AP_MEDIEVAL_19", + "Cost": 420, + "UITreeRow": 1, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "CIVIC_AP_MEDIEVAL_20", + "Cost": 340, + "UITreeRow": 3, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "CIVIC_AP_RENAISSANCE_21", + "Cost": 440, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "CIVIC_AP_RENAISSANCE_22", + "Cost": 600, + "UITreeRow": -1, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "CIVIC_AP_RENAISSANCE_23", + "Cost": 600, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "CIVIC_AP_RENAISSANCE_24", + "Cost": 440, + "UITreeRow": 3, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "CIVIC_AP_RENAISSANCE_25", + "Cost": 720, + "UITreeRow": -1, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "CIVIC_AP_RENAISSANCE_26", + "Cost": 720, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_27", + "Cost": 800, + "UITreeRow": -3, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_28", + "Cost": 1010, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_29", + "Cost": 1010, + "UITreeRow": 0, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_30", + "Cost": 800, + "UITreeRow": 2, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_31", + "Cost": 1050, + "UITreeRow": -3, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_32", + "Cost": 1210, + "UITreeRow": 2, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_33", + "Cost": 1210, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "CIVIC_AP_MODERN_34", + "Cost": 1540, + "UITreeRow": -3, + "EraType": "ERA_MODERN" + }, + { + "Type": "CIVIC_AP_MODERN_35", + "Cost": 1580, + "UITreeRow": -2, + "EraType": "ERA_MODERN" + }, + { + "Type": "CIVIC_AP_MODERN_36", + "Cost": 1715, + "UITreeRow": -2, + "EraType": "ERA_MODERN" + }, + { + "Type": "CIVIC_AP_MODERN_37", + "Cost": 1540, + "UITreeRow": -1, + "EraType": "ERA_MODERN" + }, + { + "Type": "CIVIC_AP_MODERN_38", + "Cost": 1540, + "UITreeRow": 1, + "EraType": "ERA_MODERN" + }, + { + "Type": "CIVIC_AP_MODERN_39", + "Cost": 1640, + "UITreeRow": -1, + "EraType": "ERA_MODERN" + }, + { + "Type": "CIVIC_AP_MODERN_40", + "Cost": 1640, + "UITreeRow": 0, + "EraType": "ERA_MODERN" + }, + { + "Type": "CIVIC_AP_MODERN_41", + "Cost": 1640, + "UITreeRow": 2, + "EraType": "ERA_MODERN" + }, + { + "Type": "CIVIC_AP_MODERN_42", + "Cost": 1640, + "UITreeRow": 3, + "EraType": "ERA_MODERN" + }, + { + "Type": "CIVIC_AP_ATOMIC_43", + "Cost": 2185, + "UITreeRow": -1, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "CIVIC_AP_ATOMIC_44", + "Cost": 2185, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "CIVIC_AP_ATOMIC_45", + "Cost": 1955, + "UITreeRow": -3, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "CIVIC_AP_ATOMIC_46", + "Cost": 2415, + "UITreeRow": -1, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "CIVIC_AP_ATOMIC_47", + "Cost": 2415, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "CIVIC_AP_INFORMATION_48", + "Cost": 2880, + "UITreeRow": 0, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "CIVIC_AP_INFORMATION_49", + "Cost": 2880, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "CIVIC_AP_FUTURE_50", + "Cost": 3200, + "UITreeRow": 3, + "EraType": "ERA_FUTURE" + }, + { + "Type": "CIVIC_AP_INFORMATION_51", + "Cost": 2880, + "UITreeRow": -2, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "CIVIC_AP_INFORMATION_52", + "Cost": 3000, + "UITreeRow": 0, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "CIVIC_AP_INFORMATION_53", + "Cost": 3000, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "CIVIC_AP_INFORMATION_54", + "Cost": 3000, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "CIVIC_AP_INFORMATION_55", + "Cost": 3100, + "UITreeRow": -1, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "CIVIC_AP_FUTURE_56", + "Cost": 3200, + "UITreeRow": -2, + "EraType": "ERA_FUTURE" + }, + { + "Type": "CIVIC_AP_FUTURE_57", + "Cost": 3200, + "UITreeRow": -1, + "EraType": "ERA_FUTURE" + }, + { + "Type": "CIVIC_AP_FUTURE_58", + "Cost": 3200, + "UITreeRow": 0, + "EraType": "ERA_FUTURE" + }, + { + "Type": "CIVIC_AP_FUTURE_59", + "Cost": 3200, + "UITreeRow": 1, + "EraType": "ERA_FUTURE" + }, + { + "Type": "CIVIC_AP_FUTURE_60", + "Cost": 3200, + "UITreeRow": 2, + "EraType": "ERA_FUTURE" + } +] diff --git a/worlds/civ_6/data/new_tech.json b/worlds/civ_6/data/new_tech.json index e5b3c9d83601..3bfd188cfb75 100644 --- a/worlds/civ_6/data/new_tech.json +++ b/worlds/civ_6/data/new_tech.json @@ -1,79 +1,464 @@ [ -{"Type": "TECH_AP_ANCIENT_00", "Cost": 25, "UITreeRow": 0, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_ANCIENT_01", "Cost": 25, "UITreeRow": 1, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_ANCIENT_02", "Cost": 25, "UITreeRow": 3, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_ANCIENT_03", "Cost": 50, "UITreeRow": -3, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_ANCIENT_04", "Cost": 50, "UITreeRow": -2, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_ANCIENT_05", "Cost": 50, "UITreeRow": -1, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_ANCIENT_06", "Cost": 50, "UITreeRow": 1, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_ANCIENT_07", "Cost": 50, "UITreeRow": 0, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_ANCIENT_08", "Cost": 80, "UITreeRow": 2, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_ANCIENT_09", "Cost": 80, "UITreeRow": 3, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_ANCIENT_10", "Cost": 80, "UITreeRow": 4, "EraType": "ERA_ANCIENT"}, -{"Type": "TECH_AP_CLASSICAL_11", "Cost": 120, "UITreeRow": -2, "EraType": "ERA_CLASSICAL"}, -{"Type": "TECH_AP_CLASSICAL_12", "Cost": 120, "UITreeRow": 0, "EraType": "ERA_CLASSICAL"}, -{"Type": "TECH_AP_CLASSICAL_13", "Cost": 120, "UITreeRow": 1, "EraType": "ERA_CLASSICAL"}, -{"Type": "TECH_AP_CLASSICAL_14", "Cost": 120, "UITreeRow": 3, "EraType": "ERA_CLASSICAL"}, -{"Type": "TECH_AP_CLASSICAL_15", "Cost": 200, "UITreeRow": -3, "EraType": "ERA_CLASSICAL"}, -{"Type": "TECH_AP_CLASSICAL_16", "Cost": 200, "UITreeRow": -1, "EraType": "ERA_CLASSICAL"}, -{"Type": "TECH_AP_CLASSICAL_17", "Cost": 200, "UITreeRow": 2, "EraType": "ERA_CLASSICAL"}, -{"Type": "TECH_AP_CLASSICAL_18", "Cost": 200, "UITreeRow": 4, "EraType": "ERA_CLASSICAL"}, -{"Type": "TECH_AP_MEDIEVAL_19", "Cost": 300, "UITreeRow": -2, "EraType": "ERA_MEDIEVAL"}, -{"Type": "TECH_AP_MEDIEVAL_20", "Cost": 300, "UITreeRow": 0, "EraType": "ERA_MEDIEVAL"}, -{"Type": "TECH_AP_MEDIEVAL_21", "Cost": 300, "UITreeRow": 4, "EraType": "ERA_MEDIEVAL"}, -{"Type": "TECH_AP_MEDIEVAL_22", "Cost": 390, "UITreeRow": -1, "EraType": "ERA_MEDIEVAL"}, -{"Type": "TECH_AP_MEDIEVAL_23", "Cost": 390, "UITreeRow": 1, "EraType": "ERA_MEDIEVAL"}, -{"Type": "TECH_AP_MEDIEVAL_24", "Cost": 390, "UITreeRow": 2, "EraType": "ERA_MEDIEVAL"}, -{"Type": "TECH_AP_MEDIEVAL_25", "Cost": 390, "UITreeRow": 3, "EraType": "ERA_MEDIEVAL"}, -{"Type": "TECH_AP_RENAISSANCE_26", "Cost": 600, "UITreeRow": -3, "EraType": "ERA_RENAISSANCE"}, -{"Type": "TECH_AP_RENAISSANCE_27", "Cost": 600, "UITreeRow": -2, "EraType": "ERA_RENAISSANCE"}, -{"Type": "TECH_AP_RENAISSANCE_28", "Cost": 600, "UITreeRow": 0, "EraType": "ERA_RENAISSANCE"}, -{"Type": "TECH_AP_RENAISSANCE_29", "Cost": 600, "UITreeRow": 1, "EraType": "ERA_RENAISSANCE"}, -{"Type": "TECH_AP_RENAISSANCE_30", "Cost": 600, "UITreeRow": 4, "EraType": "ERA_RENAISSANCE"}, -{"Type": "TECH_AP_RENAISSANCE_31", "Cost": 730, "UITreeRow": -3, "EraType": "ERA_RENAISSANCE"}, -{"Type": "TECH_AP_RENAISSANCE_32", "Cost": 730, "UITreeRow": -1, "EraType": "ERA_RENAISSANCE"}, -{"Type": "TECH_AP_RENAISSANCE_33", "Cost": 730, "UITreeRow": 1, "EraType": "ERA_RENAISSANCE"}, -{"Type": "TECH_AP_RENAISSANCE_34", "Cost": 730, "UITreeRow": 3, "EraType": "ERA_RENAISSANCE"}, -{"Type": "TECH_AP_INDUSTRIAL_35", "Cost": 930, "UITreeRow": -2, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "TECH_AP_INDUSTRIAL_36", "Cost": 930, "UITreeRow": -1, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "TECH_AP_INDUSTRIAL_37", "Cost": 930, "UITreeRow": 1, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "TECH_AP_INDUSTRIAL_38", "Cost": 930, "UITreeRow": 3, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "TECH_AP_INDUSTRIAL_39", "Cost": 1070, "UITreeRow": -3, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "TECH_AP_INDUSTRIAL_40", "Cost": 1070, "UITreeRow": -1, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "TECH_AP_INDUSTRIAL_41", "Cost": 1070, "UITreeRow": 0, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "TECH_AP_INDUSTRIAL_42", "Cost": 1070, "UITreeRow": 2, "EraType": "ERA_INDUSTRIAL"}, -{"Type": "TECH_AP_MODERN_43", "Cost": 1250, "UITreeRow": -2, "EraType": "ERA_MODERN"}, -{"Type": "TECH_AP_MODERN_44", "Cost": 1250, "UITreeRow": 0, "EraType": "ERA_MODERN"}, -{"Type": "TECH_AP_MODERN_45", "Cost": 1250, "UITreeRow": 1, "EraType": "ERA_MODERN"}, -{"Type": "TECH_AP_MODERN_46", "Cost": 1370, "UITreeRow": -3, "EraType": "ERA_MODERN"}, -{"Type": "TECH_AP_MODERN_47", "Cost": 1370, "UITreeRow": -2, "EraType": "ERA_MODERN"}, -{"Type": "TECH_AP_MODERN_48", "Cost": 1370, "UITreeRow": -1, "EraType": "ERA_MODERN"}, -{"Type": "TECH_AP_MODERN_49", "Cost": 1370, "UITreeRow": 2, "EraType": "ERA_MODERN"}, -{"Type": "TECH_AP_ATOMIC_50", "Cost": 1480, "UITreeRow": -2, "EraType": "ERA_ATOMIC"}, -{"Type": "TECH_AP_ATOMIC_51", "Cost": 1480, "UITreeRow": -1, "EraType": "ERA_ATOMIC"}, -{"Type": "TECH_AP_ATOMIC_52", "Cost": 1480, "UITreeRow": 0, "EraType": "ERA_ATOMIC"}, -{"Type": "TECH_AP_ATOMIC_53", "Cost": 1480, "UITreeRow": 1, "EraType": "ERA_ATOMIC"}, -{"Type": "TECH_AP_ATOMIC_54", "Cost": 1480, "UITreeRow": 2, "EraType": "ERA_ATOMIC"}, -{"Type": "TECH_AP_ATOMIC_55", "Cost": 1660, "UITreeRow": -3, "EraType": "ERA_ATOMIC"}, -{"Type": "TECH_AP_ATOMIC_56", "Cost": 1660, "UITreeRow": 1, "EraType": "ERA_ATOMIC"}, -{"Type": "TECH_AP_ATOMIC_57", "Cost": 1660, "UITreeRow": 2, "EraType": "ERA_ATOMIC"}, -{"Type": "TECH_AP_INFORMATION_58", "Cost": 1850, "UITreeRow": -3, "EraType": "ERA_INFORMATION"}, -{"Type": "TECH_AP_INFORMATION_59", "Cost": 1850, "UITreeRow": -1, "EraType": "ERA_INFORMATION"}, -{"Type": "TECH_AP_INFORMATION_60", "Cost": 1850, "UITreeRow": 0, "EraType": "ERA_INFORMATION"}, -{"Type": "TECH_AP_INFORMATION_61", "Cost": 1850, "UITreeRow": 1, "EraType": "ERA_INFORMATION"}, -{"Type": "TECH_AP_INFORMATION_62", "Cost": 1850, "UITreeRow": 2, "EraType": "ERA_INFORMATION"}, -{"Type": "TECH_AP_INFORMATION_63", "Cost": 1850, "UITreeRow": 3, "EraType": "ERA_INFORMATION"}, -{"Type": "TECH_AP_INFORMATION_64", "Cost": 2155, "UITreeRow": -2, "EraType": "ERA_INFORMATION"}, -{"Type": "TECH_AP_INFORMATION_65", "Cost": 2155, "UITreeRow": 2, "EraType": "ERA_INFORMATION"}, -{"Type": "TECH_AP_INFORMATION_66", "Cost": 2155, "UITreeRow": 1, "EraType": "ERA_INFORMATION"}, -{"Type": "TECH_AP_MEDIEVAL_67", "Cost": 300, "UITreeRow": -3, "EraType": "ERA_MEDIEVAL"}, -{"Type": "TECH_AP_MODERN_68", "Cost": 1250, "UITreeRow": 3, "EraType": "ERA_MODERN"}, -{"Type": "TECH_AP_FUTURE_69", "Cost": 2200, "UITreeRow": -3, "EraType": "ERA_FUTURE"}, -{"Type": "TECH_AP_FUTURE_70", "Cost": 2200, "UITreeRow": -2, "EraType": "ERA_FUTURE"}, -{"Type": "TECH_AP_FUTURE_71", "Cost": 2200, "UITreeRow": -1, "EraType": "ERA_FUTURE"}, -{"Type": "TECH_AP_FUTURE_72", "Cost": 2200, "UITreeRow": 0, "EraType": "ERA_FUTURE"}, -{"Type": "TECH_AP_FUTURE_73", "Cost": 2200, "UITreeRow": 1, "EraType": "ERA_FUTURE"}, -{"Type": "TECH_AP_FUTURE_74", "Cost": 2200, "UITreeRow": 2, "EraType": "ERA_FUTURE"}, -{"Type": "TECH_AP_FUTURE_75", "Cost": 2500, "UITreeRow": 0, "EraType": "ERA_FUTURE"}, -{"Type": "TECH_AP_FUTURE_76", "Cost": 2600, "UITreeRow": 0, "EraType": "ERA_FUTURE"} -] \ No newline at end of file + { + "Type": "TECH_AP_ANCIENT_00", + "Cost": 25, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_ANCIENT_01", + "Cost": 25, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_ANCIENT_02", + "Cost": 25, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_ANCIENT_03", + "Cost": 50, + "UITreeRow": -3, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_ANCIENT_04", + "Cost": 50, + "UITreeRow": -2, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_ANCIENT_05", + "Cost": 50, + "UITreeRow": -1, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_ANCIENT_06", + "Cost": 50, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_ANCIENT_07", + "Cost": 50, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_ANCIENT_08", + "Cost": 80, + "UITreeRow": 2, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_ANCIENT_09", + "Cost": 80, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_ANCIENT_10", + "Cost": 80, + "UITreeRow": 4, + "EraType": "ERA_ANCIENT" + }, + { + "Type": "TECH_AP_CLASSICAL_11", + "Cost": 120, + "UITreeRow": -2, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "TECH_AP_CLASSICAL_12", + "Cost": 120, + "UITreeRow": 0, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "TECH_AP_CLASSICAL_13", + "Cost": 120, + "UITreeRow": 1, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "TECH_AP_CLASSICAL_14", + "Cost": 120, + "UITreeRow": 3, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "TECH_AP_CLASSICAL_15", + "Cost": 200, + "UITreeRow": -3, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "TECH_AP_CLASSICAL_16", + "Cost": 200, + "UITreeRow": -1, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "TECH_AP_CLASSICAL_17", + "Cost": 200, + "UITreeRow": 2, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "TECH_AP_CLASSICAL_18", + "Cost": 200, + "UITreeRow": 4, + "EraType": "ERA_CLASSICAL" + }, + { + "Type": "TECH_AP_MEDIEVAL_19", + "Cost": 300, + "UITreeRow": -2, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "TECH_AP_MEDIEVAL_20", + "Cost": 300, + "UITreeRow": 0, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "TECH_AP_MEDIEVAL_21", + "Cost": 300, + "UITreeRow": 4, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "TECH_AP_MEDIEVAL_22", + "Cost": 390, + "UITreeRow": -1, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "TECH_AP_MEDIEVAL_23", + "Cost": 390, + "UITreeRow": 1, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "TECH_AP_MEDIEVAL_24", + "Cost": 390, + "UITreeRow": 2, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "TECH_AP_MEDIEVAL_25", + "Cost": 390, + "UITreeRow": 3, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "TECH_AP_RENAISSANCE_26", + "Cost": 600, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "TECH_AP_RENAISSANCE_27", + "Cost": 600, + "UITreeRow": -2, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "TECH_AP_RENAISSANCE_28", + "Cost": 600, + "UITreeRow": 0, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "TECH_AP_RENAISSANCE_29", + "Cost": 600, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "TECH_AP_RENAISSANCE_30", + "Cost": 600, + "UITreeRow": 4, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "TECH_AP_RENAISSANCE_31", + "Cost": 730, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "TECH_AP_RENAISSANCE_32", + "Cost": 730, + "UITreeRow": -1, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "TECH_AP_RENAISSANCE_33", + "Cost": 730, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "TECH_AP_RENAISSANCE_34", + "Cost": 730, + "UITreeRow": 3, + "EraType": "ERA_RENAISSANCE" + }, + { + "Type": "TECH_AP_INDUSTRIAL_35", + "Cost": 930, + "UITreeRow": -2, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "TECH_AP_INDUSTRIAL_36", + "Cost": 930, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "TECH_AP_INDUSTRIAL_37", + "Cost": 930, + "UITreeRow": 1, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "TECH_AP_INDUSTRIAL_38", + "Cost": 930, + "UITreeRow": 3, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "TECH_AP_INDUSTRIAL_39", + "Cost": 1070, + "UITreeRow": -3, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "TECH_AP_INDUSTRIAL_40", + "Cost": 1070, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "TECH_AP_INDUSTRIAL_41", + "Cost": 1070, + "UITreeRow": 0, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "TECH_AP_INDUSTRIAL_42", + "Cost": 1070, + "UITreeRow": 2, + "EraType": "ERA_INDUSTRIAL" + }, + { + "Type": "TECH_AP_MODERN_43", + "Cost": 1250, + "UITreeRow": -2, + "EraType": "ERA_MODERN" + }, + { + "Type": "TECH_AP_MODERN_44", + "Cost": 1250, + "UITreeRow": 0, + "EraType": "ERA_MODERN" + }, + { + "Type": "TECH_AP_MODERN_45", + "Cost": 1250, + "UITreeRow": 1, + "EraType": "ERA_MODERN" + }, + { + "Type": "TECH_AP_MODERN_46", + "Cost": 1370, + "UITreeRow": -3, + "EraType": "ERA_MODERN" + }, + { + "Type": "TECH_AP_MODERN_47", + "Cost": 1370, + "UITreeRow": -2, + "EraType": "ERA_MODERN" + }, + { + "Type": "TECH_AP_MODERN_48", + "Cost": 1370, + "UITreeRow": -1, + "EraType": "ERA_MODERN" + }, + { + "Type": "TECH_AP_MODERN_49", + "Cost": 1370, + "UITreeRow": 2, + "EraType": "ERA_MODERN" + }, + { + "Type": "TECH_AP_ATOMIC_50", + "Cost": 1480, + "UITreeRow": -2, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "TECH_AP_ATOMIC_51", + "Cost": 1480, + "UITreeRow": -1, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "TECH_AP_ATOMIC_52", + "Cost": 1480, + "UITreeRow": 0, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "TECH_AP_ATOMIC_53", + "Cost": 1480, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "TECH_AP_ATOMIC_54", + "Cost": 1480, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "TECH_AP_ATOMIC_55", + "Cost": 1660, + "UITreeRow": -3, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "TECH_AP_ATOMIC_56", + "Cost": 1660, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "TECH_AP_ATOMIC_57", + "Cost": 1660, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC" + }, + { + "Type": "TECH_AP_INFORMATION_58", + "Cost": 1850, + "UITreeRow": -3, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "TECH_AP_INFORMATION_59", + "Cost": 1850, + "UITreeRow": -1, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "TECH_AP_INFORMATION_60", + "Cost": 1850, + "UITreeRow": 0, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "TECH_AP_INFORMATION_61", + "Cost": 1850, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "TECH_AP_INFORMATION_62", + "Cost": 1850, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "TECH_AP_INFORMATION_63", + "Cost": 1850, + "UITreeRow": 3, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "TECH_AP_INFORMATION_64", + "Cost": 2155, + "UITreeRow": -2, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "TECH_AP_INFORMATION_65", + "Cost": 2155, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "TECH_AP_INFORMATION_66", + "Cost": 2155, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION" + }, + { + "Type": "TECH_AP_MEDIEVAL_67", + "Cost": 300, + "UITreeRow": -3, + "EraType": "ERA_MEDIEVAL" + }, + { + "Type": "TECH_AP_MODERN_68", + "Cost": 1250, + "UITreeRow": 3, + "EraType": "ERA_MODERN" + }, + { + "Type": "TECH_AP_FUTURE_69", + "Cost": 2200, + "UITreeRow": -3, + "EraType": "ERA_FUTURE" + }, + { + "Type": "TECH_AP_FUTURE_70", + "Cost": 2200, + "UITreeRow": -2, + "EraType": "ERA_FUTURE" + }, + { + "Type": "TECH_AP_FUTURE_71", + "Cost": 2200, + "UITreeRow": -1, + "EraType": "ERA_FUTURE" + }, + { + "Type": "TECH_AP_FUTURE_72", + "Cost": 2200, + "UITreeRow": 0, + "EraType": "ERA_FUTURE" + }, + { + "Type": "TECH_AP_FUTURE_73", + "Cost": 2200, + "UITreeRow": 1, + "EraType": "ERA_FUTURE" + }, + { + "Type": "TECH_AP_FUTURE_74", + "Cost": 2200, + "UITreeRow": 2, + "EraType": "ERA_FUTURE" + }, + { + "Type": "TECH_AP_FUTURE_75", + "Cost": 2500, + "UITreeRow": 0, + "EraType": "ERA_FUTURE" + }, + { + "Type": "TECH_AP_FUTURE_76", + "Cost": 2600, + "UITreeRow": 0, + "EraType": "ERA_FUTURE" + } +] diff --git a/worlds/civ_6/data/new_tech_prereqs.json b/worlds/civ_6/data/new_tech_prereqs.json index 2674292bc11e..d70a4b81a99e 100644 --- a/worlds/civ_6/data/new_tech_prereqs.json +++ b/worlds/civ_6/data/new_tech_prereqs.json @@ -1,104 +1,207 @@ [ -{"Technology": "TECH_AP_ANCIENT_06", "PrereqTech": "TECH_AP_ANCIENT_01"}, -{"Technology": "TECH_AP_ANCIENT_07", "PrereqTech": "TECH_AP_ANCIENT_00"}, -{"Technology": "TECH_AP_ANCIENT_05", "PrereqTech": "TECH_AP_ANCIENT_00"}, -{"Technology": "TECH_AP_ANCIENT_08", "PrereqTech": "TECH_AP_ANCIENT_02"}, -{"Technology": "TECH_AP_ANCIENT_09", "PrereqTech": "TECH_AP_ANCIENT_02"}, -{"Technology": "TECH_AP_ANCIENT_10", "PrereqTech": "TECH_AP_ANCIENT_02"}, -{"Technology": "TECH_AP_CLASSICAL_15", "PrereqTech": "TECH_AP_ANCIENT_03"}, -{"Technology": "TECH_AP_CLASSICAL_11", "PrereqTech": "TECH_AP_ANCIENT_03"}, -{"Technology": "TECH_AP_CLASSICAL_11", "PrereqTech": "TECH_AP_ANCIENT_04"}, -{"Technology": "TECH_AP_CLASSICAL_12", "PrereqTech": "TECH_AP_ANCIENT_07"}, -{"Technology": "TECH_AP_CLASSICAL_13", "PrereqTech": "TECH_AP_ANCIENT_06"}, -{"Technology": "TECH_AP_CLASSICAL_14", "PrereqTech": "TECH_AP_ANCIENT_09"}, -{"Technology": "TECH_AP_CLASSICAL_16", "PrereqTech": "TECH_AP_CLASSICAL_12"}, -{"Technology": "TECH_AP_CLASSICAL_17", "PrereqTech": "TECH_AP_ANCIENT_08"}, -{"Technology": "TECH_AP_CLASSICAL_17", "PrereqTech": "TECH_AP_CLASSICAL_13"}, -{"Technology": "TECH_AP_CLASSICAL_18", "PrereqTech": "TECH_AP_ANCIENT_10"}, -{"Technology": "TECH_AP_MEDIEVAL_19", "PrereqTech": "TECH_AP_CLASSICAL_16"}, -{"Technology": "TECH_AP_MEDIEVAL_20", "PrereqTech": "TECH_AP_CLASSICAL_12"}, -{"Technology": "TECH_AP_MEDIEVAL_20", "PrereqTech": "TECH_AP_CLASSICAL_13"}, -{"Technology": "TECH_AP_MEDIEVAL_23", "PrereqTech": "TECH_AP_CLASSICAL_13"}, -{"Technology": "TECH_AP_MEDIEVAL_21", "PrereqTech": "TECH_AP_CLASSICAL_14"}, -{"Technology": "TECH_AP_MEDIEVAL_21", "PrereqTech": "TECH_AP_CLASSICAL_18"}, -{"Technology": "TECH_AP_MEDIEVAL_22", "PrereqTech": "TECH_AP_CLASSICAL_16"}, -{"Technology": "TECH_AP_MEDIEVAL_22", "PrereqTech": "TECH_AP_MEDIEVAL_20"}, -{"Technology": "TECH_AP_MEDIEVAL_25", "PrereqTech": "TECH_AP_CLASSICAL_17"}, -{"Technology": "TECH_AP_MEDIEVAL_24", "PrereqTech": "TECH_AP_CLASSICAL_17"}, -{"Technology": "TECH_AP_RENAISSANCE_27", "PrereqTech": "TECH_AP_MEDIEVAL_22"}, -{"Technology": "TECH_AP_RENAISSANCE_28", "PrereqTech": "TECH_AP_MEDIEVAL_22"}, -{"Technology": "TECH_AP_RENAISSANCE_28", "PrereqTech": "TECH_AP_MEDIEVAL_23"}, -{"Technology": "TECH_AP_RENAISSANCE_29", "PrereqTech": "TECH_AP_MEDIEVAL_20"}, -{"Technology": "TECH_AP_RENAISSANCE_29", "PrereqTech": "TECH_AP_MEDIEVAL_23"}, -{"Technology": "TECH_AP_RENAISSANCE_29", "PrereqTech": "TECH_AP_MEDIEVAL_24"}, -{"Technology": "TECH_AP_RENAISSANCE_30", "PrereqTech": "TECH_AP_MEDIEVAL_21"}, -{"Technology": "TECH_AP_RENAISSANCE_31", "PrereqTech": "TECH_AP_RENAISSANCE_26"}, -{"Technology": "TECH_AP_RENAISSANCE_32", "PrereqTech": "TECH_AP_MEDIEVAL_22"}, -{"Technology": "TECH_AP_RENAISSANCE_33", "PrereqTech": "TECH_AP_RENAISSANCE_29"}, -{"Technology": "TECH_AP_RENAISSANCE_34", "PrereqTech": "TECH_AP_MEDIEVAL_25"}, -{"Technology": "TECH_AP_INDUSTRIAL_35", "PrereqTech": "TECH_AP_RENAISSANCE_31"}, -{"Technology": "TECH_AP_INDUSTRIAL_35", "PrereqTech": "TECH_AP_RENAISSANCE_27"}, -{"Technology": "TECH_AP_INDUSTRIAL_36", "PrereqTech": "TECH_AP_RENAISSANCE_32"}, -{"Technology": "TECH_AP_INDUSTRIAL_36", "PrereqTech": "TECH_AP_RENAISSANCE_28"}, -{"Technology": "TECH_AP_INDUSTRIAL_41", "PrereqTech": "TECH_AP_INDUSTRIAL_36"}, -{"Technology": "TECH_AP_INDUSTRIAL_41", "PrereqTech": "TECH_AP_RENAISSANCE_33"}, -{"Technology": "TECH_AP_INDUSTRIAL_38", "PrereqTech": "TECH_AP_RENAISSANCE_34"}, -{"Technology": "TECH_AP_INDUSTRIAL_38", "PrereqTech": "TECH_AP_RENAISSANCE_30"}, -{"Technology": "TECH_AP_INDUSTRIAL_39", "PrereqTech": "TECH_AP_INDUSTRIAL_35"}, -{"Technology": "TECH_AP_INDUSTRIAL_40", "PrereqTech": "TECH_AP_INDUSTRIAL_36"}, -{"Technology": "TECH_AP_INDUSTRIAL_37", "PrereqTech": "TECH_AP_RENAISSANCE_33"}, -{"Technology": "TECH_AP_INDUSTRIAL_42", "PrereqTech": "TECH_AP_INDUSTRIAL_37"}, -{"Technology": "TECH_AP_INDUSTRIAL_42", "PrereqTech": "TECH_AP_INDUSTRIAL_38"}, -{"Technology": "TECH_AP_MODERN_43", "PrereqTech": "TECH_AP_INDUSTRIAL_35"}, -{"Technology": "TECH_AP_MODERN_43", "PrereqTech": "TECH_AP_INDUSTRIAL_36"}, -{"Technology": "TECH_AP_MODERN_44", "PrereqTech": "TECH_AP_INDUSTRIAL_41"}, -{"Technology": "TECH_AP_MODERN_45", "PrereqTech": "TECH_AP_INDUSTRIAL_42"}, -{"Technology": "TECH_AP_MODERN_46", "PrereqTech": "TECH_AP_INDUSTRIAL_39"}, -{"Technology": "TECH_AP_MODERN_47", "PrereqTech": "TECH_AP_INDUSTRIAL_39"}, -{"Technology": "TECH_AP_MODERN_47", "PrereqTech": "TECH_AP_MODERN_43"}, -{"Technology": "TECH_AP_MODERN_48", "PrereqTech": "TECH_AP_INDUSTRIAL_40"}, -{"Technology": "TECH_AP_MODERN_49", "PrereqTech": "TECH_AP_MODERN_45"}, -{"Technology": "TECH_AP_ATOMIC_55", "PrereqTech": "TECH_AP_MODERN_46"}, -{"Technology": "TECH_AP_ATOMIC_55", "PrereqTech": "TECH_AP_MODERN_47"}, -{"Technology": "TECH_AP_ATOMIC_50", "PrereqTech": "TECH_AP_MODERN_47"}, -{"Technology": "TECH_AP_ATOMIC_51", "PrereqTech": "TECH_AP_MODERN_47"}, -{"Technology": "TECH_AP_ATOMIC_51", "PrereqTech": "TECH_AP_MODERN_48"}, -{"Technology": "TECH_AP_ATOMIC_52", "PrereqTech": "TECH_AP_MODERN_44"}, -{"Technology": "TECH_AP_ATOMIC_52", "PrereqTech": "TECH_AP_MODERN_45"}, -{"Technology": "TECH_AP_ATOMIC_53", "PrereqTech": "TECH_AP_MODERN_45"}, -{"Technology": "TECH_AP_ATOMIC_53", "PrereqTech": "TECH_AP_MODERN_49"}, -{"Technology": "TECH_AP_ATOMIC_56", "PrereqTech": "TECH_AP_ATOMIC_52"}, -{"Technology": "TECH_AP_ATOMIC_56", "PrereqTech": "TECH_AP_ATOMIC_53"}, -{"Technology": "TECH_AP_ATOMIC_54", "PrereqTech": "TECH_AP_MODERN_49"}, -{"Technology": "TECH_AP_ATOMIC_57", "PrereqTech": "TECH_AP_ATOMIC_54"}, -{"Technology": "TECH_AP_INFORMATION_58", "PrereqTech": "TECH_AP_ATOMIC_55"}, -{"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_ATOMIC_55"}, -{"Technology": "TECH_AP_INFORMATION_59", "PrereqTech": "TECH_AP_ATOMIC_50"}, -{"Technology": "TECH_AP_INFORMATION_59", "PrereqTech": "TECH_AP_ATOMIC_51"}, -{"Technology": "TECH_AP_INFORMATION_60", "PrereqTech": "TECH_AP_ATOMIC_51"}, -{"Technology": "TECH_AP_INFORMATION_60", "PrereqTech": "TECH_AP_ATOMIC_52"}, -{"Technology": "TECH_AP_INFORMATION_61", "PrereqTech": "TECH_AP_ATOMIC_56"}, -{"Technology": "TECH_AP_INFORMATION_62", "PrereqTech": "TECH_AP_ATOMIC_57"}, -{"Technology": "TECH_AP_INFORMATION_63", "PrereqTech": "TECH_AP_ATOMIC_57"}, -{"Technology": "TECH_AP_INFORMATION_65", "PrereqTech": "TECH_AP_INFORMATION_62"}, -{"Technology": "TECH_AP_INFORMATION_66", "PrereqTech": "TECH_AP_INFORMATION_61"}, -{"Technology": "TECH_AP_MEDIEVAL_67", "PrereqTech": "TECH_AP_CLASSICAL_15"}, -{"Technology": "TECH_AP_MEDIEVAL_67", "PrereqTech": "TECH_AP_CLASSICAL_16"}, -{"Technology": "TECH_AP_MEDIEVAL_23", "PrereqTech": "TECH_AP_MEDIEVAL_20"}, -{"Technology": "TECH_AP_MODERN_68", "PrereqTech": "TECH_AP_INDUSTRIAL_42"}, -{"Technology": "TECH_AP_MODERN_49", "PrereqTech": "TECH_AP_MODERN_68"}, -{"Technology": "TECH_AP_RENAISSANCE_26", "PrereqTech": "TECH_AP_MEDIEVAL_67"}, -{"Technology": "TECH_AP_RENAISSANCE_27", "PrereqTech": "TECH_AP_MEDIEVAL_67"}, -{"Technology": "TECH_AP_RENAISSANCE_27", "PrereqTech": "TECH_AP_MEDIEVAL_19"}, -{"Technology": "TECH_AP_MODERN_48", "PrereqTech": "TECH_AP_MODERN_44"}, -{"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_INFORMATION_59"}, -{"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_INFORMATION_60"}, -{"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_INFORMATION_61"}, -{"Technology": "TECH_AP_FUTURE_69", "PrereqTech": "TECH_AP_AP60"}, -{"Technology": "TECH_AP_FUTURE_70", "PrereqTech": "TECH_AP_AP60"}, -{"Technology": "TECH_AP_FUTURE_71", "PrereqTech": "TECH_AP_AP60"}, -{"Technology": "TECH_AP_FUTURE_72", "PrereqTech": "TECH_AP_AP60"}, -{"Technology": "TECH_AP_FUTURE_73", "PrereqTech": "TECH_AP_AP60"}, -{"Technology": "TECH_AP_FUTURE_74", "PrereqTech": "TECH_AP_AP60"}, -{"Technology": "TECH_AP_FUTURE_75", "PrereqTech": "TECH_AP_AP60"}, -{"Technology": "TECH_AP_FUTURE_76", "PrereqTech": "TECH_AP_AP60"}] \ No newline at end of file + { "Technology": "TECH_AP_ANCIENT_06", "PrereqTech": "TECH_AP_ANCIENT_01" }, + { "Technology": "TECH_AP_ANCIENT_07", "PrereqTech": "TECH_AP_ANCIENT_00" }, + { "Technology": "TECH_AP_ANCIENT_05", "PrereqTech": "TECH_AP_ANCIENT_00" }, + { "Technology": "TECH_AP_ANCIENT_08", "PrereqTech": "TECH_AP_ANCIENT_02" }, + { "Technology": "TECH_AP_ANCIENT_09", "PrereqTech": "TECH_AP_ANCIENT_02" }, + { "Technology": "TECH_AP_ANCIENT_10", "PrereqTech": "TECH_AP_ANCIENT_02" }, + { "Technology": "TECH_AP_CLASSICAL_15", "PrereqTech": "TECH_AP_ANCIENT_03" }, + { "Technology": "TECH_AP_CLASSICAL_11", "PrereqTech": "TECH_AP_ANCIENT_03" }, + { "Technology": "TECH_AP_CLASSICAL_11", "PrereqTech": "TECH_AP_ANCIENT_04" }, + { "Technology": "TECH_AP_CLASSICAL_12", "PrereqTech": "TECH_AP_ANCIENT_07" }, + { "Technology": "TECH_AP_CLASSICAL_13", "PrereqTech": "TECH_AP_ANCIENT_06" }, + { "Technology": "TECH_AP_CLASSICAL_14", "PrereqTech": "TECH_AP_ANCIENT_09" }, + { + "Technology": "TECH_AP_CLASSICAL_16", + "PrereqTech": "TECH_AP_CLASSICAL_12" + }, + { "Technology": "TECH_AP_CLASSICAL_17", "PrereqTech": "TECH_AP_ANCIENT_08" }, + { + "Technology": "TECH_AP_CLASSICAL_17", + "PrereqTech": "TECH_AP_CLASSICAL_13" + }, + { "Technology": "TECH_AP_CLASSICAL_18", "PrereqTech": "TECH_AP_ANCIENT_10" }, + { "Technology": "TECH_AP_MEDIEVAL_19", "PrereqTech": "TECH_AP_CLASSICAL_16" }, + { "Technology": "TECH_AP_MEDIEVAL_20", "PrereqTech": "TECH_AP_CLASSICAL_12" }, + { "Technology": "TECH_AP_MEDIEVAL_20", "PrereqTech": "TECH_AP_CLASSICAL_13" }, + { "Technology": "TECH_AP_MEDIEVAL_23", "PrereqTech": "TECH_AP_CLASSICAL_13" }, + { "Technology": "TECH_AP_MEDIEVAL_21", "PrereqTech": "TECH_AP_CLASSICAL_14" }, + { "Technology": "TECH_AP_MEDIEVAL_21", "PrereqTech": "TECH_AP_CLASSICAL_18" }, + { "Technology": "TECH_AP_MEDIEVAL_22", "PrereqTech": "TECH_AP_CLASSICAL_16" }, + { "Technology": "TECH_AP_MEDIEVAL_22", "PrereqTech": "TECH_AP_MEDIEVAL_20" }, + { "Technology": "TECH_AP_MEDIEVAL_25", "PrereqTech": "TECH_AP_CLASSICAL_17" }, + { "Technology": "TECH_AP_MEDIEVAL_24", "PrereqTech": "TECH_AP_CLASSICAL_17" }, + { + "Technology": "TECH_AP_RENAISSANCE_27", + "PrereqTech": "TECH_AP_MEDIEVAL_22" + }, + { + "Technology": "TECH_AP_RENAISSANCE_28", + "PrereqTech": "TECH_AP_MEDIEVAL_22" + }, + { + "Technology": "TECH_AP_RENAISSANCE_28", + "PrereqTech": "TECH_AP_MEDIEVAL_23" + }, + { + "Technology": "TECH_AP_RENAISSANCE_29", + "PrereqTech": "TECH_AP_MEDIEVAL_20" + }, + { + "Technology": "TECH_AP_RENAISSANCE_29", + "PrereqTech": "TECH_AP_MEDIEVAL_23" + }, + { + "Technology": "TECH_AP_RENAISSANCE_29", + "PrereqTech": "TECH_AP_MEDIEVAL_24" + }, + { + "Technology": "TECH_AP_RENAISSANCE_30", + "PrereqTech": "TECH_AP_MEDIEVAL_21" + }, + { + "Technology": "TECH_AP_RENAISSANCE_31", + "PrereqTech": "TECH_AP_RENAISSANCE_26" + }, + { + "Technology": "TECH_AP_RENAISSANCE_32", + "PrereqTech": "TECH_AP_MEDIEVAL_22" + }, + { + "Technology": "TECH_AP_RENAISSANCE_33", + "PrereqTech": "TECH_AP_RENAISSANCE_29" + }, + { + "Technology": "TECH_AP_RENAISSANCE_34", + "PrereqTech": "TECH_AP_MEDIEVAL_25" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_35", + "PrereqTech": "TECH_AP_RENAISSANCE_31" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_35", + "PrereqTech": "TECH_AP_RENAISSANCE_27" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_36", + "PrereqTech": "TECH_AP_RENAISSANCE_32" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_36", + "PrereqTech": "TECH_AP_RENAISSANCE_28" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_41", + "PrereqTech": "TECH_AP_INDUSTRIAL_36" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_41", + "PrereqTech": "TECH_AP_RENAISSANCE_33" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_38", + "PrereqTech": "TECH_AP_RENAISSANCE_34" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_38", + "PrereqTech": "TECH_AP_RENAISSANCE_30" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_39", + "PrereqTech": "TECH_AP_INDUSTRIAL_35" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_40", + "PrereqTech": "TECH_AP_INDUSTRIAL_36" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_37", + "PrereqTech": "TECH_AP_RENAISSANCE_33" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_42", + "PrereqTech": "TECH_AP_INDUSTRIAL_37" + }, + { + "Technology": "TECH_AP_INDUSTRIAL_42", + "PrereqTech": "TECH_AP_INDUSTRIAL_38" + }, + { "Technology": "TECH_AP_MODERN_43", "PrereqTech": "TECH_AP_INDUSTRIAL_35" }, + { "Technology": "TECH_AP_MODERN_43", "PrereqTech": "TECH_AP_INDUSTRIAL_36" }, + { "Technology": "TECH_AP_MODERN_44", "PrereqTech": "TECH_AP_INDUSTRIAL_41" }, + { "Technology": "TECH_AP_MODERN_45", "PrereqTech": "TECH_AP_INDUSTRIAL_42" }, + { "Technology": "TECH_AP_MODERN_46", "PrereqTech": "TECH_AP_INDUSTRIAL_39" }, + { "Technology": "TECH_AP_MODERN_47", "PrereqTech": "TECH_AP_INDUSTRIAL_39" }, + { "Technology": "TECH_AP_MODERN_47", "PrereqTech": "TECH_AP_MODERN_43" }, + { "Technology": "TECH_AP_MODERN_48", "PrereqTech": "TECH_AP_INDUSTRIAL_40" }, + { "Technology": "TECH_AP_MODERN_49", "PrereqTech": "TECH_AP_MODERN_45" }, + { "Technology": "TECH_AP_ATOMIC_55", "PrereqTech": "TECH_AP_MODERN_46" }, + { "Technology": "TECH_AP_ATOMIC_55", "PrereqTech": "TECH_AP_MODERN_47" }, + { "Technology": "TECH_AP_ATOMIC_50", "PrereqTech": "TECH_AP_MODERN_47" }, + { "Technology": "TECH_AP_ATOMIC_51", "PrereqTech": "TECH_AP_MODERN_47" }, + { "Technology": "TECH_AP_ATOMIC_51", "PrereqTech": "TECH_AP_MODERN_48" }, + { "Technology": "TECH_AP_ATOMIC_52", "PrereqTech": "TECH_AP_MODERN_44" }, + { "Technology": "TECH_AP_ATOMIC_52", "PrereqTech": "TECH_AP_MODERN_45" }, + { "Technology": "TECH_AP_ATOMIC_53", "PrereqTech": "TECH_AP_MODERN_45" }, + { "Technology": "TECH_AP_ATOMIC_53", "PrereqTech": "TECH_AP_MODERN_49" }, + { "Technology": "TECH_AP_ATOMIC_56", "PrereqTech": "TECH_AP_ATOMIC_52" }, + { "Technology": "TECH_AP_ATOMIC_56", "PrereqTech": "TECH_AP_ATOMIC_53" }, + { "Technology": "TECH_AP_ATOMIC_54", "PrereqTech": "TECH_AP_MODERN_49" }, + { "Technology": "TECH_AP_ATOMIC_57", "PrereqTech": "TECH_AP_ATOMIC_54" }, + { "Technology": "TECH_AP_INFORMATION_58", "PrereqTech": "TECH_AP_ATOMIC_55" }, + { "Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_ATOMIC_55" }, + { "Technology": "TECH_AP_INFORMATION_59", "PrereqTech": "TECH_AP_ATOMIC_50" }, + { "Technology": "TECH_AP_INFORMATION_59", "PrereqTech": "TECH_AP_ATOMIC_51" }, + { "Technology": "TECH_AP_INFORMATION_60", "PrereqTech": "TECH_AP_ATOMIC_51" }, + { "Technology": "TECH_AP_INFORMATION_60", "PrereqTech": "TECH_AP_ATOMIC_52" }, + { "Technology": "TECH_AP_INFORMATION_61", "PrereqTech": "TECH_AP_ATOMIC_56" }, + { "Technology": "TECH_AP_INFORMATION_62", "PrereqTech": "TECH_AP_ATOMIC_57" }, + { "Technology": "TECH_AP_INFORMATION_63", "PrereqTech": "TECH_AP_ATOMIC_57" }, + { + "Technology": "TECH_AP_INFORMATION_65", + "PrereqTech": "TECH_AP_INFORMATION_62" + }, + { + "Technology": "TECH_AP_INFORMATION_66", + "PrereqTech": "TECH_AP_INFORMATION_61" + }, + { "Technology": "TECH_AP_MEDIEVAL_67", "PrereqTech": "TECH_AP_CLASSICAL_15" }, + { "Technology": "TECH_AP_MEDIEVAL_67", "PrereqTech": "TECH_AP_CLASSICAL_16" }, + { "Technology": "TECH_AP_MEDIEVAL_23", "PrereqTech": "TECH_AP_MEDIEVAL_20" }, + { "Technology": "TECH_AP_MODERN_68", "PrereqTech": "TECH_AP_INDUSTRIAL_42" }, + { "Technology": "TECH_AP_MODERN_49", "PrereqTech": "TECH_AP_MODERN_68" }, + { + "Technology": "TECH_AP_RENAISSANCE_26", + "PrereqTech": "TECH_AP_MEDIEVAL_67" + }, + { + "Technology": "TECH_AP_RENAISSANCE_27", + "PrereqTech": "TECH_AP_MEDIEVAL_67" + }, + { + "Technology": "TECH_AP_RENAISSANCE_27", + "PrereqTech": "TECH_AP_MEDIEVAL_19" + }, + { "Technology": "TECH_AP_MODERN_48", "PrereqTech": "TECH_AP_MODERN_44" }, + { + "Technology": "TECH_AP_INFORMATION_64", + "PrereqTech": "TECH_AP_INFORMATION_59" + }, + { + "Technology": "TECH_AP_INFORMATION_64", + "PrereqTech": "TECH_AP_INFORMATION_60" + }, + { + "Technology": "TECH_AP_INFORMATION_64", + "PrereqTech": "TECH_AP_INFORMATION_61" + }, + { "Technology": "TECH_AP_FUTURE_69", "PrereqTech": "TECH_AP_AP60" }, + { "Technology": "TECH_AP_FUTURE_70", "PrereqTech": "TECH_AP_AP60" }, + { "Technology": "TECH_AP_FUTURE_71", "PrereqTech": "TECH_AP_AP60" }, + { "Technology": "TECH_AP_FUTURE_72", "PrereqTech": "TECH_AP_AP60" }, + { "Technology": "TECH_AP_FUTURE_73", "PrereqTech": "TECH_AP_AP60" }, + { "Technology": "TECH_AP_FUTURE_74", "PrereqTech": "TECH_AP_AP60" }, + { "Technology": "TECH_AP_FUTURE_75", "PrereqTech": "TECH_AP_AP60" }, + { "Technology": "TECH_AP_FUTURE_76", "PrereqTech": "TECH_AP_AP60" } +] diff --git a/worlds/civ_6/data/progressive_districts.json b/worlds/civ_6/data/progressive_districts.json index 1de4a185b4d6..f02e33a1b0f8 100644 --- a/worlds/civ_6/data/progressive_districts.json +++ b/worlds/civ_6/data/progressive_districts.json @@ -1,11 +1,7 @@ { "PROGRESSIVE_CAMPUS": ["TECH_WRITING", "TECH_EDUCATION", "TECH_CHEMISTRY"], - "PROGRESSIVE_THEATER": [ - "CIVIC_DRAMA_POETRY", - "CIVIC_HUMANISM", - "TECH_RADIO" - ], + "PROGRESSIVE_THEATER": ["CIVIC_DRAMA_POETRY", "CIVIC_HUMANISM", "TECH_RADIO"], "PROGRESSIVE_HOLY_SITE": ["TECH_ASTROLOGY", "CIVIC_THEOLOGY"], @@ -21,10 +17,7 @@ "TECH_ECONOMICS" ], - "PROGRESSIVE_HARBOR": [ - "TECH_CELESTIAL_NAVIGATION", - "TECH_MASS_PRODUCTION" - ], + "PROGRESSIVE_HARBOR": ["TECH_CELESTIAL_NAVIGATION", "TECH_MASS_PRODUCTION"], "PROGRESSIVE_INDUSTRIAL_ZONE": [ "TECH_APPRENTICESHIP", From 9e4aebf5349f9797b1702a6a11b399d553a192d7 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 15 Oct 2024 19:13:08 -0600 Subject: [PATCH 34/69] Remove exclude missable boosts --- worlds/civ_6/Options.py | 6 ------ worlds/civ_6/Regions.py | 12 ------------ 2 files changed, 18 deletions(-) diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 9dc87801eb24..322d5253be24 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -27,11 +27,6 @@ class BoostSanity(Toggle): display_name = "Boostsanity" -class ExcludeMissableBoosts(Toggle): - """If Boostsanity is enabled, this will prevent any boosts that are 'missable' from having progression items. Disabling this will potentially require multiple playthroughs to complete the seed.""" - display_name = "Exclude Missable Boosts" - - class ResearchCostMultiplier(Choice): """Multiplier for research cost of techs and civics, higher values make research more expensive. Cheap = 0.5x, Expensive = 1.5x.""" display_name = "Tech/Civic Cost Multiplier" @@ -95,7 +90,6 @@ class CivVIOptions(PerGameCommonOptions): progression_style: ProgressionStyle shuffle_goody_hut_rewards: ShuffleGoodyHuts boostsanity: BoostSanity - exclude_missable_boosts: ExcludeMissableBoosts research_cost_multiplier: ResearchCostMultiplier pre_hint_items: PreHintItems hide_item_names: HideItemNames diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 406a96aece1f..90cd021c3c13 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -153,15 +153,3 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): world.multiworld.completion_condition[player] = lambda state: state.can_reach( EraType.ERA_FUTURE.value, "Region", player) - - if world.options.boostsanity and not world.options.exclude_missable_boosts: - _update_boost_locations_to_include_missable(world) - - -def _update_boost_locations_to_include_missable(world: 'CivVIWorld'): - """If the player has exclude missable boosts disabled, set them all to default if they are excluded""" - for loc_data in world.location_table.values(): - if loc_data.location_type == CivVICheckType.BOOST: - location = world.get_location(loc_data.name) - if location.progress_type == LocationProgressType.EXCLUDED: - location.progress_type = LocationProgressType.DEFAULT From 3a48699bbb9ff807c815096c45f1185b5ab5a028 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 15 Oct 2024 19:17:49 -0600 Subject: [PATCH 35/69] Update options (update goody hut text, make research multiplier a range) --- worlds/civ_6/Container.py | 3 ++- worlds/civ_6/Options.py | 13 ++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py index d2ea665ed4b5..43672a0084af 100644 --- a/worlds/civ_6/Container.py +++ b/worlds/civ_6/Container.py @@ -50,7 +50,8 @@ def get_cost(world, location: CivVILocationData) -> int: Returns the cost of the item based on the game options """ options: CivVIOptions = world.options - multiplier = options.research_cost_multiplier + # Research cost is between 1 and 100 where 50 equals the default cost + multiplier = options.research_cost_multiplier / 50 return int(world.location_table[location.name].cost * multiplier) diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 322d5253be24..fdfd5d411ab7 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -18,7 +18,7 @@ class ProgressionStyle(Choice): class ShuffleGoodyHuts(DefaultOnToggle): - """Shuffles the goody hut rewards. Goody huts will only contain junk items and location checks are received sequentially (GOODY_HUT_1, GOODY_HUT_2, etc.).""" + """Shuffles the goody hut rewards. Goody huts will only contain junk items and locations are checked sequentially (First goody hut gives GOODY_HUT_1, second gives GOODY_HUT_2...).""" display_name = "Shuffle Goody Hut Rewards" @@ -27,13 +27,12 @@ class BoostSanity(Toggle): display_name = "Boostsanity" -class ResearchCostMultiplier(Choice): - """Multiplier for research cost of techs and civics, higher values make research more expensive. Cheap = 0.5x, Expensive = 1.5x.""" +class ResearchCostMultiplier(Range): + """Multiplier for research cost of techs and civics, higher values make research more expensive.""" display_name = "Tech/Civic Cost Multiplier" - option_cheap = 0.5 - option_default = 1 - option_expensive = 1.5 - default = 1 + range_start = 1 + range_end = 100 + default = 50 class PreHintItems(Choice): From c5b0e9bfe505147d917f4ceb84849c29d265e685 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 15 Oct 2024 19:20:20 -0600 Subject: [PATCH 36/69] Update docs punctuation and slot data init --- worlds/civ_6/Civ6Client.py | 1 + worlds/civ_6/docs/en_Civilization VI.md | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/worlds/civ_6/Civ6Client.py b/worlds/civ_6/Civ6Client.py index 5d8b79de5088..854c5d3e35ca 100644 --- a/worlds/civ_6/Civ6Client.py +++ b/worlds/civ_6/Civ6Client.py @@ -71,6 +71,7 @@ class CivVIContext(CommonContext): def __init__(self, server_address, password, apcivvi_file=None): super().__init__(server_address, password) + self.slot_data = {} self.game_interface = CivVIInterface(logger) location_by_era = generate_era_location_table() self.item_table = generate_item_table() diff --git a/worlds/civ_6/docs/en_Civilization VI.md b/worlds/civ_6/docs/en_Civilization VI.md index d4664e70ea7b..30ad9c275358 100644 --- a/worlds/civ_6/docs/en_Civilization VI.md +++ b/worlds/civ_6/docs/en_Civilization VI.md @@ -30,14 +30,14 @@ A short period after receiving an item, you will get a notification indicating y - "Oh no! I received the Machinery tech and now instead of getting an Archer next turn, I have to wait an additional 10 turns to get a Crossbowman!" - Vanilla prevents you from building units of the same class from an earlier tech level after you have researched a later variant. For example, this could be problematic if someone unlocks Crossbowmen for you right out the gate since you won't be able to make Archers (which have a much lower production cost). Solution: You can now go in to the tech tree, click "Toggle Archipelago Tree" to view your unlocked techs, and then can click any tech you have unlocked to toggle whether it is currently active or not. -- "How does DeathLink work? Am I going to have to start a new game every time one of my friends dies??" +- "How does DeathLink work? Am I going to have to start a new game every time one of my friends dies?" - Heavens no, my fellow Archipelago appreciator. When configuring your Archipelago options for Civilization on the options page, there are several choices available for you to fine tune the way you'd like to be punished for the follies of your friends. These include: Having a random unit destroyed, losing a percentage of gold or faith, or even losing a point on your era score. If you can't make up your mind, you can elect to have any of them be selected every time a death link is sent your way. In the event you lose one of your units in combat (this means captured units don't count), then you will send a death link event to the rest of your friends. - I enabled `progressive districts` but I have no idea techs/civics what items are locked behind progression now! - - Any technology or civic that grants you a new building in a district (or grants you the district itself) is now locked behind a progressive item. For example, `PROGRESSIVE_CAMPUS` would give you these items in the following order: - 1. `TECH_WRITING` - 2. `TECH_EDUCATION` + - Any technology or civic that grants you a new building in a district (or grants you the district itself) is now locked behind a progressive item. For example, `PROGRESSIVE_CAMPUS` would give you these items in the following order: + 1. `TECH_WRITING` + 2. `TECH_EDUCATION` 3. `TECH_CHEMISTRY` - If you want to see the details around each item, you can review [this file](https://github.com/hesto2/civilization_vi_apworld/blob/main/data/progressive_districts.json). @@ -48,7 +48,7 @@ Boosts have logic associated with them in order to verify you can always reach t ### Boostsanity FAQs - Someone sent me a tech/civic, and I'm worried I won't be able to boost it anymore! - Fear not! Through a lot of wizardry 🧙‍♂️ you can boost civics/techs that have already been received. Additionally, the UI has been updated to show you whether they have been boosted or not after receiving them. -- I need to kill a unit with a slinger/archer/musketman or some other obsolete unit I can't build anymore, how can I do this?? +- I need to kill a unit with a slinger/archer/musketman or some other obsolete unit I can't build anymore, how can I do this? - Don't forget you can go into the Tech Tree and click on a Vanilla tech you've received in order to toggle it on/off. This is necessary in order to pursue some of the boosts if you receive techs in certain orders. - Something happened, and I'm not able to unlock the boost due to game rules! - A few scenarios you may worry about: "Found a religion", "Make an alliance with another player", "Develop an alliance to level 2", "Build a wonder from X Era", to name a few. Any boost that is "miss-able" has been flagged as an "Excluded" location and will not ever receive a progression item. For a list of how each boost is flagged, take a look [here](https://github.com/hesto2/civilization_vi_apworld/blob/main/data/boosts.json). From dd486a00777cac163375a31ec82ee47e3e9b50f3 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 15 Oct 2024 20:39:54 -0600 Subject: [PATCH 37/69] Move priority/excluded locations into options --- worlds/civ_6/Locations.py | 12 ++++++------ worlds/civ_6/__init__.py | 14 +++++++++++--- worlds/civ_6/test/TestGoodyHuts.py | 13 +++++++------ 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index e8fc29691580..c1da4916dafd 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -91,12 +91,12 @@ def __init__(self, player: int, name: str = "", address: Optional[int] = None, p elif name.split("_")[0] == "BOOST": self.location_type = CivVICheckType.BOOST - if self.name in PRIORITY_LOCATIONS: - self.progress_type = LocationProgressType.PRIORITY - elif self.name in EXCLUDED_LOCATIONS: - self.progress_type = LocationProgressType.EXCLUDED - else: - self.progress_type = LocationProgressType.DEFAULT + # if self.name in PRIORITY_LOCATIONS: + # self.progress_type = LocationProgressType.PRIORITY + # elif self.name in EXCLUDED_LOCATIONS: + # self.progress_type = LocationProgressType.EXCLUDED + # else: + self.progress_type = LocationProgressType.DEFAULT if self.location_type == CivVICheckType.BOOST: boost_data_list = get_boosts_data() diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index bcd166af981b..62c1709e5404 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -11,7 +11,7 @@ from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql from .Enum import CivVICheckType from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIItemData, FillerItemRarity, format_item_name, generate_item_table, CivVIItem, get_random_filler_by_rarity -from .Locations import CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table +from .Locations import EXCLUDED_LOCATIONS, PRIORITY_LOCATIONS, CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table from .Options import CivVIOptions from .Regions import create_regions from BaseClasses import Item, ItemClassification, MultiWorld, Tutorial @@ -71,10 +71,18 @@ def __init__(self, multiworld: "MultiWorld", player: int): self.location_table: Dict[str, CivVILocationData] = {} self.item_table = generate_item_table() - for _era, locations in self.location_by_era.items(): - for _item_name, location in locations.items(): + for _, locations in self.location_by_era.items(): + for __, location in locations.items(): self.location_table[location.name] = location + def generate_early(self) -> None: + for location in self.location_table.values(): + name = location.name + if name in EXCLUDED_LOCATIONS: + self.options.exclude_locations.value.add(name) + if name in PRIORITY_LOCATIONS: + self.options.priority_locations.value.add(name) + def get_filler_item_name(self) -> str: return get_random_filler_by_rarity(self, FillerItemRarity.COMMON).name diff --git a/worlds/civ_6/test/TestGoodyHuts.py b/worlds/civ_6/test/TestGoodyHuts.py index 8c978dc7ffe5..c30f4da6c1e8 100644 --- a/worlds/civ_6/test/TestGoodyHuts.py +++ b/worlds/civ_6/test/TestGoodyHuts.py @@ -18,14 +18,13 @@ class TestGoodyHutsIncluded(CivVITestBase): def test_goody_huts_get_included(self) -> None: self.world_setup() - distribute_items_restrictive + self.world.generate_early() distribute_items_restrictive(self.multiworld) expected_goody_huts = 10 found = 0 - for i in range(expected_goody_huts): - location = self.multiworld.get_location(f"GOODY_HUT_{i + 1}", self.player) - self.assertEqual(location.item.classification, ItemClassification.filler) - found += 1 + for location in self.multiworld.get_locations(self.player): + if location.name.startswith("GOODY_HUT_"): + found += 1 self.assertEqual(found, expected_goody_huts) @@ -42,6 +41,7 @@ class TestGoodyHutsExcluded(CivVITestBase): def test_goody_huts_are_not_included(self) -> None: self.world_setup() + self.world.generate_early() distribute_items_restrictive found_goody_huts = 0 for location in self.multiworld.get_locations(self.player): @@ -64,6 +64,7 @@ class TestFillerItemsIncludedByRarity(CivVITestBase): def test_filler_items_are_included_by_rarity(self) -> None: self.world_setup() + self.world.generate_early() distribute_items_restrictive rarity_counts: Dict[FillerItemRarity, int] = { FillerItemRarity.COMMON: 0, @@ -87,7 +88,6 @@ def test_filler_items_are_included_by_rarity(self) -> None: self.assertEqual(rarity_counts[rarity], expected, f"Expected {expected} {rarity} items, found {rarity_counts[rarity]}") - class TestFillerItemsIncludedByRarityWithoutBoostsanity(CivVITestBase): auto_construct = False options = { @@ -102,6 +102,7 @@ class TestFillerItemsIncludedByRarityWithoutBoostsanity(CivVITestBase): def test_filler_items_are_included_by_rarity_without_boostsanity(self) -> None: self.world_setup() + self.world.generate_early() distribute_items_restrictive rarity_counts: Dict[FillerItemRarity, int] = { FillerItemRarity.COMMON: 0, From 7478de49124900a86820a0ca9a8967ecb3854e62 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 15 Oct 2024 20:50:04 -0600 Subject: [PATCH 38/69] Implement docs PR feedback --- worlds/civ_6/docs/en_Civilization VI.md | 4 ++-- worlds/civ_6/docs/setup_en.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/worlds/civ_6/docs/en_Civilization VI.md b/worlds/civ_6/docs/en_Civilization VI.md index 30ad9c275358..501a811e4b9c 100644 --- a/worlds/civ_6/docs/en_Civilization VI.md +++ b/worlds/civ_6/docs/en_Civilization VI.md @@ -20,7 +20,7 @@ A short period after receiving an item, you will get a notification indicating y ## FAQs - Do I need the DLC to play this? - - Yes, you need both Rise & Fall and Gathering Storm. If there is enough interest, then I can eventually add support for Archipelago runs that don't require both expansions. + - Yes, you need both Rise & Fall and Gathering Storm. - Does this work with Multiplayer? - It does not and, despite my best efforts, probably won't until there's a new way for external programs to be able to interact with the game. - Does my mod that reskins Barbarians as various Pro Wrestlers work with this? @@ -34,7 +34,7 @@ Solution: You can now go in to the tech tree, click "Toggle Archipelago Tree" to - Heavens no, my fellow Archipelago appreciator. When configuring your Archipelago options for Civilization on the options page, there are several choices available for you to fine tune the way you'd like to be punished for the follies of your friends. These include: Having a random unit destroyed, losing a percentage of gold or faith, or even losing a point on your era score. If you can't make up your mind, you can elect to have any of them be selected every time a death link is sent your way. In the event you lose one of your units in combat (this means captured units don't count), then you will send a death link event to the rest of your friends. -- I enabled `progressive districts` but I have no idea techs/civics what items are locked behind progression now! +- I enabled `progressive districts` but I have no idea what tech or civic a progressive district unlocks for me! - Any technology or civic that grants you a new building in a district (or grants you the district itself) is now locked behind a progressive item. For example, `PROGRESSIVE_CAMPUS` would give you these items in the following order: 1. `TECH_WRITING` 2. `TECH_EDUCATION` diff --git a/worlds/civ_6/docs/setup_en.md b/worlds/civ_6/docs/setup_en.md index f437b58fd956..09f6ff55c5e0 100644 --- a/worlds/civ_6/docs/setup_en.md +++ b/worlds/civ_6/docs/setup_en.md @@ -25,7 +25,7 @@ Once you have located your `AppOptions.txt`, do a search for `Enable FireTuner`. 1. Download and unzip the latest release of the mod from [GitHub](https://github.com/hesto2/civilization_archipelago_mod/releases/latest). -2. Copy the folder containing the mod files to your Civ VI mods folder. On Windows, this is usually located at `C:\Users\YOUR_USER\Documents\My Games\Sid Meier's Civilization VI\Mods` +2. Copy the folder containing the mod files to your Civ VI mods folder. On Windows, this is usually located at `C:\Users\YOUR_USER\Documents\My Games\Sid Meier's Civilization VI\Mods`. 3. After the Archipelago host generates a game, you should be given a `.apcivvi` file. Associate the file with the Archipelago Launcher and double click it. @@ -48,4 +48,4 @@ When configuring your game, make sure to start the game in the Ancient Era and l - If you are getting an error: "The remote computer refused the network connection", or something else related to the client (or tuner) not being able to connect, it likely indicates the tuner is not actually enabled. One simple way to verify that it is enabled is, after completing the setup steps, go to Main Menu → Options → Look for an option named "Tuner" and verify it is set to "Enabled" -- If your game gets in a state where someone has sent you items or you have sent locations but these are not correctly replicated to the multiworld, you can run `/resync` from the Civ 6 client. This may take up to a minute depending on how many items there are. +- If your game gets in a state where someone has sent you items or you have sent locations but these are not correctly sent to the multiworld, you can run `/resync` from the Civ 6 client. This may take up to a minute depending on how many items there are. From d205ace9662bc92bac39f56058d8925c0c7fdb5e Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 15 Oct 2024 20:56:56 -0600 Subject: [PATCH 39/69] PR Feedback for options --- worlds/civ_6/Options.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index fdfd5d411ab7..030afe31b4a8 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -4,11 +4,15 @@ class ProgressionStyle(Choice): """Determines what progressive items (if any) should be included. - Districts Only: Each tech/civic that would normally unlock a district or district building now has a logical progression. Example: TECH_BRONZE_WORKING is now PROGRESSIVE_ENCAMPMENT - Eras and Districts: Players will be defeated if they play until the world era advances beyond the currently unlocked maximum era. - A notification will be shown as the end of the era approaches letting the player know if they don't have enough progressive era items. + Districts Only: Each tech/civic that would normally unlock a district or building now has a logical progression. + Example: TECH_BRONZE_WORKING is now PROGRESSIVE_ENCAMPMENT + Eras and Districts: Players will be defeated if they play until the world era advances beyond the currently unlocked + maximum era. + A notification will be shown as the end of the era approaches letting the player know if they don't have enough + progressive era items. Unlocked eras can be seen in both the tech and civic trees. Includes all progressive districts. - None: No progressive items will be included. This means you can get district upgrades that won't be usable until the relevant district is unlocked. + None: No progressive items will be included. This means you can get district upgrades that won't be usable until the + relevant district is unlocked. """ display_name = "Progression Style" option_districts_only = 0 @@ -18,12 +22,15 @@ class ProgressionStyle(Choice): class ShuffleGoodyHuts(DefaultOnToggle): - """Shuffles the goody hut rewards. Goody huts will only contain junk items and locations are checked sequentially (First goody hut gives GOODY_HUT_1, second gives GOODY_HUT_2...).""" + """Shuffles the goody hut rewards. Goody huts will only contain junk items and locations are checked sequentially + (First goody hut gives GOODY_HUT_1, second gives GOODY_HUT_2, etc.).""" display_name = "Shuffle Goody Hut Rewards" class BoostSanity(Toggle): - """Boosts for Civics/Techs are location checks. Boosts can now be triggered even if the item has already been researched. If it is dependent upon a unit that is now obsolete, you can click to toggle on/off the relevant tech in the tech tree.""" + """Boosts for Civics/Techs are location checks. Boosts can now be triggered even if the item has already been + researched. If it is dependent upon a unit that is now obsolete, you can click to toggle on/off the relevant tech in + the tech tree.""" display_name = "Boostsanity" @@ -51,7 +58,8 @@ class PreHintItems(Choice): class HideItemNames(Toggle): - """Each Tech and Civic Location will have a title of 'Unrevealed' until its prereqs have been researched. Note that hints will still be precollected if that option is enabled.""" + """Each Tech and Civic Location will have a title of 'Unrevealed' until its prereqs have been researched. Note that + hints will still be precollected if that option is enabled.""" display_name = "Hide Item Names" @@ -62,9 +70,13 @@ class InGameFlagProgressionItems(DefaultOnToggle): class DeathLinkEffect(Choice): """What happens when a unit dies. - Faith, and Gold will be decreased by the amount specified in 'Death Link Effect Percent'. - Era score is decreased by 1. - Any will select any of these options any time a death link is received.""" + Unit Killed: A random unit will be killed when a death link is received. + Faith: Faith will be decreased by the amount specified in 'Death Link Effect Percent'. + Gold: Gold will be decreased by the amount specified in 'Death Link Effect Percent'. + Era Score: Era score is decreased by 1. + Any: Selects from any of these options whenever a death link is received. + Any Except Era Score: Selects from any of these options except for Era Score whenever a death link is received. + """ display_name = "Death Link Effect" option_unit_killed = 0 option_era_score = 1 From d38fcaa233f2d2a1a6ab93aaa95800c1b2ef5110 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 15 Oct 2024 21:27:54 -0600 Subject: [PATCH 40/69] PR feedback misc --- worlds/civ_6/Civ6Client.py | 6 +++--- worlds/civ_6/CivVIInterface.py | 1 + worlds/civ_6/Items.py | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/worlds/civ_6/Civ6Client.py b/worlds/civ_6/Civ6Client.py index 854c5d3e35ca..fe639a85e400 100644 --- a/worlds/civ_6/Civ6Client.py +++ b/worlds/civ_6/Civ6Client.py @@ -290,9 +290,9 @@ async def _main(connect, password, name): ctx = CivVIContext(connect, password, args.apcivvi_file) if args.apcivvi_file: - parent_dir = os.path.dirname(args.apcivvi_file) - target_name = os.path.basename(args.apcivvi_file).replace(".apcivvi", "-MOD-FILES") - target_path = os.path.join(parent_dir, target_name) + parent_dir: str = os.path.dirname(args.apcivvi_file) + target_name: str = os.path.basename(args.apcivvi_file).replace(".apcivvi", "-MOD-FILES") + target_path: str = os.path.join(parent_dir, target_name) if not os.path.exists(target_path): os.makedirs(target_path, exist_ok=True) logger.info("Extracting mod files to %s", target_path) diff --git a/worlds/civ_6/CivVIInterface.py b/worlds/civ_6/CivVIInterface.py index 51c1914a5ac8..8eae23b858ff 100644 --- a/worlds/civ_6/CivVIInterface.py +++ b/worlds/civ_6/CivVIInterface.py @@ -45,6 +45,7 @@ async def is_in_game(self) -> ConnectionState: if "attempt to index a nil valuestack traceback" in str(e) \ or ".. is not supported for string .. nilstack traceback" in str(e): return ConnectionState.IN_MENU + return ConnectionState.DISCONNECTED def print_connection_error(self, error: str) -> None: if error != self.last_error: diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index 0667b19f1ef8..c7fe874586de 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Dict, Optional, TYPE_CHECKING, List +from typing import Dict, Optional, TYPE_CHECKING, List, Union from BaseClasses import Item, ItemClassification from .Data import get_era_required_items_data, get_existing_civics_data, get_existing_techs_data, get_goody_hut_rewards_data, get_progressive_districts_data from .Enum import CivVICheckType, EraType @@ -86,7 +86,7 @@ def get_filler_item_data() -> Dict[str, FillerItemData]: class CivVIItemData: - civ_vi_id: int + civ_vi_id: Union[int, str] classification: ItemClassification name: str code: int From 2e9705bda04885209c364eb374883f93a3b8d1ca Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Wed, 16 Oct 2024 07:44:55 -0600 Subject: [PATCH 41/69] Update location classification and fix client type --- worlds/civ_6/DeathLink.py | 8 ++++--- worlds/civ_6/Locations.py | 42 +++++------------------------------ worlds/civ_6/__init__.py | 10 ++++----- worlds/civ_6/data/boosts.json | 2 +- 4 files changed, 16 insertions(+), 46 deletions(-) diff --git a/worlds/civ_6/DeathLink.py b/worlds/civ_6/DeathLink.py index 22ee579cfbd9..47f220798b55 100644 --- a/worlds/civ_6/DeathLink.py +++ b/worlds/civ_6/DeathLink.py @@ -1,12 +1,14 @@ import random -from CommonClient import CommonContext +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from .Civ6Client import CivVIContext # any is also an option but should not be considered an effect DEATH_LINK_EFFECTS = ["Gold", "Faith", "Era Score", "Unit Killed"] -async def handle_receive_deathlink(ctx: CommonContext, message): +async def handle_receive_deathlink(ctx: 'CivVIContext', message): """Resolves the effects of a deathlink received from the multiworld based on the options selected by the player""" chosen_effect = ctx.slot_data["death_link_effect"] effect: str = "Gold" @@ -32,7 +34,7 @@ async def handle_receive_deathlink(ctx: CommonContext, message): await ctx.game_interface.kill_unit(message) -async def handle_check_deathlink(ctx: CommonContext): +async def handle_check_deathlink(ctx: 'CivVIContext'): """Checks if the local player should send out a deathlink to the multiworld as well as if we should respond to any pending deathlinks sent to us """ # check if we received a death link if ctx.received_death_link: diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index c1da4916dafd..6579538df516 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -7,37 +7,7 @@ CIV_VI_AP_LOCATION_ID_BASE = 5041000 -# Locs that should have progression items (keypoint techs/civics, ~1 per era) -PRIORITY_LOCATIONS = [ - "TECH_ANCIENT_09", - "TECH_CLASSICAL_15", - "TECH_MEDIEVAL_20", - "TECH_RENAISSANCE_33", - "TECH_INDUSTRIAL_35", - "TECH_MODERN_47", - "TECH_ATOMIC_51", - "TECH_INFORMATION_59", - - "CIVIC_ANCIENT_04", - "CIVIC_CLASSICAL_08", - "CIVIC_MEDIEVAL_19", - "CIVIC_RENAISSANCE_26", - "CIVIC_INDUSTRIAL_33", - "CIVIC_MODERN_39", - "CIVIC_ATOMIC_46", - "CIVIC_INFORMATION_48", - - "ERA_CLASSICAL", - "ERA_MEDIEVAL", - "ERA_RENAISSANCE", - "ERA_INDUSTRIAL", - "ERA_MODERN", - "ERA_ATOMIC", - "ERA_INFORMATION", - "ERA_FUTURE" -] - -# Locs that should not have progression items (future techs/civics) +# Locs that should not have progression items EXCLUDED_LOCATIONS = [ "GOODY_HUT_1", "GOODY_HUT_2", @@ -91,12 +61,10 @@ def __init__(self, player: int, name: str = "", address: Optional[int] = None, p elif name.split("_")[0] == "BOOST": self.location_type = CivVICheckType.BOOST - # if self.name in PRIORITY_LOCATIONS: - # self.progress_type = LocationProgressType.PRIORITY - # elif self.name in EXCLUDED_LOCATIONS: - # self.progress_type = LocationProgressType.EXCLUDED - # else: - self.progress_type = LocationProgressType.DEFAULT + if self.name in EXCLUDED_LOCATIONS: + self.progress_type = LocationProgressType.EXCLUDED + else: + self.progress_type = LocationProgressType.DEFAULT if self.location_type == CivVICheckType.BOOST: boost_data_list = get_boosts_data() diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 62c1709e5404..dff3b09de666 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -1,3 +1,4 @@ +import logging import math import os from typing import Any, Dict, Set @@ -11,7 +12,7 @@ from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql from .Enum import CivVICheckType from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIItemData, FillerItemRarity, format_item_name, generate_item_table, CivVIItem, get_random_filler_by_rarity -from .Locations import EXCLUDED_LOCATIONS, PRIORITY_LOCATIONS, CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table +from .Locations import EXCLUDED_LOCATIONS, CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table from .Options import CivVIOptions from .Regions import create_regions from BaseClasses import Item, ItemClassification, MultiWorld, Tutorial @@ -78,10 +79,9 @@ def __init__(self, multiworld: "MultiWorld", player: int): def generate_early(self) -> None: for location in self.location_table.values(): name = location.name - if name in EXCLUDED_LOCATIONS: - self.options.exclude_locations.value.add(name) - if name in PRIORITY_LOCATIONS: - self.options.priority_locations.value.add(name) + if name in EXCLUDED_LOCATIONS and name in self.options.priority_locations.value: + self.options.priority_locations.value.remove(name) + logging.warning(f"Excluded location {name} is in priority locations, removing.") def get_filler_item_name(self) -> str: return get_random_filler_by_rarity(self, FillerItemRarity.COMMON).name diff --git a/worlds/civ_6/data/boosts.json b/worlds/civ_6/data/boosts.json index 0662e0e9c657..7c82fdead1fb 100644 --- a/worlds/civ_6/data/boosts.json +++ b/worlds/civ_6/data/boosts.json @@ -32,7 +32,7 @@ "EraType": "ERA_ANCIENT", "Prereq": [], "PrereqRequiredCount": 0, - "Classification": "PRIORITY" + "Classification": "DEFAULT" }, { "Type": "BOOST_TECH_MASONRY", From 62645f60abc9a787eee8166385bd946891e9887b Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Wed, 16 Oct 2024 09:10:57 -0600 Subject: [PATCH 42/69] Fix typings --- worlds/civ_6/Civ6Client.py | 27 +++++----- worlds/civ_6/CivVIInterface.py | 2 +- worlds/civ_6/Container.py | 27 +++++----- worlds/civ_6/Data.py | 57 ++++++++++++++++----- worlds/civ_6/DeathLink.py | 10 ++-- worlds/civ_6/Items.py | 22 ++++---- worlds/civ_6/Locations.py | 32 +++++------- worlds/civ_6/ProgressiveDistricts.py | 2 +- worlds/civ_6/Regions.py | 12 ++--- worlds/civ_6/TunerClient.py | 7 +-- worlds/civ_6/__init__.py | 18 ++++--- worlds/civ_6/test/TestBoostsanity.py | 4 +- worlds/civ_6/test/TestGoodyHuts.py | 6 +-- worlds/civ_6/test/TestRegionRequirements.py | 4 +- worlds/civ_6/test/TestStartingHints.py | 18 ++++--- 15 files changed, 142 insertions(+), 106 deletions(-) diff --git a/worlds/civ_6/Civ6Client.py b/worlds/civ_6/Civ6Client.py index fe639a85e400..e74caf5fa903 100644 --- a/worlds/civ_6/Civ6Client.py +++ b/worlds/civ_6/Civ6Client.py @@ -2,7 +2,7 @@ import logging import os import traceback -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional import zipfile from CommonClient import ClientCommandProcessor, CommonContext, get_base_parser, logger, server_loop, gui_enabled @@ -49,7 +49,7 @@ class CivVIContext(CommonContext): command_processor = CivVICommandProcessor game = "Civilization VI" items_handling = 0b111 - tuner_sync_task = None + tuner_sync_task: Optional[asyncio.Task[None]] = None game_interface: CivVIInterface location_name_to_civ_location: Dict[str, CivVILocationData] = {} location_name_to_id: Dict[str, int] = {} @@ -59,6 +59,7 @@ class CivVIContext(CommonContext): received_death_link = False death_link_message = "" death_link_enabled = False + slot_data: Dict[str, Any] death_link_just_changed = False # Used to prevent the deathlink from triggering when someone re enables it @@ -69,21 +70,21 @@ class CivVIContext(CommonContext): item.name: item.code for item in generate_item_table().values()} connection_state = ConnectionState.DISCONNECTED - def __init__(self, server_address, password, apcivvi_file=None): + def __init__(self, server_address: Optional[str], password: Optional[str], apcivvi_file: Optional[str] = None): super().__init__(server_address, password) - self.slot_data = {} + self.slot_data: Dict[str, Any] = {} self.game_interface = CivVIInterface(logger) location_by_era = generate_era_location_table() self.item_table = generate_item_table() self.apcivvi_file = apcivvi_file # Get tables formatted in a way that is easier to use here - for era, locations in location_by_era.items(): - for item_name, location in locations.items(): + for _era, locations in location_by_era.items(): + for _item_name, location in locations.items(): self.location_name_to_id[location.name] = location.code self.location_name_to_civ_location[location.name] = location - for item_name, item in self.item_table.items(): + for _item_name, item in self.item_table.items(): self.item_id_to_civ_item[item.code] = item async def resync(self): @@ -124,7 +125,7 @@ class CivVIManager(GameManager): self.ui = CivVIManager(self) self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") - def on_package(self, cmd: str, args: dict): + def on_package(self, cmd: str, args: Dict[str, Any]): if cmd == "Connected": self.slot_data = args["slot_data"] if "death_link" in args["slot_data"]: @@ -133,7 +134,7 @@ def on_package(self, cmd: str, args: dict): bool(args["slot_data"]["death_link"]))) -def update_connection_status(ctx: CivVIContext, status): +def update_connection_status(ctx: CivVIContext, status: ConnectionState): if ctx.connection_state == status: return elif status == ConnectionState.IN_GAME: @@ -216,7 +217,7 @@ async def handle_receive_items(ctx: CivVIContext, last_received_index_override: item: CivVIItemData = ctx.item_id_to_civ_item[network_item.item] item_to_send: CivVIItemData = ctx.item_id_to_civ_item[network_item.item] if index > last_received_index: - if item.item_type == CivVICheckType.PROGRESSIVE_DISTRICT: + if item.item_type == CivVICheckType.PROGRESSIVE_DISTRICT and item.civ_name: # if the item is progressive, then check how far in that progression type we are and send the appropriate item count = sum( 1 for count_item in progressive_districts if count_item.civ_name == item.civ_name) @@ -234,7 +235,7 @@ async def handle_receive_items(ctx: CivVIContext, last_received_index_override: if item.item_type == CivVICheckType.ERA: count = len(progressive_eras) + 1 await ctx.game_interface.give_item_to_player(item_to_send, sender, count) - elif item.item_type == CivVICheckType.GOODY: + elif item.item_type == CivVICheckType.GOODY and item_to_send.civ_name: item_to_send.civ_vi_id = item_to_send.civ_name await ctx.game_interface.give_item_to_player(item_to_send, sender) else: @@ -280,10 +281,10 @@ async def _handle_game_ready(ctx: CivVIContext): await asyncio.sleep(3) -def main(connect=None, password=None, name=None): +def main(connect: Optional[str] = None, password: Optional[str] = None, name: Optional[str] = None): Utils.init_logging("Civilization VI Client") - async def _main(connect, password, name): + async def _main(connect: Optional[str], password: Optional[str], name: Optional[str]): parser = get_base_parser() parser.add_argument("apcivvi_file", default="", type=str, nargs="?", help="Path to apcivvi file") args = parser.parse_args() diff --git a/worlds/civ_6/CivVIInterface.py b/worlds/civ_6/CivVIInterface.py index 8eae23b858ff..5ab9fabc3239 100644 --- a/worlds/civ_6/CivVIInterface.py +++ b/worlds/civ_6/CivVIInterface.py @@ -91,7 +91,7 @@ async def get_last_received_index(self) -> int: result = await self.tuner.send_game_command(command) return int(result) - async def send_notification(self, item: CivVIItemData, sender="someone") -> None: + async def send_notification(self, item: CivVIItemData, sender: str = "someone") -> None: command = f"GameCore.NotificationManager:SendNotification(GameCore.NotificationTypes.USER_DEFINED_2, \"{item.name} Received\", \"You have received {item.name} from \" .. \"{sender}\", 0, {item.civ_vi_id})" await self.tuner.send_command(command) diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py index 43672a0084af..792090b0b920 100644 --- a/worlds/civ_6/Container.py +++ b/worlds/civ_6/Container.py @@ -1,6 +1,6 @@ from dataclasses import dataclass import os -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING, Dict, List, Optional import zipfile from BaseClasses import ItemClassification, Location from worlds.Files import APContainer @@ -30,10 +30,10 @@ class CivVIContainer(APContainer): """ Responsible for generating the dynamic mod files for the Civ VI multiworld """ - game: str = "Civilization VI" + game: Optional[str] = "Civilization VI" - def __init__(self, patch_data: dict, base_path: str, output_directory: str, - player=None, player_name: str = "", server: str = ""): + def __init__(self, patch_data: Dict[str, str], base_path: str, output_directory: str, + player: Optional[int] = None, player_name: str = "", server: str = ""): self.patch_data = patch_data self.file_path = base_path container_path = os.path.join(output_directory, base_path + ".apcivvi") @@ -45,7 +45,7 @@ def write_contents(self, opened_zipfile: zipfile.ZipFile) -> None: super().write_contents(opened_zipfile) -def get_cost(world, location: CivVILocationData) -> int: +def get_cost(world: 'CivVIWorld', location: CivVILocationData) -> int: """ Returns the cost of the item based on the game options """ @@ -55,7 +55,7 @@ def get_cost(world, location: CivVILocationData) -> int: return int(world.location_table[location.name].cost * multiplier) -def get_formatted_player_name(world, player) -> str: +def get_formatted_player_name(world: 'CivVIWorld', player: int) -> str: """ Returns the name of the player in the world """ @@ -66,7 +66,7 @@ def get_formatted_player_name(world, player) -> str: def get_advisor_type(world: 'CivVIWorld', location: Location) -> str: - if world.options.advisor_show_progression_items and location.item.classification == ItemClassification.progression: + if world.options.advisor_show_progression_items and location.item and location.item.classification == ItemClassification.progression: return "ADVISOR_PROGRESSIVE" else: return "ADVISOR_GENERIC" @@ -76,7 +76,8 @@ def generate_new_items(world: 'CivVIWorld') -> str: """ Generates the XML for the new techs/civics as well as the blockers used to prevent players from researching their own items """ - locations: List[CivVILocation] = world.multiworld.get_filled_locations(world.player) + raw_locations = world.multiworld.get_filled_locations(world.player) + locations: List[CivVILocation] = [location for location in raw_locations if isinstance(location, CivVILocation)] techs = [location for location in locations if location.location_type == CivVICheckType.TECH] civics = [location for location in locations if location.location_type == @@ -118,7 +119,7 @@ def generate_new_items(world: 'CivVIWorld') -> str: f'Description="{location.name}" ' f'AdvisorType="{get_advisor_type(world, location)}"' f'/>{nl}' - for location in techs])} + for location in techs if location.item])} {"".join([f'{tab}{nl}' for location in boost_techs])} @@ -134,7 +135,7 @@ def generate_new_items(world: 'CivVIWorld') -> str: f'Description="{location.name}" ' f'AdvisorType="{get_advisor_type(world, location)}"' f'/>{nl}' - for location in civics])} + for location in civics if location.item])} {"".join([f'{tab}{nl}' for location in boost_civics])} @@ -152,7 +153,7 @@ def generate_new_items(world: 'CivVIWorld') -> str: """ -def generate_setup_file(world) -> str: +def generate_setup_file(world: 'CivVIWorld') -> str: """ Generates the Lua for the setup file. This sets initial variables and state that affect gameplay around Progressive Eras """ @@ -177,7 +178,7 @@ def generate_setup_file(world) -> str: return setup -def generate_goody_hut_sql(world) -> str: +def generate_goody_hut_sql(world: 'CivVIWorld') -> str: """ Generates the SQL for the goody huts or an empty string if they are disabled since the mod expects the file to be there """ @@ -213,7 +214,7 @@ def generate_goody_hut_sql(world) -> str: return "-- Goody Huts are disabled, no changes needed" -def generate_update_boosts_sql(world) -> str: +def generate_update_boosts_sql(world: 'CivVIWorld') -> str: """ Generates the SQL for existing boosts in boostsanity or an empty string if they are disabled since the mod expects the file to be there """ diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index 90d04c79b50b..fd74b952e983 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -2,18 +2,20 @@ import json import os import pkgutil -from typing import Dict, List +from typing import Any, Dict, List, TypedDict -_cache = {} +_cache: Dict[Any, Any] = {} -def _get_data(key: str): +def _get_data(key: str) -> Any: global _cache if key not in _cache: path = os.path.join("data", f"{key}.json") - _cache[key] = json.loads( - pkgutil.get_data(__name__, path).decode()) + data = pkgutil.get_data(__name__, path) + if data is None: + raise FileNotFoundError(f"Data file not found: {path}") + _cache[key] = json.loads(data.decode()) return _cache[key] @@ -28,7 +30,7 @@ class CivVIBoostData: def get_boosts_data() -> List[CivVIBoostData]: boosts_json = _get_data("boosts") - boosts = [] + boosts: List[CivVIBoostData] = [] for boost in boosts_json: boosts.append(CivVIBoostData( Type=boost["Type"], @@ -45,33 +47,60 @@ def get_era_required_items_data() -> Dict[str, List[str]]: return _get_data("era_required_items") -def get_existing_civics_data(): +class NewItemData(TypedDict): + Type: str + Cost: int + UITreeRow: int + EraType: str + + +class ExistingItemData(NewItemData): + Name: str + + +def get_existing_civics_data() -> List[ExistingItemData]: return _get_data("existing_civics") -def get_existing_techs_data(): +def get_existing_techs_data() -> List[ExistingItemData]: return _get_data("existing_tech") -def get_goody_hut_rewards_data(): +class GoodyHutRewardData(TypedDict): + Type: str + Name: str + Rarity: str + + +def get_goody_hut_rewards_data() -> List[GoodyHutRewardData]: return _get_data("goody_hut_rewards") -def get_new_civic_prereqs_data(): +class CivicPrereqData(TypedDict): + Civic: str + PrereqTech: str + + +def get_new_civic_prereqs_data() -> List[CivicPrereqData]: return _get_data("new_civic_prereqs") -def get_new_civics_data(): +def get_new_civics_data() -> List[NewItemData]: return _get_data("new_civics") -def get_new_tech_prereqs_data(): +class TechPrereqData(TypedDict): + Technology: str + PrereqTech: str + + +def get_new_tech_prereqs_data() -> List[TechPrereqData]: return _get_data("new_tech_prereqs") -def get_new_techs_data(): +def get_new_techs_data() -> List[NewItemData]: return _get_data("new_tech") -def get_progressive_districts_data(): +def get_progressive_districts_data() -> Dict[str, List[str]]: return _get_data("progressive_districts") diff --git a/worlds/civ_6/DeathLink.py b/worlds/civ_6/DeathLink.py index 47f220798b55..a03a78ea3b7d 100644 --- a/worlds/civ_6/DeathLink.py +++ b/worlds/civ_6/DeathLink.py @@ -8,7 +8,7 @@ DEATH_LINK_EFFECTS = ["Gold", "Faith", "Era Score", "Unit Killed"] -async def handle_receive_deathlink(ctx: 'CivVIContext', message): +async def handle_receive_deathlink(ctx: 'CivVIContext', message: str): """Resolves the effects of a deathlink received from the multiworld based on the options selected by the player""" chosen_effect = ctx.slot_data["death_link_effect"] effect: str = "Gold" @@ -73,6 +73,8 @@ async def handle_check_deathlink(ctx: 'CivVIContext'): f"is reconsidering their pursuit of a domination victory", f"had their plans toppled by a {result}", ] - player = ctx.player_names[ctx.slot] - message = random.choice(messages) - await ctx.send_death(f"{player} {message}") + + if ctx.slot is not None: + player = ctx.player_names[ctx.slot] + message = random.choice(messages) + await ctx.send_death(f"{player} {message}") diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index c7fe874586de..5dd3ca3b1317 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Dict, Optional, TYPE_CHECKING, List, Union from BaseClasses import Item, ItemClassification -from .Data import get_era_required_items_data, get_existing_civics_data, get_existing_techs_data, get_goody_hut_rewards_data, get_progressive_districts_data +from .Data import GoodyHutRewardData, get_era_required_items_data, get_existing_civics_data, get_existing_techs_data, get_goody_hut_rewards_data, get_progressive_districts_data from .Enum import CivVICheckType, EraType from .ProgressiveDistricts import get_flat_progressive_districts if TYPE_CHECKING: @@ -68,7 +68,7 @@ class FillerItemData: rarity: FillerItemRarity civ_name: str - def __init__(self, data: Dict[str, str]): + def __init__(self, data: GoodyHutRewardData): self.name = data["Name"] self.rarity = FillerItemRarity(data["Rarity"]) self.civ_name = data["Type"] @@ -78,7 +78,7 @@ def get_filler_item_data() -> Dict[str, FillerItemData]: """ Returns a dictionary of filler items with their data """ - goody_huts: List[Dict[str, str]] = get_goody_hut_rewards_data() + goody_huts = get_goody_hut_rewards_data() # Create a FillerItemData object for each item cached_filler_items = {item["Name"]: FillerItemData(item) for item in goody_huts} @@ -96,7 +96,7 @@ class CivVIItemData: civ_name: Optional[str] era: Optional[EraType] - def __init__(self, name, civ_vi_id: int, cost: int, item_type: CivVICheckType, id_offset: int, classification: ItemClassification, progression_name: Optional[str], civ_name: Optional[str] = None, era: Optional[EraType] = None): + def __init__(self, name: str, civ_vi_id: int, cost: int, item_type: CivVICheckType, id_offset: int, classification: ItemClassification, progression_name: Optional[str], civ_name: Optional[str] = None, era: Optional[EraType] = None): self.classification = classification self.civ_vi_id = civ_vi_id self.name = name @@ -110,7 +110,7 @@ def __init__(self, name, civ_vi_id: int, cost: int, item_type: CivVICheckType, i class CivVIItem(Item): game: str = "Civilization VI" - civ_vi_id: int + civ_vi_id: Union[int, str] item_type: CivVICheckType def __init__(self, item: CivVIItemData, player: int, classification: Optional[ItemClassification] = None): @@ -136,7 +136,7 @@ def get_item_by_civ_name(item_name: str, item_table: Dict[str, 'CivVIItemData']) def _generate_tech_items(id_base: int, required_items: List[str], progressive_items: Dict[str, str]) -> Dict[str, CivVIItemData]: # Generate Techs existing_techs = get_existing_techs_data() - tech_table = {} + tech_table: Dict[str, CivVIItemData] = {} tech_id = 0 for tech in existing_techs: @@ -169,7 +169,7 @@ def _generate_tech_items(id_base: int, required_items: List[str], progressive_it def _generate_civics_items(id_base: int, required_items: List[str], progressive_items: Dict[str, str]) -> Dict[str, CivVIItemData]: civic_id = 0 - civic_table = {} + civic_table: Dict[str, CivVIItemData] = {} existing_civics = get_existing_civics_data() for civic in existing_civics: @@ -203,7 +203,7 @@ def _generate_civics_items(id_base: int, required_items: List[str], progressive_ def _generate_progressive_district_items(id_base: int) -> Dict[str, CivVIItemData]: - progressive_table = {} + progressive_table: Dict[str, CivVIItemData] = {} progressive_id_base = 0 progressive_items = get_progressive_districts_data() for item_name in progressive_items.keys(): @@ -227,7 +227,7 @@ def _generate_progressive_district_items(id_base: int) -> Dict[str, CivVIItemDat def _generate_progressive_era_items(id_base: int) -> Dict[str, CivVIItemData]: """Generates the single progressive district item""" - era_table = {} + era_table: Dict[str, CivVIItemData] = {} # Generate progressive eras progressive_era_name = format_item_name("PROGRESSIVE_ERA") era_table[progressive_era_name] = CivVIItemData( @@ -246,7 +246,7 @@ def _generate_progressive_era_items(id_base: int) -> Dict[str, CivVIItemData]: def _generate_goody_hut_items(id_base: int) -> Dict[str, CivVIItemData]: # Generate goody hut items goody_huts = get_filler_item_data() - goody_table = {} + goody_table: Dict[str, CivVIItemData] = {} goody_base = 0 for value in goody_huts.values(): goody_table[value.name] = CivVIItemData( @@ -266,7 +266,7 @@ def _generate_goody_hut_items(id_base: int) -> Dict[str, CivVIItemData]: def generate_item_table() -> Dict[str, CivVIItemData]: era_required_items = get_era_required_items_data() required_items: List[str] = [] - for key, value in era_required_items.items(): + for _key, value in era_required_items.items(): required_items += value progressive_items = get_flat_progressive_districts() diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index 6579538df516..a53ef97bf92c 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -1,7 +1,7 @@ -from typing import List, Optional, Dict +from typing import List, Optional, Dict, Union from BaseClasses import Location, LocationProgressType, Region -from .Data import get_boosts_data, get_new_civic_prereqs_data, get_new_civics_data, get_new_tech_prereqs_data, get_new_techs_data +from .Data import CivicPrereqData, TechPrereqData, get_boosts_data, get_new_civics_data, get_new_techs_data from .Enum import CivVICheckType, EraType @@ -29,18 +29,17 @@ class CivVILocationData: uiTreeRow: int civ_id: int code: int - era_type: EraType + era_type: str location_type: CivVICheckType - pre_reqs: Optional[List[str]] + pre_reqs: Optional[List[Union[CivicPrereqData, TechPrereqData]]] - def __init__(self, name: str, cost: int, uiTreeRow: int, id: int, era_type: EraType, location_type: CivVICheckType, pre_reqs: Optional[List[str]] = None): + def __init__(self, name: str, cost: int, uiTreeRow: int, id: int, era_type: str, location_type: CivVICheckType): self.name = name self.cost = cost self.uiTreeRow = uiTreeRow self.civ_id = id self.code = id + CIV_VI_AP_LOCATION_ID_BASE self.era_type = era_type - self.pre_reqs = pre_reqs self.location_type = location_type @@ -84,14 +83,14 @@ def generate_flat_location_table() -> Dict[str, CivVILocationData]: } """ era_locations = generate_era_location_table() - flat_locations = {} - for era_type, locations in era_locations.items(): + flat_locations: Dict[str, CivVILocationData] = {} + for _era_type, locations in era_locations.items(): for location_id, location_data in locations.items(): flat_locations[location_id] = location_data return flat_locations -def generate_era_location_table() -> Dict[EraType, Dict[str, CivVILocationData]]: +def generate_era_location_table() -> Dict[str, Dict[str, CivVILocationData]]: """ Uses the data from existing_tech.json to generate a location table in the following format: { @@ -103,10 +102,9 @@ def generate_era_location_table() -> Dict[EraType, Dict[str, CivVILocationData]] ... } """ - new_tech_prereqs = get_new_tech_prereqs_data() new_techs = get_new_techs_data() - era_locations = {} + era_locations: Dict[str, Dict[str, CivVILocationData]] = {} id_base = 0 # Techs for data in new_techs: @@ -114,24 +112,18 @@ def generate_era_location_table() -> Dict[EraType, Dict[str, CivVILocationData]] if era_type not in era_locations: era_locations[era_type] = {} - prereq_data = [ - item for item in new_tech_prereqs if item["Technology"] == data["Type"]] - era_locations[era_type][data["Type"]] = CivVILocationData( - data["Type"], data["Cost"], data["UITreeRow"], id_base, era_type, CivVICheckType.TECH, prereq_data) + data["Type"], data["Cost"], data["UITreeRow"], id_base, era_type, CivVICheckType.TECH) id_base += 1 # Civics - new_civic_prereqs = get_new_civic_prereqs_data() new_civics = get_new_civics_data() for data in new_civics: era_type = data["EraType"] if era_type not in era_locations: era_locations[era_type] = {} - prereq_data = [ - item for item in new_civic_prereqs if item["Civic"] == data["Type"]] era_locations[era_type][data["Type"]] = CivVILocationData( - data["Type"], data["Cost"], data["UITreeRow"], id_base, era_type, CivVICheckType.CIVIC, prereq_data) + data["Type"], data["Cost"], data["UITreeRow"], id_base, era_type, CivVICheckType.CIVIC) id_base += 1 # Eras @@ -154,7 +146,7 @@ def generate_era_location_table() -> Dict[EraType, Dict[str, CivVILocationData]] boosts = get_boosts_data() for boost in boosts: location = CivVILocationData( - boost.Type, 0, 0, id_base, boost.EraType, CivVICheckType.BOOST, pre_reqs=boost.Prereq) + boost.Type, 0, 0, id_base, boost.EraType, CivVICheckType.BOOST) era_locations[boost.EraType][boost.Type] = location id_base += 1 diff --git a/worlds/civ_6/ProgressiveDistricts.py b/worlds/civ_6/ProgressiveDistricts.py index b81ebba4b13f..116b30392223 100644 --- a/worlds/civ_6/ProgressiveDistricts.py +++ b/worlds/civ_6/ProgressiveDistricts.py @@ -8,7 +8,7 @@ def get_flat_progressive_districts() -> Dict[str, str]: Key is the item name ("TECH_WRITING") and the value is the associated progressive item ("PROGRESSIVE_CAMPUS")""" progressive_districts = get_progressive_districts_data() - flat_progressive_techs = {} + flat_progressive_techs: Dict[str, str] = {} for key, value in progressive_districts.items(): for item in value: flat_progressive_techs[item] = key diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 90cd021c3c13..4691711c7041 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -1,8 +1,8 @@ from typing import TYPE_CHECKING, Dict, List, Optional -from BaseClasses import CollectionState, LocationProgressType, Region +from BaseClasses import CollectionState, Region from .Data import get_era_required_items_data, get_progressive_districts_data from .Items import CivVIItemData, format_item_name, get_item_by_civ_name -from .Enum import CivVICheckType, EraType +from .Enum import EraType from .Locations import CivVILocation from .ProgressiveDistricts import get_flat_progressive_districts from .Options import CivVIOptions @@ -11,14 +11,14 @@ from . import CivVIWorld -def get_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool = True, item_table: Optional[Dict[str, CivVIItemData]] = None) -> List[CivVIItemData]: +def get_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool, item_table: Dict[str, CivVIItemData]) -> List[CivVIItemData]: """Gets the specific techs/civics that are required for the specified era""" era_required_items = get_era_required_items_data()[end_era.value].copy() # If we are excluding progressive items, we need to remove them from the list of expected items (TECH_BRONZE_WORKING won't be here since it will be PROGRESSIVE_ENCAMPMENT) if exclude_progressive_items: flat_progressive_items = get_flat_progressive_districts() - prereqs_without_progressive_items = [] + prereqs_without_progressive_items: List[str] = [] for item in era_required_items: if item in flat_progressive_items: continue @@ -95,8 +95,8 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): regions: List[Region] = [] for era in EraType: era_region = Region(era.value, player, world.multiworld) - era_locations = {location.name: location.code for key, - location in world.location_by_era[era.value].items()} + era_locations: Dict[str, Optional[int]] = {location.name: location.code for _key, + location in world.location_by_era[era.value].items()} if not has_progressive_eras: era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "ERA"} diff --git a/worlds/civ_6/TunerClient.py b/worlds/civ_6/TunerClient.py index bf08d1c9aeba..c4ff461eb948 100644 --- a/worlds/civ_6/TunerClient.py +++ b/worlds/civ_6/TunerClient.py @@ -1,6 +1,7 @@ import asyncio from logging import Logger import socket +from typing import Any ADDRESS = "127.0.0.1" PORT = 4318 @@ -9,7 +10,7 @@ CLIENT_POSTFIX = ":APEND" -def decode_mixed_string(data): +def decode_mixed_string(data: bytes) -> str: return "".join(chr(b) if 32 <= b < 127 else "?" for b in data) @@ -33,7 +34,7 @@ class TunerClient: """Interfaces with Civilization via the tuner socket""" logger: Logger - def __init__(self, logger): + def __init__(self, logger: Logger): self.logger = logger def __parse_response(self, response: str) -> str: @@ -99,6 +100,6 @@ async def send_command(self, command_string: str, size: int = 64): finally: sock.close() - async def async_recv(self, sock, timeout=2.0, size=4096): + async def async_recv(self, sock: Any, timeout: float = 2.0, size: int = 4096): response = await asyncio.wait_for(asyncio.get_event_loop().sock_recv(sock, size), timeout) return response diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index dff3b09de666..8d4797907088 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -11,16 +11,16 @@ from .Rules import create_boost_rules from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql from .Enum import CivVICheckType -from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIItemData, FillerItemRarity, format_item_name, generate_item_table, CivVIItem, get_random_filler_by_rarity +from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity from .Locations import EXCLUDED_LOCATIONS, CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table from .Options import CivVIOptions from .Regions import create_regions from BaseClasses import Item, ItemClassification, MultiWorld, Tutorial from worlds.AutoWorld import World, WebWorld -from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components, launch_subprocess +from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components, launch_subprocess # type: ignore -def run_client(*args): +def run_client(*args: Any): print("Running Civ6 Client") from .Civ6Client import main # lazy import launch_subprocess(main, name="Civ6Client") @@ -52,7 +52,7 @@ class CivVIWorld(World): game = "Civilization VI" topology_present = False options_dataclass = CivVIOptions - options: CivVIOptions + options: CivVIOptions # type: ignore web = CivVIWeb() @@ -62,7 +62,7 @@ class CivVIWorld(World): location.name: location.code for location in generate_flat_location_table().values()} item_table: Dict[str, CivVIItemData] = {} - location_by_era: Dict[EraType, Dict[str, CivVILocationData]] + location_by_era: Dict[str, Dict[str, CivVILocationData]] required_client_version = (0, 4, 5) def __init__(self, multiworld: "MultiWorld", player: int): @@ -137,7 +137,11 @@ def create_items(self) -> None: for era in EraType: if era.value == "ERA_ANCIENT": continue - self.multiworld.itempool += [self.create_item(self.item_table.get("Progressive Era").name)] + progressive_era_item = self.item_table.get("Progressive Era") + if progressive_era_item: + self.multiworld.itempool += [self.create_item(progressive_era_item.name)] + else: + logging.error("Progressive Era item not found in item_table.") self.multiworld.early_items[self.player]["Progressive Era"] = 2 num_filler_items = 0 @@ -175,7 +179,7 @@ def post_fill(self) -> None: if location_data.location_type != CivVICheckType.CIVIC and location_data.location_type != CivVICheckType.TECH: continue - location: CivVILocation = self.get_location(location_name) + location: CivVILocation = self.get_location(location_name) # type: ignore if not location.item or not show_flags.get(location.item.classification, False): continue diff --git a/worlds/civ_6/test/TestBoostsanity.py b/worlds/civ_6/test/TestBoostsanity.py index 430f2b58e3d0..dac10553aee7 100644 --- a/worlds/civ_6/test/TestBoostsanity.py +++ b/worlds/civ_6/test/TestBoostsanity.py @@ -21,7 +21,7 @@ def test_boosts_get_included(self) -> None: locations = self.multiworld.get_locations(self.player) found_locations = 0 for location in locations: - if "BOOST" in location.name != -1: + if "BOOST" in location.name: found_locations += 1 num_boost_locations = len(get_boosts_data()) self.assertEqual(found_locations, num_boost_locations) @@ -45,6 +45,6 @@ def test_boosts_are_not_included(self) -> None: locations = self.multiworld.get_locations(self.player) found_locations = 0 for location in locations: - if "BOOST" in location.name != -1: + if "BOOST" in location.name: found_locations += 1 self.assertEqual(found_locations, 0) diff --git a/worlds/civ_6/test/TestGoodyHuts.py b/worlds/civ_6/test/TestGoodyHuts.py index c30f4da6c1e8..73e700836d10 100644 --- a/worlds/civ_6/test/TestGoodyHuts.py +++ b/worlds/civ_6/test/TestGoodyHuts.py @@ -42,7 +42,7 @@ class TestGoodyHutsExcluded(CivVITestBase): def test_goody_huts_are_not_included(self) -> None: self.world_setup() self.world.generate_early() - distribute_items_restrictive + distribute_items_restrictive(self.multiworld) found_goody_huts = 0 for location in self.multiworld.get_locations(self.player): if location.name.startswith("GOODY_HUT_"): @@ -65,7 +65,7 @@ class TestFillerItemsIncludedByRarity(CivVITestBase): def test_filler_items_are_included_by_rarity(self) -> None: self.world_setup() self.world.generate_early() - distribute_items_restrictive + distribute_items_restrictive(self.multiworld) rarity_counts: Dict[FillerItemRarity, int] = { FillerItemRarity.COMMON: 0, FillerItemRarity.UNCOMMON: 0, @@ -103,7 +103,7 @@ class TestFillerItemsIncludedByRarityWithoutBoostsanity(CivVITestBase): def test_filler_items_are_included_by_rarity_without_boostsanity(self) -> None: self.world_setup() self.world.generate_early() - distribute_items_restrictive + distribute_items_restrictive(self.multiworld) rarity_counts: Dict[FillerItemRarity, int] = { FillerItemRarity.COMMON: 0, FillerItemRarity.UNCOMMON: 0, diff --git a/worlds/civ_6/test/TestRegionRequirements.py b/worlds/civ_6/test/TestRegionRequirements.py index b1a9b0f656d4..9e0b777d6965 100644 --- a/worlds/civ_6/test/TestRegionRequirements.py +++ b/worlds/civ_6/test/TestRegionRequirements.py @@ -1,4 +1,4 @@ -from typing import List +from typing import Callable, List from BaseClasses import CollectionState from ..Data import get_era_required_items_data @@ -22,7 +22,7 @@ def collect_items_for_era_progressive(test: CivVITestBase, era: EraType) -> None test.collect_by_name(items) -def verify_eras_accessible(test: CivVITestBase, state: CollectionState, collect_func): +def verify_eras_accessible(test: CivVITestBase, state: CollectionState, collect_func: Callable[[CivVITestBase, EraType], None]) -> None: """Collect for an era, then check if the next era is accessible and the one after that is not""" for era in EraType: if era == EraType.ERA_ANCIENT: diff --git a/worlds/civ_6/test/TestStartingHints.py b/worlds/civ_6/test/TestStartingHints.py index d7a94cdc066f..253c1136d508 100644 --- a/worlds/civ_6/test/TestStartingHints.py +++ b/worlds/civ_6/test/TestStartingHints.py @@ -5,7 +5,7 @@ class TestStartingHints(CivVITestBase): - run_default_tests = False + run_default_tests = False # type: ignore auto_construct = False options = { "progressive_eras": "true", @@ -28,7 +28,7 @@ def test_all_tech_civic_items_are_hinted_default(self) -> None: class TestOnlyProgressionItemsHinted(CivVITestBase): - run_default_tests = False + run_default_tests = False # type: ignore auto_construct = False options = { "progressive_eras": "true", @@ -46,11 +46,14 @@ def test_only_progression_items_are_hinted(self) -> None: self.assertTrue(len(start_location_hints) > 0) for hint in start_location_hints: location_data = self.world.get_location(hint) - self.assertTrue(location_data.item.classification == ItemClassification.progression) + if location_data.item: + self.assertTrue(location_data.item.classification == ItemClassification.progression) + else: + self.assertTrue(False, "Location has no item") class TestNoJunkItemsHinted(CivVITestBase): - run_default_tests = False + run_default_tests = False # type: ignore auto_construct = False options = { "progressive_eras": "true", @@ -68,11 +71,14 @@ def test_no_junk_items_are_hinted(self) -> None: self.assertTrue(len(start_location_hints) > 0) for hint in start_location_hints: location_data = self.world.get_location(hint) - self.assertTrue(location_data.item.classification == ItemClassification.progression or location_data.item.classification == ItemClassification.useful) + if location_data.item: + self.assertTrue(location_data.item.classification == ItemClassification.progression or location_data.item.classification == ItemClassification.useful) + else: + self.assertTrue(False, "Location has no item") class TestNoItemsHinted(CivVITestBase): - run_default_tests = False + run_default_tests = False # type: ignore auto_construct = False options = { "progressive_eras": "true", From 29942b0f7ead7e0691130bbdf6c8497023b92f92 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Wed, 16 Oct 2024 16:10:55 -0600 Subject: [PATCH 43/69] Update research cost multiplier --- worlds/civ_6/Container.py | 4 ++-- worlds/civ_6/Options.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py index 792090b0b920..36f278683b83 100644 --- a/worlds/civ_6/Container.py +++ b/worlds/civ_6/Container.py @@ -50,8 +50,8 @@ def get_cost(world: 'CivVIWorld', location: CivVILocationData) -> int: Returns the cost of the item based on the game options """ options: CivVIOptions = world.options - # Research cost is between 1 and 100 where 50 equals the default cost - multiplier = options.research_cost_multiplier / 50 + # Research cost is between 50 and 150 where 100 equals the default cost + multiplier = options.research_cost_multiplier / 100 return int(world.location_table[location.name].cost * multiplier) diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 030afe31b4a8..32eb80fff7e0 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -37,9 +37,9 @@ class BoostSanity(Toggle): class ResearchCostMultiplier(Range): """Multiplier for research cost of techs and civics, higher values make research more expensive.""" display_name = "Tech/Civic Cost Multiplier" - range_start = 1 - range_end = 100 - default = 50 + range_start = 50 + range_end = 150 + default = 100 class PreHintItems(Choice): From 4e9ac87275d79ae08a7a759b5825e89282e9a658 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Wed, 16 Oct 2024 16:12:14 -0600 Subject: [PATCH 44/69] Remove unnecessary location priority code --- worlds/civ_6/Locations.py | 2 -- worlds/civ_6/__init__.py | 7 ------- 2 files changed, 9 deletions(-) diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index a53ef97bf92c..3257ea948b4f 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -62,8 +62,6 @@ def __init__(self, player: int, name: str = "", address: Optional[int] = None, p if self.name in EXCLUDED_LOCATIONS: self.progress_type = LocationProgressType.EXCLUDED - else: - self.progress_type = LocationProgressType.DEFAULT if self.location_type == CivVICheckType.BOOST: boost_data_list = get_boosts_data() diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 8d4797907088..498d94595397 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -76,13 +76,6 @@ def __init__(self, multiworld: "MultiWorld", player: int): for __, location in locations.items(): self.location_table[location.name] = location - def generate_early(self) -> None: - for location in self.location_table.values(): - name = location.name - if name in EXCLUDED_LOCATIONS and name in self.options.priority_locations.value: - self.options.priority_locations.value.remove(name) - logging.warning(f"Excluded location {name} is in priority locations, removing.") - def get_filler_item_name(self) -> str: return get_random_filler_by_rarity(self, FillerItemRarity.COMMON).name From f371c922158cc14f82138fc6e6c95c0c99c55f53 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Wed, 16 Oct 2024 16:16:47 -0600 Subject: [PATCH 45/69] Remove extrenous use of items() --- worlds/civ_6/Civ6Client.py | 6 +++--- worlds/civ_6/Items.py | 2 +- worlds/civ_6/Locations.py | 2 +- worlds/civ_6/__init__.py | 7 ++++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/worlds/civ_6/Civ6Client.py b/worlds/civ_6/Civ6Client.py index e74caf5fa903..bd2cacf4be1f 100644 --- a/worlds/civ_6/Civ6Client.py +++ b/worlds/civ_6/Civ6Client.py @@ -79,12 +79,12 @@ def __init__(self, server_address: Optional[str], password: Optional[str], apciv self.apcivvi_file = apcivvi_file # Get tables formatted in a way that is easier to use here - for _era, locations in location_by_era.items(): - for _item_name, location in locations.items(): + for locations in location_by_era.values(): + for location in locations.values(): self.location_name_to_id[location.name] = location.code self.location_name_to_civ_location[location.name] = location - for _item_name, item in self.item_table.items(): + for item in self.item_table.values(): self.item_id_to_civ_item[item.code] = item async def resync(self): diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index 5dd3ca3b1317..9054a6d5268f 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -266,7 +266,7 @@ def _generate_goody_hut_items(id_base: int) -> Dict[str, CivVIItemData]: def generate_item_table() -> Dict[str, CivVIItemData]: era_required_items = get_era_required_items_data() required_items: List[str] = [] - for _key, value in era_required_items.items(): + for value in era_required_items.values(): required_items += value progressive_items = get_flat_progressive_districts() diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index 3257ea948b4f..2f651b98e1e2 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -82,7 +82,7 @@ def generate_flat_location_table() -> Dict[str, CivVILocationData]: """ era_locations = generate_era_location_table() flat_locations: Dict[str, CivVILocationData] = {} - for _era_type, locations in era_locations.items(): + for locations in era_locations.values(): for location_id, location_data in locations.items(): flat_locations[location_id] = location_data return flat_locations diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 498d94595397..fb3bec6d68a4 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -12,7 +12,7 @@ from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql from .Enum import CivVICheckType from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity -from .Locations import EXCLUDED_LOCATIONS, CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table +from .Locations import CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table from .Options import CivVIOptions from .Regions import create_regions from BaseClasses import Item, ItemClassification, MultiWorld, Tutorial @@ -64,6 +64,7 @@ class CivVIWorld(World): item_table: Dict[str, CivVIItemData] = {} location_by_era: Dict[str, Dict[str, CivVILocationData]] required_client_version = (0, 4, 5) + location_table: Dict[str, CivVILocationData] def __init__(self, multiworld: "MultiWorld", player: int): super().__init__(multiworld, player) @@ -72,8 +73,8 @@ def __init__(self, multiworld: "MultiWorld", player: int): self.location_table: Dict[str, CivVILocationData] = {} self.item_table = generate_item_table() - for _, locations in self.location_by_era.items(): - for __, location in locations.items(): + for locations in self.location_by_era.values(): + for location in locations.values(): self.location_table[location.name] = location def get_filler_item_name(self) -> str: From 29b0d8a4a1a731b13acdbf9f8c7ecd6d7fd9e2e9 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Thu, 17 Oct 2024 21:07:30 -0600 Subject: [PATCH 46/69] WIP PR Feedback --- worlds/civ_6/Civ6Client.py | 3 +-- worlds/civ_6/CivVIInterface.py | 6 +++--- worlds/civ_6/Container.py | 35 +++++++++++----------------------- worlds/civ_6/DeathLink.py | 2 +- worlds/civ_6/Items.py | 26 +++++++++++++++---------- worlds/civ_6/Regions.py | 32 +++++++++++++++---------------- worlds/civ_6/Rules.py | 8 +++----- worlds/civ_6/__init__.py | 18 +++++++---------- 8 files changed, 57 insertions(+), 73 deletions(-) diff --git a/worlds/civ_6/Civ6Client.py b/worlds/civ_6/Civ6Client.py index bd2cacf4be1f..9fd6b267c92d 100644 --- a/worlds/civ_6/Civ6Client.py +++ b/worlds/civ_6/Civ6Client.py @@ -236,8 +236,7 @@ async def handle_receive_items(ctx: CivVIContext, last_received_index_override: count = len(progressive_eras) + 1 await ctx.game_interface.give_item_to_player(item_to_send, sender, count) elif item.item_type == CivVICheckType.GOODY and item_to_send.civ_name: - item_to_send.civ_vi_id = item_to_send.civ_name - await ctx.game_interface.give_item_to_player(item_to_send, sender) + await ctx.game_interface.give_item_to_player(item_to_send, sender, game_id_override=item_to_send.civ_name) else: await ctx.game_interface.give_item_to_player(item_to_send, sender) await asyncio.sleep(0.02) diff --git a/worlds/civ_6/CivVIInterface.py b/worlds/civ_6/CivVIInterface.py index 5ab9fabc3239..c74d45675a0e 100644 --- a/worlds/civ_6/CivVIInterface.py +++ b/worlds/civ_6/CivVIInterface.py @@ -52,9 +52,9 @@ def print_connection_error(self, error: str) -> None: self.last_error = error self.logger.info(error) - async def give_item_to_player(self, item: CivVIItemData, sender: str = "", amount: int = 1) -> None: - if isinstance(item.civ_vi_id, str): - item_id = f'"{item.civ_vi_id}"' + async def give_item_to_player(self, item: CivVIItemData, sender: str = "", amount: int = 1, game_id_override: Optional[str] = None) -> None: + if game_id_override: + item_id = f'"{game_id_override}"' else: item_id = item.civ_vi_id diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py index 36f278683b83..3be443d33072 100644 --- a/worlds/civ_6/Container.py +++ b/worlds/civ_6/Container.py @@ -1,13 +1,12 @@ from dataclasses import dataclass import os -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional, cast import zipfile -from BaseClasses import ItemClassification, Location +from BaseClasses import Location from worlds.Files import APContainer from .Enum import CivVICheckType from .Locations import CivVILocation, CivVILocationData -from .Options import CivVIOptions if TYPE_CHECKING: from worlds.civ_6 import CivVIWorld @@ -49,9 +48,8 @@ def get_cost(world: 'CivVIWorld', location: CivVILocationData) -> int: """ Returns the cost of the item based on the game options """ - options: CivVIOptions = world.options # Research cost is between 50 and 150 where 100 equals the default cost - multiplier = options.research_cost_multiplier / 100 + multiplier = world.options.research_cost_multiplier / 100 return int(world.location_table[location.name].cost * multiplier) @@ -61,23 +59,20 @@ def get_formatted_player_name(world: 'CivVIWorld', player: int) -> str: """ if player != world.player: return f"{world.multiworld.player_name[player]}{apo}s" - else: - return "Your" + return "Your" def get_advisor_type(world: 'CivVIWorld', location: Location) -> str: - if world.options.advisor_show_progression_items and location.item and location.item.classification == ItemClassification.progression: + if world.options.advisor_show_progression_items and location.item and location.item.advancement: return "ADVISOR_PROGRESSIVE" - else: - return "ADVISOR_GENERIC" + return "ADVISOR_GENERIC" def generate_new_items(world: 'CivVIWorld') -> str: """ Generates the XML for the new techs/civics as well as the blockers used to prevent players from researching their own items """ - raw_locations = world.multiworld.get_filled_locations(world.player) - locations: List[CivVILocation] = [location for location in raw_locations if isinstance(location, CivVILocation)] + locations: List[CivVILocation] = cast(List[CivVILocation], world.multiworld.get_filled_locations(world.player)) techs = [location for location in locations if location.location_type == CivVICheckType.TECH] civics = [location for location in locations if location.location_type == @@ -86,18 +81,12 @@ def generate_new_items(world: 'CivVIWorld') -> str: boost_techs = [] boost_civics = [] - hidden_techs = [] - hidden_civics = [] if world.options.boostsanity: boost_techs = [location for location in locations if location.location_type == CivVICheckType.BOOST and location.name.split("_")[1] == "TECH"] boost_civics = [location for location in locations if location.location_type == CivVICheckType.BOOST and location.name.split("_")[1] == "CIVIC"] techs += boost_techs civics += boost_civics - if world.options.hide_item_names: - hidden_techs = [tech.name for tech in techs] - hidden_civics = [civic.name for civic in civics] - return f""" @@ -142,11 +131,11 @@ def generate_new_items(world: 'CivVIWorld') -> str: - {"".join([f'{tab}{nl}' for location in hidden_civics])} + {"".join([f'{tab}{nl}' for location in civics if world.options.hide_item_names])} - {"".join([f'{tab}{nl}' for location in hidden_techs])} + {"".join([f'{tab}{nl}' for location in techs if world.options.hide_item_names])} @@ -210,8 +199,7 @@ def generate_goody_hut_sql(world: 'CivVIWorld') -> str: WHERE GoodyHut NOT IN ('METEOR_GOODIES', 'GOODYHUT_SAILOR_WONDROUS', 'DUMMY_GOODY_BUILDIER') AND Weight > 0; """ - else: - return "-- Goody Huts are disabled, no changes needed" + return "-- Goody Huts are disabled, no changes needed" def generate_update_boosts_sql(world: 'CivVIWorld') -> str: @@ -228,5 +216,4 @@ def generate_update_boosts_sql(world: 'CivVIWorld') -> str: SET CivicType = 'BOOST_' || CivicType WHERE CivicType IS NOT NULL AND CivicType NOT IN ('CIVIC_CORPORATE_LIBERTARIANISM', 'CIVIC_DIGITAL_DEMOCRACY', 'CIVIC_SYNTHETIC_TECHNOCRACY', 'CIVIC_NEAR_FUTURE_GOVERNANCE'); """ - else: - return "-- Boostsanity is disabled, no changes needed" + return "-- Boostsanity is disabled, no changes needed" diff --git a/worlds/civ_6/DeathLink.py b/worlds/civ_6/DeathLink.py index a03a78ea3b7d..612dce97f169 100644 --- a/worlds/civ_6/DeathLink.py +++ b/worlds/civ_6/DeathLink.py @@ -65,7 +65,7 @@ async def handle_check_deathlink(ctx: 'CivVIContext'): f"now understands the importance of not fighting a {result}", f"let a {result} get the better of them", f"allowed a {result} to show them the error of their ways", - f"heard the tragedy of Darth Plageuis the Wise from a {result}", + f"heard the tragedy of Darth Plagueis the Wise from a {result}", f"refused to join a {result} in their quest for power", f"was tired of sitting in BK and decided to fight a {result} instead", f"purposely lost to a {result} as a cry for help", diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index 9054a6d5268f..503a0f68e652 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -1,11 +1,14 @@ from enum import Enum -from typing import Dict, Optional, TYPE_CHECKING, List, Union +from typing import Dict, Optional, TYPE_CHECKING, List from BaseClasses import Item, ItemClassification from .Data import GoodyHutRewardData, get_era_required_items_data, get_existing_civics_data, get_existing_techs_data, get_goody_hut_rewards_data, get_progressive_districts_data from .Enum import CivVICheckType, EraType from .ProgressiveDistricts import get_flat_progressive_districts if TYPE_CHECKING: from . import CivVIWorld + +_items_by_civ_name: Dict[str, 'CivVIItemData'] = {} + CIV_VI_AP_ITEM_ID_BASE = 5041000 NON_PROGRESSION_DISTRICTS = [ @@ -86,7 +89,7 @@ def get_filler_item_data() -> Dict[str, FillerItemData]: class CivVIItemData: - civ_vi_id: Union[int, str] + civ_vi_id: int classification: ItemClassification name: str code: int @@ -110,7 +113,7 @@ def __init__(self, name: str, civ_vi_id: int, cost: int, item_type: CivVICheckTy class CivVIItem(Item): game: str = "Civilization VI" - civ_vi_id: Union[int, str] + civ_vi_id: int item_type: CivVICheckType def __init__(self, item: CivVIItemData, player: int, classification: Optional[ItemClassification] = None): @@ -126,11 +129,14 @@ def format_item_name(name: str) -> str: def get_item_by_civ_name(item_name: str, item_table: Dict[str, 'CivVIItemData']) -> 'CivVIItemData': """Gets the names of the items in the item_table""" - for item in item_table.values(): - if item_name == item.civ_name: - return item + global _items_by_civ_name + if not _items_by_civ_name: + _items_by_civ_name = {item.civ_name: item for item in item_table.values() if item.civ_name} - raise Exception(f"Item {item_name} not found in item_table") + item = _items_by_civ_name.get(item_name, None) + if not item: + raise Exception(f"Item {item_name} not found in item_table") + return item def _generate_tech_items(id_base: int, required_items: List[str], progressive_items: Dict[str, str]) -> Dict[str, CivVIItemData]: @@ -207,9 +213,9 @@ def _generate_progressive_district_items(id_base: int) -> Dict[str, CivVIItemDat progressive_id_base = 0 progressive_items = get_progressive_districts_data() for item_name in progressive_items.keys(): - progression = ItemClassification.progression + classification = ItemClassification.progression if item_name in NON_PROGRESSION_DISTRICTS: - progression = ItemClassification.useful + classification = ItemClassification.useful name = format_item_name(item_name) progressive_table[name] = CivVIItemData( name=name, @@ -217,7 +223,7 @@ def _generate_progressive_district_items(id_base: int) -> Dict[str, CivVIItemDat cost=0, item_type=CivVICheckType.PROGRESSIVE_DISTRICT, id_offset=id_base, - classification=progression, + classification=classification, progression_name=None, civ_name=item_name ) diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 4691711c7041..4748bd900af3 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -5,7 +5,6 @@ from .Enum import EraType from .Locations import CivVILocation from .ProgressiveDistricts import get_flat_progressive_districts -from .Options import CivVIOptions if TYPE_CHECKING: from . import CivVIWorld @@ -22,8 +21,7 @@ def get_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool, item_ for item in era_required_items: if item in flat_progressive_items: continue - else: - prereqs_without_progressive_items.append(item) + prereqs_without_progressive_items.append(item) return [get_item_by_civ_name(prereq, item_table) for prereq in prereqs_without_progressive_items] @@ -36,9 +34,9 @@ def has_required_progressive_districts(state: CollectionState, era: EraType, pla item_table = state.multiworld.worlds[player].item_table # Verify we can still reach non progressive items - all_previous_items_no_progression = get_prereqs_for_era( + all_previous_items_no_progressives = get_prereqs_for_era( era, True, item_table) - if not state.has_all([item.name for item in all_previous_items_no_progression], player): + if not state.has_all([item.name for item in all_previous_items_no_progressives], player): return False # Verify we have the correct amount of progressive items @@ -79,22 +77,20 @@ def has_required_items(state: CollectionState, era: EraType, world: 'CivVIWorld' if not required_items: return False - required_eras = has_required_progressive_eras(state, era, player) if has_progressive_eras else True + return not has_progressive_eras or has_required_progressive_eras(state, era, player) - return required_items and required_eras - -def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): - menu = Region("Menu", player, world.multiworld) +def create_regions(world: 'CivVIWorld'): + menu = Region("Menu", world.player, world.multiworld) world.multiworld.regions.append(menu) - has_progressive_eras = options.progression_style == "eras_and_districts" - has_goody_huts = options.shuffle_goody_hut_rewards - has_boosts = options.boostsanity + has_progressive_eras = world.options.progression_style == "eras_and_districts" + has_goody_huts = world.options.shuffle_goody_hut_rewards + has_boosts = world.options.boostsanity regions: List[Region] = [] for era in EraType: - era_region = Region(era.value, player, world.multiworld) + era_region = Region(era.value, world.player, world.multiworld) era_locations: Dict[str, Optional[int]] = {location.name: location.code for _key, location in world.location_by_era[era.value].items()} @@ -149,7 +145,9 @@ def create_regions(world: 'CivVIWorld', options: CivVIOptions, player: int): ) world.get_region(EraType.ERA_INFORMATION.value).connect( - world.get_region(EraType.ERA_FUTURE.value), None, lambda state: has_required_items(state, EraType.ERA_INFORMATION, world)) + world.get_region(EraType.ERA_FUTURE.value), None, lambda state: has_required_items( + state, EraType.ERA_INFORMATION, world) + ) - world.multiworld.completion_condition[player] = lambda state: state.can_reach( - EraType.ERA_FUTURE.value, "Region", player) + world.multiworld.completion_condition[world.player] = lambda state: state.can_reach( + EraType.ERA_FUTURE.value, "Region", world.player) diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py index b546cf35ed72..9c00618d82e7 100644 --- a/worlds/civ_6/Rules.py +++ b/worlds/civ_6/Rules.py @@ -31,19 +31,17 @@ def has_required_items(state: CollectionState, prereqs: List[str], required_coun if has_progressive_items: count = 0 items = [get_item_by_civ_name(item, world.item_table).name for item in convert_items_to_have_progression(prereqs)] - progressive_items: Dict[str, int] = {} + collected_progressive_items: Dict[str, int] = {} for item in items: if "Progressive" in item: - if not progressive_items.get(item): - progressive_items[item] = 0 - progressive_items[item] += 1 + collected_progressive_items[item] = collected_progressive_items.get(item, 0) + 1 else: if state.has(item, player): count += 1 # early out if we've already gotten enough if count >= required_count: return True - for item, required_progressive_item_count in progressive_items.items(): + for item, required_progressive_item_count in collected_progressive_items.items(): if state.has(item, player, required_progressive_item_count): count += required_progressive_item_count # early out if we've already gotten enough diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index fb3bec6d68a4..8d69ec04c646 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -81,7 +81,7 @@ def get_filler_item_name(self) -> str: return get_random_filler_by_rarity(self, FillerItemRarity.COMMON).name def create_regions(self) -> None: - create_regions(self, self.options, self.player) + create_regions(self) def set_rules(self) -> None: if self.options.boostsanity: @@ -147,16 +147,12 @@ def create_items(self) -> None: num_filler_items += len(get_boosts_data()) filler_count = {rarity: math.ceil(FILLER_DISTRIBUTION[rarity] * num_filler_items) for rarity in FillerItemRarity.__reversed__()} - min_count = 1 - # Add filler items by rarity - total_created = 0 - for rarity, count in filler_count.items(): - for _ in range(max(min_count, count)): - if total_created >= num_filler_items: - break - self.multiworld.itempool += [self.create_item( - get_random_filler_by_rarity(self, rarity).name)] - total_created += 1 + filler_count[FillerItemRarity.COMMON] -= sum(filler_count.values()) - num_filler_items + self.multiworld.itempool += [ + self.create_item(get_random_filler_by_rarity(self, rarity).name) + for rarity, count in filler_count.items() + for _ in range(count) + ] def post_fill(self) -> None: if self.options.pre_hint_items == "none": From c8e8dd31762bb825de287cfb7a14d2232269f333 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Fri, 18 Oct 2024 15:27:31 -0600 Subject: [PATCH 47/69] WIP PR Feedback --- BaseClasses.py | 3 + worlds/civ_6/Items.py | 4 + worlds/civ_6/Locations.py | 2 +- worlds/civ_6/ProgressiveDistricts.py | 7 + worlds/civ_6/Regions.py | 306 +++++++++++++-------------- worlds/civ_6/Rules.py | 66 +++--- worlds/civ_6/__init__.py | 30 +-- worlds/civ_6/test/TestBoostsanity.py | 69 +++++- 8 files changed, 281 insertions(+), 206 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 0d4f34e51445..9ae7180ae0fe 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1170,6 +1170,9 @@ def can_fill(self, state: CollectionState, item: Item, check_access: bool = True def can_reach(self, state: CollectionState) -> bool: # Region.can_reach is just a cache lookup, so placing it first for faster abort on average assert self.parent_region, f"called can_reach on a Location \"{self}\" with no parent_region" + if(self.name == "BOOST_TECH_ADVANCED_BALLISTICS"): + # print("BOOST_TECH_ADVANCED_BALLISTICS") + pass return self.parent_region.can_reach(state) and self.access_rule(state) def place_locked_item(self, item: Item): diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index 503a0f68e652..98eb538beef0 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -111,6 +111,10 @@ def __init__(self, name: str, civ_vi_id: int, cost: int, item_type: CivVICheckTy self.era = era +class CivVIEvent(Item): + game: str = "Civilization VI" + + class CivVIItem(Item): game: str = "Civilization VI" civ_vi_id: int diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index 2f651b98e1e2..48e3b03d8a05 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -145,7 +145,7 @@ def generate_era_location_table() -> Dict[str, Dict[str, CivVILocationData]]: for boost in boosts: location = CivVILocationData( boost.Type, 0, 0, id_base, boost.EraType, CivVICheckType.BOOST) - era_locations[boost.EraType][boost.Type] = location + era_locations["ERA_ANCIENT"][boost.Type] = location id_base += 1 return era_locations diff --git a/worlds/civ_6/ProgressiveDistricts.py b/worlds/civ_6/ProgressiveDistricts.py index 116b30392223..70228d514570 100644 --- a/worlds/civ_6/ProgressiveDistricts.py +++ b/worlds/civ_6/ProgressiveDistricts.py @@ -20,3 +20,10 @@ def convert_items_to_have_progression(items: List[str]): they have one. ["TECH_MINING", "TECH_WRITING"] -> ["TECH_MINING", "PROGRESSIVE_CAMPUS]""" flat_progressive_techs = get_flat_progressive_districts() return [flat_progressive_techs.get(item, item) for item in items] + + +def convert_item_to_have_progression(item: str): + """ converts an items to instead be its associated progressive item if + it has one. "TECH_WRITING" -> "PROGRESSIVE_CAMPUS""" + flat_progressive_techs = get_flat_progressive_districts() + return flat_progressive_techs.get(item, item) diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 4748bd900af3..44d4c2b1b932 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -1,153 +1,153 @@ -from typing import TYPE_CHECKING, Dict, List, Optional -from BaseClasses import CollectionState, Region -from .Data import get_era_required_items_data, get_progressive_districts_data -from .Items import CivVIItemData, format_item_name, get_item_by_civ_name -from .Enum import EraType -from .Locations import CivVILocation -from .ProgressiveDistricts import get_flat_progressive_districts - -if TYPE_CHECKING: - from . import CivVIWorld - - -def get_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool, item_table: Dict[str, CivVIItemData]) -> List[CivVIItemData]: - """Gets the specific techs/civics that are required for the specified era""" - era_required_items = get_era_required_items_data()[end_era.value].copy() - - # If we are excluding progressive items, we need to remove them from the list of expected items (TECH_BRONZE_WORKING won't be here since it will be PROGRESSIVE_ENCAMPMENT) - if exclude_progressive_items: - flat_progressive_items = get_flat_progressive_districts() - prereqs_without_progressive_items: List[str] = [] - for item in era_required_items: - if item in flat_progressive_items: - continue - prereqs_without_progressive_items.append(item) - - return [get_item_by_civ_name(prereq, item_table) for prereq in prereqs_without_progressive_items] - - return [get_item_by_civ_name(prereq, item_table) for prereq in era_required_items] - - -def has_required_progressive_districts(state: CollectionState, era: EraType, player: int) -> bool: - """ If player has progressive items enabled, it will count how many progressive techs it should have, otherwise return the default array""" - progressive_districts = get_progressive_districts_data() - - item_table = state.multiworld.worlds[player].item_table - # Verify we can still reach non progressive items - all_previous_items_no_progressives = get_prereqs_for_era( - era, True, item_table) - if not state.has_all([item.name for item in all_previous_items_no_progressives], player): - return False - - # Verify we have the correct amount of progressive items - all_previous_items = get_prereqs_for_era( - era, False, item_table) - required_counts: Dict[str, int] = {} - - for key, value in progressive_districts.items(): - required_counts[key] = 0 - for item in all_previous_items: - if item.civ_name in value: - required_counts[key] += 1 - - return state.has_all_counts({format_item_name(key): value for key, value in required_counts.items()}, player) - - -def has_required_progressive_eras(state: CollectionState, era: EraType, player: int) -> bool: - """Checks, for the given era, how many are required to proceed to the next era. Ancient = 0, Classical = 1, etc.""" - if era == EraType.ERA_FUTURE or era == EraType.ERA_INFORMATION: - return True - - eras = [e.value for e in EraType] - era_index = eras.index(era.value) - return state.has(format_item_name("PROGRESSIVE_ERA"), player, era_index + 1) - - -def has_required_items(state: CollectionState, era: EraType, world: 'CivVIWorld') -> bool: - player = world.player - has_progressive_districts = world.options.progression_style != "none" - has_progressive_eras = world.options.progression_style == "eras_and_districts" - - if has_progressive_districts: - required_items = has_required_progressive_districts(state, era, player) - else: - era_required_items = [get_item_by_civ_name(item, world.item_table).name for item in get_era_required_items_data()[era.value]] - required_items = state.has_all(era_required_items, player) - - if not required_items: - return False - - return not has_progressive_eras or has_required_progressive_eras(state, era, player) - - -def create_regions(world: 'CivVIWorld'): - menu = Region("Menu", world.player, world.multiworld) - world.multiworld.regions.append(menu) - - has_progressive_eras = world.options.progression_style == "eras_and_districts" - has_goody_huts = world.options.shuffle_goody_hut_rewards - has_boosts = world.options.boostsanity - - regions: List[Region] = [] - for era in EraType: - era_region = Region(era.value, world.player, world.multiworld) - era_locations: Dict[str, Optional[int]] = {location.name: location.code for _key, - location in world.location_by_era[era.value].items()} - - if not has_progressive_eras: - era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "ERA"} - if not has_goody_huts: - era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "GOODY"} - if not has_boosts: - era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "BOOST"} - - era_region.add_locations(era_locations, CivVILocation) - - regions.append(era_region) - world.multiworld.regions.append(era_region) - - menu.connect(world.get_region(EraType.ERA_ANCIENT.value)) - - world.get_region(EraType.ERA_ANCIENT.value).connect( - world.get_region(EraType.ERA_CLASSICAL.value), None, - lambda state: has_required_items( - state, EraType.ERA_ANCIENT, world) - ) - - world.get_region(EraType.ERA_CLASSICAL.value).connect( - world.get_region(EraType.ERA_MEDIEVAL.value), None, lambda state: has_required_items( - state, EraType.ERA_CLASSICAL, world) - ) - - world.get_region(EraType.ERA_MEDIEVAL.value).connect( - world.get_region(EraType.ERA_RENAISSANCE.value), None, lambda state: has_required_items( - state, EraType.ERA_MEDIEVAL, world) - ) - - world.get_region(EraType.ERA_RENAISSANCE.value).connect( - world.get_region(EraType.ERA_INDUSTRIAL.value), None, lambda state: has_required_items( - state, EraType.ERA_RENAISSANCE, world) - ) - - world.get_region(EraType.ERA_INDUSTRIAL.value).connect( - world.get_region(EraType.ERA_MODERN.value), None, lambda state: has_required_items( - state, EraType.ERA_INDUSTRIAL, world) - ) - - world.get_region(EraType.ERA_MODERN.value).connect( - world.get_region(EraType.ERA_ATOMIC.value), None, lambda state: has_required_items( - state, EraType.ERA_MODERN, world) - ) - - world.get_region(EraType.ERA_ATOMIC.value).connect( - world.get_region(EraType.ERA_INFORMATION.value), None, lambda state: has_required_items( - state, EraType.ERA_ATOMIC, world) - ) - - world.get_region(EraType.ERA_INFORMATION.value).connect( - world.get_region(EraType.ERA_FUTURE.value), None, lambda state: has_required_items( - state, EraType.ERA_INFORMATION, world) - ) - - world.multiworld.completion_condition[world.player] = lambda state: state.can_reach( - EraType.ERA_FUTURE.value, "Region", world.player) +from typing import TYPE_CHECKING, Dict, List, Optional +from BaseClasses import CollectionState, Region +from .Data import get_era_required_items_data, get_progressive_districts_data +from .Items import CivVIItemData, format_item_name, get_item_by_civ_name +from .Enum import EraType +from .Locations import CivVILocation +from .ProgressiveDistricts import get_flat_progressive_districts + +if TYPE_CHECKING: + from . import CivVIWorld + + +def get_prereqs_for_era(end_era: EraType, exclude_progressive_items: bool, item_table: Dict[str, CivVIItemData]) -> List[CivVIItemData]: + """Gets the specific techs/civics that are required for the specified era""" + era_required_items = get_era_required_items_data()[end_era.value].copy() + + # If we are excluding progressive items, we need to remove them from the list of expected items (TECH_BRONZE_WORKING won't be here since it will be PROGRESSIVE_ENCAMPMENT) + if exclude_progressive_items: + flat_progressive_items = get_flat_progressive_districts() + prereqs_without_progressive_items: List[str] = [] + for item in era_required_items: + if item in flat_progressive_items: + continue + prereqs_without_progressive_items.append(item) + + return [get_item_by_civ_name(prereq, item_table) for prereq in prereqs_without_progressive_items] + + return [get_item_by_civ_name(prereq, item_table) for prereq in era_required_items] + + +def has_required_progressive_districts(state: CollectionState, era: EraType, player: int) -> bool: + """ If player has progressive items enabled, it will count how many progressive techs it should have, otherwise return the default array""" + progressive_districts = get_progressive_districts_data() + + item_table = state.multiworld.worlds[player].item_table + # Verify we can still reach non progressive items + all_previous_items_no_progressives = get_prereqs_for_era( + era, True, item_table) + if not state.has_all([item.name for item in all_previous_items_no_progressives], player): + return False + + # Verify we have the correct amount of progressive items + all_previous_items = get_prereqs_for_era( + era, False, item_table) + required_counts: Dict[str, int] = {} + + for key, value in progressive_districts.items(): + required_counts[key] = 0 + for item in all_previous_items: + if item.civ_name in value: + required_counts[key] += 1 + + return state.has_all_counts({format_item_name(key): value for key, value in required_counts.items()}, player) + + +def has_required_progressive_eras(state: CollectionState, era: EraType, player: int) -> bool: + """Checks, for the given era, how many are required to proceed to the next era. Ancient = 0, Classical = 1, etc.""" + if era == EraType.ERA_FUTURE or era == EraType.ERA_INFORMATION: + return True + + eras = [e.value for e in EraType] + era_index = eras.index(era.value) + return state.has(format_item_name("PROGRESSIVE_ERA"), player, era_index + 1) + + +def has_required_items(state: CollectionState, era: EraType, world: 'CivVIWorld') -> bool: + player = world.player + has_progressive_districts = world.options.progression_style != "none" + has_progressive_eras = world.options.progression_style == "eras_and_districts" + + if has_progressive_districts: + required_items = has_required_progressive_districts(state, era, player) + else: + era_required_items = [get_item_by_civ_name(item, world.item_table).name for item in get_era_required_items_data()[era.value]] + required_items = state.has_all(era_required_items, player) + + if not required_items: + return False + + return not has_progressive_eras or has_required_progressive_eras(state, era, player) + + +def create_regions(world: 'CivVIWorld'): + menu = Region("Menu", world.player, world.multiworld) + world.multiworld.regions.append(menu) + + has_progressive_eras = world.options.progression_style == "eras_and_districts" + has_goody_huts = world.options.shuffle_goody_hut_rewards + has_boosts = world.options.boostsanity + + regions: List[Region] = [] + for era in EraType: + era_region = Region(era.value, world.player, world.multiworld) + era_locations: Dict[str, Optional[int]] = {location.name: location.code for _key, + location in world.location_by_era[era.value].items()} + + if not has_progressive_eras: + era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "ERA"} + if not has_goody_huts: + era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "GOODY"} + if not has_boosts: + era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "BOOST"} + + era_region.add_locations(era_locations, CivVILocation) + + regions.append(era_region) + world.multiworld.regions.append(era_region) + + menu.connect(world.get_region(EraType.ERA_ANCIENT.value)) + + world.get_region(EraType.ERA_ANCIENT.value).connect( + world.get_region(EraType.ERA_CLASSICAL.value), None, + lambda state: has_required_items( + state, EraType.ERA_ANCIENT, world) + ) + + world.get_region(EraType.ERA_CLASSICAL.value).connect( + world.get_region(EraType.ERA_MEDIEVAL.value), None, lambda state: has_required_items( + state, EraType.ERA_CLASSICAL, world) + ) + + world.get_region(EraType.ERA_MEDIEVAL.value).connect( + world.get_region(EraType.ERA_RENAISSANCE.value), None, lambda state: has_required_items( + state, EraType.ERA_MEDIEVAL, world) + ) + + world.get_region(EraType.ERA_RENAISSANCE.value).connect( + world.get_region(EraType.ERA_INDUSTRIAL.value), None, lambda state: has_required_items( + state, EraType.ERA_RENAISSANCE, world) + ) + + world.get_region(EraType.ERA_INDUSTRIAL.value).connect( + world.get_region(EraType.ERA_MODERN.value), None, lambda state: has_required_items( + state, EraType.ERA_INDUSTRIAL, world) + ) + + world.get_region(EraType.ERA_MODERN.value).connect( + world.get_region(EraType.ERA_ATOMIC.value), None, lambda state: has_required_items( + state, EraType.ERA_MODERN, world) + ) + + world.get_region(EraType.ERA_ATOMIC.value).connect( + world.get_region(EraType.ERA_INFORMATION.value), None, lambda state: has_required_items( + state, EraType.ERA_ATOMIC, world) + ) + + world.get_region(EraType.ERA_INFORMATION.value).connect( + world.get_region(EraType.ERA_FUTURE.value), None, lambda state: has_required_items( + state, EraType.ERA_INFORMATION, world) + ) + + world.multiworld.completion_condition[world.player] = lambda state: state.can_reach( + EraType.ERA_FUTURE.value, "Region", world.player) diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py index 9c00618d82e7..297947728e43 100644 --- a/worlds/civ_6/Rules.py +++ b/worlds/civ_6/Rules.py @@ -1,9 +1,9 @@ -from typing import TYPE_CHECKING, List, Dict +from typing import TYPE_CHECKING, List from BaseClasses import CollectionState from .Items import get_item_by_civ_name -from .Data import get_boosts_data +from .Data import get_boosts_data, get_progressive_districts_data from .Enum import CivVICheckType -from .ProgressiveDistricts import convert_items_to_have_progression +from .ProgressiveDistricts import convert_item_to_have_progression from worlds.generic.Rules import forbid_item, set_rule @@ -15,41 +15,35 @@ def create_boost_rules(world: 'CivVIWorld'): boost_data_list = get_boosts_data() boost_locations = [location for location in world.location_table.values() if location.location_type == CivVICheckType.BOOST] + required_items_func = has_required_items_progressive if world.options.progression_style != "none" else has_required_items_non_progressive for location in boost_locations: boost_data = next((boost for boost in boost_data_list if boost.Type == location.name), None) world_location = world.get_location(location.name) forbid_item(world_location, "Progressive Era", world.player) - if not boost_data or boost_data.PrereqRequiredCount == 0: - continue - - set_rule(world_location, lambda state, prereqs=boost_data.Prereq, required_count=boost_data.PrereqRequiredCount: has_required_items(state, prereqs, required_count, world)) - - -def has_required_items(state: CollectionState, prereqs: List[str], required_count: int, world: 'CivVIWorld') -> bool: + if boost_data and boost_data.PrereqRequiredCount > 0: + set_rule(world_location, lambda state, prereqs=boost_data.Prereq, required_count=boost_data.PrereqRequiredCount: required_items_func(state, prereqs, required_count, world)) + + +def has_required_items_progressive(state: CollectionState, prereqs: List[str], required_count: int, world: 'CivVIWorld') -> bool: + collected_count = 0 + for item in prereqs: + progressive_item_name = convert_item_to_have_progression(item) + ap_item_name = get_item_by_civ_name(progressive_item_name, world.item_table).name + if "PROGRESSIVE" in progressive_item_name: + progression_amount = get_progressive_districts_data()[progressive_item_name].index(item) + 1 + if state.has(ap_item_name, world.player, progression_amount): + collected_count += 1 + elif state.has(ap_item_name, world.player): + collected_count += 1 + # early out if we've already gotten enough + if collected_count >= required_count: + return True + return False + + +def has_required_items_non_progressive(state: CollectionState, prereqs: List[str], required_count: int, world: 'CivVIWorld') -> bool: player = world.player - has_progressive_items = world.options.progression_style != "none" - if has_progressive_items: - count = 0 - items = [get_item_by_civ_name(item, world.item_table).name for item in convert_items_to_have_progression(prereqs)] - collected_progressive_items: Dict[str, int] = {} - for item in items: - if "Progressive" in item: - collected_progressive_items[item] = collected_progressive_items.get(item, 0) + 1 - else: - if state.has(item, player): - count += 1 - # early out if we've already gotten enough - if count >= required_count: - return True - for item, required_progressive_item_count in collected_progressive_items.items(): - if state.has(item, player, required_progressive_item_count): - count += required_progressive_item_count - # early out if we've already gotten enough - if count >= required_count: - return True - return False - else: - return state.has_from_list_unique( - [ - get_item_by_civ_name(prereq, world.item_table).name for prereq in prereqs - ], player, required_count) + return state.has_from_list_unique( + [ + get_item_by_civ_name(prereq, world.item_table).name for prereq in prereqs + ], player, required_count) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 8d69ec04c646..f98e43ff2417 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -1,4 +1,3 @@ -import logging import math import os from typing import Any, Dict, Set @@ -11,7 +10,7 @@ from .Rules import create_boost_rules from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql from .Enum import CivVICheckType -from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity +from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIEvent, CivVIItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity from .Locations import CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table from .Options import CivVIOptions from .Regions import create_regions @@ -66,7 +65,7 @@ class CivVIWorld(World): required_client_version = (0, 4, 5) location_table: Dict[str, CivVILocationData] - def __init__(self, multiworld: "MultiWorld", player: int): + def __init__(self, multiworld: MultiWorld, player: int): super().__init__(multiworld, player) self.location_by_era = generate_era_location_table() @@ -87,6 +86,9 @@ def set_rules(self) -> None: if self.options.boostsanity: create_boost_rules(self) + def create_event(self, event: str): + return CivVIEvent(event, ItemClassification.progression, None, self.player) + def create_item(self, name: str) -> Item: item: CivVIItemData = self.item_table[name] classification = item.classification @@ -132,10 +134,9 @@ def create_items(self) -> None: if era.value == "ERA_ANCIENT": continue progressive_era_item = self.item_table.get("Progressive Era") - if progressive_era_item: - self.multiworld.itempool += [self.create_item(progressive_era_item.name)] - else: - logging.error("Progressive Era item not found in item_table.") + assert progressive_era_item is not None + self.multiworld.itempool += [self.create_item(progressive_era_item.name)] + self.multiworld.early_items[self.player]["Progressive Era"] = 2 num_filler_items = 0 @@ -177,14 +178,13 @@ def post_fill(self) -> None: start_location_hints.add(location_name) def fill_slot_data(self) -> Dict[str, Any]: - return { - "progression_style": self.options.progression_style.value, - "death_link": self.options.death_link.value, - "research_cost_multiplier": self.options.research_cost_multiplier.value, - "death_link_effect": self.options.death_link_effect.value, - "death_link_effect_percent": self.options.death_link_effect_percent.value, - - } + return self.options.as_dict( + "progression_style", + "death_link", + "research_cost_multiplier", + "death_link_effect", + "death_link_effect_percent", + ) def generate_output(self, output_directory: str): mod_name = self.multiworld.get_out_file_name_base(self.player) diff --git a/worlds/civ_6/test/TestBoostsanity.py b/worlds/civ_6/test/TestBoostsanity.py index dac10553aee7..cdcb2a4adad8 100644 --- a/worlds/civ_6/test/TestBoostsanity.py +++ b/worlds/civ_6/test/TestBoostsanity.py @@ -10,7 +10,42 @@ class TestBoostsanityIncluded(CivVITestBase): "death_link": "true", "boostsanity": "true", "death_link_effect": "unit_killed", - "progressive_districts": "true", + "progression_style": "none", + "shuffle_goody_hut_rewards": "false", + "pre_hint_items": "all", + } + + def test_boosts_get_included(self) -> None: + self.world_setup() + distribute_items_restrictive(self.multiworld) + locations = self.multiworld.get_locations(self.player) + found_locations = 0 + for location in locations: + if "BOOST" in location.name: + found_locations += 1 + num_boost_locations = len(get_boosts_data()) + self.assertEqual(found_locations, num_boost_locations) + + def test_boosts_require_prereqs_no_progressives(self) -> None: + self.world_setup() + location = "BOOST_TECH_ADVANCED_BALLISTICS" + items_to_give = ["Refining", "Electricity", "Apprenticeship", "Industrialization"] + self.assertFalse(self.can_reach_location(location)) + + for prereq in items_to_give: + self.collect_by_name(prereq) + is_last_prereq = prereq == items_to_give[-1] + self.assertEqual(self.can_reach_location(location), is_last_prereq) + + +class TestBoostsanityIncludedNoProgressiveDistricts(CivVITestBase): + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "boostsanity": "true", + "death_link_effect": "unit_killed", + "progression_style": "districts_only", "shuffle_goody_hut_rewards": "false", "pre_hint_items": "all", } @@ -27,6 +62,38 @@ def test_boosts_get_included(self) -> None: self.assertEqual(found_locations, num_boost_locations) +class TestBoostsanityPrereqsWithProgressiveDistricts(CivVITestBase): + options = { + "progressive_eras": "true", + "death_link": "true", + "boostsanity": "true", + "death_link_effect": "unit_killed", + "progression_style": "districts_only", + "shuffle_goody_hut_rewards": "false", + "pre_hint_items": "all", + } + + def test_boosts_require_progressive_prereqs_optional(self) -> None: + location = "BOOST_TECH_NUCLEAR_FUSION" + items_to_give = ["Progressive Industrial Zone", "Progressive Industrial Zone"] + + self.assertFalse(self.can_reach_location(location)) + for prereq in items_to_give: + self.collect_by_name(prereq) + is_last_prereq = prereq == items_to_give[-1] + self.assertEqual(self.can_reach_location(location), is_last_prereq) + + def tests_boosts_require_correct_progressive_district_count(self) -> None: + location = "BOOST_TECH_RIFLING" + items_to_give = ["Mining", "Progressive Encampment", "Progressive Encampment"] + + self.assertFalse(self.can_reach_location(location)) + for prereq in items_to_give: + self.collect_by_name(prereq) + is_last_prereq = prereq == items_to_give[-1] + self.assertEqual(self.can_reach_location(location), is_last_prereq) + + class TestBoostsanityExcluded(CivVITestBase): auto_construct = False options = { From 56a55b5bf46871f33763fc07eca79e6fe1af5ba8 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Fri, 18 Oct 2024 15:38:12 -0600 Subject: [PATCH 48/69] Add victory event --- worlds/civ_6/Enum.py | 1 + worlds/civ_6/Locations.py | 2 ++ worlds/civ_6/Regions.py | 14 +++++++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/worlds/civ_6/Enum.py b/worlds/civ_6/Enum.py index 498e9f29efab..664d73cd2acf 100644 --- a/worlds/civ_6/Enum.py +++ b/worlds/civ_6/Enum.py @@ -20,3 +20,4 @@ class CivVICheckType(Enum): ERA = "ERA" GOODY = "GOODY" BOOST = "BOOST" + EVENT = "EVENT" diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index 48e3b03d8a05..a45b36839a35 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -59,6 +59,8 @@ def __init__(self, player: int, name: str = "", address: Optional[int] = None, p self.location_type = CivVICheckType.GOODY elif name.split("_")[0] == "BOOST": self.location_type = CivVICheckType.BOOST + else: + self.location_type = CivVICheckType.EVENT if self.name in EXCLUDED_LOCATIONS: self.progress_type = LocationProgressType.EXCLUDED diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 44d4c2b1b932..1ea76ec22bc1 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING, Dict, List, Optional from BaseClasses import CollectionState, Region +from ..generic.Rules import set_rule from .Data import get_era_required_items_data, get_progressive_districts_data from .Items import CivVIItemData, format_item_name, get_item_by_civ_name from .Enum import EraType @@ -144,10 +145,17 @@ def create_regions(world: 'CivVIWorld'): state, EraType.ERA_ATOMIC, world) ) - world.get_region(EraType.ERA_INFORMATION.value).connect( + future_era = world.get_region(EraType.ERA_INFORMATION.value) + future_era.connect( world.get_region(EraType.ERA_FUTURE.value), None, lambda state: has_required_items( state, EraType.ERA_INFORMATION, world) ) - world.multiworld.completion_condition[world.player] = lambda state: state.can_reach( - EraType.ERA_FUTURE.value, "Region", world.player) + victory = CivVILocation(world.player, "Complete a victory type", None, future_era) + victory.place_locked_item(world.create_event("Victory")) + future_era.locations.append(victory) + + set_rule(victory, lambda state: state.can_reach( + EraType.ERA_FUTURE.value, "Region", world.player)) + + world.multiworld.completion_condition[world.player] = lambda state: state.has("Victory", world.player) From 54939f04e792cd87d28a526e28bea41ea0f81208 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Fri, 18 Oct 2024 21:12:07 -0600 Subject: [PATCH 49/69] Add option set for death link effect --- worlds/civ_6/DeathLink.py | 12 +++--------- worlds/civ_6/Options.py | 15 ++++----------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/worlds/civ_6/DeathLink.py b/worlds/civ_6/DeathLink.py index 612dce97f169..2af98fea07bb 100644 --- a/worlds/civ_6/DeathLink.py +++ b/worlds/civ_6/DeathLink.py @@ -1,6 +1,6 @@ import random -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, List if TYPE_CHECKING: from .Civ6Client import CivVIContext @@ -10,14 +10,8 @@ async def handle_receive_deathlink(ctx: 'CivVIContext', message: str): """Resolves the effects of a deathlink received from the multiworld based on the options selected by the player""" - chosen_effect = ctx.slot_data["death_link_effect"] - effect: str = "Gold" - if chosen_effect == "Any Except Era Score": - effect = random.choice( - [effect for effect in DEATH_LINK_EFFECTS if effect != "Era Score"]) - else: - effect = chosen_effect if chosen_effect != "Any" else random.choice( - DEATH_LINK_EFFECTS) + chosen_effects: List[str] = ctx.slot_data["death_link_effect"] + effect = random.choice(chosen_effects) percent = ctx.slot_data["death_link_effect_percent"] if effect == "Gold": diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 32eb80fff7e0..d09004d23898 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from Options import Choice, DeathLink, DefaultOnToggle, PerGameCommonOptions, Range, StartInventoryPool, Toggle +from Options import Choice, DeathLink, DefaultOnToggle, OptionSet, PerGameCommonOptions, Range, StartInventoryPool, Toggle class ProgressionStyle(Choice): @@ -68,23 +68,16 @@ class InGameFlagProgressionItems(DefaultOnToggle): display_name = "Advisor Indicates Progression Items" -class DeathLinkEffect(Choice): +class DeathLinkEffect(OptionSet): """What happens when a unit dies. Unit Killed: A random unit will be killed when a death link is received. Faith: Faith will be decreased by the amount specified in 'Death Link Effect Percent'. Gold: Gold will be decreased by the amount specified in 'Death Link Effect Percent'. Era Score: Era score is decreased by 1. - Any: Selects from any of these options whenever a death link is received. - Any Except Era Score: Selects from any of these options except for Era Score whenever a death link is received. """ display_name = "Death Link Effect" - option_unit_killed = 0 - option_era_score = 1 - option_gold = 2 - option_faith = 3 - option_any = 4 - option_any_except_era_score = 5 - default = option_unit_killed + valid_keys = ["Unit Killed", "Faith", "Gold", "Era Score"] # type: ignore + default = {"Unit Killed"} class DeathLinkEffectPercent(Range): From 96dd8a5fadb7f9f3a504b247e16b7cb0fe3249c9 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Sat, 19 Oct 2024 10:55:36 -0600 Subject: [PATCH 50/69] PR improvements --- BaseClasses.py | 3 --- worlds/civ_6/Container.py | 2 +- worlds/civ_6/Items.py | 15 ++---------- worlds/civ_6/Options.py | 2 +- worlds/civ_6/ProgressiveDistricts.py | 26 +++++++++++++-------- worlds/civ_6/Regions.py | 7 ++---- worlds/civ_6/Rules.py | 4 ++-- worlds/civ_6/__init__.py | 7 ++++-- worlds/civ_6/test/TestGoodyHuts.py | 6 ++--- worlds/civ_6/test/TestRegionRequirements.py | 4 ++-- 10 files changed, 34 insertions(+), 42 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 9ae7180ae0fe..0d4f34e51445 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1170,9 +1170,6 @@ def can_fill(self, state: CollectionState, item: Item, check_access: bool = True def can_reach(self, state: CollectionState) -> bool: # Region.can_reach is just a cache lookup, so placing it first for faster abort on average assert self.parent_region, f"called can_reach on a Location \"{self}\" with no parent_region" - if(self.name == "BOOST_TECH_ADVANCED_BALLISTICS"): - # print("BOOST_TECH_ADVANCED_BALLISTICS") - pass return self.parent_region.can_reach(state) and self.access_rule(state) def place_locked_item(self, item: Item): diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py index 3be443d33072..7f828b511510 100644 --- a/worlds/civ_6/Container.py +++ b/worlds/civ_6/Container.py @@ -9,7 +9,7 @@ from .Locations import CivVILocation, CivVILocationData if TYPE_CHECKING: - from worlds.civ_6 import CivVIWorld + from . import CivVIWorld # Python fstrings don't allow backslashes, so we use this workaround diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index 98eb538beef0..dcd23b0dbd5a 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -77,17 +77,6 @@ def __init__(self, data: GoodyHutRewardData): self.civ_name = data["Type"] -def get_filler_item_data() -> Dict[str, FillerItemData]: - """ - Returns a dictionary of filler items with their data - """ - goody_huts = get_goody_hut_rewards_data() - # Create a FillerItemData object for each item - cached_filler_items = {item["Name"]: FillerItemData(item) for item in goody_huts} - - return cached_filler_items - - class CivVIItemData: civ_vi_id: int classification: ItemClassification @@ -255,7 +244,7 @@ def _generate_progressive_era_items(id_base: int) -> Dict[str, CivVIItemData]: def _generate_goody_hut_items(id_base: int) -> Dict[str, CivVIItemData]: # Generate goody hut items - goody_huts = get_filler_item_data() + goody_huts = {item["Name"]: FillerItemData(item) for item in get_goody_hut_rewards_data()} goody_table: Dict[str, CivVIItemData] = {} goody_base = 0 for value in goody_huts.values(): @@ -306,5 +295,5 @@ def get_random_filler_by_rarity(world: 'CivVIWorld', rarity: FillerItemRarity) - """ Returns a random filler item by rarity """ - items = [item for item in get_filler_item_data().values() if item.rarity == rarity] + items = [item for item in world.filler_data.values() if item.rarity == rarity] return world.random.choice(items) diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index d09004d23898..48d2698ef03c 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -77,7 +77,7 @@ class DeathLinkEffect(OptionSet): """ display_name = "Death Link Effect" valid_keys = ["Unit Killed", "Faith", "Gold", "Era Score"] # type: ignore - default = {"Unit Killed"} + default = frozenset({"Unit Killed"}) class DeathLinkEffectPercent(Range): diff --git a/worlds/civ_6/ProgressiveDistricts.py b/worlds/civ_6/ProgressiveDistricts.py index 70228d514570..4820e2c91573 100644 --- a/worlds/civ_6/ProgressiveDistricts.py +++ b/worlds/civ_6/ProgressiveDistricts.py @@ -1,29 +1,35 @@ -from typing import Dict, List +from typing import Dict, List, Optional from .Data import get_progressive_districts_data +_flat_progressive_districts: Optional[Dict[str, str]] = None + def get_flat_progressive_districts() -> Dict[str, str]: """Returns a dictionary of all items that are associated with a progressive item. Key is the item name ("TECH_WRITING") and the value is the associated progressive item ("PROGRESSIVE_CAMPUS")""" + global _flat_progressive_districts + if _flat_progressive_districts is not None: + return _flat_progressive_districts + progressive_districts = get_progressive_districts_data() - flat_progressive_techs: Dict[str, str] = {} + flat_progressive_districts: Dict[str, str] = {} for key, value in progressive_districts.items(): for item in value: - flat_progressive_techs[item] = key - return flat_progressive_techs + flat_progressive_districts[item] = key + return flat_progressive_districts -def convert_items_to_have_progression(items: List[str]): +def convert_items_to_progressive_items(items: List[str]): """ converts a list of items to instead be their associated progressive item if they have one. ["TECH_MINING", "TECH_WRITING"] -> ["TECH_MINING", "PROGRESSIVE_CAMPUS]""" - flat_progressive_techs = get_flat_progressive_districts() - return [flat_progressive_techs.get(item, item) for item in items] + flat_progressive_districts = get_flat_progressive_districts() + return [flat_progressive_districts.get(item, item) for item in items] -def convert_item_to_have_progression(item: str): +def convert_item_to_progressive_item(item: str): """ converts an items to instead be its associated progressive item if it has one. "TECH_WRITING" -> "PROGRESSIVE_CAMPUS""" - flat_progressive_techs = get_flat_progressive_districts() - return flat_progressive_techs.get(item, item) + flat_progressive_districts = get_flat_progressive_districts() + return flat_progressive_districts.get(item, item) diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 1ea76ec22bc1..4ad9ff4713bd 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Dict, List, Optional from BaseClasses import CollectionState, Region -from ..generic.Rules import set_rule +from worlds.generic.Rules import set_rule from .Data import get_era_required_items_data, get_progressive_districts_data from .Items import CivVIItemData, format_item_name, get_item_by_civ_name from .Enum import EraType @@ -75,10 +75,7 @@ def has_required_items(state: CollectionState, era: EraType, world: 'CivVIWorld' era_required_items = [get_item_by_civ_name(item, world.item_table).name for item in get_era_required_items_data()[era.value]] required_items = state.has_all(era_required_items, player) - if not required_items: - return False - - return not has_progressive_eras or has_required_progressive_eras(state, era, player) + return required_items and (not has_progressive_eras or has_required_progressive_eras(state, era, player)) def create_regions(world: 'CivVIWorld'): diff --git a/worlds/civ_6/Rules.py b/worlds/civ_6/Rules.py index 297947728e43..881f3bd136c5 100644 --- a/worlds/civ_6/Rules.py +++ b/worlds/civ_6/Rules.py @@ -3,7 +3,7 @@ from .Items import get_item_by_civ_name from .Data import get_boosts_data, get_progressive_districts_data from .Enum import CivVICheckType -from .ProgressiveDistricts import convert_item_to_have_progression +from .ProgressiveDistricts import convert_item_to_progressive_item from worlds.generic.Rules import forbid_item, set_rule @@ -27,7 +27,7 @@ def create_boost_rules(world: 'CivVIWorld'): def has_required_items_progressive(state: CollectionState, prereqs: List[str], required_count: int, world: 'CivVIWorld') -> bool: collected_count = 0 for item in prereqs: - progressive_item_name = convert_item_to_have_progression(item) + progressive_item_name = convert_item_to_progressive_item(item) ap_item_name = get_item_by_civ_name(progressive_item_name, world.item_table).name if "PROGRESSIVE" in progressive_item_name: progression_amount = get_progressive_districts_data()[progressive_item_name].index(item) + 1 diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index f98e43ff2417..8ac8ef16b281 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -5,12 +5,12 @@ from worlds.generic.Rules import forbid_item -from .Data import get_boosts_data, get_era_required_items_data +from .Data import get_boosts_data, get_era_required_items_data, get_goody_hut_rewards_data from .Rules import create_boost_rules from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql from .Enum import CivVICheckType -from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIEvent, CivVIItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity +from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIEvent, CivVIItemData, FillerItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity from .Locations import CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table from .Options import CivVIOptions from .Regions import create_regions @@ -64,6 +64,7 @@ class CivVIWorld(World): location_by_era: Dict[str, Dict[str, CivVILocationData]] required_client_version = (0, 4, 5) location_table: Dict[str, CivVILocationData] + filler_data: Dict[str, FillerItemData] def __init__(self, multiworld: MultiWorld, player: int): super().__init__(multiworld, player) @@ -76,6 +77,8 @@ def __init__(self, multiworld: MultiWorld, player: int): for location in locations.values(): self.location_table[location.name] = location + self.filler_data = {item["Name"]: FillerItemData(item) for item in get_goody_hut_rewards_data()} + def get_filler_item_name(self) -> str: return get_random_filler_by_rarity(self, FillerItemRarity.COMMON).name diff --git a/worlds/civ_6/test/TestGoodyHuts.py b/worlds/civ_6/test/TestGoodyHuts.py index 73e700836d10..b1ffb2c6f886 100644 --- a/worlds/civ_6/test/TestGoodyHuts.py +++ b/worlds/civ_6/test/TestGoodyHuts.py @@ -1,7 +1,7 @@ from typing import Dict from BaseClasses import ItemClassification from Fill import distribute_items_restrictive -from ..Items import FillerItemRarity, get_filler_item_data +from ..Items import FillerItemRarity from . import CivVITestBase @@ -74,7 +74,7 @@ def test_filler_items_are_included_by_rarity(self) -> None: total_filler_items = 0 for item in self.multiworld.itempool: if item.classification == ItemClassification.filler: - rarity = get_filler_item_data()[item.name].rarity + rarity = self.world.filler_data[item.name].rarity rarity_counts[rarity] += 1 total_filler_items += 1 @@ -112,7 +112,7 @@ def test_filler_items_are_included_by_rarity_without_boostsanity(self) -> None: total_filler_items = 0 for item in self.multiworld.itempool: if item.classification == ItemClassification.filler: - rarity = get_filler_item_data()[item.name].rarity + rarity = self.world.filler_data[item.name].rarity rarity_counts[rarity] += 1 total_filler_items += 1 diff --git a/worlds/civ_6/test/TestRegionRequirements.py b/worlds/civ_6/test/TestRegionRequirements.py index 9e0b777d6965..719a88533b6c 100644 --- a/worlds/civ_6/test/TestRegionRequirements.py +++ b/worlds/civ_6/test/TestRegionRequirements.py @@ -3,7 +3,7 @@ from BaseClasses import CollectionState from ..Data import get_era_required_items_data from ..Enum import EraType -from ..ProgressiveDistricts import convert_items_to_have_progression +from ..ProgressiveDistricts import convert_items_to_progressive_items from ..Items import get_item_by_civ_name from . import CivVITestBase @@ -16,7 +16,7 @@ def collect_items_for_era(test: CivVITestBase, era: EraType) -> None: def collect_items_for_era_progressive(test: CivVITestBase, era: EraType) -> None: era_progression_items = get_era_required_items_data() - progressive_items = convert_items_to_have_progression( + progressive_items = convert_items_to_progressive_items( era_progression_items[era.value]) items = [get_item_by_civ_name(item, test.world.item_table).name for item in progressive_items] test.collect_by_name(items) From 3ffcbe335fac5799ec1a4401a4bb2e2208791c31 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Sat, 19 Oct 2024 12:19:34 -0600 Subject: [PATCH 51/69] Update post fill hint to support items with multiple classifications --- worlds/civ_6/Enum.py | 17 +++++++++++++++++ worlds/civ_6/Options.py | 16 ++++------------ worlds/civ_6/__init__.py | 17 +++++------------ worlds/civ_6/test/TestBoostsanity.py | 10 ---------- worlds/civ_6/test/TestGoodyHuts.py | 12 ------------ worlds/civ_6/test/TestRegionRequirements.py | 18 ------------------ worlds/civ_6/test/TestStartingHints.py | 8 ++++---- 7 files changed, 30 insertions(+), 68 deletions(-) diff --git a/worlds/civ_6/Enum.py b/worlds/civ_6/Enum.py index 664d73cd2acf..b266d8b393c6 100644 --- a/worlds/civ_6/Enum.py +++ b/worlds/civ_6/Enum.py @@ -1,5 +1,7 @@ from enum import Enum +from BaseClasses import ItemClassification + class EraType(Enum): ERA_ANCIENT = "ERA_ANCIENT" @@ -21,3 +23,18 @@ class CivVICheckType(Enum): GOODY = "GOODY" BOOST = "BOOST" EVENT = "EVENT" + + +class CivVIHintClassification(Enum): + PROGRESSION = "Progression" + USEFUL = "Useful" + FILLER = "Filler" + + def to_item_classification(self) -> ItemClassification: + if self == CivVIHintClassification.PROGRESSION: + return ItemClassification.progression + if self == CivVIHintClassification.USEFUL: + return ItemClassification.useful + if self == CivVIHintClassification.FILLER: + return ItemClassification.filler + assert False diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 48d2698ef03c..28b08882e3b4 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -1,5 +1,6 @@ from dataclasses import dataclass from Options import Choice, DeathLink, DefaultOnToggle, OptionSet, PerGameCommonOptions, Range, StartInventoryPool, Toggle +from .Enum import CivVIHintClassification class ProgressionStyle(Choice): @@ -42,19 +43,10 @@ class ResearchCostMultiplier(Range): default = 100 -class PreHintItems(Choice): - """Controls if/what items in the tech/civics trees are pre-hinted for the multiworld. - All: All items in the tech & civics trees are pre-hinted. - Progression items: Only locations in the trees containing progression items are pre-hinted. - No Junk: Pre-hint the progression and useful items. - None: No items are pre-hinted. - """ +class PreHintItems(OptionSet): + """Controls if/what items in the tech/civics trees are pre-hinted for the multiworld.""" display_name = "Tech/Civic Tree pre-hinted Items" - option_all = 0 - option_progression_items = 1 - option_no_junk = 2 - option_none = 3 - default = option_progression_items + valid_keys = [classificaiton.value for classificaiton in CivVIHintClassification] # type: ignore class HideItemNames(Toggle): diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 8ac8ef16b281..f3c7ff8e9407 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -9,7 +9,7 @@ from .Rules import create_boost_rules from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql -from .Enum import CivVICheckType +from .Enum import CivVICheckType, CivVIHintClassification from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIEvent, CivVIItemData, FillerItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity from .Locations import CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table from .Options import CivVIOptions @@ -159,26 +159,19 @@ def create_items(self) -> None: ] def post_fill(self) -> None: - if self.options.pre_hint_items == "none": + if not len(self.options.pre_hint_items.value): return - show_flags = { - ItemClassification.progression: self.options.pre_hint_items != "none", - ItemClassification.useful: self.options.pre_hint_items == "no_junk" or self.options.pre_hint_items == "all", - ItemClassification.filler: self.options.pre_hint_items == "all", - } - start_location_hints: Set[str] = self.options.start_location_hints.value + valid_flags = [CivVIHintClassification(flag).to_item_classification() for flag in self.options.pre_hint_items.value] for location_name, location_data in self.location_table.items(): if location_data.location_type != CivVICheckType.CIVIC and location_data.location_type != CivVICheckType.TECH: continue location: CivVILocation = self.get_location(location_name) # type: ignore - if not location.item or not show_flags.get(location.item.classification, False): - continue - - start_location_hints.add(location_name) + if location.item and any(flag in location.item.classification for flag in valid_flags): + start_location_hints.add(location_name) def fill_slot_data(self) -> Dict[str, Any]: return self.options.as_dict( diff --git a/worlds/civ_6/test/TestBoostsanity.py b/worlds/civ_6/test/TestBoostsanity.py index cdcb2a4adad8..6efed6c66e25 100644 --- a/worlds/civ_6/test/TestBoostsanity.py +++ b/worlds/civ_6/test/TestBoostsanity.py @@ -7,12 +7,9 @@ class TestBoostsanityIncluded(CivVITestBase): auto_construct = False options = { "progressive_eras": "true", - "death_link": "true", "boostsanity": "true", - "death_link_effect": "unit_killed", "progression_style": "none", "shuffle_goody_hut_rewards": "false", - "pre_hint_items": "all", } def test_boosts_get_included(self) -> None: @@ -42,12 +39,9 @@ class TestBoostsanityIncludedNoProgressiveDistricts(CivVITestBase): auto_construct = False options = { "progressive_eras": "true", - "death_link": "true", "boostsanity": "true", - "death_link_effect": "unit_killed", "progression_style": "districts_only", "shuffle_goody_hut_rewards": "false", - "pre_hint_items": "all", } def test_boosts_get_included(self) -> None: @@ -65,12 +59,9 @@ def test_boosts_get_included(self) -> None: class TestBoostsanityPrereqsWithProgressiveDistricts(CivVITestBase): options = { "progressive_eras": "true", - "death_link": "true", "boostsanity": "true", - "death_link_effect": "unit_killed", "progression_style": "districts_only", "shuffle_goody_hut_rewards": "false", - "pre_hint_items": "all", } def test_boosts_require_progressive_prereqs_optional(self) -> None: @@ -103,7 +94,6 @@ class TestBoostsanityExcluded(CivVITestBase): "death_link_effect": "unit_killed", "progressive_districts": "true", "shuffle_goody_hut_rewards": "false", - "pre_hint_items": "all", } def test_boosts_are_not_included(self) -> None: diff --git a/worlds/civ_6/test/TestGoodyHuts.py b/worlds/civ_6/test/TestGoodyHuts.py index b1ffb2c6f886..464800aeb145 100644 --- a/worlds/civ_6/test/TestGoodyHuts.py +++ b/worlds/civ_6/test/TestGoodyHuts.py @@ -9,11 +9,8 @@ class TestGoodyHutsIncluded(CivVITestBase): auto_construct = False options = { "progressive_eras": "true", - "death_link": "true", - "death_link_effect": "unit_killed", "progressive_districts": "true", "shuffle_goody_hut_rewards": "true", - "pre_hint_items": "all", } def test_goody_huts_get_included(self) -> None: @@ -32,11 +29,8 @@ class TestGoodyHutsExcluded(CivVITestBase): auto_construct = False options = { "progressive_eras": "true", - "death_link": "true", - "death_link_effect": "unit_killed", "progressive_districts": "true", "shuffle_goody_hut_rewards": "false", - "pre_hint_items": "all", } def test_goody_huts_are_not_included(self) -> None: @@ -54,11 +48,8 @@ class TestFillerItemsIncludedByRarity(CivVITestBase): auto_construct = False options = { "progressive_eras": "true", - "death_link": "true", - "death_link_effect": "unit_killed", "progressive_districts": "true", "shuffle_goody_hut_rewards": "true", - "pre_hint_items": "all", "boostsanity": "true" } @@ -92,11 +83,8 @@ class TestFillerItemsIncludedByRarityWithoutBoostsanity(CivVITestBase): auto_construct = False options = { "progressive_eras": "true", - "death_link": "true", - "death_link_effect": "unit_killed", "progressive_districts": "true", "shuffle_goody_hut_rewards": "true", - "pre_hint_items": "all", "boostsanity": "false" } diff --git a/worlds/civ_6/test/TestRegionRequirements.py b/worlds/civ_6/test/TestRegionRequirements.py index 719a88533b6c..fcd203f39660 100644 --- a/worlds/civ_6/test/TestRegionRequirements.py +++ b/worlds/civ_6/test/TestRegionRequirements.py @@ -51,10 +51,7 @@ def verify_eras_accessible(test: CivVITestBase, state: CollectionState, collect_ class TestNonProgressiveRegionRequirements(CivVITestBase): options = { - "pre_hint_items": "all", "progression_style": "none", - "death_link": "false", - "death_link_effect": "unit_killed", "boostsanity": "false", } @@ -65,10 +62,7 @@ def test_eras_are_accessible_without_progressive_districts(self) -> None: class TestNonProgressiveRegionRequirementsWithBoostsanity(CivVITestBase): options = { - "pre_hint_items": "all", "progression_style": "none", - "death_link": "false", - "death_link_effect": "unit_killed", "boostsanity": "true", } @@ -79,10 +73,7 @@ def test_eras_are_accessible_without_progressive_districts(self) -> None: class TestProgressiveDistrictRequirementsWithBoostsanity(CivVITestBase): options = { - "pre_hint_items": "all", "progression_style": "districts_only", - "death_link": "false", - "death_link_effect": "unit_killed", "boostsanity": "true", } @@ -93,10 +84,7 @@ def test_eras_are_accessible_with_progressive_districts(self) -> None: class TestProgressiveDistrictRequirements(CivVITestBase): options = { - "pre_hint_items": "all", "progression_style": "districts_only", - "death_link": "false", - "death_link_effect": "unit_killed", "boostsanity": "false", } @@ -114,10 +102,7 @@ def test_progressive_districts_are_required(self) -> None: class TestProgressiveEraRequirements(CivVITestBase): options = { - "pre_hint_items": "all", "progression_style": "eras_and_districts", - "death_link": "false", - "death_link_effect": "unit_killed" } def test_eras_are_accessible_with_progressive_eras(self) -> None: @@ -171,10 +156,7 @@ def check_eras_accessible(eras: List[EraType]): class TestProgressiveEraRequirementsWithBoostsanity(CivVITestBase): options = { - "pre_hint_items": "all", "progression_style": "eras_and_districts", - "death_link": "false", - "death_link_effect": "unit_killed", "boostsanity": "true", } diff --git a/worlds/civ_6/test/TestStartingHints.py b/worlds/civ_6/test/TestStartingHints.py index 253c1136d508..c7faaab1d202 100644 --- a/worlds/civ_6/test/TestStartingHints.py +++ b/worlds/civ_6/test/TestStartingHints.py @@ -12,7 +12,7 @@ class TestStartingHints(CivVITestBase): "death_link": "true", "death_link_effect": "unit_killed", "progressive_districts": "true", - "pre_hint_items": "all", + "pre_hint_items": {"Progression", "Useful", "Filler"}, } def test_all_tech_civic_items_are_hinted_default(self) -> None: @@ -35,7 +35,7 @@ class TestOnlyProgressionItemsHinted(CivVITestBase): "death_link": "true", "death_link_effect": "unit_killed", "progressive_districts": "true", - "pre_hint_items": "progression_items", + "pre_hint_items": {"Progression"}, } def test_only_progression_items_are_hinted(self) -> None: @@ -60,7 +60,7 @@ class TestNoJunkItemsHinted(CivVITestBase): "death_link": "true", "death_link_effect": "unit_killed", "progressive_districts": "true", - "pre_hint_items": "no_junk", + "pre_hint_items": {"Progression", "Useful"}, } def test_no_junk_items_are_hinted(self) -> None: @@ -85,7 +85,7 @@ class TestNoItemsHinted(CivVITestBase): "death_link": "true", "death_link_effect": "unit_killed", "progressive_districts": "true", - "pre_hint_items": "none", + "pre_hint_items": {}, } def test_no_items_are_hinted(self) -> None: From d88813d915ae264eda01caca9493bbeb0cbaeb1e Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Sat, 19 Oct 2024 12:23:23 -0600 Subject: [PATCH 52/69] remove unnecessary len --- worlds/civ_6/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index f3c7ff8e9407..db3be9160a0e 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -159,7 +159,7 @@ def create_items(self) -> None: ] def post_fill(self) -> None: - if not len(self.options.pre_hint_items.value): + if not self.options.pre_hint_items.value: return start_location_hints: Set[str] = self.options.start_location_hints.value From 80f94eeb51831c6fe1fc6cf006e6a6b954f5d743 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Sat, 19 Oct 2024 12:57:51 -0600 Subject: [PATCH 53/69] Move location exclusion logic --- worlds/civ_6/Locations.py | 13 ++----------- worlds/civ_6/Regions.py | 25 +++++++++++++++++++++---- worlds/civ_6/__init__.py | 2 +- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index a45b36839a35..b7740d38be7d 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -1,5 +1,5 @@ from typing import List, Optional, Dict, Union -from BaseClasses import Location, LocationProgressType, Region +from BaseClasses import Location, Region from .Data import CivicPrereqData, TechPrereqData, get_boosts_data, get_new_civics_data, get_new_techs_data @@ -8,7 +8,7 @@ CIV_VI_AP_LOCATION_ID_BASE = 5041000 # Locs that should not have progression items -EXCLUDED_LOCATIONS = [ +GOODY_HUT_LOCATION_NAMES = [ "GOODY_HUT_1", "GOODY_HUT_2", "GOODY_HUT_3", @@ -62,15 +62,6 @@ def __init__(self, player: int, name: str = "", address: Optional[int] = None, p else: self.location_type = CivVICheckType.EVENT - if self.name in EXCLUDED_LOCATIONS: - self.progress_type = LocationProgressType.EXCLUDED - - if self.location_type == CivVICheckType.BOOST: - boost_data_list = get_boosts_data() - boost_data = next((boost for boost in boost_data_list if boost.Type == name), None) - if boost_data and boost_data.Classification == "EXCLUDED": - self.progress_type = LocationProgressType.EXCLUDED - def generate_flat_location_table() -> Dict[str, CivVILocationData]: """ diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index 4ad9ff4713bd..e7d82aa3cd9e 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -1,10 +1,10 @@ -from typing import TYPE_CHECKING, Dict, List, Optional -from BaseClasses import CollectionState, Region +from typing import TYPE_CHECKING, Dict, List, Optional, Set +from BaseClasses import CollectionState, LocationProgressType, Region from worlds.generic.Rules import set_rule -from .Data import get_era_required_items_data, get_progressive_districts_data +from .Data import get_boosts_data, get_era_required_items_data, get_progressive_districts_data from .Items import CivVIItemData, format_item_name, get_item_by_civ_name from .Enum import EraType -from .Locations import CivVILocation +from .Locations import GOODY_HUT_LOCATION_NAMES, CivVILocation from .ProgressiveDistricts import get_flat_progressive_districts if TYPE_CHECKING: @@ -156,3 +156,20 @@ def create_regions(world: 'CivVIWorld'): EraType.ERA_FUTURE.value, "Region", world.player)) world.multiworld.completion_condition[world.player] = lambda state: state.has("Victory", world.player) + exclude_necessary_locations(world) + + +def exclude_necessary_locations(world: 'CivVIWorld'): + forced_excluded_location_names: Set[str] = set() + + if world.options.shuffle_goody_hut_rewards: + forced_excluded_location_names.update(GOODY_HUT_LOCATION_NAMES) + + if world.options.boostsanity: + boost_data_list = get_boosts_data() + excluded_boosts = {boost_data.Type for boost_data in boost_data_list if boost_data.Classification == "EXCLUDED"} + forced_excluded_location_names.update(excluded_boosts) + + for location_name in forced_excluded_location_names: + location = world.get_location(location_name) + location.progress_type = LocationProgressType.EXCLUDED diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index db3be9160a0e..1e8fca632fb3 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -34,7 +34,7 @@ def run_client(*args: Any): class CivVIWeb(WebWorld): tutorials = [Tutorial( "Multiworld Setup Guide", - "A guide to setting up Civlization VI for MultiWorld.", + "A guide to setting up Civilization VI for MultiWorld.", "English", "setup_en.md", "setup/en", From 40750cb22170fbd2b11df4d05f1fd6d13fd32f50 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Sat, 19 Oct 2024 15:13:05 -0600 Subject: [PATCH 54/69] Update test to use set instead of accidental dict --- worlds/civ_6/test/TestStartingHints.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/worlds/civ_6/test/TestStartingHints.py b/worlds/civ_6/test/TestStartingHints.py index c7faaab1d202..f5d97182a8e2 100644 --- a/worlds/civ_6/test/TestStartingHints.py +++ b/worlds/civ_6/test/TestStartingHints.py @@ -12,7 +12,7 @@ class TestStartingHints(CivVITestBase): "death_link": "true", "death_link_effect": "unit_killed", "progressive_districts": "true", - "pre_hint_items": {"Progression", "Useful", "Filler"}, + "pre_hint_items": set({"Progression", "Useful", "Filler"}), } def test_all_tech_civic_items_are_hinted_default(self) -> None: @@ -35,7 +35,7 @@ class TestOnlyProgressionItemsHinted(CivVITestBase): "death_link": "true", "death_link_effect": "unit_killed", "progressive_districts": "true", - "pre_hint_items": {"Progression"}, + "pre_hint_items": set({"Progression"}), } def test_only_progression_items_are_hinted(self) -> None: @@ -60,7 +60,7 @@ class TestNoJunkItemsHinted(CivVITestBase): "death_link": "true", "death_link_effect": "unit_killed", "progressive_districts": "true", - "pre_hint_items": {"Progression", "Useful"}, + "pre_hint_items": set({"Progression", "Useful"}), } def test_no_junk_items_are_hinted(self) -> None: @@ -85,7 +85,7 @@ class TestNoItemsHinted(CivVITestBase): "death_link": "true", "death_link_effect": "unit_killed", "progressive_districts": "true", - "pre_hint_items": {}, + "pre_hint_items": set({}), } def test_no_items_are_hinted(self) -> None: From 555d8522bc0ee1649542e76b090b07eb8947b55c Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Sun, 20 Oct 2024 13:07:40 -0600 Subject: [PATCH 55/69] Update docs around progressive eras and boost locations --- worlds/civ_6/data/boosts.json | 2 +- worlds/civ_6/docs/en_Civilization VI.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/civ_6/data/boosts.json b/worlds/civ_6/data/boosts.json index 7c82fdead1fb..bbba394b2135 100644 --- a/worlds/civ_6/data/boosts.json +++ b/worlds/civ_6/data/boosts.json @@ -649,7 +649,7 @@ "Type": "BOOST_CIVIC_THEOLOGY", "EraType": "ERA_CLASSICAL", "Prereq": ["TECH_ASTROLOGY"], - "PrereqRequiredCount": 0, + "PrereqRequiredCount": 1, "Classification": "EXCLUDED" }, { diff --git a/worlds/civ_6/docs/en_Civilization VI.md b/worlds/civ_6/docs/en_Civilization VI.md index 501a811e4b9c..65996571704c 100644 --- a/worlds/civ_6/docs/en_Civilization VI.md +++ b/worlds/civ_6/docs/en_Civilization VI.md @@ -53,7 +53,7 @@ Boosts have logic associated with them in order to verify you can always reach t - Something happened, and I'm not able to unlock the boost due to game rules! - A few scenarios you may worry about: "Found a religion", "Make an alliance with another player", "Develop an alliance to level 2", "Build a wonder from X Era", to name a few. Any boost that is "miss-able" has been flagged as an "Excluded" location and will not ever receive a progression item. For a list of how each boost is flagged, take a look [here](https://github.com/hesto2/civilization_vi_apworld/blob/main/data/boosts.json). - I'm worried that my `PROGRESSIVE_ERA` item is going to be stuck in a boost I won't have time to complete before my maximum unlocked era ends! - - Due to the unpredictable timing of boosts and unlocking them, this could lead to a hard lock in certain scenarios. As a result, `PROGRESSIVE_ERA` items will never be located at a boost check. + - The unpredictable timing of boosts and unlocking them can occasionally lead to scenarios where you'll have to first encounter a locked era defeat and then load a previous save. To help reduce the frequency of this, local `PROGRESSIVE_ERA` items will never be located at a boost check. - There's too many boosts, how will I know which one's I should focus on?! - In order to give a little more focus to all the boosts rather than just arbitrarily picking them at random, items in both of the vanilla trees will now have an advisor icon on them if its associated boost contains a progression item. From 0f10369f4c3456ddc9a733c90aa3e369694d3f4f Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Sun, 20 Oct 2024 14:01:55 -0600 Subject: [PATCH 56/69] Update docs for options to be more readable --- worlds/civ_6/Options.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index 28b08882e3b4..c602ab40602f 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -4,17 +4,16 @@ class ProgressionStyle(Choice): - """Determines what progressive items (if any) should be included. - Districts Only: Each tech/civic that would normally unlock a district or building now has a logical progression. + """ + **Districts Only**: Each tech/civic that would normally unlock a district or building now has a logical progression. Example: TECH_BRONZE_WORKING is now PROGRESSIVE_ENCAMPMENT - Eras and Districts: Players will be defeated if they play until the world era advances beyond the currently unlocked - maximum era. - A notification will be shown as the end of the era approaches letting the player know if they don't have enough - progressive era items. + + **Eras and Districts**: Players will be defeated if they play until the world era advances beyond the currently unlocked maximum era. Unlocked eras can be seen in both the tech and civic trees. Includes all progressive districts. - None: No progressive items will be included. This means you can get district upgrades that won't be usable until the - relevant district is unlocked. + + **None**: No progressive items will be included. This means you can get district upgrades that won't be usable until the relevant district is unlocked. """ + rich_text_doc = True display_name = "Progression Style" option_districts_only = 0 option_eras_and_districts = 1 @@ -23,15 +22,18 @@ class ProgressionStyle(Choice): class ShuffleGoodyHuts(DefaultOnToggle): - """Shuffles the goody hut rewards. Goody huts will only contain junk items and locations are checked sequentially - (First goody hut gives GOODY_HUT_1, second gives GOODY_HUT_2, etc.).""" + """Shuffles the goody hut rewards. + Goody huts will only contain junk items and locations are checked sequentially (First goody hut gives GOODY_HUT_1, second gives GOODY_HUT_2, etc.).""" display_name = "Shuffle Goody Hut Rewards" class BoostSanity(Toggle): """Boosts for Civics/Techs are location checks. Boosts can now be triggered even if the item has already been - researched. If it is dependent upon a unit that is now obsolete, you can click to toggle on/off the relevant tech in + researched. + + **Note**: If a boost is dependent upon a unit that is now obsolete, you can click to toggle on/off the relevant tech in the tech tree.""" + rich_text_doc = True display_name = "Boostsanity" @@ -62,11 +64,16 @@ class InGameFlagProgressionItems(DefaultOnToggle): class DeathLinkEffect(OptionSet): """What happens when a unit dies. - Unit Killed: A random unit will be killed when a death link is received. - Faith: Faith will be decreased by the amount specified in 'Death Link Effect Percent'. - Gold: Gold will be decreased by the amount specified in 'Death Link Effect Percent'. - Era Score: Era score is decreased by 1. + + **Unit Killed**: A random unit will be killed when a death link is received. + + **Faith**: Faith will be decreased by the amount specified in 'Death Link Effect Percent'. + + **Gold**: Gold will be decreased by the amount specified in 'Death Link Effect Percent'. + + **Era Score**: Era score is decreased by 1. """ + rich_text_doc = True display_name = "Death Link Effect" valid_keys = ["Unit Killed", "Faith", "Gold", "Era Score"] # type: ignore default = frozenset({"Unit Killed"}) From e4f9901e4122e5f84d575fb9a027104c3af71675 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 22 Oct 2024 07:41:36 -0600 Subject: [PATCH 57/69] Fix issue with filler items and prehints --- worlds/civ_6/Options.py | 6 +++- worlds/civ_6/__init__.py | 13 ++++++-- worlds/civ_6/test/TestStartingHints.py | 41 ++++++++++++++++++++++---- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/worlds/civ_6/Options.py b/worlds/civ_6/Options.py index c602ab40602f..dd745b34f6ca 100644 --- a/worlds/civ_6/Options.py +++ b/worlds/civ_6/Options.py @@ -46,7 +46,11 @@ class ResearchCostMultiplier(Range): class PreHintItems(OptionSet): - """Controls if/what items in the tech/civics trees are pre-hinted for the multiworld.""" + """Controls what items from the tech/civics trees are pre-hinted for the multiworld. + **Progression**: Include Progression items in hints + **Useful**: Include Useful items in hints + **Filler**: Include Filler items in hints + """ display_name = "Tech/Civic Tree pre-hinted Items" valid_keys = [classificaiton.value for classificaiton in CivVIHintClassification] # type: ignore diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 1e8fca632fb3..63ca536f1d6d 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -162,15 +162,24 @@ def post_fill(self) -> None: if not self.options.pre_hint_items.value: return + def is_hintable_filler_item(item: Item) -> bool: + return item.classification == 0 and CivVIHintClassification.FILLER.value in self.options.pre_hint_items.value + start_location_hints: Set[str] = self.options.start_location_hints.value - valid_flags = [CivVIHintClassification(flag).to_item_classification() for flag in self.options.pre_hint_items.value] + non_filler_flags = [ + CivVIHintClassification(flag).to_item_classification() + for flag in self.options.pre_hint_items.value + if flag != CivVIHintClassification.FILLER.value + ] for location_name, location_data in self.location_table.items(): if location_data.location_type != CivVICheckType.CIVIC and location_data.location_type != CivVICheckType.TECH: continue location: CivVILocation = self.get_location(location_name) # type: ignore - if location.item and any(flag in location.item.classification for flag in valid_flags): + if location.item and ( + is_hintable_filler_item(location.item) or any(flag in location.item.classification for flag in non_filler_flags) + ): start_location_hints.add(location_name) def fill_slot_data(self) -> Dict[str, Any]: diff --git a/worlds/civ_6/test/TestStartingHints.py b/worlds/civ_6/test/TestStartingHints.py index f5d97182a8e2..da198c6a45e8 100644 --- a/worlds/civ_6/test/TestStartingHints.py +++ b/worlds/civ_6/test/TestStartingHints.py @@ -61,20 +61,49 @@ class TestNoJunkItemsHinted(CivVITestBase): "death_link_effect": "unit_killed", "progressive_districts": "true", "pre_hint_items": set({"Progression", "Useful"}), + "boostsanity": "true", + "shuffle_goody_hut_rewards": "true", } def test_no_junk_items_are_hinted(self) -> None: self.world_setup() distribute_items_restrictive(self.multiworld) + item = self.multiworld.get_location("TECH_AP_ANCIENT_01", self.player).item + self.assertIsNotNone(item) + + if item: + item.classification = ItemClassification.filler + self.world.post_fill() start_location_hints = self.world.options.start_location_hints.value self.assertTrue(len(start_location_hints) > 0) - for hint in start_location_hints: - location_data = self.world.get_location(hint) - if location_data.item: - self.assertTrue(location_data.item.classification == ItemClassification.progression or location_data.item.classification == ItemClassification.useful) - else: - self.assertTrue(False, "Location has no item") + self.assertNotIn("TECH_AP_ANCIENT_01", start_location_hints) + + +class TestOnlyJunkItemsHinted(CivVITestBase): + run_default_tests = False # type: ignore + auto_construct = False + options = { + "progressive_eras": "true", + "death_link": "true", + "death_link_effect": "unit_killed", + "progressive_districts": "true", + "pre_hint_items": set({"Filler"}), + } + + def test_only_junk_items_are_hinted(self) -> None: + self.world_setup() + distribute_items_restrictive(self.multiworld) + item = self.multiworld.get_location("TECH_AP_ANCIENT_01", self.player).item + self.assertIsNotNone(item) + + if item: + item.classification = ItemClassification.filler + + self.world.post_fill() + start_location_hints = self.world.options.start_location_hints.value + self.assertEqual(len(start_location_hints), 1) + self.assertIn("TECH_AP_ANCIENT_01", start_location_hints) class TestNoItemsHinted(CivVITestBase): From 8d18207b1964a6c8a4d09bf79543cfddc5f92840 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 22 Oct 2024 08:55:38 -0600 Subject: [PATCH 58/69] Update filler_data to be static --- worlds/civ_6/Items.py | 5 ++++- worlds/civ_6/__init__.py | 7 ++----- worlds/civ_6/test/TestGoodyHuts.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index dcd23b0dbd5a..46108dc24280 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -77,6 +77,9 @@ def __init__(self, data: GoodyHutRewardData): self.civ_name = data["Type"] +filler_data: Dict[str, FillerItemData] = {item["Name"]: FillerItemData(item) for item in get_goody_hut_rewards_data()} + + class CivVIItemData: civ_vi_id: int classification: ItemClassification @@ -295,5 +298,5 @@ def get_random_filler_by_rarity(world: 'CivVIWorld', rarity: FillerItemRarity) - """ Returns a random filler item by rarity """ - items = [item for item in world.filler_data.values() if item.rarity == rarity] + items = [item for item in filler_data.values() if item.rarity == rarity] return world.random.choice(items) diff --git a/worlds/civ_6/__init__.py b/worlds/civ_6/__init__.py index 63ca536f1d6d..58c51265f02b 100644 --- a/worlds/civ_6/__init__.py +++ b/worlds/civ_6/__init__.py @@ -5,12 +5,12 @@ from worlds.generic.Rules import forbid_item -from .Data import get_boosts_data, get_era_required_items_data, get_goody_hut_rewards_data +from .Data import get_boosts_data, get_era_required_items_data from .Rules import create_boost_rules from .Container import CivVIContainer, generate_goody_hut_sql, generate_new_items, generate_setup_file, generate_update_boosts_sql from .Enum import CivVICheckType, CivVIHintClassification -from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIEvent, CivVIItemData, FillerItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity +from .Items import BOOSTSANITY_PROGRESSION_ITEMS, FILLER_DISTRIBUTION, CivVIEvent, CivVIItemData, FillerItemRarity, generate_item_table, CivVIItem, get_random_filler_by_rarity from .Locations import CivVILocation, CivVILocationData, EraType, generate_era_location_table, generate_flat_location_table from .Options import CivVIOptions from .Regions import create_regions @@ -64,7 +64,6 @@ class CivVIWorld(World): location_by_era: Dict[str, Dict[str, CivVILocationData]] required_client_version = (0, 4, 5) location_table: Dict[str, CivVILocationData] - filler_data: Dict[str, FillerItemData] def __init__(self, multiworld: MultiWorld, player: int): super().__init__(multiworld, player) @@ -77,8 +76,6 @@ def __init__(self, multiworld: MultiWorld, player: int): for location in locations.values(): self.location_table[location.name] = location - self.filler_data = {item["Name"]: FillerItemData(item) for item in get_goody_hut_rewards_data()} - def get_filler_item_name(self) -> str: return get_random_filler_by_rarity(self, FillerItemRarity.COMMON).name diff --git a/worlds/civ_6/test/TestGoodyHuts.py b/worlds/civ_6/test/TestGoodyHuts.py index 464800aeb145..a55c74f38ed4 100644 --- a/worlds/civ_6/test/TestGoodyHuts.py +++ b/worlds/civ_6/test/TestGoodyHuts.py @@ -1,7 +1,7 @@ from typing import Dict from BaseClasses import ItemClassification from Fill import distribute_items_restrictive -from ..Items import FillerItemRarity +from ..Items import FillerItemRarity, filler_data from . import CivVITestBase @@ -65,7 +65,7 @@ def test_filler_items_are_included_by_rarity(self) -> None: total_filler_items = 0 for item in self.multiworld.itempool: if item.classification == ItemClassification.filler: - rarity = self.world.filler_data[item.name].rarity + rarity = filler_data[item.name].rarity rarity_counts[rarity] += 1 total_filler_items += 1 @@ -100,7 +100,7 @@ def test_filler_items_are_included_by_rarity_without_boostsanity(self) -> None: total_filler_items = 0 for item in self.multiworld.itempool: if item.classification == ItemClassification.filler: - rarity = self.world.filler_data[item.name].rarity + rarity = filler_data[item.name].rarity rarity_counts[rarity] += 1 total_filler_items += 1 From e08b884055dd068a8daaa0245e4484e49cee310d Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Tue, 22 Oct 2024 14:10:52 -0600 Subject: [PATCH 59/69] Update links in docs --- worlds/civ_6/docs/en_Civilization VI.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/civ_6/docs/en_Civilization VI.md b/worlds/civ_6/docs/en_Civilization VI.md index 65996571704c..3b1fbbdb055a 100644 --- a/worlds/civ_6/docs/en_Civilization VI.md +++ b/worlds/civ_6/docs/en_Civilization VI.md @@ -39,7 +39,7 @@ Solution: You can now go in to the tech tree, click "Toggle Archipelago Tree" to 1. `TECH_WRITING` 2. `TECH_EDUCATION` 3. `TECH_CHEMISTRY` - - If you want to see the details around each item, you can review [this file](https://github.com/hesto2/civilization_vi_apworld/blob/main/data/progressive_districts.json). + - If you want to see the details around each item, you can review [this file](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/civ_6/data/progressive_districts.json). ## Boostsanity Boostsanity takes all of the Eureka & Inspiration events and makes them location checks. This feature is the one to change up the way Civilization is played in an AP multiworld/randomizer. What normally are mundane tasks that are passively collected now become a novel and interesting bucket list that you need to pay attention to in order to unlock items for yourself and others! @@ -51,7 +51,7 @@ Boosts have logic associated with them in order to verify you can always reach t - I need to kill a unit with a slinger/archer/musketman or some other obsolete unit I can't build anymore, how can I do this? - Don't forget you can go into the Tech Tree and click on a Vanilla tech you've received in order to toggle it on/off. This is necessary in order to pursue some of the boosts if you receive techs in certain orders. - Something happened, and I'm not able to unlock the boost due to game rules! - - A few scenarios you may worry about: "Found a religion", "Make an alliance with another player", "Develop an alliance to level 2", "Build a wonder from X Era", to name a few. Any boost that is "miss-able" has been flagged as an "Excluded" location and will not ever receive a progression item. For a list of how each boost is flagged, take a look [here](https://github.com/hesto2/civilization_vi_apworld/blob/main/data/boosts.json). + - A few scenarios you may worry about: "Found a religion", "Make an alliance with another player", "Develop an alliance to level 2", "Build a wonder from X Era", to name a few. Any boost that is "miss-able" has been flagged as an "Excluded" location and will not ever receive a progression item. For a list of how each boost is flagged, take a look [here](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/civ_6/data/boosts.json). - I'm worried that my `PROGRESSIVE_ERA` item is going to be stuck in a boost I won't have time to complete before my maximum unlocked era ends! - The unpredictable timing of boosts and unlocking them can occasionally lead to scenarios where you'll have to first encounter a locked era defeat and then load a previous save. To help reduce the frequency of this, local `PROGRESSIVE_ERA` items will never be located at a boost check. - There's too many boosts, how will I know which one's I should focus on?! From 957931d691d89374b4a87b75850782b41e974e22 Mon Sep 17 00:00:00 2001 From: Carter Hesterman Date: Wed, 23 Oct 2024 14:01:43 -0600 Subject: [PATCH 60/69] Minor updates and PR feedback --- worlds/civ_6/Items.py | 4 +--- worlds/civ_6/Locations.py | 11 ++++++----- worlds/civ_6/Regions.py | 25 ++++++++++++------------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/worlds/civ_6/Items.py b/worlds/civ_6/Items.py index 46108dc24280..1ae0ca7feb86 100644 --- a/worlds/civ_6/Items.py +++ b/worlds/civ_6/Items.py @@ -209,9 +209,7 @@ def _generate_progressive_district_items(id_base: int) -> Dict[str, CivVIItemDat progressive_id_base = 0 progressive_items = get_progressive_districts_data() for item_name in progressive_items.keys(): - classification = ItemClassification.progression - if item_name in NON_PROGRESSION_DISTRICTS: - classification = ItemClassification.useful + classification = ItemClassification.useful if item_name in NON_PROGRESSION_DISTRICTS else ItemClassification.progression name = format_item_name(item_name) progressive_table[name] = CivVIItemData( name=name, diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py index b7740d38be7d..988d61bd7d24 100644 --- a/worlds/civ_6/Locations.py +++ b/worlds/civ_6/Locations.py @@ -49,15 +49,16 @@ class CivVILocation(Location): def __init__(self, player: int, name: str = "", address: Optional[int] = None, parent: Optional[Region] = None): super().__init__(player, name, address, parent) - if name.split("_")[0] == "TECH": + category = name.split("_")[0] + if category == "TECH": self.location_type = CivVICheckType.TECH - elif name.split("_")[0] == "CIVIC": + elif category == "CIVIC": self.location_type = CivVICheckType.CIVIC - elif name.split("_")[0] == "ERA": + elif category == "ERA": self.location_type = CivVICheckType.ERA - elif name.split("_")[0] == "GOODY": + elif category == "GOODY": self.location_type = CivVICheckType.GOODY - elif name.split("_")[0] == "BOOST": + elif category == "BOOST": self.location_type = CivVICheckType.BOOST else: self.location_type = CivVICheckType.EVENT diff --git a/worlds/civ_6/Regions.py b/worlds/civ_6/Regions.py index e7d82aa3cd9e..c4a60a1545eb 100644 --- a/worlds/civ_6/Regions.py +++ b/worlds/civ_6/Regions.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Dict, List, Optional, Set +from typing import TYPE_CHECKING, Dict, List, Optional, Set, Union from BaseClasses import CollectionState, LocationProgressType, Region from worlds.generic.Rules import set_rule from .Data import get_boosts_data, get_era_required_items_data, get_progressive_districts_data @@ -82,22 +82,21 @@ def create_regions(world: 'CivVIWorld'): menu = Region("Menu", world.player, world.multiworld) world.multiworld.regions.append(menu) - has_progressive_eras = world.options.progression_style == "eras_and_districts" - has_goody_huts = world.options.shuffle_goody_hut_rewards - has_boosts = world.options.boostsanity + optional_location_inclusions: Dict[str, Union[bool, int]] = { + "ERA": world.options.progression_style == world.options.progression_style.option_eras_and_districts, + "GOODY": world.options.shuffle_goody_hut_rewards.value, + "BOOST": world.options.boostsanity.value + } regions: List[Region] = [] for era in EraType: era_region = Region(era.value, world.player, world.multiworld) - era_locations: Dict[str, Optional[int]] = {location.name: location.code for _key, - location in world.location_by_era[era.value].items()} - - if not has_progressive_eras: - era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "ERA"} - if not has_goody_huts: - era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "GOODY"} - if not has_boosts: - era_locations = {key: value for key, value in era_locations.items() if key.split("_")[0] != "BOOST"} + era_locations: Dict[str, Optional[int]] = {} + + for key, location in world.location_by_era[era.value].items(): + category = key.split("_")[0] + if optional_location_inclusions.get(category, True): + era_locations[location.name] = location.code era_region.add_locations(era_locations, CivVILocation) From dba1b28725e99f920fba8b75e35961d107b5e8a7 Mon Sep 17 00:00:00 2001 From: hesto2 Date: Sun, 1 Dec 2024 11:10:25 -0700 Subject: [PATCH 61/69] Update boosts data --- worlds/civ_6/Data.py | 22 +- worlds/civ_6/data/boosts.json | 920 --------------------------------- worlds/civ_6/data/boosts.py | 927 ++++++++++++++++++++++++++++++++++ 3 files changed, 930 insertions(+), 939 deletions(-) delete mode 100644 worlds/civ_6/data/boosts.json create mode 100644 worlds/civ_6/data/boosts.py diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index fd74b952e983..ef3ef78b546a 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -4,6 +4,8 @@ import pkgutil from typing import Any, Dict, List, TypedDict +from worlds.civ_6.data.boosts import CivVIBoostData + _cache: Dict[Any, Any] = {} @@ -19,26 +21,8 @@ def _get_data(key: str) -> Any: return _cache[key] -@dataclass -class CivVIBoostData: - Type: str - EraType: str - Prereq: List[str] - PrereqRequiredCount: int - Classification: str - - def get_boosts_data() -> List[CivVIBoostData]: - boosts_json = _get_data("boosts") - boosts: List[CivVIBoostData] = [] - for boost in boosts_json: - boosts.append(CivVIBoostData( - Type=boost["Type"], - EraType=boost["EraType"], - Prereq=boost["Prereq"], - PrereqRequiredCount=boost["PrereqRequiredCount"], - Classification=boost["Classification"] - )) + from .data.boosts import boosts return boosts diff --git a/worlds/civ_6/data/boosts.json b/worlds/civ_6/data/boosts.json deleted file mode 100644 index bbba394b2135..000000000000 --- a/worlds/civ_6/data/boosts.json +++ /dev/null @@ -1,920 +0,0 @@ -[ - { - "Type": "BOOST_TECH_SAILING", - "EraType": "ERA_ANCIENT", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_ASTROLOGY", - "EraType": "ERA_ANCIENT", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_IRRIGATION", - "EraType": "ERA_ANCIENT", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_ARCHERY", - "EraType": "ERA_ANCIENT", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_WRITING", - "EraType": "ERA_ANCIENT", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_MASONRY", - "EraType": "ERA_ANCIENT", - "Prereq": ["TECH_MINING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_BRONZE_WORKING", - "EraType": "ERA_ANCIENT", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_THE_WHEEL", - "EraType": "ERA_ANCIENT", - "Prereq": ["TECH_MINING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_CELESTIAL_NAVIGATION", - "EraType": "ERA_CLASSICAL", - "Prereq": ["TECH_SAILING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_CURRENCY", - "EraType": "ERA_CLASSICAL", - "Prereq": ["CIVIC_FOREIGN_TRADE"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_HORSEBACK_RIDING", - "EraType": "ERA_CLASSICAL", - "Prereq": ["TECH_ANIMAL_HUSBANDRY"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_IRON_WORKING", - "EraType": "ERA_CLASSICAL", - "Prereq": ["TECH_MINING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_SHIPBUILDING", - "EraType": "ERA_CLASSICAL", - "Prereq": ["TECH_SAILING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_MATHEMATICS", - "EraType": "ERA_CLASSICAL", - "Prereq": [ - "TECH_CURRENCY", - "TECH_BRONZE_WORKING", - "TECH_CELESTIAL_NAVIGATION", - "TECH_WRITING", - "TECH_APPRENTICESHIP", - "TECH_FLIGHT", - "CIVIC_GAMES_RECREATION", - "CIVIC_DRAMA_POETRY" - ], - "PrereqRequiredCount": 3, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_CONSTRUCTION", - "EraType": "ERA_CLASSICAL", - "Prereq": ["TECH_THE_WHEEL"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_ENGINEERING", - "EraType": "ERA_CLASSICAL", - "Prereq": ["TECH_MASONRY"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_MILITARY_TACTICS", - "EraType": "ERA_MEDIEVAL", - "Prereq": ["TECH_BRONZE_WORKING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_APPRENTICESHIP", - "EraType": "ERA_MEDIEVAL", - "Prereq": ["TECH_MINING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_MACHINERY", - "EraType": "ERA_MEDIEVAL", - "Prereq": ["TECH_ARCHERY"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_EDUCATION", - "EraType": "ERA_MEDIEVAL", - "Prereq": ["TECH_WRITING"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_STIRRUPS", - "EraType": "ERA_MEDIEVAL", - "Prereq": ["CIVIC_FEUDALISM"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_MILITARY_ENGINEERING", - "EraType": "ERA_MEDIEVAL", - "Prereq": ["TECH_ENGINEERING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_CASTLES", - "EraType": "ERA_MEDIEVAL", - "Prereq": [ - "CIVIC_DIVINE_RIGHT", - "CIVIC_EXPLORATION", - "CIVIC_REFORMED_CHURCH", - "CIVIC_SUFFRAGE", - "CIVIC_TOTALITARIANISM", - "CIVIC_CLASS_STRUGGLE", - "CIVIC_DIGITAL_DEMOCRACY", - "CIVIC_CORPORATE_LIBERTARIANISM", - "CIVIC_SYNTHETIC_TECHNOCRACY" - ], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_CARTOGRAPHY", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_CELESTIAL_NAVIGATION"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_MASS_PRODUCTION", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_CONSTRUCTION"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_BANKING", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["CIVIC_GUILDS"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_GUNPOWDER", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_BRONZE_WORKING", "TECH_MILITARY_ENGINEERING"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_PRINTING", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_WRITING", "TECH_EDUCATION"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_SQUARE_RIGGING", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_GUNPOWDER"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_ASTRONOMY", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_EDUCATION"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_METAL_CASTING", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_MACHINERY"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_SIEGE_TACTICS", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_MILITARY_ENGINEERING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_INDUSTRIALIZATION", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["TECH_APPRENTICESHIP"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_SCIENTIFIC_THEORY", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["CIVIC_THE_ENLIGHTENMENT"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_BALLISTICS", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["TECH_SIEGE_TACTICS", "TECH_MILITARY_ENGINEERING"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_MILITARY_SCIENCE", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["TECH_STIRRUPS"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_STEAM_POWER", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["TECH_MASS_PRODUCTION"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_SANITATION", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["CIVIC_URBANIZATION"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_ECONOMICS", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["TECH_CURRENCY", "TECH_BANKING"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_RIFLING", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["TECH_MINING", "TECH_MILITARY_ENGINEERING"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_FLIGHT", - "EraType": "ERA_MODERN", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_REPLACEABLE_PARTS", - "EraType": "ERA_MODERN", - "Prereq": ["TECH_MILITARY_SCIENCE"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_STEEL", - "EraType": "ERA_MODERN", - "Prereq": ["TECH_MINING", "TECH_STEAM_POWER", "TECH_INDUSTRIALIZATION"], - "PrereqRequiredCount": 3, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_ELECTRICITY", - "EraType": "ERA_MODERN", - "Prereq": ["CIVIC_MERCANTILISM", "TECH_CELESTIAL_NAVIGATION"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_RADIO", - "EraType": "ERA_MODERN", - "Prereq": ["CIVIC_CONSERVATION"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_CHEMISTRY", - "EraType": "ERA_MODERN", - "Prereq": ["CIVIC_CIVIL_SERVICE"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_COMBUSTION", - "EraType": "ERA_MODERN", - "Prereq": ["CIVIC_NATURAL_HISTORY", "CIVIC_HUMANISM"], - "PrereqRequiredCount": 2, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_ADVANCED_FLIGHT", - "EraType": "ERA_ATOMIC", - "Prereq": ["TECH_FLIGHT"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_ROCKETRY", - "EraType": "ERA_ATOMIC", - "Prereq": ["CIVIC_DIPLOMATIC_SERVICE"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_ADVANCED_BALLISTICS", - "EraType": "ERA_ATOMIC", - "Prereq": [ - "TECH_ELECTRICITY", - "TECH_REFINING", - "TECH_APPRENTICESHIP", - "TECH_INDUSTRIALIZATION" - ], - "PrereqRequiredCount": 4, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_COMBINED_ARMS", - "EraType": "ERA_ATOMIC", - "Prereq": ["CIVIC_MOBILIZATION", "CIVIC_NATIONALISM"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_PLASTICS", - "EraType": "ERA_ATOMIC", - "Prereq": ["TECH_REFINING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_COMPUTERS", - "EraType": "ERA_ATOMIC", - "Prereq": [ - "CIVIC_SUFFRAGE", - "CIVIC_TOTALITARIANISM", - "CIVIC_CLASS_STRUGGLE", - "CIVIC_DIGITAL_DEMOCRACY", - "CIVIC_CORPORATE_LIBERTARIANISM", - "CIVIC_SYNTHETIC_TECHNOCRACY" - ], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_NUCLEAR_FISSION", - "EraType": "ERA_ATOMIC", - "Prereq": ["CIVIC_DIPLOMATIC_SERVICE"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_SYNTHETIC_MATERIALS", - "EraType": "ERA_ATOMIC", - "Prereq": ["TECH_FLIGHT"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_TELECOMMUNICATIONS", - "EraType": "ERA_INFORMATION", - "Prereq": ["CIVIC_DIPLOMATIC_SERVICE"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_SATELLITES", - "EraType": "ERA_INFORMATION", - "Prereq": ["CIVIC_DRAMA_POETRY", "CIVIC_HUMANISM", "TECH_RADIO"], - "PrereqRequiredCount": 3, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_GUIDANCE_SYSTEMS", - "EraType": "ERA_INFORMATION", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_LASERS", - "EraType": "ERA_INFORMATION", - "Prereq": ["TECH_COMPUTERS"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_COMPOSITES", - "EraType": "ERA_INFORMATION", - "Prereq": ["TECH_COMBUSTION"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_STEALTH_TECHNOLOGY", - "EraType": "ERA_INFORMATION", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_ROBOTICS", - "EraType": "ERA_INFORMATION", - "Prereq": ["CIVIC_GLOBALIZATION"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_NANOTECHNOLOGY", - "EraType": "ERA_INFORMATION", - "Prereq": ["TECH_MINING", "TECH_RADIO"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_NUCLEAR_FUSION", - "EraType": "ERA_INFORMATION", - "Prereq": [ - "TECH_APPRENTICESHIP", - "TECH_INDUSTRIALIZATION", - "TECH_ELECTRICITY", - "TECH_NUCLEAR_FISSION" - ], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_BUTTRESS", - "EraType": "ERA_MEDIEVAL", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_REFINING", - "EraType": "ERA_MODERN", - "Prereq": ["TECH_INDUSTRIALIZATION", "TECH_MINING", "TECH_APPRENTICESHIP"], - "PrereqRequiredCount": 3, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_TECH_SEASTEADS", - "EraType": "ERA_FUTURE", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_ADVANCED_AI", - "EraType": "ERA_FUTURE", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_ADVANCED_POWER_CELLS", - "EraType": "ERA_FUTURE", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_CYBERNETICS", - "EraType": "ERA_FUTURE", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_SMART_MATERIALS", - "EraType": "ERA_FUTURE", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_PREDICTIVE_SYSTEMS", - "EraType": "ERA_FUTURE", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_TECH_OFFWORLD_MISSION", - "EraType": "ERA_FUTURE", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_CRAFTSMANSHIP", - "EraType": "ERA_ANCIENT", - "Prereq": [ - "TECH_IRRIGATION", - "TECH_MINING", - "TECH_CONSTRUCTION", - "TECH_ANIMAL_HUSBANDRY", - "TECH_SAILING" - ], - "PrereqRequiredCount": 3, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_FOREIGN_TRADE", - "EraType": "ERA_ANCIENT", - "Prereq": ["TECH_CARTOGRAPHY"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_MILITARY_TRADITION", - "EraType": "ERA_ANCIENT", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_STATE_WORKFORCE", - "EraType": "ERA_ANCIENT", - "Prereq": [ - "TECH_CURRENCY", - "TECH_BRONZE_WORKING", - "TECH_CELESTIAL_NAVIGATION", - "TECH_WRITING", - "TECH_APPRENTICESHIP", - "TECH_FLIGHT", - "CIVIC_GAMES_RECREATION", - "CIVIC_DRAMA_POETRY" - ], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_EARLY_EMPIRE", - "EraType": "ERA_ANCIENT", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_MYSTICISM", - "EraType": "ERA_ANCIENT", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_GAMES_RECREATION", - "EraType": "ERA_CLASSICAL", - "Prereq": ["TECH_CONSTRUCTION"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_POLITICAL_PHILOSOPHY", - "EraType": "ERA_CLASSICAL", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_DRAMA_POETRY", - "EraType": "ERA_CLASSICAL", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_MILITARY_TRAINING", - "EraType": "ERA_CLASSICAL", - "Prereq": ["TECH_BRONZE_WORKING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_DEFENSIVE_TACTICS", - "EraType": "ERA_CLASSICAL", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_RECORDED_HISTORY", - "EraType": "ERA_CLASSICAL", - "Prereq": ["TECH_WRITING"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_THEOLOGY", - "EraType": "ERA_CLASSICAL", - "Prereq": ["TECH_ASTROLOGY"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_NAVAL_TRADITION", - "EraType": "ERA_MEDIEVAL", - "Prereq": ["TECH_SHIPBUILDING"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_FEUDALISM", - "EraType": "ERA_MEDIEVAL", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_CIVIL_SERVICE", - "EraType": "ERA_MEDIEVAL", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_MERCENARIES", - "EraType": "ERA_MEDIEVAL", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_MEDIEVAL_FAIRES", - "EraType": "ERA_MEDIEVAL", - "Prereq": ["CIVIC_FOREIGN_TRADE", "TECH_CURRENCY"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_GUILDS", - "EraType": "ERA_MEDIEVAL", - "Prereq": ["TECH_CURRENCY"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_DIVINE_RIGHT", - "EraType": "ERA_MEDIEVAL", - "Prereq": ["CIVIC_THEOLOGY", "TECH_ASTROLOGY"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_EXPLORATION", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_CARTOGRAPHY", "TECH_CELESTIAL_NAVIGATION"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_HUMANISM", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["CIVIC_DRAMA_POETRY"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_DIPLOMATIC_SERVICE", - "EraType": "ERA_RENAISSANCE", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_REFORMED_CHURCH", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_ASTROLOGY"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_MERCANTILISM", - "EraType": "ERA_RENAISSANCE", - "Prereq": ["TECH_CURRENCY"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_THE_ENLIGHTENMENT", - "EraType": "ERA_RENAISSANCE", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_COLONIALISM", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["TECH_ASTRONOMY"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_CIVIL_ENGINEERING", - "EraType": "ERA_INDUSTRIAL", - "Prereq": [ - "TECH_CURRENCY", - "TECH_BRONZE_WORKING", - "TECH_CELESTIAL_NAVIGATION", - "TECH_WRITING", - "TECH_APPRENTICESHIP", - "TECH_FLIGHT", - "CIVIC_GAMES_RECREATION", - "CIVIC_DRAMA_POETRY" - ], - "PrereqRequiredCount": 8, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_NATIONALISM", - "EraType": "ERA_INDUSTRIAL", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_OPERA_BALLET", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["CIVIC_HUMANISM", "CIVIC_DRAMA_POETRY"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_NATURAL_HISTORY", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["CIVIC_HUMANISM", "CIVIC_DRAMA_POETRY"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_SCORCHED_EARTH", - "EraType": "ERA_INDUSTRIAL", - "Prereq": ["TECH_BALLISTICS"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_URBANIZATION", - "EraType": "ERA_INDUSTRIAL", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_CONSERVATION", - "EraType": "ERA_MODERN", - "Prereq": ["CIVIC_URBANIZATION"], - "PrereqRequiredCount": 1, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_CAPITALISM", - "EraType": "ERA_MODERN", - "Prereq": ["TECH_CURRENCY", "TECH_BANKING", "TECH_ECONOMICS"], - "PrereqRequiredCount": 3, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_NUCLEAR_PROGRAM", - "EraType": "ERA_MODERN", - "Prereq": ["TECH_WRITING", "TECH_EDUCATION", "TECH_CHEMISTRY"], - "PrereqRequiredCount": 3, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_MASS_MEDIA", - "EraType": "ERA_MODERN", - "Prereq": ["TECH_RADIO"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_MOBILIZATION", - "EraType": "ERA_MODERN", - "Prereq": ["CIVIC_NATIONALISM"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_SUFFRAGE", - "EraType": "ERA_MODERN", - "Prereq": ["TECH_SANITATION"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_TOTALITARIANISM", - "EraType": "ERA_MODERN", - "Prereq": [ - "TECH_BRONZE_WORKING", - "TECH_MILITARY_ENGINEERING", - "TECH_MILITARY_SCIENCE" - ], - "PrereqRequiredCount": 3, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_CLASS_STRUGGLE", - "EraType": "ERA_MODERN", - "Prereq": ["TECH_APPRENTICESHIP", "TECH_INDUSTRIALIZATION"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_COLD_WAR", - "EraType": "ERA_ATOMIC", - "Prereq": ["TECH_NUCLEAR_FISSION"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_PROFESSIONAL_SPORTS", - "EraType": "ERA_ATOMIC", - "Prereq": ["CIVIC_GAMES_RECREATION"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_CULTURAL_HERITAGE", - "EraType": "ERA_ATOMIC", - "Prereq": [], - "PrereqRequiredCount": 0, - "Classification": "EXCLUDED" - }, - { - "Type": "BOOST_CIVIC_RAPID_DEPLOYMENT", - "EraType": "ERA_ATOMIC", - "Prereq": ["TECH_FLIGHT", "TECH_CARTOGRAPHY", "TECH_SHIPBUILDING"], - "PrereqRequiredCount": 3, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_SPACE_RACE", - "EraType": "ERA_ATOMIC", - "Prereq": ["TECH_ROCKETRY"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_GLOBALIZATION", - "EraType": "ERA_INFORMATION", - "Prereq": ["TECH_FLIGHT", "TECH_ADVANCED_FLIGHT"], - "PrereqRequiredCount": 2, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_SOCIAL_MEDIA", - "EraType": "ERA_INFORMATION", - "Prereq": ["TECH_TELECOMMUNICATIONS"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - }, - { - "Type": "BOOST_CIVIC_ENVIRONMENTALISM", - "EraType": "ERA_INFORMATION", - "Prereq": ["TECH_SATELLITES"], - "PrereqRequiredCount": 1, - "Classification": "DEFAULT" - } -] diff --git a/worlds/civ_6/data/boosts.py b/worlds/civ_6/data/boosts.py new file mode 100644 index 000000000000..dbff9c3899fa --- /dev/null +++ b/worlds/civ_6/data/boosts.py @@ -0,0 +1,927 @@ +from dataclasses import dataclass +from typing import List + + +@dataclass +class CivVIBoostData: + Type: str + EraType: str + Prereq: List[str] + PrereqRequiredCount: int + Classification: str + + +boosts: List[CivVIBoostData] = [ + CivVIBoostData("BOOST_TECH_SAILING", "ERA_ANCIENT", [], 0, "DEFAULT"), + CivVIBoostData( + "BOOST_TECH_ASTROLOGY", + "ERA_ANCIENT", + [], + 0, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_IRRIGATION", + "ERA_ANCIENT", + [], + 0, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_ARCHERY", + "ERA_ANCIENT", + [], + 0, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_WRITING", + "ERA_ANCIENT", + [], + 0, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_MASONRY", + "ERA_ANCIENT", + ["TECH_MINING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_BRONZE_WORKING", + "ERA_ANCIENT", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_THE_WHEEL", + "ERA_ANCIENT", + ["TECH_MINING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_CELESTIAL_NAVIGATION", + "ERA_CLASSICAL", + ["TECH_SAILING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_CURRENCY", + "ERA_CLASSICAL", + ["CIVIC_FOREIGN_TRADE"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_HORSEBACK_RIDING", + "ERA_CLASSICAL", + ["TECH_ANIMAL_HUSBANDRY"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_IRON_WORKING", + "ERA_CLASSICAL", + ["TECH_MINING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_SHIPBUILDING", + "ERA_CLASSICAL", + ["TECH_SAILING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_MATHEMATICS", + "ERA_CLASSICAL", + [ + "TECH_CURRENCY", + "TECH_BRONZE_WORKING", + "TECH_CELESTIAL_NAVIGATION", + "TECH_WRITING", + "TECH_APPRENTICESHIP", + "TECH_FLIGHT", + "CIVIC_GAMES_RECREATION", + "CIVIC_DRAMA_POETRY", + ], + 3, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_CONSTRUCTION", + "ERA_CLASSICAL", + ["TECH_THE_WHEEL"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_ENGINEERING", + "ERA_CLASSICAL", + ["TECH_MASONRY"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_MILITARY_TACTICS", + "ERA_MEDIEVAL", + ["TECH_BRONZE_WORKING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_APPRENTICESHIP", + "ERA_MEDIEVAL", + ["TECH_MINING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_MACHINERY", + "ERA_MEDIEVAL", + ["TECH_ARCHERY"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_EDUCATION", + "ERA_MEDIEVAL", + ["TECH_WRITING"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_STIRRUPS", + "ERA_MEDIEVAL", + ["CIVIC_FEUDALISM"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_MILITARY_ENGINEERING", + "ERA_MEDIEVAL", + ["TECH_ENGINEERING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_CASTLES", + "ERA_MEDIEVAL", + [ + "CIVIC_DIVINE_RIGHT", + "CIVIC_EXPLORATION", + "CIVIC_REFORMED_CHURCH", + "CIVIC_SUFFRAGE", + "CIVIC_TOTALITARIANISM", + "CIVIC_CLASS_STRUGGLE", + "CIVIC_DIGITAL_DEMOCRACY", + "CIVIC_CORPORATE_LIBERTARIANISM", + "CIVIC_SYNTHETIC_TECHNOCRACY", + ], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_CARTOGRAPHY", + "ERA_RENAISSANCE", + ["TECH_CELESTIAL_NAVIGATION"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_MASS_PRODUCTION", + "ERA_RENAISSANCE", + ["TECH_CONSTRUCTION"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_BANKING", + "ERA_RENAISSANCE", + ["CIVIC_GUILDS"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_GUNPOWDER", + "ERA_RENAISSANCE", + ["TECH_BRONZE_WORKING", "TECH_MILITARY_ENGINEERING"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_PRINTING", + "ERA_RENAISSANCE", + ["TECH_WRITING", "TECH_EDUCATION"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_SQUARE_RIGGING", + "ERA_RENAISSANCE", + ["TECH_GUNPOWDER"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_ASTRONOMY", + "ERA_RENAISSANCE", + ["TECH_EDUCATION"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_METAL_CASTING", + "ERA_RENAISSANCE", + ["TECH_MACHINERY"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_SIEGE_TACTICS", + "ERA_RENAISSANCE", + ["TECH_MILITARY_ENGINEERING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_INDUSTRIALIZATION", + "ERA_INDUSTRIAL", + ["TECH_APPRENTICESHIP"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_SCIENTIFIC_THEORY", + "ERA_INDUSTRIAL", + ["CIVIC_THE_ENLIGHTENMENT"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_BALLISTICS", + "ERA_INDUSTRIAL", + ["TECH_SIEGE_TACTICS", "TECH_MILITARY_ENGINEERING"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_MILITARY_SCIENCE", + "ERA_INDUSTRIAL", + ["TECH_STIRRUPS"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_STEAM_POWER", + "ERA_INDUSTRIAL", + ["TECH_MASS_PRODUCTION"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_SANITATION", + "ERA_INDUSTRIAL", + ["CIVIC_URBANIZATION"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_ECONOMICS", + "ERA_INDUSTRIAL", + ["TECH_CURRENCY", "TECH_BANKING"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_RIFLING", + "ERA_INDUSTRIAL", + ["TECH_MINING", "TECH_MILITARY_ENGINEERING"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_FLIGHT", + "ERA_MODERN", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_REPLACEABLE_PARTS", + "ERA_MODERN", + ["TECH_MILITARY_SCIENCE"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_STEEL", + "ERA_MODERN", + ["TECH_MINING", "TECH_STEAM_POWER", "TECH_INDUSTRIALIZATION"], + 3, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_ELECTRICITY", + "ERA_MODERN", + ["CIVIC_MERCANTILISM", "TECH_CELESTIAL_NAVIGATION"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_RADIO", + "ERA_MODERN", + ["CIVIC_CONSERVATION"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_CHEMISTRY", + "ERA_MODERN", + ["CIVIC_CIVIL_SERVICE"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_COMBUSTION", + "ERA_MODERN", + ["CIVIC_NATURAL_HISTORY", "CIVIC_HUMANISM"], + 2, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_ADVANCED_FLIGHT", + "ERA_ATOMIC", + ["TECH_FLIGHT"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_ROCKETRY", + "ERA_ATOMIC", + ["CIVIC_DIPLOMATIC_SERVICE"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_ADVANCED_BALLISTICS", + "ERA_ATOMIC", + [ + "TECH_ELECTRICITY", + "TECH_REFINING", + "TECH_APPRENTICESHIP", + "TECH_INDUSTRIALIZATION", + ], + 4, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_COMBINED_ARMS", + "ERA_ATOMIC", + ["CIVIC_MOBILIZATION", "CIVIC_NATIONALISM"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_PLASTICS", + "ERA_ATOMIC", + ["TECH_REFINING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_COMPUTERS", + "ERA_ATOMIC", + [ + "CIVIC_SUFFRAGE", + "CIVIC_TOTALITARIANISM", + "CIVIC_CLASS_STRUGGLE", + "CIVIC_DIGITAL_DEMOCRACY", + "CIVIC_CORPORATE_LIBERTARIANISM", + "CIVIC_SYNTHETIC_TECHNOCRACY", + ], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_NUCLEAR_FISSION", + "ERA_ATOMIC", + ["CIVIC_DIPLOMATIC_SERVICE"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_SYNTHETIC_MATERIALS", + "ERA_ATOMIC", + ["TECH_FLIGHT"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_TELECOMMUNICATIONS", + "ERA_INFORMATION", + ["CIVIC_DIPLOMATIC_SERVICE"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_SATELLITES", + "ERA_INFORMATION", + ["CIVIC_DRAMA_POETRY", "CIVIC_HUMANISM", "TECH_RADIO"], + 3, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_GUIDANCE_SYSTEMS", + "ERA_INFORMATION", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_LASERS", + "ERA_INFORMATION", + ["TECH_COMPUTERS"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_COMPOSITES", + "ERA_INFORMATION", + ["TECH_COMBUSTION"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_STEALTH_TECHNOLOGY", + "ERA_INFORMATION", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_ROBOTICS", + "ERA_INFORMATION", + ["CIVIC_GLOBALIZATION"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_NANOTECHNOLOGY", + "ERA_INFORMATION", + ["TECH_MINING", "TECH_RADIO"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_NUCLEAR_FUSION", + "ERA_INFORMATION", + [ + "TECH_APPRENTICESHIP", + "TECH_INDUSTRIALIZATION", + "TECH_ELECTRICITY", + "TECH_NUCLEAR_FISSION", + ], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_BUTTRESS", + "ERA_MEDIEVAL", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_REFINING", + "ERA_MODERN", + ["TECH_INDUSTRIALIZATION", "TECH_MINING", "TECH_APPRENTICESHIP"], + 3, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_TECH_SEASTEADS", + "ERA_FUTURE", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_ADVANCED_AI", + "ERA_FUTURE", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_ADVANCED_POWER_CELLS", + "ERA_FUTURE", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_CYBERNETICS", + "ERA_FUTURE", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_SMART_MATERIALS", + "ERA_FUTURE", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_PREDICTIVE_SYSTEMS", + "ERA_FUTURE", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_TECH_OFFWORLD_MISSION", + "ERA_FUTURE", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_CRAFTSMANSHIP", + "ERA_ANCIENT", + [ + "TECH_IRRIGATION", + "TECH_MINING", + "TECH_CONSTRUCTION", + "TECH_ANIMAL_HUSBANDRY", + "TECH_SAILING", + ], + 3, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_FOREIGN_TRADE", + "ERA_ANCIENT", + ["TECH_CARTOGRAPHY"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_MILITARY_TRADITION", + "ERA_ANCIENT", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_STATE_WORKFORCE", + "ERA_ANCIENT", + [ + "TECH_CURRENCY", + "TECH_BRONZE_WORKING", + "TECH_CELESTIAL_NAVIGATION", + "TECH_WRITING", + "TECH_APPRENTICESHIP", + "TECH_FLIGHT", + "CIVIC_GAMES_RECREATION", + "CIVIC_DRAMA_POETRY", + ], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_EARLY_EMPIRE", + "ERA_ANCIENT", + [], + 0, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_MYSTICISM", + "ERA_ANCIENT", + [], + 0, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_GAMES_RECREATION", + "ERA_CLASSICAL", + ["TECH_CONSTRUCTION"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_POLITICAL_PHILOSOPHY", + "ERA_CLASSICAL", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_DRAMA_POETRY", + "ERA_CLASSICAL", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_MILITARY_TRAINING", + "ERA_CLASSICAL", + ["TECH_BRONZE_WORKING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_DEFENSIVE_TACTICS", + "ERA_CLASSICAL", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_RECORDED_HISTORY", + "ERA_CLASSICAL", + ["TECH_WRITING"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_THEOLOGY", + "ERA_CLASSICAL", + ["TECH_ASTROLOGY"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_NAVAL_TRADITION", + "ERA_MEDIEVAL", + ["TECH_SHIPBUILDING"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_FEUDALISM", + "ERA_MEDIEVAL", + [], + 0, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_CIVIL_SERVICE", + "ERA_MEDIEVAL", + [], + 0, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_MERCENARIES", + "ERA_MEDIEVAL", + [], + 0, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_MEDIEVAL_FAIRES", + "ERA_MEDIEVAL", + ["CIVIC_FOREIGN_TRADE", "TECH_CURRENCY"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_GUILDS", + "ERA_MEDIEVAL", + ["TECH_CURRENCY"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_DIVINE_RIGHT", + "ERA_MEDIEVAL", + ["CIVIC_THEOLOGY", "TECH_ASTROLOGY"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_EXPLORATION", + "ERA_RENAISSANCE", + ["TECH_CARTOGRAPHY", "TECH_CELESTIAL_NAVIGATION"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_HUMANISM", + "ERA_RENAISSANCE", + ["CIVIC_DRAMA_POETRY"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_DIPLOMATIC_SERVICE", + "ERA_RENAISSANCE", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_REFORMED_CHURCH", + "ERA_RENAISSANCE", + ["TECH_ASTROLOGY"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_MERCANTILISM", + "ERA_RENAISSANCE", + ["TECH_CURRENCY"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_THE_ENLIGHTENMENT", + "ERA_RENAISSANCE", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_COLONIALISM", + "ERA_INDUSTRIAL", + ["TECH_ASTRONOMY"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_CIVIL_ENGINEERING", + "ERA_INDUSTRIAL", + [ + "TECH_CURRENCY", + "TECH_BRONZE_WORKING", + "TECH_CELESTIAL_NAVIGATION", + "TECH_WRITING", + "TECH_APPRENTICESHIP", + "TECH_FLIGHT", + "CIVIC_GAMES_RECREATION", + "CIVIC_DRAMA_POETRY", + ], + 8, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_NATIONALISM", + "ERA_INDUSTRIAL", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_OPERA_BALLET", + "ERA_INDUSTRIAL", + ["CIVIC_HUMANISM", "CIVIC_DRAMA_POETRY"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_NATURAL_HISTORY", + "ERA_INDUSTRIAL", + ["CIVIC_HUMANISM", "CIVIC_DRAMA_POETRY"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_SCORCHED_EARTH", + "ERA_INDUSTRIAL", + ["TECH_BALLISTICS"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_URBANIZATION", + "ERA_INDUSTRIAL", + [], + 0, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_CONSERVATION", + "ERA_MODERN", + ["CIVIC_URBANIZATION"], + 1, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_CAPITALISM", + "ERA_MODERN", + ["TECH_CURRENCY", "TECH_BANKING", "TECH_ECONOMICS"], + 3, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_NUCLEAR_PROGRAM", + "ERA_MODERN", + ["TECH_WRITING", "TECH_EDUCATION", "TECH_CHEMISTRY"], + 3, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_MASS_MEDIA", + "ERA_MODERN", + ["TECH_RADIO"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_MOBILIZATION", + "ERA_MODERN", + ["CIVIC_NATIONALISM"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_SUFFRAGE", + "ERA_MODERN", + ["TECH_SANITATION"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_TOTALITARIANISM", + "ERA_MODERN", + [ + "TECH_BRONZE_WORKING", + "TECH_MILITARY_ENGINEERING", + "TECH_MILITARY_SCIENCE", + ], + 3, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_CLASS_STRUGGLE", + "ERA_MODERN", + ["TECH_APPRENTICESHIP", "TECH_INDUSTRIALIZATION"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_COLD_WAR", + "ERA_ATOMIC", + ["TECH_NUCLEAR_FISSION"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_PROFESSIONAL_SPORTS", + "ERA_ATOMIC", + ["CIVIC_GAMES_RECREATION"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_CULTURAL_HERITAGE", + "ERA_ATOMIC", + [], + 0, + "EXCLUDED", + ), + CivVIBoostData( + "BOOST_CIVIC_RAPID_DEPLOYMENT", + "ERA_ATOMIC", + ["TECH_FLIGHT", "TECH_CARTOGRAPHY", "TECH_SHIPBUILDING"], + 3, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_SPACE_RACE", + "ERA_ATOMIC", + ["TECH_ROCKETRY"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_GLOBALIZATION", + "ERA_INFORMATION", + ["TECH_FLIGHT", "TECH_ADVANCED_FLIGHT"], + 2, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_SOCIAL_MEDIA", + "ERA_INFORMATION", + ["TECH_TELECOMMUNICATIONS"], + 1, + "DEFAULT", + ), + CivVIBoostData( + "BOOST_CIVIC_ENVIRONMENTALISM", + "ERA_INFORMATION", + ["TECH_SATELLITES"], + 1, + "DEFAULT", + ), +] From 6027b776a26634b1c338e35f5ce6913e82210f1c Mon Sep 17 00:00:00 2001 From: hesto2 Date: Sun, 1 Dec 2024 11:11:56 -0700 Subject: [PATCH 62/69] Update era required items --- worlds/civ_6/Data.py | 4 +- worlds/civ_6/data/era_required_items.json | 72 ---------------------- worlds/civ_6/data/era_required_items.py | 75 +++++++++++++++++++++++ 3 files changed, 78 insertions(+), 73 deletions(-) delete mode 100644 worlds/civ_6/data/era_required_items.json create mode 100644 worlds/civ_6/data/era_required_items.py diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index ef3ef78b546a..29ca2e26f69a 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -28,7 +28,9 @@ def get_boosts_data() -> List[CivVIBoostData]: def get_era_required_items_data() -> Dict[str, List[str]]: - return _get_data("era_required_items") + from .data.era_required_items import era_required_items + + return era_required_items class NewItemData(TypedDict): diff --git a/worlds/civ_6/data/era_required_items.json b/worlds/civ_6/data/era_required_items.json deleted file mode 100644 index 606028e85b7d..000000000000 --- a/worlds/civ_6/data/era_required_items.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "ERA_ANCIENT": [ - "TECH_MINING", - "TECH_BRONZE_WORKING", - "TECH_ASTROLOGY", - "TECH_WRITING", - "TECH_IRRIGATION", - "TECH_SAILING", - "TECH_ANIMAL_HUSBANDRY", - "CIVIC_STATE_WORKFORCE", - "CIVIC_FOREIGN_TRADE" - ], - "ERA_CLASSICAL": [ - "TECH_CELESTIAL_NAVIGATION", - "TECH_CURRENCY", - "TECH_MATHEMATICS", - "TECH_SHIPBUILDING", - "CIVIC_GAMES_RECREATION", - "CIVIC_POLITICAL_PHILOSOPHY", - "CIVIC_DRAMA_POETRY", - "CIVIC_THEOLOGY" - ], - "ERA_MEDIEVAL": [ - "TECH_APPRENTICESHIP", - "TECH_EDUCATION", - "TECH_MILITARY_ENGINEERING", - "CIVIC_DIVINE_RIGHT" - ], - "ERA_RENAISSANCE": [ - "TECH_MASS_PRODUCTION", - "TECH_BANKING", - "CIVIC_EXPLORATION", - "CIVIC_HUMANISM", - "CIVIC_REFORMED_CHURCH", - "CIVIC_DIPLOMATIC_SERVICE", - "TECH_CARTOGRAPHY" - ], - "ERA_INDUSTRIAL": [ - "TECH_INDUSTRIALIZATION", - "TECH_MILITARY_SCIENCE", - "TECH_ECONOMICS", - "CIVIC_NATIONALISM", - "CIVIC_NATURAL_HISTORY" - ], - "ERA_MODERN": [ - "TECH_FLIGHT", - "TECH_REFINING", - "TECH_ELECTRICITY", - "TECH_RADIO", - "TECH_CHEMISTRY", - "CIVIC_SUFFRAGE", - "CIVIC_TOTALITARIANISM", - "CIVIC_CLASS_STRUGGLE" - ], - "ERA_ATOMIC": [ - "TECH_ADVANCED_FLIGHT", - "TECH_ROCKETRY", - "TECH_COMBINED_ARMS", - "TECH_PLASTICS", - "TECH_NUCLEAR_FISSION", - "CIVIC_PROFESSIONAL_SPORTS" - ], - "ERA_INFORMATION": [ - "TECH_SATELLITES", - "TECH_NANOTECHNOLOGY", - "TECH_SMART_MATERIALS", - "CIVIC_CORPORATE_LIBERTARIANISM", - "CIVIC_DIGITAL_DEMOCRACY", - "CIVIC_SYNTHETIC_TECHNOCRACY" - ], - "ERA_FUTURE": [] -} diff --git a/worlds/civ_6/data/era_required_items.py b/worlds/civ_6/data/era_required_items.py new file mode 100644 index 000000000000..fd3bca6954dc --- /dev/null +++ b/worlds/civ_6/data/era_required_items.py @@ -0,0 +1,75 @@ +from typing import Dict, List + + +era_required_items: Dict[str, List[str]] = { + "ERA_ANCIENT": [ + "TECH_MINING", + "TECH_BRONZE_WORKING", + "TECH_ASTROLOGY", + "TECH_WRITING", + "TECH_IRRIGATION", + "TECH_SAILING", + "TECH_ANIMAL_HUSBANDRY", + "CIVIC_STATE_WORKFORCE", + "CIVIC_FOREIGN_TRADE", + ], + "ERA_CLASSICAL": [ + "TECH_CELESTIAL_NAVIGATION", + "TECH_CURRENCY", + "TECH_MATHEMATICS", + "TECH_SHIPBUILDING", + "CIVIC_GAMES_RECREATION", + "CIVIC_POLITICAL_PHILOSOPHY", + "CIVIC_DRAMA_POETRY", + "CIVIC_THEOLOGY", + ], + "ERA_MEDIEVAL": [ + "TECH_APPRENTICESHIP", + "TECH_EDUCATION", + "TECH_MILITARY_ENGINEERING", + "CIVIC_DIVINE_RIGHT", + ], + "ERA_RENAISSANCE": [ + "TECH_MASS_PRODUCTION", + "TECH_BANKING", + "CIVIC_EXPLORATION", + "CIVIC_HUMANISM", + "CIVIC_REFORMED_CHURCH", + "CIVIC_DIPLOMATIC_SERVICE", + "TECH_CARTOGRAPHY", + ], + "ERA_INDUSTRIAL": [ + "TECH_INDUSTRIALIZATION", + "TECH_MILITARY_SCIENCE", + "TECH_ECONOMICS", + "CIVIC_NATIONALISM", + "CIVIC_NATURAL_HISTORY", + ], + "ERA_MODERN": [ + "TECH_FLIGHT", + "TECH_REFINING", + "TECH_ELECTRICITY", + "TECH_RADIO", + "TECH_CHEMISTRY", + "CIVIC_SUFFRAGE", + "CIVIC_TOTALITARIANISM", + "CIVIC_CLASS_STRUGGLE", + ], + "ERA_ATOMIC": [ + "TECH_ADVANCED_FLIGHT", + "TECH_ROCKETRY", + "TECH_COMBINED_ARMS", + "TECH_PLASTICS", + "TECH_NUCLEAR_FISSION", + "CIVIC_PROFESSIONAL_SPORTS", + ], + "ERA_INFORMATION": [ + "TECH_SATELLITES", + "TECH_NANOTECHNOLOGY", + "TECH_SMART_MATERIALS", + "CIVIC_CORPORATE_LIBERTARIANISM", + "CIVIC_DIGITAL_DEMOCRACY", + "CIVIC_SYNTHETIC_TECHNOCRACY", + ], + "ERA_FUTURE": [], +} From 48a2a2a75cb47e7d67bf5535643494ca7231907d Mon Sep 17 00:00:00 2001 From: hesto2 Date: Sun, 1 Dec 2024 11:29:25 -0700 Subject: [PATCH 63/69] Update existing techs --- worlds/civ_6/Data.py | 16 +- worlds/civ_6/ItemData.py | 12 + worlds/civ_6/data/existing_civics.json | 429 ------------------------ worlds/civ_6/data/existing_civics.py | 435 +++++++++++++++++++++++++ 4 files changed, 451 insertions(+), 441 deletions(-) create mode 100644 worlds/civ_6/ItemData.py delete mode 100644 worlds/civ_6/data/existing_civics.json create mode 100644 worlds/civ_6/data/existing_civics.py diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index 29ca2e26f69a..f634928db696 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -4,6 +4,7 @@ import pkgutil from typing import Any, Dict, List, TypedDict +from worlds.civ_6.ItemData import ExistingItemData, NewItemData from worlds.civ_6.data.boosts import CivVIBoostData @@ -33,19 +34,10 @@ def get_era_required_items_data() -> Dict[str, List[str]]: return era_required_items -class NewItemData(TypedDict): - Type: str - Cost: int - UITreeRow: int - EraType: str - - -class ExistingItemData(NewItemData): - Name: str - - def get_existing_civics_data() -> List[ExistingItemData]: - return _get_data("existing_civics") + from .data.existing_civics import existing_civics + + return existing_civics def get_existing_techs_data() -> List[ExistingItemData]: diff --git a/worlds/civ_6/ItemData.py b/worlds/civ_6/ItemData.py new file mode 100644 index 000000000000..709d3255ccc4 --- /dev/null +++ b/worlds/civ_6/ItemData.py @@ -0,0 +1,12 @@ +from typing import TypedDict + + +class NewItemData(TypedDict): + Type: str + Cost: int + UITreeRow: int + EraType: str + + +class ExistingItemData(NewItemData): + Name: str diff --git a/worlds/civ_6/data/existing_civics.json b/worlds/civ_6/data/existing_civics.json deleted file mode 100644 index d63cc24e86de..000000000000 --- a/worlds/civ_6/data/existing_civics.json +++ /dev/null @@ -1,429 +0,0 @@ -[ - { - "Type": "CIVIC_CODE_OF_LAWS", - "Name": "Code of Laws", - "Cost": 20, - "EraType": "ERA_ANCIENT", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_CRAFTSMANSHIP", - "Name": "Craftsmanship", - "Cost": 40, - "EraType": "ERA_ANCIENT", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_FOREIGN_TRADE", - "Name": "Foreign Trade", - "Cost": 40, - "EraType": "ERA_ANCIENT", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_MILITARY_TRADITION", - "Name": "Military Tradition", - "Cost": 50, - "EraType": "ERA_ANCIENT", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_STATE_WORKFORCE", - "Name": "State Workforce", - "Cost": 70, - "EraType": "ERA_ANCIENT", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_EARLY_EMPIRE", - "Name": "Early Empire", - "Cost": 70, - "EraType": "ERA_ANCIENT", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_MYSTICISM", - "Name": "Mysticism", - "Cost": 50, - "EraType": "ERA_ANCIENT", - "UITreeRow": 3 - }, - { - "Type": "CIVIC_GAMES_RECREATION", - "Name": "Games Recreation", - "Cost": 110, - "EraType": "ERA_CLASSICAL", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_POLITICAL_PHILOSOPHY", - "Name": "Political Philosophy", - "Cost": 110, - "EraType": "ERA_CLASSICAL", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_DRAMA_POETRY", - "Name": "Drama and Poetry", - "Cost": 110, - "EraType": "ERA_CLASSICAL", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_MILITARY_TRAINING", - "Name": "Military Training", - "Cost": 120, - "EraType": "ERA_CLASSICAL", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_DEFENSIVE_TACTICS", - "Name": "Defensive Tactics", - "Cost": 175, - "EraType": "ERA_CLASSICAL", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_RECORDED_HISTORY", - "Name": "Recorded History", - "Cost": 175, - "EraType": "ERA_CLASSICAL", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_THEOLOGY", - "Name": "Theology", - "Cost": 120, - "EraType": "ERA_CLASSICAL", - "UITreeRow": 3 - }, - { - "Type": "CIVIC_NAVAL_TRADITION", - "Name": "Naval Tradition", - "Cost": 220, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_FEUDALISM", - "Name": "Feudalism", - "Cost": 300, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_CIVIL_SERVICE", - "Name": "Civil Service", - "Cost": 300, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_MERCENARIES", - "Name": "Mercenaries", - "Cost": 340, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_MEDIEVAL_FAIRES", - "Name": "Medieval Faires", - "Cost": 420, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_GUILDS", - "Name": "Guilds", - "Cost": 420, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_DIVINE_RIGHT", - "Name": "Divine Right", - "Cost": 340, - "EraType": "ERA_MEDIEVAL", - "UITreeRow": 3 - }, - { - "Type": "CIVIC_EXPLORATION", - "Name": "Exploration", - "Cost": 440, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_HUMANISM", - "Name": "Humanism", - "Cost": 600, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_DIPLOMATIC_SERVICE", - "Name": "Diplomatic Service", - "Cost": 600, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_REFORMED_CHURCH", - "Name": "Reformed Church", - "Cost": 440, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": 3 - }, - { - "Type": "CIVIC_MERCANTILISM", - "Name": "Mercantilism", - "Cost": 720, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_THE_ENLIGHTENMENT", - "Name": "The Enlightenment", - "Cost": 720, - "EraType": "ERA_RENAISSANCE", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_COLONIALISM", - "Name": "Colonialism", - "Cost": 800, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_CIVIL_ENGINEERING", - "Name": "Civil Engineering", - "Cost": 1010, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_NATIONALISM", - "Name": "Nationalism", - "Cost": 1010, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_OPERA_BALLET", - "Name": "Opera and Ballet", - "Cost": 800, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_NATURAL_HISTORY", - "Name": "Natural History", - "Cost": 1050, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_SCORCHED_EARTH", - "Name": "Scorched Earth", - "Cost": 1210, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_URBANIZATION", - "Name": "Urbanization", - "Cost": 1210, - "EraType": "ERA_INDUSTRIAL", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_CONSERVATION", - "Name": "Conservation", - "Cost": 1540, - "EraType": "ERA_MODERN", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_CAPITALISM", - "Name": "Capitalism", - "Cost": 1580, - "EraType": "ERA_MODERN", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_NUCLEAR_PROGRAM", - "Name": "Nuclear Program", - "Cost": 1715, - "EraType": "ERA_MODERN", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_MASS_MEDIA", - "Name": "Mass Media", - "Cost": 1540, - "EraType": "ERA_MODERN", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_MOBILIZATION", - "Name": "Mobilization", - "Cost": 1540, - "EraType": "ERA_MODERN", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_IDEOLOGY", - "Name": "Ideology", - "Cost": 1640, - "EraType": "ERA_MODERN", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_SUFFRAGE", - "Name": "Suffrage", - "Cost": 1640, - "EraType": "ERA_MODERN", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_TOTALITARIANISM", - "Name": "Totalitarianism", - "Cost": 1640, - "EraType": "ERA_MODERN", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_CLASS_STRUGGLE", - "Name": "Class Struggle", - "Cost": 1640, - "EraType": "ERA_MODERN", - "UITreeRow": 3 - }, - { - "Type": "CIVIC_COLD_WAR", - "Name": "Cold War", - "Cost": 2185, - "EraType": "ERA_ATOMIC", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_PROFESSIONAL_SPORTS", - "Name": "Professional Sports", - "Cost": 2185, - "EraType": "ERA_ATOMIC", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_CULTURAL_HERITAGE", - "Name": "Cultural Heritage", - "Cost": 1955, - "EraType": "ERA_ATOMIC", - "UITreeRow": -3 - }, - { - "Type": "CIVIC_RAPID_DEPLOYMENT", - "Name": "Rapid Deployment", - "Cost": 2415, - "EraType": "ERA_ATOMIC", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_SPACE_RACE", - "Name": "Space Race", - "Cost": 2415, - "EraType": "ERA_ATOMIC", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_GLOBALIZATION", - "Name": "Globalization", - "Cost": 2880, - "EraType": "ERA_INFORMATION", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_SOCIAL_MEDIA", - "Name": "Social Media", - "Cost": 2880, - "EraType": "ERA_INFORMATION", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_FUTURE_CIVIC", - "Name": "Future Civic", - "Cost": 3500, - "EraType": "ERA_FUTURE", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_ENVIRONMENTALISM", - "Name": "Environmentalism", - "Cost": 2880, - "EraType": "ERA_INFORMATION", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_CORPORATE_LIBERTARIANISM", - "Name": "Corporate Libertarianism", - "Cost": 3000, - "EraType": "ERA_INFORMATION", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_DIGITAL_DEMOCRACY", - "Name": "Digital Democracy", - "Cost": 3000, - "EraType": "ERA_INFORMATION", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_SYNTHETIC_TECHNOCRACY", - "Name": "Synthetic Technocracy", - "Cost": 3000, - "EraType": "ERA_INFORMATION", - "UITreeRow": 2 - }, - { - "Type": "CIVIC_NEAR_FUTURE_GOVERNANCE", - "Name": "Near Future Governance", - "Cost": 3100, - "EraType": "ERA_INFORMATION", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_GLOBAL_WARMING_MITIGATION", - "Name": "Global Warming Mitigation", - "Cost": 3200, - "EraType": "ERA_FUTURE", - "UITreeRow": -2 - }, - { - "Type": "CIVIC_SMART_POWER_DOCTRINE", - "Name": "Smart Power Doctrine", - "Cost": 3200, - "EraType": "ERA_FUTURE", - "UITreeRow": -1 - }, - { - "Type": "CIVIC_INFORMATION_WARFARE", - "Name": "Information Warfare", - "Cost": 3200, - "EraType": "ERA_FUTURE", - "UITreeRow": 0 - }, - { - "Type": "CIVIC_EXODUS_IMPERATIVE", - "Name": "Exodus Imperative", - "Cost": 3200, - "EraType": "ERA_FUTURE", - "UITreeRow": 1 - }, - { - "Type": "CIVIC_CULTURAL_HEGEMONY", - "Name": "Cultural Hegemony", - "Cost": 3200, - "EraType": "ERA_FUTURE", - "UITreeRow": 2 - } -] diff --git a/worlds/civ_6/data/existing_civics.py b/worlds/civ_6/data/existing_civics.py new file mode 100644 index 000000000000..b7f2583301fb --- /dev/null +++ b/worlds/civ_6/data/existing_civics.py @@ -0,0 +1,435 @@ +from typing import TYPE_CHECKING, List + +if TYPE_CHECKING: + from worlds.civ_6.Data import ExistingItemData + + +existing_civics: List["ExistingItemData"] = [ + { + "Type": "CIVIC_CODE_OF_LAWS", + "Name": "Code of Laws", + "Cost": 20, + "EraType": "ERA_ANCIENT", + "UITreeRow": 0, + }, + { + "Type": "CIVIC_CRAFTSMANSHIP", + "Name": "Craftsmanship", + "Cost": 40, + "EraType": "ERA_ANCIENT", + "UITreeRow": -2, + }, + { + "Type": "CIVIC_FOREIGN_TRADE", + "Name": "Foreign Trade", + "Cost": 40, + "EraType": "ERA_ANCIENT", + "UITreeRow": 2, + }, + { + "Type": "CIVIC_MILITARY_TRADITION", + "Name": "Military Tradition", + "Cost": 50, + "EraType": "ERA_ANCIENT", + "UITreeRow": -3, + }, + { + "Type": "CIVIC_STATE_WORKFORCE", + "Name": "State Workforce", + "Cost": 70, + "EraType": "ERA_ANCIENT", + "UITreeRow": 0, + }, + { + "Type": "CIVIC_EARLY_EMPIRE", + "Name": "Early Empire", + "Cost": 70, + "EraType": "ERA_ANCIENT", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_MYSTICISM", + "Name": "Mysticism", + "Cost": 50, + "EraType": "ERA_ANCIENT", + "UITreeRow": 3, + }, + { + "Type": "CIVIC_GAMES_RECREATION", + "Name": "Games Recreation", + "Cost": 110, + "EraType": "ERA_CLASSICAL", + "UITreeRow": -2, + }, + { + "Type": "CIVIC_POLITICAL_PHILOSOPHY", + "Name": "Political Philosophy", + "Cost": 110, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 0, + }, + { + "Type": "CIVIC_DRAMA_POETRY", + "Name": "Drama and Poetry", + "Cost": 110, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 2, + }, + { + "Type": "CIVIC_MILITARY_TRAINING", + "Name": "Military Training", + "Cost": 120, + "EraType": "ERA_CLASSICAL", + "UITreeRow": -3, + }, + { + "Type": "CIVIC_DEFENSIVE_TACTICS", + "Name": "Defensive Tactics", + "Cost": 175, + "EraType": "ERA_CLASSICAL", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_RECORDED_HISTORY", + "Name": "Recorded History", + "Cost": 175, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_THEOLOGY", + "Name": "Theology", + "Cost": 120, + "EraType": "ERA_CLASSICAL", + "UITreeRow": 3, + }, + { + "Type": "CIVIC_NAVAL_TRADITION", + "Name": "Naval Tradition", + "Cost": 220, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -2, + }, + { + "Type": "CIVIC_FEUDALISM", + "Name": "Feudalism", + "Cost": 300, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_CIVIL_SERVICE", + "Name": "Civil Service", + "Cost": 300, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_MERCENARIES", + "Name": "Mercenaries", + "Cost": 340, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -3, + }, + { + "Type": "CIVIC_MEDIEVAL_FAIRES", + "Name": "Medieval Faires", + "Cost": 420, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_GUILDS", + "Name": "Guilds", + "Cost": 420, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_DIVINE_RIGHT", + "Name": "Divine Right", + "Cost": 340, + "EraType": "ERA_MEDIEVAL", + "UITreeRow": 3, + }, + { + "Type": "CIVIC_EXPLORATION", + "Name": "Exploration", + "Cost": 440, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": -3, + }, + { + "Type": "CIVIC_HUMANISM", + "Name": "Humanism", + "Cost": 600, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_DIPLOMATIC_SERVICE", + "Name": "Diplomatic Service", + "Cost": 600, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_REFORMED_CHURCH", + "Name": "Reformed Church", + "Cost": 440, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": 3, + }, + { + "Type": "CIVIC_MERCANTILISM", + "Name": "Mercantilism", + "Cost": 720, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_THE_ENLIGHTENMENT", + "Name": "The Enlightenment", + "Cost": 720, + "EraType": "ERA_RENAISSANCE", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_COLONIALISM", + "Name": "Colonialism", + "Cost": 800, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -3, + }, + { + "Type": "CIVIC_CIVIL_ENGINEERING", + "Name": "Civil Engineering", + "Cost": 1010, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_NATIONALISM", + "Name": "Nationalism", + "Cost": 1010, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": 0, + }, + { + "Type": "CIVIC_OPERA_BALLET", + "Name": "Opera and Ballet", + "Cost": 800, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": 2, + }, + { + "Type": "CIVIC_NATURAL_HISTORY", + "Name": "Natural History", + "Cost": 1050, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -3, + }, + { + "Type": "CIVIC_SCORCHED_EARTH", + "Name": "Scorched Earth", + "Cost": 1210, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": 2, + }, + { + "Type": "CIVIC_URBANIZATION", + "Name": "Urbanization", + "Cost": 1210, + "EraType": "ERA_INDUSTRIAL", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_CONSERVATION", + "Name": "Conservation", + "Cost": 1540, + "EraType": "ERA_MODERN", + "UITreeRow": -3, + }, + { + "Type": "CIVIC_CAPITALISM", + "Name": "Capitalism", + "Cost": 1580, + "EraType": "ERA_MODERN", + "UITreeRow": -2, + }, + { + "Type": "CIVIC_NUCLEAR_PROGRAM", + "Name": "Nuclear Program", + "Cost": 1715, + "EraType": "ERA_MODERN", + "UITreeRow": -2, + }, + { + "Type": "CIVIC_MASS_MEDIA", + "Name": "Mass Media", + "Cost": 1540, + "EraType": "ERA_MODERN", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_MOBILIZATION", + "Name": "Mobilization", + "Cost": 1540, + "EraType": "ERA_MODERN", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_IDEOLOGY", + "Name": "Ideology", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_SUFFRAGE", + "Name": "Suffrage", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": 0, + }, + { + "Type": "CIVIC_TOTALITARIANISM", + "Name": "Totalitarianism", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": 2, + }, + { + "Type": "CIVIC_CLASS_STRUGGLE", + "Name": "Class Struggle", + "Cost": 1640, + "EraType": "ERA_MODERN", + "UITreeRow": 3, + }, + { + "Type": "CIVIC_COLD_WAR", + "Name": "Cold War", + "Cost": 2185, + "EraType": "ERA_ATOMIC", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_PROFESSIONAL_SPORTS", + "Name": "Professional Sports", + "Cost": 2185, + "EraType": "ERA_ATOMIC", + "UITreeRow": 2, + }, + { + "Type": "CIVIC_CULTURAL_HERITAGE", + "Name": "Cultural Heritage", + "Cost": 1955, + "EraType": "ERA_ATOMIC", + "UITreeRow": -3, + }, + { + "Type": "CIVIC_RAPID_DEPLOYMENT", + "Name": "Rapid Deployment", + "Cost": 2415, + "EraType": "ERA_ATOMIC", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_SPACE_RACE", + "Name": "Space Race", + "Cost": 2415, + "EraType": "ERA_ATOMIC", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_GLOBALIZATION", + "Name": "Globalization", + "Cost": 2880, + "EraType": "ERA_INFORMATION", + "UITreeRow": 0, + }, + { + "Type": "CIVIC_SOCIAL_MEDIA", + "Name": "Social Media", + "Cost": 2880, + "EraType": "ERA_INFORMATION", + "UITreeRow": 2, + }, + { + "Type": "CIVIC_FUTURE_CIVIC", + "Name": "Future Civic", + "Cost": 3500, + "EraType": "ERA_FUTURE", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_ENVIRONMENTALISM", + "Name": "Environmentalism", + "Cost": 2880, + "EraType": "ERA_INFORMATION", + "UITreeRow": -2, + }, + { + "Type": "CIVIC_CORPORATE_LIBERTARIANISM", + "Name": "Corporate Libertarianism", + "Cost": 3000, + "EraType": "ERA_INFORMATION", + "UITreeRow": 0, + }, + { + "Type": "CIVIC_DIGITAL_DEMOCRACY", + "Name": "Digital Democracy", + "Cost": 3000, + "EraType": "ERA_INFORMATION", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_SYNTHETIC_TECHNOCRACY", + "Name": "Synthetic Technocracy", + "Cost": 3000, + "EraType": "ERA_INFORMATION", + "UITreeRow": 2, + }, + { + "Type": "CIVIC_NEAR_FUTURE_GOVERNANCE", + "Name": "Near Future Governance", + "Cost": 3100, + "EraType": "ERA_INFORMATION", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_GLOBAL_WARMING_MITIGATION", + "Name": "Global Warming Mitigation", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": -2, + }, + { + "Type": "CIVIC_SMART_POWER_DOCTRINE", + "Name": "Smart Power Doctrine", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": -1, + }, + { + "Type": "CIVIC_INFORMATION_WARFARE", + "Name": "Information Warfare", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": 0, + }, + { + "Type": "CIVIC_EXODUS_IMPERATIVE", + "Name": "Exodus Imperative", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": 1, + }, + { + "Type": "CIVIC_CULTURAL_HEGEMONY", + "Name": "Cultural Hegemony", + "Cost": 3200, + "EraType": "ERA_FUTURE", + "UITreeRow": 2, + }, +] From f50602a74ab37621f1f427edaf03fde26bdec7c6 Mon Sep 17 00:00:00 2001 From: hesto2 Date: Sun, 1 Dec 2024 11:34:28 -0700 Subject: [PATCH 64/69] Update existing techs --- worlds/civ_6/Data.py | 4 +- worlds/civ_6/data/existing_tech.json | 541 -------------------------- worlds/civ_6/data/existing_tech.py | 546 +++++++++++++++++++++++++++ 3 files changed, 549 insertions(+), 542 deletions(-) delete mode 100644 worlds/civ_6/data/existing_tech.json create mode 100644 worlds/civ_6/data/existing_tech.py diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index f634928db696..063a7d738eeb 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -41,7 +41,9 @@ def get_existing_civics_data() -> List[ExistingItemData]: def get_existing_techs_data() -> List[ExistingItemData]: - return _get_data("existing_tech") + from .data.existing_tech import existing_tech + + return existing_tech class GoodyHutRewardData(TypedDict): diff --git a/worlds/civ_6/data/existing_tech.json b/worlds/civ_6/data/existing_tech.json deleted file mode 100644 index 109df28fadd8..000000000000 --- a/worlds/civ_6/data/existing_tech.json +++ /dev/null @@ -1,541 +0,0 @@ -[ - { - "Type": "TECH_POTTERY", - "Cost": 25, - "UITreeRow": 0, - "EraType": "ERA_ANCIENT", - "Name": "Pottery" - }, - { - "Type": "TECH_ANIMAL_HUSBANDRY", - "Cost": 25, - "UITreeRow": 1, - "EraType": "ERA_ANCIENT", - "Name": "Animal Husbandry" - }, - { - "Type": "TECH_MINING", - "Cost": 25, - "UITreeRow": 3, - "EraType": "ERA_ANCIENT", - "Name": "Mining" - }, - { - "Type": "TECH_SAILING", - "Cost": 50, - "UITreeRow": -3, - "EraType": "ERA_ANCIENT", - "Name": "Sailing" - }, - { - "Type": "TECH_ASTROLOGY", - "Cost": 50, - "UITreeRow": -2, - "EraType": "ERA_ANCIENT", - "Name": "Astrology" - }, - { - "Type": "TECH_IRRIGATION", - "Cost": 50, - "UITreeRow": -1, - "EraType": "ERA_ANCIENT", - "Name": "Irrigation" - }, - { - "Type": "TECH_ARCHERY", - "Cost": 50, - "UITreeRow": 1, - "EraType": "ERA_ANCIENT", - "Name": "Archery" - }, - { - "Type": "TECH_WRITING", - "Cost": 50, - "UITreeRow": 0, - "EraType": "ERA_ANCIENT", - "Name": "Writing" - }, - { - "Type": "TECH_MASONRY", - "Cost": 80, - "UITreeRow": 2, - "EraType": "ERA_ANCIENT", - "Name": "Masonry" - }, - { - "Type": "TECH_BRONZE_WORKING", - "Cost": 80, - "UITreeRow": 3, - "EraType": "ERA_ANCIENT", - "Name": "Bronze Working" - }, - { - "Type": "TECH_THE_WHEEL", - "Cost": 80, - "UITreeRow": 4, - "EraType": "ERA_ANCIENT", - "Name": "The Wheel" - }, - { - "Type": "TECH_CELESTIAL_NAVIGATION", - "Cost": 120, - "UITreeRow": -2, - "EraType": "ERA_CLASSICAL", - "Name": "Celestial Navigation" - }, - { - "Type": "TECH_CURRENCY", - "Cost": 120, - "UITreeRow": 0, - "EraType": "ERA_CLASSICAL", - "Name": "Currency" - }, - { - "Type": "TECH_HORSEBACK_RIDING", - "Cost": 120, - "UITreeRow": 1, - "EraType": "ERA_CLASSICAL", - "Name": "Horseback Riding" - }, - { - "Type": "TECH_IRON_WORKING", - "Cost": 120, - "UITreeRow": 3, - "EraType": "ERA_CLASSICAL", - "Name": "Iron Working" - }, - { - "Type": "TECH_SHIPBUILDING", - "Cost": 200, - "UITreeRow": -3, - "EraType": "ERA_CLASSICAL", - "Name": "Shipbuilding" - }, - { - "Type": "TECH_MATHEMATICS", - "Cost": 200, - "UITreeRow": -1, - "EraType": "ERA_CLASSICAL", - "Name": "Mathematics" - }, - { - "Type": "TECH_CONSTRUCTION", - "Cost": 200, - "UITreeRow": 2, - "EraType": "ERA_CLASSICAL", - "Name": "Construction" - }, - { - "Type": "TECH_ENGINEERING", - "Cost": 200, - "UITreeRow": 4, - "EraType": "ERA_CLASSICAL", - "Name": "Engineering" - }, - { - "Type": "TECH_MILITARY_TACTICS", - "Cost": 300, - "UITreeRow": -2, - "EraType": "ERA_MEDIEVAL", - "Name": "Military Tactics" - }, - { - "Type": "TECH_APPRENTICESHIP", - "Cost": 300, - "UITreeRow": 0, - "EraType": "ERA_MEDIEVAL", - "Name": "Apprenticeship" - }, - { - "Type": "TECH_MACHINERY", - "Cost": 300, - "UITreeRow": 4, - "EraType": "ERA_MEDIEVAL", - "Name": "Machinery" - }, - { - "Type": "TECH_EDUCATION", - "Cost": 390, - "UITreeRow": -1, - "EraType": "ERA_MEDIEVAL", - "Name": "Education" - }, - { - "Type": "TECH_STIRRUPS", - "Cost": 390, - "UITreeRow": 1, - "EraType": "ERA_MEDIEVAL", - "Name": "Stirrups" - }, - { - "Type": "TECH_MILITARY_ENGINEERING", - "Cost": 390, - "UITreeRow": 2, - "EraType": "ERA_MEDIEVAL", - "Name": "Military Engineering" - }, - { - "Type": "TECH_CASTLES", - "Cost": 390, - "UITreeRow": 3, - "EraType": "ERA_MEDIEVAL", - "Name": "Castles" - }, - { - "Type": "TECH_CARTOGRAPHY", - "Cost": 600, - "UITreeRow": -3, - "EraType": "ERA_RENAISSANCE", - "Name": "Cartography" - }, - { - "Type": "TECH_MASS_PRODUCTION", - "Cost": 600, - "UITreeRow": -2, - "EraType": "ERA_RENAISSANCE", - "Name": "Mass Production" - }, - { - "Type": "TECH_BANKING", - "Cost": 600, - "UITreeRow": 0, - "EraType": "ERA_RENAISSANCE", - "Name": "Banking" - }, - { - "Type": "TECH_GUNPOWDER", - "Cost": 600, - "UITreeRow": 1, - "EraType": "ERA_RENAISSANCE", - "Name": "Gunpowder" - }, - { - "Type": "TECH_PRINTING", - "Cost": 600, - "UITreeRow": 4, - "EraType": "ERA_RENAISSANCE", - "Name": "Printing" - }, - { - "Type": "TECH_SQUARE_RIGGING", - "Cost": 730, - "UITreeRow": -3, - "EraType": "ERA_RENAISSANCE", - "Name": "Square Rigging" - }, - { - "Type": "TECH_ASTRONOMY", - "Cost": 730, - "UITreeRow": -1, - "EraType": "ERA_RENAISSANCE", - "Name": "Astronomy" - }, - { - "Type": "TECH_METAL_CASTING", - "Cost": 730, - "UITreeRow": 1, - "EraType": "ERA_RENAISSANCE", - "Name": "Metal Casting" - }, - { - "Type": "TECH_SIEGE_TACTICS", - "Cost": 730, - "UITreeRow": 3, - "EraType": "ERA_RENAISSANCE", - "Name": "Siege Tactics" - }, - { - "Type": "TECH_INDUSTRIALIZATION", - "Cost": 930, - "UITreeRow": -2, - "EraType": "ERA_INDUSTRIAL", - "Name": "Industrialization" - }, - { - "Type": "TECH_SCIENTIFIC_THEORY", - "Cost": 930, - "UITreeRow": -1, - "EraType": "ERA_INDUSTRIAL", - "Name": "Scientific Theory" - }, - { - "Type": "TECH_BALLISTICS", - "Cost": 930, - "UITreeRow": 1, - "EraType": "ERA_INDUSTRIAL", - "Name": "Ballistics" - }, - { - "Type": "TECH_MILITARY_SCIENCE", - "Cost": 930, - "UITreeRow": 3, - "EraType": "ERA_INDUSTRIAL", - "Name": "Military Science" - }, - { - "Type": "TECH_STEAM_POWER", - "Cost": 1070, - "UITreeRow": -3, - "EraType": "ERA_INDUSTRIAL", - "Name": "Steam Power" - }, - { - "Type": "TECH_SANITATION", - "Cost": 1070, - "UITreeRow": -1, - "EraType": "ERA_INDUSTRIAL", - "Name": "Sanitation" - }, - { - "Type": "TECH_ECONOMICS", - "Cost": 1070, - "UITreeRow": 0, - "EraType": "ERA_INDUSTRIAL", - "Name": "Economics" - }, - { - "Type": "TECH_RIFLING", - "Cost": 1070, - "UITreeRow": 2, - "EraType": "ERA_INDUSTRIAL", - "Name": "Rifling" - }, - { - "Type": "TECH_FLIGHT", - "Cost": 1250, - "UITreeRow": -2, - "EraType": "ERA_MODERN", - "Name": "Flight" - }, - { - "Type": "TECH_REPLACEABLE_PARTS", - "Cost": 1250, - "UITreeRow": 0, - "EraType": "ERA_MODERN", - "Name": "Replaceable Parts" - }, - { - "Type": "TECH_STEEL", - "Cost": 1250, - "UITreeRow": 1, - "EraType": "ERA_MODERN", - "Name": "Steel" - }, - { - "Type": "TECH_ELECTRICITY", - "Cost": 1370, - "UITreeRow": -3, - "EraType": "ERA_MODERN", - "Name": "Electricity" - }, - { - "Type": "TECH_RADIO", - "Cost": 1370, - "UITreeRow": -2, - "EraType": "ERA_MODERN", - "Name": "Radio" - }, - { - "Type": "TECH_CHEMISTRY", - "Cost": 1370, - "UITreeRow": -1, - "EraType": "ERA_MODERN", - "Name": "Chemistry" - }, - { - "Type": "TECH_COMBUSTION", - "Cost": 1370, - "UITreeRow": 2, - "EraType": "ERA_MODERN", - "Name": "Combustion" - }, - { - "Type": "TECH_ADVANCED_FLIGHT", - "Cost": 1480, - "UITreeRow": -2, - "EraType": "ERA_ATOMIC", - "Name": "Advanced Flight" - }, - { - "Type": "TECH_ROCKETRY", - "Cost": 1480, - "UITreeRow": -1, - "EraType": "ERA_ATOMIC", - "Name": "Rocketry" - }, - { - "Type": "TECH_ADVANCED_BALLISTICS", - "Cost": 1480, - "UITreeRow": 0, - "EraType": "ERA_ATOMIC", - "Name": "Advanced Ballistics" - }, - { - "Type": "TECH_COMBINED_ARMS", - "Cost": 1480, - "UITreeRow": 1, - "EraType": "ERA_ATOMIC", - "Name": "Combined Arms" - }, - { - "Type": "TECH_PLASTICS", - "Cost": 1480, - "UITreeRow": 2, - "EraType": "ERA_ATOMIC", - "Name": "Plastics" - }, - { - "Type": "TECH_COMPUTERS", - "Cost": 1660, - "UITreeRow": -3, - "EraType": "ERA_ATOMIC", - "Name": "Computers" - }, - { - "Type": "TECH_NUCLEAR_FISSION", - "Cost": 1660, - "UITreeRow": 1, - "EraType": "ERA_ATOMIC", - "Name": "Nuclear Fission" - }, - { - "Type": "TECH_SYNTHETIC_MATERIALS", - "Cost": 1660, - "UITreeRow": 2, - "EraType": "ERA_ATOMIC", - "Name": "Synthetic Materials" - }, - { - "Type": "TECH_TELECOMMUNICATIONS", - "Cost": 1850, - "UITreeRow": -3, - "EraType": "ERA_INFORMATION", - "Name": "Telecommunications" - }, - { - "Type": "TECH_SATELLITES", - "Cost": 1850, - "UITreeRow": -1, - "EraType": "ERA_INFORMATION", - "Name": "Satellites" - }, - { - "Type": "TECH_GUIDANCE_SYSTEMS", - "Cost": 1850, - "UITreeRow": 0, - "EraType": "ERA_INFORMATION", - "Name": "Guidance Systems" - }, - { - "Type": "TECH_LASERS", - "Cost": 1850, - "UITreeRow": 1, - "EraType": "ERA_INFORMATION", - "Name": "Lasers" - }, - { - "Type": "TECH_COMPOSITES", - "Cost": 1850, - "UITreeRow": 2, - "EraType": "ERA_INFORMATION", - "Name": "Composites" - }, - { - "Type": "TECH_STEALTH_TECHNOLOGY", - "Cost": 1850, - "UITreeRow": 3, - "EraType": "ERA_INFORMATION", - "Name": "Stealth Technology" - }, - { - "Type": "TECH_ROBOTICS", - "Cost": 2155, - "UITreeRow": -2, - "EraType": "ERA_INFORMATION", - "Name": "Robotics" - }, - { - "Type": "TECH_NANOTECHNOLOGY", - "Cost": 2155, - "UITreeRow": 2, - "EraType": "ERA_INFORMATION", - "Name": "Nanotechnology" - }, - { - "Type": "TECH_NUCLEAR_FUSION", - "Cost": 2155, - "UITreeRow": 1, - "EraType": "ERA_INFORMATION", - "Name": "Nuclear Fusion" - }, - { - "Type": "TECH_BUTTRESS", - "Cost": 300, - "UITreeRow": -3, - "EraType": "ERA_MEDIEVAL", - "Name": "Buttress" - }, - { - "Type": "TECH_REFINING", - "Cost": 1250, - "UITreeRow": 3, - "EraType": "ERA_MODERN", - "Name": "Refining" - }, - { - "Type": "TECH_SEASTEADS", - "Cost": 2200, - "UITreeRow": -3, - "EraType": "ERA_FUTURE", - "Name": "Seasteads" - }, - { - "Type": "TECH_ADVANCED_AI", - "Cost": 2200, - "UITreeRow": -2, - "EraType": "ERA_FUTURE", - "Name": "Advanced AI" - }, - { - "Type": "TECH_ADVANCED_POWER_CELLS", - "Cost": 2200, - "UITreeRow": -1, - "EraType": "ERA_FUTURE", - "Name": "Advanced Power Cells" - }, - { - "Type": "TECH_CYBERNETICS", - "Cost": 2200, - "UITreeRow": 0, - "EraType": "ERA_FUTURE", - "Name": "Cybernetics" - }, - { - "Type": "TECH_SMART_MATERIALS", - "Cost": 2200, - "UITreeRow": 1, - "EraType": "ERA_FUTURE", - "Name": "Smart Materials" - }, - { - "Type": "TECH_PREDICTIVE_SYSTEMS", - "Cost": 2200, - "UITreeRow": 2, - "EraType": "ERA_FUTURE", - "Name": "Predictive Systems" - }, - { - "Type": "TECH_OFFWORLD_MISSION", - "Cost": 2500, - "UITreeRow": 0, - "EraType": "ERA_FUTURE", - "Name": "Offworld Mission" - }, - { - "Type": "TECH_FUTURE_TECH", - "Cost": 2600, - "UITreeRow": 0, - "EraType": "ERA_FUTURE", - "Name": "Future Tech" - } -] diff --git a/worlds/civ_6/data/existing_tech.py b/worlds/civ_6/data/existing_tech.py new file mode 100644 index 000000000000..87c06817b341 --- /dev/null +++ b/worlds/civ_6/data/existing_tech.py @@ -0,0 +1,546 @@ +from typing import List + +from worlds.civ_6.ItemData import ExistingItemData + + +existing_tech: List[ExistingItemData] = [ + { + "Type": "TECH_POTTERY", + "Cost": 25, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT", + "Name": "Pottery", + }, + { + "Type": "TECH_ANIMAL_HUSBANDRY", + "Cost": 25, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT", + "Name": "Animal Husbandry", + }, + { + "Type": "TECH_MINING", + "Cost": 25, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT", + "Name": "Mining", + }, + { + "Type": "TECH_SAILING", + "Cost": 50, + "UITreeRow": -3, + "EraType": "ERA_ANCIENT", + "Name": "Sailing", + }, + { + "Type": "TECH_ASTROLOGY", + "Cost": 50, + "UITreeRow": -2, + "EraType": "ERA_ANCIENT", + "Name": "Astrology", + }, + { + "Type": "TECH_IRRIGATION", + "Cost": 50, + "UITreeRow": -1, + "EraType": "ERA_ANCIENT", + "Name": "Irrigation", + }, + { + "Type": "TECH_ARCHERY", + "Cost": 50, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT", + "Name": "Archery", + }, + { + "Type": "TECH_WRITING", + "Cost": 50, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT", + "Name": "Writing", + }, + { + "Type": "TECH_MASONRY", + "Cost": 80, + "UITreeRow": 2, + "EraType": "ERA_ANCIENT", + "Name": "Masonry", + }, + { + "Type": "TECH_BRONZE_WORKING", + "Cost": 80, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT", + "Name": "Bronze Working", + }, + { + "Type": "TECH_THE_WHEEL", + "Cost": 80, + "UITreeRow": 4, + "EraType": "ERA_ANCIENT", + "Name": "The Wheel", + }, + { + "Type": "TECH_CELESTIAL_NAVIGATION", + "Cost": 120, + "UITreeRow": -2, + "EraType": "ERA_CLASSICAL", + "Name": "Celestial Navigation", + }, + { + "Type": "TECH_CURRENCY", + "Cost": 120, + "UITreeRow": 0, + "EraType": "ERA_CLASSICAL", + "Name": "Currency", + }, + { + "Type": "TECH_HORSEBACK_RIDING", + "Cost": 120, + "UITreeRow": 1, + "EraType": "ERA_CLASSICAL", + "Name": "Horseback Riding", + }, + { + "Type": "TECH_IRON_WORKING", + "Cost": 120, + "UITreeRow": 3, + "EraType": "ERA_CLASSICAL", + "Name": "Iron Working", + }, + { + "Type": "TECH_SHIPBUILDING", + "Cost": 200, + "UITreeRow": -3, + "EraType": "ERA_CLASSICAL", + "Name": "Shipbuilding", + }, + { + "Type": "TECH_MATHEMATICS", + "Cost": 200, + "UITreeRow": -1, + "EraType": "ERA_CLASSICAL", + "Name": "Mathematics", + }, + { + "Type": "TECH_CONSTRUCTION", + "Cost": 200, + "UITreeRow": 2, + "EraType": "ERA_CLASSICAL", + "Name": "Construction", + }, + { + "Type": "TECH_ENGINEERING", + "Cost": 200, + "UITreeRow": 4, + "EraType": "ERA_CLASSICAL", + "Name": "Engineering", + }, + { + "Type": "TECH_MILITARY_TACTICS", + "Cost": 300, + "UITreeRow": -2, + "EraType": "ERA_MEDIEVAL", + "Name": "Military Tactics", + }, + { + "Type": "TECH_APPRENTICESHIP", + "Cost": 300, + "UITreeRow": 0, + "EraType": "ERA_MEDIEVAL", + "Name": "Apprenticeship", + }, + { + "Type": "TECH_MACHINERY", + "Cost": 300, + "UITreeRow": 4, + "EraType": "ERA_MEDIEVAL", + "Name": "Machinery", + }, + { + "Type": "TECH_EDUCATION", + "Cost": 390, + "UITreeRow": -1, + "EraType": "ERA_MEDIEVAL", + "Name": "Education", + }, + { + "Type": "TECH_STIRRUPS", + "Cost": 390, + "UITreeRow": 1, + "EraType": "ERA_MEDIEVAL", + "Name": "Stirrups", + }, + { + "Type": "TECH_MILITARY_ENGINEERING", + "Cost": 390, + "UITreeRow": 2, + "EraType": "ERA_MEDIEVAL", + "Name": "Military Engineering", + }, + { + "Type": "TECH_CASTLES", + "Cost": 390, + "UITreeRow": 3, + "EraType": "ERA_MEDIEVAL", + "Name": "Castles", + }, + { + "Type": "TECH_CARTOGRAPHY", + "Cost": 600, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE", + "Name": "Cartography", + }, + { + "Type": "TECH_MASS_PRODUCTION", + "Cost": 600, + "UITreeRow": -2, + "EraType": "ERA_RENAISSANCE", + "Name": "Mass Production", + }, + { + "Type": "TECH_BANKING", + "Cost": 600, + "UITreeRow": 0, + "EraType": "ERA_RENAISSANCE", + "Name": "Banking", + }, + { + "Type": "TECH_GUNPOWDER", + "Cost": 600, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE", + "Name": "Gunpowder", + }, + { + "Type": "TECH_PRINTING", + "Cost": 600, + "UITreeRow": 4, + "EraType": "ERA_RENAISSANCE", + "Name": "Printing", + }, + { + "Type": "TECH_SQUARE_RIGGING", + "Cost": 730, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE", + "Name": "Square Rigging", + }, + { + "Type": "TECH_ASTRONOMY", + "Cost": 730, + "UITreeRow": -1, + "EraType": "ERA_RENAISSANCE", + "Name": "Astronomy", + }, + { + "Type": "TECH_METAL_CASTING", + "Cost": 730, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE", + "Name": "Metal Casting", + }, + { + "Type": "TECH_SIEGE_TACTICS", + "Cost": 730, + "UITreeRow": 3, + "EraType": "ERA_RENAISSANCE", + "Name": "Siege Tactics", + }, + { + "Type": "TECH_INDUSTRIALIZATION", + "Cost": 930, + "UITreeRow": -2, + "EraType": "ERA_INDUSTRIAL", + "Name": "Industrialization", + }, + { + "Type": "TECH_SCIENTIFIC_THEORY", + "Cost": 930, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL", + "Name": "Scientific Theory", + }, + { + "Type": "TECH_BALLISTICS", + "Cost": 930, + "UITreeRow": 1, + "EraType": "ERA_INDUSTRIAL", + "Name": "Ballistics", + }, + { + "Type": "TECH_MILITARY_SCIENCE", + "Cost": 930, + "UITreeRow": 3, + "EraType": "ERA_INDUSTRIAL", + "Name": "Military Science", + }, + { + "Type": "TECH_STEAM_POWER", + "Cost": 1070, + "UITreeRow": -3, + "EraType": "ERA_INDUSTRIAL", + "Name": "Steam Power", + }, + { + "Type": "TECH_SANITATION", + "Cost": 1070, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL", + "Name": "Sanitation", + }, + { + "Type": "TECH_ECONOMICS", + "Cost": 1070, + "UITreeRow": 0, + "EraType": "ERA_INDUSTRIAL", + "Name": "Economics", + }, + { + "Type": "TECH_RIFLING", + "Cost": 1070, + "UITreeRow": 2, + "EraType": "ERA_INDUSTRIAL", + "Name": "Rifling", + }, + { + "Type": "TECH_FLIGHT", + "Cost": 1250, + "UITreeRow": -2, + "EraType": "ERA_MODERN", + "Name": "Flight", + }, + { + "Type": "TECH_REPLACEABLE_PARTS", + "Cost": 1250, + "UITreeRow": 0, + "EraType": "ERA_MODERN", + "Name": "Replaceable Parts", + }, + { + "Type": "TECH_STEEL", + "Cost": 1250, + "UITreeRow": 1, + "EraType": "ERA_MODERN", + "Name": "Steel", + }, + { + "Type": "TECH_ELECTRICITY", + "Cost": 1370, + "UITreeRow": -3, + "EraType": "ERA_MODERN", + "Name": "Electricity", + }, + { + "Type": "TECH_RADIO", + "Cost": 1370, + "UITreeRow": -2, + "EraType": "ERA_MODERN", + "Name": "Radio", + }, + { + "Type": "TECH_CHEMISTRY", + "Cost": 1370, + "UITreeRow": -1, + "EraType": "ERA_MODERN", + "Name": "Chemistry", + }, + { + "Type": "TECH_COMBUSTION", + "Cost": 1370, + "UITreeRow": 2, + "EraType": "ERA_MODERN", + "Name": "Combustion", + }, + { + "Type": "TECH_ADVANCED_FLIGHT", + "Cost": 1480, + "UITreeRow": -2, + "EraType": "ERA_ATOMIC", + "Name": "Advanced Flight", + }, + { + "Type": "TECH_ROCKETRY", + "Cost": 1480, + "UITreeRow": -1, + "EraType": "ERA_ATOMIC", + "Name": "Rocketry", + }, + { + "Type": "TECH_ADVANCED_BALLISTICS", + "Cost": 1480, + "UITreeRow": 0, + "EraType": "ERA_ATOMIC", + "Name": "Advanced Ballistics", + }, + { + "Type": "TECH_COMBINED_ARMS", + "Cost": 1480, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC", + "Name": "Combined Arms", + }, + { + "Type": "TECH_PLASTICS", + "Cost": 1480, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC", + "Name": "Plastics", + }, + { + "Type": "TECH_COMPUTERS", + "Cost": 1660, + "UITreeRow": -3, + "EraType": "ERA_ATOMIC", + "Name": "Computers", + }, + { + "Type": "TECH_NUCLEAR_FISSION", + "Cost": 1660, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC", + "Name": "Nuclear Fission", + }, + { + "Type": "TECH_SYNTHETIC_MATERIALS", + "Cost": 1660, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC", + "Name": "Synthetic Materials", + }, + { + "Type": "TECH_TELECOMMUNICATIONS", + "Cost": 1850, + "UITreeRow": -3, + "EraType": "ERA_INFORMATION", + "Name": "Telecommunications", + }, + { + "Type": "TECH_SATELLITES", + "Cost": 1850, + "UITreeRow": -1, + "EraType": "ERA_INFORMATION", + "Name": "Satellites", + }, + { + "Type": "TECH_GUIDANCE_SYSTEMS", + "Cost": 1850, + "UITreeRow": 0, + "EraType": "ERA_INFORMATION", + "Name": "Guidance Systems", + }, + { + "Type": "TECH_LASERS", + "Cost": 1850, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION", + "Name": "Lasers", + }, + { + "Type": "TECH_COMPOSITES", + "Cost": 1850, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION", + "Name": "Composites", + }, + { + "Type": "TECH_STEALTH_TECHNOLOGY", + "Cost": 1850, + "UITreeRow": 3, + "EraType": "ERA_INFORMATION", + "Name": "Stealth Technology", + }, + { + "Type": "TECH_ROBOTICS", + "Cost": 2155, + "UITreeRow": -2, + "EraType": "ERA_INFORMATION", + "Name": "Robotics", + }, + { + "Type": "TECH_NANOTECHNOLOGY", + "Cost": 2155, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION", + "Name": "Nanotechnology", + }, + { + "Type": "TECH_NUCLEAR_FUSION", + "Cost": 2155, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION", + "Name": "Nuclear Fusion", + }, + { + "Type": "TECH_BUTTRESS", + "Cost": 300, + "UITreeRow": -3, + "EraType": "ERA_MEDIEVAL", + "Name": "Buttress", + }, + { + "Type": "TECH_REFINING", + "Cost": 1250, + "UITreeRow": 3, + "EraType": "ERA_MODERN", + "Name": "Refining", + }, + { + "Type": "TECH_SEASTEADS", + "Cost": 2200, + "UITreeRow": -3, + "EraType": "ERA_FUTURE", + "Name": "Seasteads", + }, + { + "Type": "TECH_ADVANCED_AI", + "Cost": 2200, + "UITreeRow": -2, + "EraType": "ERA_FUTURE", + "Name": "Advanced AI", + }, + { + "Type": "TECH_ADVANCED_POWER_CELLS", + "Cost": 2200, + "UITreeRow": -1, + "EraType": "ERA_FUTURE", + "Name": "Advanced Power Cells", + }, + { + "Type": "TECH_CYBERNETICS", + "Cost": 2200, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + "Name": "Cybernetics", + }, + { + "Type": "TECH_SMART_MATERIALS", + "Cost": 2200, + "UITreeRow": 1, + "EraType": "ERA_FUTURE", + "Name": "Smart Materials", + }, + { + "Type": "TECH_PREDICTIVE_SYSTEMS", + "Cost": 2200, + "UITreeRow": 2, + "EraType": "ERA_FUTURE", + "Name": "Predictive Systems", + }, + { + "Type": "TECH_OFFWORLD_MISSION", + "Cost": 2500, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + "Name": "Offworld Mission", + }, + { + "Type": "TECH_FUTURE_TECH", + "Cost": 2600, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + "Name": "Future Tech", + }, +] From e65ca0886d02bd4ad5fc2b1c90558f604e8ef992 Mon Sep 17 00:00:00 2001 From: hesto2 Date: Sun, 1 Dec 2024 11:41:21 -0700 Subject: [PATCH 65/69] move boost data class --- worlds/civ_6/Data.py | 3 +-- worlds/civ_6/ItemData.py | 12 +++++++++++- worlds/civ_6/data/boosts.py | 9 +-------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index 063a7d738eeb..d77235fa66dd 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -4,8 +4,7 @@ import pkgutil from typing import Any, Dict, List, TypedDict -from worlds.civ_6.ItemData import ExistingItemData, NewItemData -from worlds.civ_6.data.boosts import CivVIBoostData +from worlds.civ_6.ItemData import CivVIBoostData, ExistingItemData, NewItemData _cache: Dict[Any, Any] = {} diff --git a/worlds/civ_6/ItemData.py b/worlds/civ_6/ItemData.py index 709d3255ccc4..a6e6bfd56026 100644 --- a/worlds/civ_6/ItemData.py +++ b/worlds/civ_6/ItemData.py @@ -1,4 +1,5 @@ -from typing import TypedDict +from dataclasses import dataclass +from typing import List, TypedDict class NewItemData(TypedDict): @@ -10,3 +11,12 @@ class NewItemData(TypedDict): class ExistingItemData(NewItemData): Name: str + + +@dataclass +class CivVIBoostData: + Type: str + EraType: str + Prereq: List[str] + PrereqRequiredCount: int + Classification: str diff --git a/worlds/civ_6/data/boosts.py b/worlds/civ_6/data/boosts.py index dbff9c3899fa..33f357415fb8 100644 --- a/worlds/civ_6/data/boosts.py +++ b/worlds/civ_6/data/boosts.py @@ -1,14 +1,7 @@ from dataclasses import dataclass from typing import List - -@dataclass -class CivVIBoostData: - Type: str - EraType: str - Prereq: List[str] - PrereqRequiredCount: int - Classification: str +from worlds.civ_6.ItemData import CivVIBoostData boosts: List[CivVIBoostData] = [ From 7ce8b23d71a45e1d4707f9b2d8db086a1095b6f0 Mon Sep 17 00:00:00 2001 From: hesto2 Date: Sun, 1 Dec 2024 11:56:08 -0700 Subject: [PATCH 66/69] Update reward data --- worlds/civ_6/Data.py | 11 +++-------- worlds/civ_6/ItemData.py | 6 ++++++ .../{goody_hut_rewards.json => goody_hut_rewards.py} | 6 +++++- 3 files changed, 14 insertions(+), 9 deletions(-) rename worlds/civ_6/data/{goody_hut_rewards.json => goody_hut_rewards.py} (92%) diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index d77235fa66dd..5bcc9d80395c 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -4,7 +4,7 @@ import pkgutil from typing import Any, Dict, List, TypedDict -from worlds.civ_6.ItemData import CivVIBoostData, ExistingItemData, NewItemData +from worlds.civ_6.ItemData import CivVIBoostData, ExistingItemData, GoodyHutRewardData, NewItemData _cache: Dict[Any, Any] = {} @@ -45,14 +45,9 @@ def get_existing_techs_data() -> List[ExistingItemData]: return existing_tech -class GoodyHutRewardData(TypedDict): - Type: str - Name: str - Rarity: str - - def get_goody_hut_rewards_data() -> List[GoodyHutRewardData]: - return _get_data("goody_hut_rewards") + from .data.goody_hut_rewards import reward_data + return reward_data class CivicPrereqData(TypedDict): diff --git a/worlds/civ_6/ItemData.py b/worlds/civ_6/ItemData.py index a6e6bfd56026..2cab2bb58481 100644 --- a/worlds/civ_6/ItemData.py +++ b/worlds/civ_6/ItemData.py @@ -20,3 +20,9 @@ class CivVIBoostData: Prereq: List[str] PrereqRequiredCount: int Classification: str + + +class GoodyHutRewardData(TypedDict): + Type: str + Name: str + Rarity: str diff --git a/worlds/civ_6/data/goody_hut_rewards.json b/worlds/civ_6/data/goody_hut_rewards.py similarity index 92% rename from worlds/civ_6/data/goody_hut_rewards.json rename to worlds/civ_6/data/goody_hut_rewards.py index 8315642fdfa5..99aefc0a7f9a 100644 --- a/worlds/civ_6/data/goody_hut_rewards.json +++ b/worlds/civ_6/data/goody_hut_rewards.py @@ -1,4 +1,8 @@ -[ +from typing import List +from worlds.civ_6.ItemData import GoodyHutRewardData + + +reward_data: List[GoodyHutRewardData] = [ { "Type": "GOODY_GOLD_SMALL_MODIFIER", "Rarity": "COMMON", From 1024bc8c9c78581d1477702159f0f7e79e15c901 Mon Sep 17 00:00:00 2001 From: hesto2 Date: Sun, 1 Dec 2024 13:24:48 -0700 Subject: [PATCH 67/69] Update prereq data --- worlds/civ_6/Data.py | 28 +-- worlds/civ_6/ItemData.py | 10 ++ worlds/civ_6/data/new_civic_prereqs.json | 144 ---------------- worlds/civ_6/data/new_civic_prereqs.py | 92 ++++++++++ worlds/civ_6/data/new_tech_prereqs.json | 207 ----------------------- worlds/civ_6/data/new_tech_prereqs.py | 110 ++++++++++++ 6 files changed, 227 insertions(+), 364 deletions(-) delete mode 100644 worlds/civ_6/data/new_civic_prereqs.json create mode 100644 worlds/civ_6/data/new_civic_prereqs.py delete mode 100644 worlds/civ_6/data/new_tech_prereqs.json create mode 100644 worlds/civ_6/data/new_tech_prereqs.py diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index 5bcc9d80395c..e06fb15b2bd4 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -4,7 +4,14 @@ import pkgutil from typing import Any, Dict, List, TypedDict -from worlds.civ_6.ItemData import CivVIBoostData, ExistingItemData, GoodyHutRewardData, NewItemData +from worlds.civ_6.ItemData import ( + CivVIBoostData, + CivicPrereqData, + ExistingItemData, + GoodyHutRewardData, + NewItemData, + TechPrereqData, +) _cache: Dict[Any, Any] = {} @@ -47,29 +54,24 @@ def get_existing_techs_data() -> List[ExistingItemData]: def get_goody_hut_rewards_data() -> List[GoodyHutRewardData]: from .data.goody_hut_rewards import reward_data - return reward_data - -class CivicPrereqData(TypedDict): - Civic: str - PrereqTech: str + return reward_data def get_new_civic_prereqs_data() -> List[CivicPrereqData]: - return _get_data("new_civic_prereqs") + from .data.new_civic_prereqs import new_civic_prereqs + + return new_civic_prereqs def get_new_civics_data() -> List[NewItemData]: return _get_data("new_civics") -class TechPrereqData(TypedDict): - Technology: str - PrereqTech: str - - def get_new_tech_prereqs_data() -> List[TechPrereqData]: - return _get_data("new_tech_prereqs") + from .data.new_tech_prereqs import new_tech_prereqs + + return new_tech_prereqs def get_new_techs_data() -> List[NewItemData]: diff --git a/worlds/civ_6/ItemData.py b/worlds/civ_6/ItemData.py index 2cab2bb58481..5f3c16a9b1ba 100644 --- a/worlds/civ_6/ItemData.py +++ b/worlds/civ_6/ItemData.py @@ -26,3 +26,13 @@ class GoodyHutRewardData(TypedDict): Type: str Name: str Rarity: str + + +class CivicPrereqData(TypedDict): + Civic: str + PrereqTech: str + + +class TechPrereqData(TypedDict): + Technology: str + PrereqTech: str diff --git a/worlds/civ_6/data/new_civic_prereqs.json b/worlds/civ_6/data/new_civic_prereqs.json deleted file mode 100644 index 90da567e873f..000000000000 --- a/worlds/civ_6/data/new_civic_prereqs.json +++ /dev/null @@ -1,144 +0,0 @@ -[ - { "Civic": "CIVIC_AP_ANCIENT_01", "PrereqCivic": "CIVIC_AP_ANCIENT_00" }, - { "Civic": "CIVIC_AP_ANCIENT_02", "PrereqCivic": "CIVIC_AP_ANCIENT_00" }, - { "Civic": "CIVIC_AP_ANCIENT_03", "PrereqCivic": "CIVIC_AP_ANCIENT_01" }, - { "Civic": "CIVIC_AP_ANCIENT_04", "PrereqCivic": "CIVIC_AP_ANCIENT_01" }, - { "Civic": "CIVIC_AP_ANCIENT_05", "PrereqCivic": "CIVIC_AP_ANCIENT_02" }, - { "Civic": "CIVIC_AP_ANCIENT_06", "PrereqCivic": "CIVIC_AP_ANCIENT_02" }, - { "Civic": "CIVIC_AP_CLASSICAL_07", "PrereqCivic": "CIVIC_AP_ANCIENT_04" }, - { "Civic": "CIVIC_AP_CLASSICAL_08", "PrereqCivic": "CIVIC_AP_ANCIENT_04" }, - { "Civic": "CIVIC_AP_CLASSICAL_08", "PrereqCivic": "CIVIC_AP_ANCIENT_05" }, - { "Civic": "CIVIC_AP_CLASSICAL_09", "PrereqCivic": "CIVIC_AP_ANCIENT_05" }, - { "Civic": "CIVIC_AP_CLASSICAL_10", "PrereqCivic": "CIVIC_AP_ANCIENT_03" }, - { "Civic": "CIVIC_AP_CLASSICAL_10", "PrereqCivic": "CIVIC_AP_CLASSICAL_07" }, - { "Civic": "CIVIC_AP_CLASSICAL_11", "PrereqCivic": "CIVIC_AP_CLASSICAL_07" }, - { "Civic": "CIVIC_AP_CLASSICAL_11", "PrereqCivic": "CIVIC_AP_CLASSICAL_08" }, - { "Civic": "CIVIC_AP_CLASSICAL_12", "PrereqCivic": "CIVIC_AP_CLASSICAL_08" }, - { "Civic": "CIVIC_AP_CLASSICAL_12", "PrereqCivic": "CIVIC_AP_CLASSICAL_09" }, - { "Civic": "CIVIC_AP_CLASSICAL_13", "PrereqCivic": "CIVIC_AP_CLASSICAL_09" }, - { "Civic": "CIVIC_AP_CLASSICAL_13", "PrereqCivic": "CIVIC_AP_ANCIENT_06" }, - { "Civic": "CIVIC_AP_MEDIEVAL_14", "PrereqCivic": "CIVIC_AP_CLASSICAL_11" }, - { "Civic": "CIVIC_AP_MEDIEVAL_15", "PrereqCivic": "CIVIC_AP_CLASSICAL_11" }, - { "Civic": "CIVIC_AP_MEDIEVAL_16", "PrereqCivic": "CIVIC_AP_CLASSICAL_11" }, - { "Civic": "CIVIC_AP_MEDIEVAL_16", "PrereqCivic": "CIVIC_AP_CLASSICAL_12" }, - { "Civic": "CIVIC_AP_MEDIEVAL_17", "PrereqCivic": "CIVIC_AP_CLASSICAL_10" }, - { "Civic": "CIVIC_AP_MEDIEVAL_17", "PrereqCivic": "CIVIC_AP_MEDIEVAL_15" }, - { "Civic": "CIVIC_AP_MEDIEVAL_18", "PrereqCivic": "CIVIC_AP_MEDIEVAL_15" }, - { "Civic": "CIVIC_AP_MEDIEVAL_19", "PrereqCivic": "CIVIC_AP_MEDIEVAL_15" }, - { "Civic": "CIVIC_AP_MEDIEVAL_19", "PrereqCivic": "CIVIC_AP_MEDIEVAL_16" }, - { "Civic": "CIVIC_AP_MEDIEVAL_20", "PrereqCivic": "CIVIC_AP_MEDIEVAL_16" }, - { "Civic": "CIVIC_AP_MEDIEVAL_20", "PrereqCivic": "CIVIC_AP_CLASSICAL_13" }, - { "Civic": "CIVIC_AP_RENAISSANCE_21", "PrereqCivic": "CIVIC_AP_MEDIEVAL_17" }, - { "Civic": "CIVIC_AP_RENAISSANCE_21", "PrereqCivic": "CIVIC_AP_MEDIEVAL_18" }, - { "Civic": "CIVIC_AP_RENAISSANCE_22", "PrereqCivic": "CIVIC_AP_MEDIEVAL_18" }, - { "Civic": "CIVIC_AP_RENAISSANCE_22", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19" }, - { "Civic": "CIVIC_AP_RENAISSANCE_23", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19" }, - { "Civic": "CIVIC_AP_RENAISSANCE_24", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19" }, - { "Civic": "CIVIC_AP_RENAISSANCE_24", "PrereqCivic": "CIVIC_AP_MEDIEVAL_20" }, - { - "Civic": "CIVIC_AP_RENAISSANCE_25", - "PrereqCivic": "CIVIC_AP_RENAISSANCE_22" - }, - { - "Civic": "CIVIC_AP_RENAISSANCE_26", - "PrereqCivic": "CIVIC_AP_RENAISSANCE_22" - }, - { - "Civic": "CIVIC_AP_RENAISSANCE_26", - "PrereqCivic": "CIVIC_AP_RENAISSANCE_23" - }, - { - "Civic": "CIVIC_AP_INDUSTRIAL_27", - "PrereqCivic": "CIVIC_AP_RENAISSANCE_25" - }, - { - "Civic": "CIVIC_AP_INDUSTRIAL_28", - "PrereqCivic": "CIVIC_AP_RENAISSANCE_25" - }, - { - "Civic": "CIVIC_AP_INDUSTRIAL_29", - "PrereqCivic": "CIVIC_AP_RENAISSANCE_26" - }, - { - "Civic": "CIVIC_AP_INDUSTRIAL_30", - "PrereqCivic": "CIVIC_AP_RENAISSANCE_26" - }, - { - "Civic": "CIVIC_AP_INDUSTRIAL_31", - "PrereqCivic": "CIVIC_AP_INDUSTRIAL_27" - }, - { - "Civic": "CIVIC_AP_INDUSTRIAL_32", - "PrereqCivic": "CIVIC_AP_INDUSTRIAL_29" - }, - { - "Civic": "CIVIC_AP_INDUSTRIAL_33", - "PrereqCivic": "CIVIC_AP_INDUSTRIAL_28" - }, - { - "Civic": "CIVIC_AP_INDUSTRIAL_33", - "PrereqCivic": "CIVIC_AP_INDUSTRIAL_29" - }, - { "Civic": "CIVIC_AP_MODERN_34", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_31" }, - { "Civic": "CIVIC_AP_MODERN_37", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_31" }, - { "Civic": "CIVIC_AP_MODERN_37", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_33" }, - { "Civic": "CIVIC_AP_MODERN_35", "PrereqCivic": "CIVIC_AP_MODERN_37" }, - { "Civic": "CIVIC_AP_MODERN_38", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_33" }, - { "Civic": "CIVIC_AP_MODERN_39", "PrereqCivic": "CIVIC_AP_MODERN_37" }, - { "Civic": "CIVIC_AP_MODERN_39", "PrereqCivic": "CIVIC_AP_MODERN_38" }, - { "Civic": "CIVIC_AP_MODERN_36", "PrereqCivic": "CIVIC_AP_MODERN_39" }, - { "Civic": "CIVIC_AP_MODERN_40", "PrereqCivic": "CIVIC_AP_MODERN_39" }, - { "Civic": "CIVIC_AP_MODERN_41", "PrereqCivic": "CIVIC_AP_MODERN_39" }, - { "Civic": "CIVIC_AP_MODERN_42", "PrereqCivic": "CIVIC_AP_MODERN_39" }, - { "Civic": "CIVIC_AP_ATOMIC_43", "PrereqCivic": "CIVIC_AP_MODERN_39" }, - { "Civic": "CIVIC_AP_ATOMIC_44", "PrereqCivic": "CIVIC_AP_MODERN_39" }, - { "Civic": "CIVIC_AP_ATOMIC_45", "PrereqCivic": "CIVIC_AP_MODERN_34" }, - { "Civic": "CIVIC_AP_ATOMIC_46", "PrereqCivic": "CIVIC_AP_ATOMIC_43" }, - { "Civic": "CIVIC_AP_ATOMIC_47", "PrereqCivic": "CIVIC_AP_ATOMIC_43" }, - { "Civic": "CIVIC_AP_INFORMATION_48", "PrereqCivic": "CIVIC_AP_ATOMIC_46" }, - { "Civic": "CIVIC_AP_INFORMATION_48", "PrereqCivic": "CIVIC_AP_ATOMIC_47" }, - { "Civic": "CIVIC_AP_INFORMATION_49", "PrereqCivic": "CIVIC_AP_ATOMIC_47" }, - { "Civic": "CIVIC_AP_INFORMATION_49", "PrereqCivic": "CIVIC_AP_ATOMIC_44" }, - { "Civic": "CIVIC_AP_FUTURE_50", "PrereqCivic": "CIVIC_AP_INFORMATION_48" }, - { "Civic": "CIVIC_AP_FUTURE_50", "PrereqCivic": "CIVIC_AP_INFORMATION_49" }, - { "Civic": "CIVIC_AP_MODERN_38", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_32" }, - { "Civic": "CIVIC_AP_INFORMATION_51", "PrereqCivic": "CIVIC_AP_ATOMIC_45" }, - { "Civic": "CIVIC_AP_INFORMATION_51", "PrereqCivic": "CIVIC_AP_ATOMIC_46" }, - { - "Civic": "CIVIC_AP_INFORMATION_52", - "PrereqCivic": "CIVIC_AP_INFORMATION_48" - }, - { - "Civic": "CIVIC_AP_INFORMATION_52", - "PrereqCivic": "CIVIC_AP_INFORMATION_49" - }, - { - "Civic": "CIVIC_AP_INFORMATION_53", - "PrereqCivic": "CIVIC_AP_INFORMATION_48" - }, - { - "Civic": "CIVIC_AP_INFORMATION_53", - "PrereqCivic": "CIVIC_AP_INFORMATION_49" - }, - { - "Civic": "CIVIC_AP_INFORMATION_54", - "PrereqCivic": "CIVIC_AP_INFORMATION_48" - }, - { - "Civic": "CIVIC_AP_INFORMATION_54", - "PrereqCivic": "CIVIC_AP_INFORMATION_49" - }, - { - "Civic": "CIVIC_AP_INFORMATION_55", - "PrereqCivic": "CIVIC_AP_INFORMATION_51" - }, - { - "Civic": "CIVIC_AP_INFORMATION_55", - "PrereqCivic": "CIVIC_AP_INFORMATION_48" - }, - { "Civic": "CIVIC_AP_FUTURE_56", "PrereqCivic": "CIVIC_AP_FUTURE_50" }, - { "Civic": "CIVIC_AP_FUTURE_57", "PrereqCivic": "CIVIC_AP_FUTURE_50" }, - { "Civic": "CIVIC_AP_FUTURE_58", "PrereqCivic": "CIVIC_AP_FUTURE_50" }, - { "Civic": "CIVIC_AP_FUTURE_59", "PrereqCivic": "CIVIC_AP_FUTURE_50" }, - { "Civic": "CIVIC_AP_FUTURE_60", "PrereqCivic": "CIVIC_AP_FUTURE_50" } -] diff --git a/worlds/civ_6/data/new_civic_prereqs.py b/worlds/civ_6/data/new_civic_prereqs.py new file mode 100644 index 000000000000..b070e3596b7b --- /dev/null +++ b/worlds/civ_6/data/new_civic_prereqs.py @@ -0,0 +1,92 @@ +from typing import List + +from worlds.civ_6.ItemData import CivicPrereqData + + +new_civic_prereqs: List[CivicPrereqData] = [ + {"Civic": "CIVIC_AP_ANCIENT_01", "PrereqCivic": "CIVIC_AP_ANCIENT_00"}, + {"Civic": "CIVIC_AP_ANCIENT_02", "PrereqCivic": "CIVIC_AP_ANCIENT_00"}, + {"Civic": "CIVIC_AP_ANCIENT_03", "PrereqCivic": "CIVIC_AP_ANCIENT_01"}, + {"Civic": "CIVIC_AP_ANCIENT_04", "PrereqCivic": "CIVIC_AP_ANCIENT_01"}, + {"Civic": "CIVIC_AP_ANCIENT_05", "PrereqCivic": "CIVIC_AP_ANCIENT_02"}, + {"Civic": "CIVIC_AP_ANCIENT_06", "PrereqCivic": "CIVIC_AP_ANCIENT_02"}, + {"Civic": "CIVIC_AP_CLASSICAL_07", "PrereqCivic": "CIVIC_AP_ANCIENT_04"}, + {"Civic": "CIVIC_AP_CLASSICAL_08", "PrereqCivic": "CIVIC_AP_ANCIENT_04"}, + {"Civic": "CIVIC_AP_CLASSICAL_08", "PrereqCivic": "CIVIC_AP_ANCIENT_05"}, + {"Civic": "CIVIC_AP_CLASSICAL_09", "PrereqCivic": "CIVIC_AP_ANCIENT_05"}, + {"Civic": "CIVIC_AP_CLASSICAL_10", "PrereqCivic": "CIVIC_AP_ANCIENT_03"}, + {"Civic": "CIVIC_AP_CLASSICAL_10", "PrereqCivic": "CIVIC_AP_CLASSICAL_07"}, + {"Civic": "CIVIC_AP_CLASSICAL_11", "PrereqCivic": "CIVIC_AP_CLASSICAL_07"}, + {"Civic": "CIVIC_AP_CLASSICAL_11", "PrereqCivic": "CIVIC_AP_CLASSICAL_08"}, + {"Civic": "CIVIC_AP_CLASSICAL_12", "PrereqCivic": "CIVIC_AP_CLASSICAL_08"}, + {"Civic": "CIVIC_AP_CLASSICAL_12", "PrereqCivic": "CIVIC_AP_CLASSICAL_09"}, + {"Civic": "CIVIC_AP_CLASSICAL_13", "PrereqCivic": "CIVIC_AP_CLASSICAL_09"}, + {"Civic": "CIVIC_AP_CLASSICAL_13", "PrereqCivic": "CIVIC_AP_ANCIENT_06"}, + {"Civic": "CIVIC_AP_MEDIEVAL_14", "PrereqCivic": "CIVIC_AP_CLASSICAL_11"}, + {"Civic": "CIVIC_AP_MEDIEVAL_15", "PrereqCivic": "CIVIC_AP_CLASSICAL_11"}, + {"Civic": "CIVIC_AP_MEDIEVAL_16", "PrereqCivic": "CIVIC_AP_CLASSICAL_11"}, + {"Civic": "CIVIC_AP_MEDIEVAL_16", "PrereqCivic": "CIVIC_AP_CLASSICAL_12"}, + {"Civic": "CIVIC_AP_MEDIEVAL_17", "PrereqCivic": "CIVIC_AP_CLASSICAL_10"}, + {"Civic": "CIVIC_AP_MEDIEVAL_17", "PrereqCivic": "CIVIC_AP_MEDIEVAL_15"}, + {"Civic": "CIVIC_AP_MEDIEVAL_18", "PrereqCivic": "CIVIC_AP_MEDIEVAL_15"}, + {"Civic": "CIVIC_AP_MEDIEVAL_19", "PrereqCivic": "CIVIC_AP_MEDIEVAL_15"}, + {"Civic": "CIVIC_AP_MEDIEVAL_19", "PrereqCivic": "CIVIC_AP_MEDIEVAL_16"}, + {"Civic": "CIVIC_AP_MEDIEVAL_20", "PrereqCivic": "CIVIC_AP_MEDIEVAL_16"}, + {"Civic": "CIVIC_AP_MEDIEVAL_20", "PrereqCivic": "CIVIC_AP_CLASSICAL_13"}, + {"Civic": "CIVIC_AP_RENAISSANCE_21", "PrereqCivic": "CIVIC_AP_MEDIEVAL_17"}, + {"Civic": "CIVIC_AP_RENAISSANCE_21", "PrereqCivic": "CIVIC_AP_MEDIEVAL_18"}, + {"Civic": "CIVIC_AP_RENAISSANCE_22", "PrereqCivic": "CIVIC_AP_MEDIEVAL_18"}, + {"Civic": "CIVIC_AP_RENAISSANCE_22", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19"}, + {"Civic": "CIVIC_AP_RENAISSANCE_23", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19"}, + {"Civic": "CIVIC_AP_RENAISSANCE_24", "PrereqCivic": "CIVIC_AP_MEDIEVAL_19"}, + {"Civic": "CIVIC_AP_RENAISSANCE_24", "PrereqCivic": "CIVIC_AP_MEDIEVAL_20"}, + {"Civic": "CIVIC_AP_RENAISSANCE_25", "PrereqCivic": "CIVIC_AP_RENAISSANCE_22"}, + {"Civic": "CIVIC_AP_RENAISSANCE_26", "PrereqCivic": "CIVIC_AP_RENAISSANCE_22"}, + {"Civic": "CIVIC_AP_RENAISSANCE_26", "PrereqCivic": "CIVIC_AP_RENAISSANCE_23"}, + {"Civic": "CIVIC_AP_INDUSTRIAL_27", "PrereqCivic": "CIVIC_AP_RENAISSANCE_25"}, + {"Civic": "CIVIC_AP_INDUSTRIAL_28", "PrereqCivic": "CIVIC_AP_RENAISSANCE_25"}, + {"Civic": "CIVIC_AP_INDUSTRIAL_29", "PrereqCivic": "CIVIC_AP_RENAISSANCE_26"}, + {"Civic": "CIVIC_AP_INDUSTRIAL_30", "PrereqCivic": "CIVIC_AP_RENAISSANCE_26"}, + {"Civic": "CIVIC_AP_INDUSTRIAL_31", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_27"}, + {"Civic": "CIVIC_AP_INDUSTRIAL_32", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_29"}, + {"Civic": "CIVIC_AP_INDUSTRIAL_33", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_28"}, + {"Civic": "CIVIC_AP_INDUSTRIAL_33", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_29"}, + {"Civic": "CIVIC_AP_MODERN_34", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_31"}, + {"Civic": "CIVIC_AP_MODERN_37", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_31"}, + {"Civic": "CIVIC_AP_MODERN_37", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_33"}, + {"Civic": "CIVIC_AP_MODERN_35", "PrereqCivic": "CIVIC_AP_MODERN_37"}, + {"Civic": "CIVIC_AP_MODERN_38", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_33"}, + {"Civic": "CIVIC_AP_MODERN_39", "PrereqCivic": "CIVIC_AP_MODERN_37"}, + {"Civic": "CIVIC_AP_MODERN_39", "PrereqCivic": "CIVIC_AP_MODERN_38"}, + {"Civic": "CIVIC_AP_MODERN_36", "PrereqCivic": "CIVIC_AP_MODERN_39"}, + {"Civic": "CIVIC_AP_MODERN_40", "PrereqCivic": "CIVIC_AP_MODERN_39"}, + {"Civic": "CIVIC_AP_MODERN_41", "PrereqCivic": "CIVIC_AP_MODERN_39"}, + {"Civic": "CIVIC_AP_MODERN_42", "PrereqCivic": "CIVIC_AP_MODERN_39"}, + {"Civic": "CIVIC_AP_ATOMIC_43", "PrereqCivic": "CIVIC_AP_MODERN_39"}, + {"Civic": "CIVIC_AP_ATOMIC_44", "PrereqCivic": "CIVIC_AP_MODERN_39"}, + {"Civic": "CIVIC_AP_ATOMIC_45", "PrereqCivic": "CIVIC_AP_MODERN_34"}, + {"Civic": "CIVIC_AP_ATOMIC_46", "PrereqCivic": "CIVIC_AP_ATOMIC_43"}, + {"Civic": "CIVIC_AP_ATOMIC_47", "PrereqCivic": "CIVIC_AP_ATOMIC_43"}, + {"Civic": "CIVIC_AP_INFORMATION_48", "PrereqCivic": "CIVIC_AP_ATOMIC_46"}, + {"Civic": "CIVIC_AP_INFORMATION_48", "PrereqCivic": "CIVIC_AP_ATOMIC_47"}, + {"Civic": "CIVIC_AP_INFORMATION_49", "PrereqCivic": "CIVIC_AP_ATOMIC_47"}, + {"Civic": "CIVIC_AP_INFORMATION_49", "PrereqCivic": "CIVIC_AP_ATOMIC_44"}, + {"Civic": "CIVIC_AP_FUTURE_50", "PrereqCivic": "CIVIC_AP_INFORMATION_48"}, + {"Civic": "CIVIC_AP_FUTURE_50", "PrereqCivic": "CIVIC_AP_INFORMATION_49"}, + {"Civic": "CIVIC_AP_MODERN_38", "PrereqCivic": "CIVIC_AP_INDUSTRIAL_32"}, + {"Civic": "CIVIC_AP_INFORMATION_51", "PrereqCivic": "CIVIC_AP_ATOMIC_45"}, + {"Civic": "CIVIC_AP_INFORMATION_51", "PrereqCivic": "CIVIC_AP_ATOMIC_46"}, + {"Civic": "CIVIC_AP_INFORMATION_52", "PrereqCivic": "CIVIC_AP_INFORMATION_48"}, + {"Civic": "CIVIC_AP_INFORMATION_52", "PrereqCivic": "CIVIC_AP_INFORMATION_49"}, + {"Civic": "CIVIC_AP_INFORMATION_53", "PrereqCivic": "CIVIC_AP_INFORMATION_48"}, + {"Civic": "CIVIC_AP_INFORMATION_53", "PrereqCivic": "CIVIC_AP_INFORMATION_49"}, + {"Civic": "CIVIC_AP_INFORMATION_54", "PrereqCivic": "CIVIC_AP_INFORMATION_48"}, + {"Civic": "CIVIC_AP_INFORMATION_54", "PrereqCivic": "CIVIC_AP_INFORMATION_49"}, + {"Civic": "CIVIC_AP_INFORMATION_55", "PrereqCivic": "CIVIC_AP_INFORMATION_51"}, + {"Civic": "CIVIC_AP_INFORMATION_55", "PrereqCivic": "CIVIC_AP_INFORMATION_48"}, + {"Civic": "CIVIC_AP_FUTURE_56", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, + {"Civic": "CIVIC_AP_FUTURE_57", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, + {"Civic": "CIVIC_AP_FUTURE_58", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, + {"Civic": "CIVIC_AP_FUTURE_59", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, + {"Civic": "CIVIC_AP_FUTURE_60", "PrereqCivic": "CIVIC_AP_FUTURE_50"}, +] diff --git a/worlds/civ_6/data/new_tech_prereqs.json b/worlds/civ_6/data/new_tech_prereqs.json deleted file mode 100644 index d70a4b81a99e..000000000000 --- a/worlds/civ_6/data/new_tech_prereqs.json +++ /dev/null @@ -1,207 +0,0 @@ -[ - { "Technology": "TECH_AP_ANCIENT_06", "PrereqTech": "TECH_AP_ANCIENT_01" }, - { "Technology": "TECH_AP_ANCIENT_07", "PrereqTech": "TECH_AP_ANCIENT_00" }, - { "Technology": "TECH_AP_ANCIENT_05", "PrereqTech": "TECH_AP_ANCIENT_00" }, - { "Technology": "TECH_AP_ANCIENT_08", "PrereqTech": "TECH_AP_ANCIENT_02" }, - { "Technology": "TECH_AP_ANCIENT_09", "PrereqTech": "TECH_AP_ANCIENT_02" }, - { "Technology": "TECH_AP_ANCIENT_10", "PrereqTech": "TECH_AP_ANCIENT_02" }, - { "Technology": "TECH_AP_CLASSICAL_15", "PrereqTech": "TECH_AP_ANCIENT_03" }, - { "Technology": "TECH_AP_CLASSICAL_11", "PrereqTech": "TECH_AP_ANCIENT_03" }, - { "Technology": "TECH_AP_CLASSICAL_11", "PrereqTech": "TECH_AP_ANCIENT_04" }, - { "Technology": "TECH_AP_CLASSICAL_12", "PrereqTech": "TECH_AP_ANCIENT_07" }, - { "Technology": "TECH_AP_CLASSICAL_13", "PrereqTech": "TECH_AP_ANCIENT_06" }, - { "Technology": "TECH_AP_CLASSICAL_14", "PrereqTech": "TECH_AP_ANCIENT_09" }, - { - "Technology": "TECH_AP_CLASSICAL_16", - "PrereqTech": "TECH_AP_CLASSICAL_12" - }, - { "Technology": "TECH_AP_CLASSICAL_17", "PrereqTech": "TECH_AP_ANCIENT_08" }, - { - "Technology": "TECH_AP_CLASSICAL_17", - "PrereqTech": "TECH_AP_CLASSICAL_13" - }, - { "Technology": "TECH_AP_CLASSICAL_18", "PrereqTech": "TECH_AP_ANCIENT_10" }, - { "Technology": "TECH_AP_MEDIEVAL_19", "PrereqTech": "TECH_AP_CLASSICAL_16" }, - { "Technology": "TECH_AP_MEDIEVAL_20", "PrereqTech": "TECH_AP_CLASSICAL_12" }, - { "Technology": "TECH_AP_MEDIEVAL_20", "PrereqTech": "TECH_AP_CLASSICAL_13" }, - { "Technology": "TECH_AP_MEDIEVAL_23", "PrereqTech": "TECH_AP_CLASSICAL_13" }, - { "Technology": "TECH_AP_MEDIEVAL_21", "PrereqTech": "TECH_AP_CLASSICAL_14" }, - { "Technology": "TECH_AP_MEDIEVAL_21", "PrereqTech": "TECH_AP_CLASSICAL_18" }, - { "Technology": "TECH_AP_MEDIEVAL_22", "PrereqTech": "TECH_AP_CLASSICAL_16" }, - { "Technology": "TECH_AP_MEDIEVAL_22", "PrereqTech": "TECH_AP_MEDIEVAL_20" }, - { "Technology": "TECH_AP_MEDIEVAL_25", "PrereqTech": "TECH_AP_CLASSICAL_17" }, - { "Technology": "TECH_AP_MEDIEVAL_24", "PrereqTech": "TECH_AP_CLASSICAL_17" }, - { - "Technology": "TECH_AP_RENAISSANCE_27", - "PrereqTech": "TECH_AP_MEDIEVAL_22" - }, - { - "Technology": "TECH_AP_RENAISSANCE_28", - "PrereqTech": "TECH_AP_MEDIEVAL_22" - }, - { - "Technology": "TECH_AP_RENAISSANCE_28", - "PrereqTech": "TECH_AP_MEDIEVAL_23" - }, - { - "Technology": "TECH_AP_RENAISSANCE_29", - "PrereqTech": "TECH_AP_MEDIEVAL_20" - }, - { - "Technology": "TECH_AP_RENAISSANCE_29", - "PrereqTech": "TECH_AP_MEDIEVAL_23" - }, - { - "Technology": "TECH_AP_RENAISSANCE_29", - "PrereqTech": "TECH_AP_MEDIEVAL_24" - }, - { - "Technology": "TECH_AP_RENAISSANCE_30", - "PrereqTech": "TECH_AP_MEDIEVAL_21" - }, - { - "Technology": "TECH_AP_RENAISSANCE_31", - "PrereqTech": "TECH_AP_RENAISSANCE_26" - }, - { - "Technology": "TECH_AP_RENAISSANCE_32", - "PrereqTech": "TECH_AP_MEDIEVAL_22" - }, - { - "Technology": "TECH_AP_RENAISSANCE_33", - "PrereqTech": "TECH_AP_RENAISSANCE_29" - }, - { - "Technology": "TECH_AP_RENAISSANCE_34", - "PrereqTech": "TECH_AP_MEDIEVAL_25" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_35", - "PrereqTech": "TECH_AP_RENAISSANCE_31" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_35", - "PrereqTech": "TECH_AP_RENAISSANCE_27" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_36", - "PrereqTech": "TECH_AP_RENAISSANCE_32" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_36", - "PrereqTech": "TECH_AP_RENAISSANCE_28" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_41", - "PrereqTech": "TECH_AP_INDUSTRIAL_36" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_41", - "PrereqTech": "TECH_AP_RENAISSANCE_33" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_38", - "PrereqTech": "TECH_AP_RENAISSANCE_34" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_38", - "PrereqTech": "TECH_AP_RENAISSANCE_30" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_39", - "PrereqTech": "TECH_AP_INDUSTRIAL_35" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_40", - "PrereqTech": "TECH_AP_INDUSTRIAL_36" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_37", - "PrereqTech": "TECH_AP_RENAISSANCE_33" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_42", - "PrereqTech": "TECH_AP_INDUSTRIAL_37" - }, - { - "Technology": "TECH_AP_INDUSTRIAL_42", - "PrereqTech": "TECH_AP_INDUSTRIAL_38" - }, - { "Technology": "TECH_AP_MODERN_43", "PrereqTech": "TECH_AP_INDUSTRIAL_35" }, - { "Technology": "TECH_AP_MODERN_43", "PrereqTech": "TECH_AP_INDUSTRIAL_36" }, - { "Technology": "TECH_AP_MODERN_44", "PrereqTech": "TECH_AP_INDUSTRIAL_41" }, - { "Technology": "TECH_AP_MODERN_45", "PrereqTech": "TECH_AP_INDUSTRIAL_42" }, - { "Technology": "TECH_AP_MODERN_46", "PrereqTech": "TECH_AP_INDUSTRIAL_39" }, - { "Technology": "TECH_AP_MODERN_47", "PrereqTech": "TECH_AP_INDUSTRIAL_39" }, - { "Technology": "TECH_AP_MODERN_47", "PrereqTech": "TECH_AP_MODERN_43" }, - { "Technology": "TECH_AP_MODERN_48", "PrereqTech": "TECH_AP_INDUSTRIAL_40" }, - { "Technology": "TECH_AP_MODERN_49", "PrereqTech": "TECH_AP_MODERN_45" }, - { "Technology": "TECH_AP_ATOMIC_55", "PrereqTech": "TECH_AP_MODERN_46" }, - { "Technology": "TECH_AP_ATOMIC_55", "PrereqTech": "TECH_AP_MODERN_47" }, - { "Technology": "TECH_AP_ATOMIC_50", "PrereqTech": "TECH_AP_MODERN_47" }, - { "Technology": "TECH_AP_ATOMIC_51", "PrereqTech": "TECH_AP_MODERN_47" }, - { "Technology": "TECH_AP_ATOMIC_51", "PrereqTech": "TECH_AP_MODERN_48" }, - { "Technology": "TECH_AP_ATOMIC_52", "PrereqTech": "TECH_AP_MODERN_44" }, - { "Technology": "TECH_AP_ATOMIC_52", "PrereqTech": "TECH_AP_MODERN_45" }, - { "Technology": "TECH_AP_ATOMIC_53", "PrereqTech": "TECH_AP_MODERN_45" }, - { "Technology": "TECH_AP_ATOMIC_53", "PrereqTech": "TECH_AP_MODERN_49" }, - { "Technology": "TECH_AP_ATOMIC_56", "PrereqTech": "TECH_AP_ATOMIC_52" }, - { "Technology": "TECH_AP_ATOMIC_56", "PrereqTech": "TECH_AP_ATOMIC_53" }, - { "Technology": "TECH_AP_ATOMIC_54", "PrereqTech": "TECH_AP_MODERN_49" }, - { "Technology": "TECH_AP_ATOMIC_57", "PrereqTech": "TECH_AP_ATOMIC_54" }, - { "Technology": "TECH_AP_INFORMATION_58", "PrereqTech": "TECH_AP_ATOMIC_55" }, - { "Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_ATOMIC_55" }, - { "Technology": "TECH_AP_INFORMATION_59", "PrereqTech": "TECH_AP_ATOMIC_50" }, - { "Technology": "TECH_AP_INFORMATION_59", "PrereqTech": "TECH_AP_ATOMIC_51" }, - { "Technology": "TECH_AP_INFORMATION_60", "PrereqTech": "TECH_AP_ATOMIC_51" }, - { "Technology": "TECH_AP_INFORMATION_60", "PrereqTech": "TECH_AP_ATOMIC_52" }, - { "Technology": "TECH_AP_INFORMATION_61", "PrereqTech": "TECH_AP_ATOMIC_56" }, - { "Technology": "TECH_AP_INFORMATION_62", "PrereqTech": "TECH_AP_ATOMIC_57" }, - { "Technology": "TECH_AP_INFORMATION_63", "PrereqTech": "TECH_AP_ATOMIC_57" }, - { - "Technology": "TECH_AP_INFORMATION_65", - "PrereqTech": "TECH_AP_INFORMATION_62" - }, - { - "Technology": "TECH_AP_INFORMATION_66", - "PrereqTech": "TECH_AP_INFORMATION_61" - }, - { "Technology": "TECH_AP_MEDIEVAL_67", "PrereqTech": "TECH_AP_CLASSICAL_15" }, - { "Technology": "TECH_AP_MEDIEVAL_67", "PrereqTech": "TECH_AP_CLASSICAL_16" }, - { "Technology": "TECH_AP_MEDIEVAL_23", "PrereqTech": "TECH_AP_MEDIEVAL_20" }, - { "Technology": "TECH_AP_MODERN_68", "PrereqTech": "TECH_AP_INDUSTRIAL_42" }, - { "Technology": "TECH_AP_MODERN_49", "PrereqTech": "TECH_AP_MODERN_68" }, - { - "Technology": "TECH_AP_RENAISSANCE_26", - "PrereqTech": "TECH_AP_MEDIEVAL_67" - }, - { - "Technology": "TECH_AP_RENAISSANCE_27", - "PrereqTech": "TECH_AP_MEDIEVAL_67" - }, - { - "Technology": "TECH_AP_RENAISSANCE_27", - "PrereqTech": "TECH_AP_MEDIEVAL_19" - }, - { "Technology": "TECH_AP_MODERN_48", "PrereqTech": "TECH_AP_MODERN_44" }, - { - "Technology": "TECH_AP_INFORMATION_64", - "PrereqTech": "TECH_AP_INFORMATION_59" - }, - { - "Technology": "TECH_AP_INFORMATION_64", - "PrereqTech": "TECH_AP_INFORMATION_60" - }, - { - "Technology": "TECH_AP_INFORMATION_64", - "PrereqTech": "TECH_AP_INFORMATION_61" - }, - { "Technology": "TECH_AP_FUTURE_69", "PrereqTech": "TECH_AP_AP60" }, - { "Technology": "TECH_AP_FUTURE_70", "PrereqTech": "TECH_AP_AP60" }, - { "Technology": "TECH_AP_FUTURE_71", "PrereqTech": "TECH_AP_AP60" }, - { "Technology": "TECH_AP_FUTURE_72", "PrereqTech": "TECH_AP_AP60" }, - { "Technology": "TECH_AP_FUTURE_73", "PrereqTech": "TECH_AP_AP60" }, - { "Technology": "TECH_AP_FUTURE_74", "PrereqTech": "TECH_AP_AP60" }, - { "Technology": "TECH_AP_FUTURE_75", "PrereqTech": "TECH_AP_AP60" }, - { "Technology": "TECH_AP_FUTURE_76", "PrereqTech": "TECH_AP_AP60" } -] diff --git a/worlds/civ_6/data/new_tech_prereqs.py b/worlds/civ_6/data/new_tech_prereqs.py new file mode 100644 index 000000000000..45cc2b454423 --- /dev/null +++ b/worlds/civ_6/data/new_tech_prereqs.py @@ -0,0 +1,110 @@ +from typing import List + +from worlds.civ_6.ItemData import TechPrereqData + + +new_tech_prereqs: List[TechPrereqData] = [ + {"Technology": "TECH_AP_ANCIENT_06", "PrereqTech": "TECH_AP_ANCIENT_01"}, + {"Technology": "TECH_AP_ANCIENT_07", "PrereqTech": "TECH_AP_ANCIENT_00"}, + {"Technology": "TECH_AP_ANCIENT_05", "PrereqTech": "TECH_AP_ANCIENT_00"}, + {"Technology": "TECH_AP_ANCIENT_08", "PrereqTech": "TECH_AP_ANCIENT_02"}, + {"Technology": "TECH_AP_ANCIENT_09", "PrereqTech": "TECH_AP_ANCIENT_02"}, + {"Technology": "TECH_AP_ANCIENT_10", "PrereqTech": "TECH_AP_ANCIENT_02"}, + {"Technology": "TECH_AP_CLASSICAL_15", "PrereqTech": "TECH_AP_ANCIENT_03"}, + {"Technology": "TECH_AP_CLASSICAL_11", "PrereqTech": "TECH_AP_ANCIENT_03"}, + {"Technology": "TECH_AP_CLASSICAL_11", "PrereqTech": "TECH_AP_ANCIENT_04"}, + {"Technology": "TECH_AP_CLASSICAL_12", "PrereqTech": "TECH_AP_ANCIENT_07"}, + {"Technology": "TECH_AP_CLASSICAL_13", "PrereqTech": "TECH_AP_ANCIENT_06"}, + {"Technology": "TECH_AP_CLASSICAL_14", "PrereqTech": "TECH_AP_ANCIENT_09"}, + {"Technology": "TECH_AP_CLASSICAL_16", "PrereqTech": "TECH_AP_CLASSICAL_12"}, + {"Technology": "TECH_AP_CLASSICAL_17", "PrereqTech": "TECH_AP_ANCIENT_08"}, + {"Technology": "TECH_AP_CLASSICAL_17", "PrereqTech": "TECH_AP_CLASSICAL_13"}, + {"Technology": "TECH_AP_CLASSICAL_18", "PrereqTech": "TECH_AP_ANCIENT_10"}, + {"Technology": "TECH_AP_MEDIEVAL_19", "PrereqTech": "TECH_AP_CLASSICAL_16"}, + {"Technology": "TECH_AP_MEDIEVAL_20", "PrereqTech": "TECH_AP_CLASSICAL_12"}, + {"Technology": "TECH_AP_MEDIEVAL_20", "PrereqTech": "TECH_AP_CLASSICAL_13"}, + {"Technology": "TECH_AP_MEDIEVAL_23", "PrereqTech": "TECH_AP_CLASSICAL_13"}, + {"Technology": "TECH_AP_MEDIEVAL_21", "PrereqTech": "TECH_AP_CLASSICAL_14"}, + {"Technology": "TECH_AP_MEDIEVAL_21", "PrereqTech": "TECH_AP_CLASSICAL_18"}, + {"Technology": "TECH_AP_MEDIEVAL_22", "PrereqTech": "TECH_AP_CLASSICAL_16"}, + {"Technology": "TECH_AP_MEDIEVAL_22", "PrereqTech": "TECH_AP_MEDIEVAL_20"}, + {"Technology": "TECH_AP_MEDIEVAL_25", "PrereqTech": "TECH_AP_CLASSICAL_17"}, + {"Technology": "TECH_AP_MEDIEVAL_24", "PrereqTech": "TECH_AP_CLASSICAL_17"}, + {"Technology": "TECH_AP_RENAISSANCE_27", "PrereqTech": "TECH_AP_MEDIEVAL_22"}, + {"Technology": "TECH_AP_RENAISSANCE_28", "PrereqTech": "TECH_AP_MEDIEVAL_22"}, + {"Technology": "TECH_AP_RENAISSANCE_28", "PrereqTech": "TECH_AP_MEDIEVAL_23"}, + {"Technology": "TECH_AP_RENAISSANCE_29", "PrereqTech": "TECH_AP_MEDIEVAL_20"}, + {"Technology": "TECH_AP_RENAISSANCE_29", "PrereqTech": "TECH_AP_MEDIEVAL_23"}, + {"Technology": "TECH_AP_RENAISSANCE_29", "PrereqTech": "TECH_AP_MEDIEVAL_24"}, + {"Technology": "TECH_AP_RENAISSANCE_30", "PrereqTech": "TECH_AP_MEDIEVAL_21"}, + {"Technology": "TECH_AP_RENAISSANCE_31", "PrereqTech": "TECH_AP_RENAISSANCE_26"}, + {"Technology": "TECH_AP_RENAISSANCE_32", "PrereqTech": "TECH_AP_MEDIEVAL_22"}, + {"Technology": "TECH_AP_RENAISSANCE_33", "PrereqTech": "TECH_AP_RENAISSANCE_29"}, + {"Technology": "TECH_AP_RENAISSANCE_34", "PrereqTech": "TECH_AP_MEDIEVAL_25"}, + {"Technology": "TECH_AP_INDUSTRIAL_35", "PrereqTech": "TECH_AP_RENAISSANCE_31"}, + {"Technology": "TECH_AP_INDUSTRIAL_35", "PrereqTech": "TECH_AP_RENAISSANCE_27"}, + {"Technology": "TECH_AP_INDUSTRIAL_36", "PrereqTech": "TECH_AP_RENAISSANCE_32"}, + {"Technology": "TECH_AP_INDUSTRIAL_36", "PrereqTech": "TECH_AP_RENAISSANCE_28"}, + {"Technology": "TECH_AP_INDUSTRIAL_41", "PrereqTech": "TECH_AP_INDUSTRIAL_36"}, + {"Technology": "TECH_AP_INDUSTRIAL_41", "PrereqTech": "TECH_AP_RENAISSANCE_33"}, + {"Technology": "TECH_AP_INDUSTRIAL_38", "PrereqTech": "TECH_AP_RENAISSANCE_34"}, + {"Technology": "TECH_AP_INDUSTRIAL_38", "PrereqTech": "TECH_AP_RENAISSANCE_30"}, + {"Technology": "TECH_AP_INDUSTRIAL_39", "PrereqTech": "TECH_AP_INDUSTRIAL_35"}, + {"Technology": "TECH_AP_INDUSTRIAL_40", "PrereqTech": "TECH_AP_INDUSTRIAL_36"}, + {"Technology": "TECH_AP_INDUSTRIAL_37", "PrereqTech": "TECH_AP_RENAISSANCE_33"}, + {"Technology": "TECH_AP_INDUSTRIAL_42", "PrereqTech": "TECH_AP_INDUSTRIAL_37"}, + {"Technology": "TECH_AP_INDUSTRIAL_42", "PrereqTech": "TECH_AP_INDUSTRIAL_38"}, + {"Technology": "TECH_AP_MODERN_43", "PrereqTech": "TECH_AP_INDUSTRIAL_35"}, + {"Technology": "TECH_AP_MODERN_43", "PrereqTech": "TECH_AP_INDUSTRIAL_36"}, + {"Technology": "TECH_AP_MODERN_44", "PrereqTech": "TECH_AP_INDUSTRIAL_41"}, + {"Technology": "TECH_AP_MODERN_45", "PrereqTech": "TECH_AP_INDUSTRIAL_42"}, + {"Technology": "TECH_AP_MODERN_46", "PrereqTech": "TECH_AP_INDUSTRIAL_39"}, + {"Technology": "TECH_AP_MODERN_47", "PrereqTech": "TECH_AP_INDUSTRIAL_39"}, + {"Technology": "TECH_AP_MODERN_47", "PrereqTech": "TECH_AP_MODERN_43"}, + {"Technology": "TECH_AP_MODERN_48", "PrereqTech": "TECH_AP_INDUSTRIAL_40"}, + {"Technology": "TECH_AP_MODERN_49", "PrereqTech": "TECH_AP_MODERN_45"}, + {"Technology": "TECH_AP_ATOMIC_55", "PrereqTech": "TECH_AP_MODERN_46"}, + {"Technology": "TECH_AP_ATOMIC_55", "PrereqTech": "TECH_AP_MODERN_47"}, + {"Technology": "TECH_AP_ATOMIC_50", "PrereqTech": "TECH_AP_MODERN_47"}, + {"Technology": "TECH_AP_ATOMIC_51", "PrereqTech": "TECH_AP_MODERN_47"}, + {"Technology": "TECH_AP_ATOMIC_51", "PrereqTech": "TECH_AP_MODERN_48"}, + {"Technology": "TECH_AP_ATOMIC_52", "PrereqTech": "TECH_AP_MODERN_44"}, + {"Technology": "TECH_AP_ATOMIC_52", "PrereqTech": "TECH_AP_MODERN_45"}, + {"Technology": "TECH_AP_ATOMIC_53", "PrereqTech": "TECH_AP_MODERN_45"}, + {"Technology": "TECH_AP_ATOMIC_53", "PrereqTech": "TECH_AP_MODERN_49"}, + {"Technology": "TECH_AP_ATOMIC_56", "PrereqTech": "TECH_AP_ATOMIC_52"}, + {"Technology": "TECH_AP_ATOMIC_56", "PrereqTech": "TECH_AP_ATOMIC_53"}, + {"Technology": "TECH_AP_ATOMIC_54", "PrereqTech": "TECH_AP_MODERN_49"}, + {"Technology": "TECH_AP_ATOMIC_57", "PrereqTech": "TECH_AP_ATOMIC_54"}, + {"Technology": "TECH_AP_INFORMATION_58", "PrereqTech": "TECH_AP_ATOMIC_55"}, + {"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_ATOMIC_55"}, + {"Technology": "TECH_AP_INFORMATION_59", "PrereqTech": "TECH_AP_ATOMIC_50"}, + {"Technology": "TECH_AP_INFORMATION_59", "PrereqTech": "TECH_AP_ATOMIC_51"}, + {"Technology": "TECH_AP_INFORMATION_60", "PrereqTech": "TECH_AP_ATOMIC_51"}, + {"Technology": "TECH_AP_INFORMATION_60", "PrereqTech": "TECH_AP_ATOMIC_52"}, + {"Technology": "TECH_AP_INFORMATION_61", "PrereqTech": "TECH_AP_ATOMIC_56"}, + {"Technology": "TECH_AP_INFORMATION_62", "PrereqTech": "TECH_AP_ATOMIC_57"}, + {"Technology": "TECH_AP_INFORMATION_63", "PrereqTech": "TECH_AP_ATOMIC_57"}, + {"Technology": "TECH_AP_INFORMATION_65", "PrereqTech": "TECH_AP_INFORMATION_62"}, + {"Technology": "TECH_AP_INFORMATION_66", "PrereqTech": "TECH_AP_INFORMATION_61"}, + {"Technology": "TECH_AP_MEDIEVAL_67", "PrereqTech": "TECH_AP_CLASSICAL_15"}, + {"Technology": "TECH_AP_MEDIEVAL_67", "PrereqTech": "TECH_AP_CLASSICAL_16"}, + {"Technology": "TECH_AP_MEDIEVAL_23", "PrereqTech": "TECH_AP_MEDIEVAL_20"}, + {"Technology": "TECH_AP_MODERN_68", "PrereqTech": "TECH_AP_INDUSTRIAL_42"}, + {"Technology": "TECH_AP_MODERN_49", "PrereqTech": "TECH_AP_MODERN_68"}, + {"Technology": "TECH_AP_RENAISSANCE_26", "PrereqTech": "TECH_AP_MEDIEVAL_67"}, + {"Technology": "TECH_AP_RENAISSANCE_27", "PrereqTech": "TECH_AP_MEDIEVAL_67"}, + {"Technology": "TECH_AP_RENAISSANCE_27", "PrereqTech": "TECH_AP_MEDIEVAL_19"}, + {"Technology": "TECH_AP_MODERN_48", "PrereqTech": "TECH_AP_MODERN_44"}, + {"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_INFORMATION_59"}, + {"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_INFORMATION_60"}, + {"Technology": "TECH_AP_INFORMATION_64", "PrereqTech": "TECH_AP_INFORMATION_61"}, + {"Technology": "TECH_AP_FUTURE_69", "PrereqTech": "TECH_AP_AP60"}, + {"Technology": "TECH_AP_FUTURE_70", "PrereqTech": "TECH_AP_AP60"}, + {"Technology": "TECH_AP_FUTURE_71", "PrereqTech": "TECH_AP_AP60"}, + {"Technology": "TECH_AP_FUTURE_72", "PrereqTech": "TECH_AP_AP60"}, + {"Technology": "TECH_AP_FUTURE_73", "PrereqTech": "TECH_AP_AP60"}, + {"Technology": "TECH_AP_FUTURE_74", "PrereqTech": "TECH_AP_AP60"}, + {"Technology": "TECH_AP_FUTURE_75", "PrereqTech": "TECH_AP_AP60"}, + {"Technology": "TECH_AP_FUTURE_76", "PrereqTech": "TECH_AP_AP60"}, +] From 9217969014b461eb157d25561dcf9d177e7b8192 Mon Sep 17 00:00:00 2001 From: hesto2 Date: Sun, 1 Dec 2024 13:50:03 -0700 Subject: [PATCH 68/69] Update new items and progressive districts --- worlds/civ_6/Data.py | 12 +- worlds/civ_6/data/boosts.py | 2 +- worlds/civ_6/data/existing_civics.py | 2 +- worlds/civ_6/data/existing_tech.py | 2 +- worlds/civ_6/data/goody_hut_rewards.py | 2 +- worlds/civ_6/data/new_civic_prereqs.py | 2 +- worlds/civ_6/data/new_civics.json | 368 --------------- worlds/civ_6/data/new_civics.py | 372 +++++++++++++++ worlds/civ_6/data/new_tech.json | 464 ------------------ worlds/civ_6/data/new_tech.py | 468 +++++++++++++++++++ worlds/civ_6/data/new_tech_prereqs.py | 2 +- worlds/civ_6/data/progressive_districts.json | 57 --- worlds/civ_6/data/progressive_districts.py | 41 ++ 13 files changed, 896 insertions(+), 898 deletions(-) delete mode 100644 worlds/civ_6/data/new_civics.json create mode 100644 worlds/civ_6/data/new_civics.py delete mode 100644 worlds/civ_6/data/new_tech.json create mode 100644 worlds/civ_6/data/new_tech.py delete mode 100644 worlds/civ_6/data/progressive_districts.json create mode 100644 worlds/civ_6/data/progressive_districts.py diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index e06fb15b2bd4..7a68494b956a 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -65,7 +65,9 @@ def get_new_civic_prereqs_data() -> List[CivicPrereqData]: def get_new_civics_data() -> List[NewItemData]: - return _get_data("new_civics") + from .data.new_civics import new_civics + + return new_civics def get_new_tech_prereqs_data() -> List[TechPrereqData]: @@ -75,8 +77,12 @@ def get_new_tech_prereqs_data() -> List[TechPrereqData]: def get_new_techs_data() -> List[NewItemData]: - return _get_data("new_tech") + from .data.new_tech import new_tech + + return new_tech def get_progressive_districts_data() -> Dict[str, List[str]]: - return _get_data("progressive_districts") + from .data.progressive_districts import progressive_districts + + return progressive_districts diff --git a/worlds/civ_6/data/boosts.py b/worlds/civ_6/data/boosts.py index 33f357415fb8..089b303b124b 100644 --- a/worlds/civ_6/data/boosts.py +++ b/worlds/civ_6/data/boosts.py @@ -1,7 +1,7 @@ from dataclasses import dataclass from typing import List -from worlds.civ_6.ItemData import CivVIBoostData +from ..ItemData import CivVIBoostData boosts: List[CivVIBoostData] = [ diff --git a/worlds/civ_6/data/existing_civics.py b/worlds/civ_6/data/existing_civics.py index b7f2583301fb..662110ab6778 100644 --- a/worlds/civ_6/data/existing_civics.py +++ b/worlds/civ_6/data/existing_civics.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING, List if TYPE_CHECKING: - from worlds.civ_6.Data import ExistingItemData + from ..Data import ExistingItemData existing_civics: List["ExistingItemData"] = [ diff --git a/worlds/civ_6/data/existing_tech.py b/worlds/civ_6/data/existing_tech.py index 87c06817b341..3ff39f7a59fc 100644 --- a/worlds/civ_6/data/existing_tech.py +++ b/worlds/civ_6/data/existing_tech.py @@ -1,6 +1,6 @@ from typing import List -from worlds.civ_6.ItemData import ExistingItemData +from ..ItemData import ExistingItemData existing_tech: List[ExistingItemData] = [ diff --git a/worlds/civ_6/data/goody_hut_rewards.py b/worlds/civ_6/data/goody_hut_rewards.py index 99aefc0a7f9a..8bef7d8159f3 100644 --- a/worlds/civ_6/data/goody_hut_rewards.py +++ b/worlds/civ_6/data/goody_hut_rewards.py @@ -1,5 +1,5 @@ from typing import List -from worlds.civ_6.ItemData import GoodyHutRewardData +from ..ItemData import GoodyHutRewardData reward_data: List[GoodyHutRewardData] = [ diff --git a/worlds/civ_6/data/new_civic_prereqs.py b/worlds/civ_6/data/new_civic_prereqs.py index b070e3596b7b..0390c2d08ca6 100644 --- a/worlds/civ_6/data/new_civic_prereqs.py +++ b/worlds/civ_6/data/new_civic_prereqs.py @@ -1,6 +1,6 @@ from typing import List -from worlds.civ_6.ItemData import CivicPrereqData +from ..ItemData import CivicPrereqData new_civic_prereqs: List[CivicPrereqData] = [ diff --git a/worlds/civ_6/data/new_civics.json b/worlds/civ_6/data/new_civics.json deleted file mode 100644 index e52478101df0..000000000000 --- a/worlds/civ_6/data/new_civics.json +++ /dev/null @@ -1,368 +0,0 @@ -[ - { - "Type": "CIVIC_AP_ANCIENT_00", - "Cost": 20, - "UITreeRow": 0, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "CIVIC_AP_ANCIENT_01", - "Cost": 40, - "UITreeRow": -2, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "CIVIC_AP_ANCIENT_02", - "Cost": 40, - "UITreeRow": 2, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "CIVIC_AP_ANCIENT_03", - "Cost": 50, - "UITreeRow": -3, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "CIVIC_AP_ANCIENT_04", - "Cost": 70, - "UITreeRow": 0, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "CIVIC_AP_ANCIENT_05", - "Cost": 70, - "UITreeRow": 1, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "CIVIC_AP_ANCIENT_06", - "Cost": 50, - "UITreeRow": 3, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "CIVIC_AP_CLASSICAL_07", - "Cost": 110, - "UITreeRow": -2, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "CIVIC_AP_CLASSICAL_08", - "Cost": 110, - "UITreeRow": 0, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "CIVIC_AP_CLASSICAL_09", - "Cost": 110, - "UITreeRow": 2, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "CIVIC_AP_CLASSICAL_10", - "Cost": 120, - "UITreeRow": -3, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "CIVIC_AP_CLASSICAL_11", - "Cost": 175, - "UITreeRow": -1, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "CIVIC_AP_CLASSICAL_12", - "Cost": 175, - "UITreeRow": 1, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "CIVIC_AP_CLASSICAL_13", - "Cost": 120, - "UITreeRow": 3, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "CIVIC_AP_MEDIEVAL_14", - "Cost": 220, - "UITreeRow": -2, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "CIVIC_AP_MEDIEVAL_15", - "Cost": 300, - "UITreeRow": -1, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "CIVIC_AP_MEDIEVAL_16", - "Cost": 300, - "UITreeRow": 1, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "CIVIC_AP_MEDIEVAL_17", - "Cost": 340, - "UITreeRow": -3, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "CIVIC_AP_MEDIEVAL_18", - "Cost": 420, - "UITreeRow": -1, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "CIVIC_AP_MEDIEVAL_19", - "Cost": 420, - "UITreeRow": 1, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "CIVIC_AP_MEDIEVAL_20", - "Cost": 340, - "UITreeRow": 3, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "CIVIC_AP_RENAISSANCE_21", - "Cost": 440, - "UITreeRow": -3, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "CIVIC_AP_RENAISSANCE_22", - "Cost": 600, - "UITreeRow": -1, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "CIVIC_AP_RENAISSANCE_23", - "Cost": 600, - "UITreeRow": 1, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "CIVIC_AP_RENAISSANCE_24", - "Cost": 440, - "UITreeRow": 3, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "CIVIC_AP_RENAISSANCE_25", - "Cost": 720, - "UITreeRow": -1, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "CIVIC_AP_RENAISSANCE_26", - "Cost": 720, - "UITreeRow": 1, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "CIVIC_AP_INDUSTRIAL_27", - "Cost": 800, - "UITreeRow": -3, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "CIVIC_AP_INDUSTRIAL_28", - "Cost": 1010, - "UITreeRow": -1, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "CIVIC_AP_INDUSTRIAL_29", - "Cost": 1010, - "UITreeRow": 0, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "CIVIC_AP_INDUSTRIAL_30", - "Cost": 800, - "UITreeRow": 2, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "CIVIC_AP_INDUSTRIAL_31", - "Cost": 1050, - "UITreeRow": -3, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "CIVIC_AP_INDUSTRIAL_32", - "Cost": 1210, - "UITreeRow": 2, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "CIVIC_AP_INDUSTRIAL_33", - "Cost": 1210, - "UITreeRow": -1, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "CIVIC_AP_MODERN_34", - "Cost": 1540, - "UITreeRow": -3, - "EraType": "ERA_MODERN" - }, - { - "Type": "CIVIC_AP_MODERN_35", - "Cost": 1580, - "UITreeRow": -2, - "EraType": "ERA_MODERN" - }, - { - "Type": "CIVIC_AP_MODERN_36", - "Cost": 1715, - "UITreeRow": -2, - "EraType": "ERA_MODERN" - }, - { - "Type": "CIVIC_AP_MODERN_37", - "Cost": 1540, - "UITreeRow": -1, - "EraType": "ERA_MODERN" - }, - { - "Type": "CIVIC_AP_MODERN_38", - "Cost": 1540, - "UITreeRow": 1, - "EraType": "ERA_MODERN" - }, - { - "Type": "CIVIC_AP_MODERN_39", - "Cost": 1640, - "UITreeRow": -1, - "EraType": "ERA_MODERN" - }, - { - "Type": "CIVIC_AP_MODERN_40", - "Cost": 1640, - "UITreeRow": 0, - "EraType": "ERA_MODERN" - }, - { - "Type": "CIVIC_AP_MODERN_41", - "Cost": 1640, - "UITreeRow": 2, - "EraType": "ERA_MODERN" - }, - { - "Type": "CIVIC_AP_MODERN_42", - "Cost": 1640, - "UITreeRow": 3, - "EraType": "ERA_MODERN" - }, - { - "Type": "CIVIC_AP_ATOMIC_43", - "Cost": 2185, - "UITreeRow": -1, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "CIVIC_AP_ATOMIC_44", - "Cost": 2185, - "UITreeRow": 2, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "CIVIC_AP_ATOMIC_45", - "Cost": 1955, - "UITreeRow": -3, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "CIVIC_AP_ATOMIC_46", - "Cost": 2415, - "UITreeRow": -1, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "CIVIC_AP_ATOMIC_47", - "Cost": 2415, - "UITreeRow": 1, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "CIVIC_AP_INFORMATION_48", - "Cost": 2880, - "UITreeRow": 0, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "CIVIC_AP_INFORMATION_49", - "Cost": 2880, - "UITreeRow": 2, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "CIVIC_AP_FUTURE_50", - "Cost": 3200, - "UITreeRow": 3, - "EraType": "ERA_FUTURE" - }, - { - "Type": "CIVIC_AP_INFORMATION_51", - "Cost": 2880, - "UITreeRow": -2, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "CIVIC_AP_INFORMATION_52", - "Cost": 3000, - "UITreeRow": 0, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "CIVIC_AP_INFORMATION_53", - "Cost": 3000, - "UITreeRow": 1, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "CIVIC_AP_INFORMATION_54", - "Cost": 3000, - "UITreeRow": 2, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "CIVIC_AP_INFORMATION_55", - "Cost": 3100, - "UITreeRow": -1, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "CIVIC_AP_FUTURE_56", - "Cost": 3200, - "UITreeRow": -2, - "EraType": "ERA_FUTURE" - }, - { - "Type": "CIVIC_AP_FUTURE_57", - "Cost": 3200, - "UITreeRow": -1, - "EraType": "ERA_FUTURE" - }, - { - "Type": "CIVIC_AP_FUTURE_58", - "Cost": 3200, - "UITreeRow": 0, - "EraType": "ERA_FUTURE" - }, - { - "Type": "CIVIC_AP_FUTURE_59", - "Cost": 3200, - "UITreeRow": 1, - "EraType": "ERA_FUTURE" - }, - { - "Type": "CIVIC_AP_FUTURE_60", - "Cost": 3200, - "UITreeRow": 2, - "EraType": "ERA_FUTURE" - } -] diff --git a/worlds/civ_6/data/new_civics.py b/worlds/civ_6/data/new_civics.py new file mode 100644 index 000000000000..e232b67f5c74 --- /dev/null +++ b/worlds/civ_6/data/new_civics.py @@ -0,0 +1,372 @@ +from typing import List +from ..ItemData import NewItemData + + +new_civics: List[NewItemData] = [ + { + "Type": "CIVIC_AP_ANCIENT_00", + "Cost": 20, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "CIVIC_AP_ANCIENT_01", + "Cost": 40, + "UITreeRow": -2, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "CIVIC_AP_ANCIENT_02", + "Cost": 40, + "UITreeRow": 2, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "CIVIC_AP_ANCIENT_03", + "Cost": 50, + "UITreeRow": -3, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "CIVIC_AP_ANCIENT_04", + "Cost": 70, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "CIVIC_AP_ANCIENT_05", + "Cost": 70, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "CIVIC_AP_ANCIENT_06", + "Cost": 50, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "CIVIC_AP_CLASSICAL_07", + "Cost": 110, + "UITreeRow": -2, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "CIVIC_AP_CLASSICAL_08", + "Cost": 110, + "UITreeRow": 0, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "CIVIC_AP_CLASSICAL_09", + "Cost": 110, + "UITreeRow": 2, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "CIVIC_AP_CLASSICAL_10", + "Cost": 120, + "UITreeRow": -3, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "CIVIC_AP_CLASSICAL_11", + "Cost": 175, + "UITreeRow": -1, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "CIVIC_AP_CLASSICAL_12", + "Cost": 175, + "UITreeRow": 1, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "CIVIC_AP_CLASSICAL_13", + "Cost": 120, + "UITreeRow": 3, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "CIVIC_AP_MEDIEVAL_14", + "Cost": 220, + "UITreeRow": -2, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "CIVIC_AP_MEDIEVAL_15", + "Cost": 300, + "UITreeRow": -1, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "CIVIC_AP_MEDIEVAL_16", + "Cost": 300, + "UITreeRow": 1, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "CIVIC_AP_MEDIEVAL_17", + "Cost": 340, + "UITreeRow": -3, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "CIVIC_AP_MEDIEVAL_18", + "Cost": 420, + "UITreeRow": -1, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "CIVIC_AP_MEDIEVAL_19", + "Cost": 420, + "UITreeRow": 1, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "CIVIC_AP_MEDIEVAL_20", + "Cost": 340, + "UITreeRow": 3, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "CIVIC_AP_RENAISSANCE_21", + "Cost": 440, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "CIVIC_AP_RENAISSANCE_22", + "Cost": 600, + "UITreeRow": -1, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "CIVIC_AP_RENAISSANCE_23", + "Cost": 600, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "CIVIC_AP_RENAISSANCE_24", + "Cost": 440, + "UITreeRow": 3, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "CIVIC_AP_RENAISSANCE_25", + "Cost": 720, + "UITreeRow": -1, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "CIVIC_AP_RENAISSANCE_26", + "Cost": 720, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_27", + "Cost": 800, + "UITreeRow": -3, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_28", + "Cost": 1010, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_29", + "Cost": 1010, + "UITreeRow": 0, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_30", + "Cost": 800, + "UITreeRow": 2, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_31", + "Cost": 1050, + "UITreeRow": -3, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_32", + "Cost": 1210, + "UITreeRow": 2, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "CIVIC_AP_INDUSTRIAL_33", + "Cost": 1210, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "CIVIC_AP_MODERN_34", + "Cost": 1540, + "UITreeRow": -3, + "EraType": "ERA_MODERN", + }, + { + "Type": "CIVIC_AP_MODERN_35", + "Cost": 1580, + "UITreeRow": -2, + "EraType": "ERA_MODERN", + }, + { + "Type": "CIVIC_AP_MODERN_36", + "Cost": 1715, + "UITreeRow": -2, + "EraType": "ERA_MODERN", + }, + { + "Type": "CIVIC_AP_MODERN_37", + "Cost": 1540, + "UITreeRow": -1, + "EraType": "ERA_MODERN", + }, + { + "Type": "CIVIC_AP_MODERN_38", + "Cost": 1540, + "UITreeRow": 1, + "EraType": "ERA_MODERN", + }, + { + "Type": "CIVIC_AP_MODERN_39", + "Cost": 1640, + "UITreeRow": -1, + "EraType": "ERA_MODERN", + }, + { + "Type": "CIVIC_AP_MODERN_40", + "Cost": 1640, + "UITreeRow": 0, + "EraType": "ERA_MODERN", + }, + { + "Type": "CIVIC_AP_MODERN_41", + "Cost": 1640, + "UITreeRow": 2, + "EraType": "ERA_MODERN", + }, + { + "Type": "CIVIC_AP_MODERN_42", + "Cost": 1640, + "UITreeRow": 3, + "EraType": "ERA_MODERN", + }, + { + "Type": "CIVIC_AP_ATOMIC_43", + "Cost": 2185, + "UITreeRow": -1, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "CIVIC_AP_ATOMIC_44", + "Cost": 2185, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "CIVIC_AP_ATOMIC_45", + "Cost": 1955, + "UITreeRow": -3, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "CIVIC_AP_ATOMIC_46", + "Cost": 2415, + "UITreeRow": -1, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "CIVIC_AP_ATOMIC_47", + "Cost": 2415, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "CIVIC_AP_INFORMATION_48", + "Cost": 2880, + "UITreeRow": 0, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "CIVIC_AP_INFORMATION_49", + "Cost": 2880, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "CIVIC_AP_FUTURE_50", + "Cost": 3200, + "UITreeRow": 3, + "EraType": "ERA_FUTURE", + }, + { + "Type": "CIVIC_AP_INFORMATION_51", + "Cost": 2880, + "UITreeRow": -2, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "CIVIC_AP_INFORMATION_52", + "Cost": 3000, + "UITreeRow": 0, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "CIVIC_AP_INFORMATION_53", + "Cost": 3000, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "CIVIC_AP_INFORMATION_54", + "Cost": 3000, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "CIVIC_AP_INFORMATION_55", + "Cost": 3100, + "UITreeRow": -1, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "CIVIC_AP_FUTURE_56", + "Cost": 3200, + "UITreeRow": -2, + "EraType": "ERA_FUTURE", + }, + { + "Type": "CIVIC_AP_FUTURE_57", + "Cost": 3200, + "UITreeRow": -1, + "EraType": "ERA_FUTURE", + }, + { + "Type": "CIVIC_AP_FUTURE_58", + "Cost": 3200, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + }, + { + "Type": "CIVIC_AP_FUTURE_59", + "Cost": 3200, + "UITreeRow": 1, + "EraType": "ERA_FUTURE", + }, + { + "Type": "CIVIC_AP_FUTURE_60", + "Cost": 3200, + "UITreeRow": 2, + "EraType": "ERA_FUTURE", + }, +] diff --git a/worlds/civ_6/data/new_tech.json b/worlds/civ_6/data/new_tech.json deleted file mode 100644 index 3bfd188cfb75..000000000000 --- a/worlds/civ_6/data/new_tech.json +++ /dev/null @@ -1,464 +0,0 @@ -[ - { - "Type": "TECH_AP_ANCIENT_00", - "Cost": 25, - "UITreeRow": 0, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_ANCIENT_01", - "Cost": 25, - "UITreeRow": 1, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_ANCIENT_02", - "Cost": 25, - "UITreeRow": 3, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_ANCIENT_03", - "Cost": 50, - "UITreeRow": -3, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_ANCIENT_04", - "Cost": 50, - "UITreeRow": -2, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_ANCIENT_05", - "Cost": 50, - "UITreeRow": -1, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_ANCIENT_06", - "Cost": 50, - "UITreeRow": 1, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_ANCIENT_07", - "Cost": 50, - "UITreeRow": 0, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_ANCIENT_08", - "Cost": 80, - "UITreeRow": 2, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_ANCIENT_09", - "Cost": 80, - "UITreeRow": 3, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_ANCIENT_10", - "Cost": 80, - "UITreeRow": 4, - "EraType": "ERA_ANCIENT" - }, - { - "Type": "TECH_AP_CLASSICAL_11", - "Cost": 120, - "UITreeRow": -2, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "TECH_AP_CLASSICAL_12", - "Cost": 120, - "UITreeRow": 0, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "TECH_AP_CLASSICAL_13", - "Cost": 120, - "UITreeRow": 1, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "TECH_AP_CLASSICAL_14", - "Cost": 120, - "UITreeRow": 3, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "TECH_AP_CLASSICAL_15", - "Cost": 200, - "UITreeRow": -3, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "TECH_AP_CLASSICAL_16", - "Cost": 200, - "UITreeRow": -1, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "TECH_AP_CLASSICAL_17", - "Cost": 200, - "UITreeRow": 2, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "TECH_AP_CLASSICAL_18", - "Cost": 200, - "UITreeRow": 4, - "EraType": "ERA_CLASSICAL" - }, - { - "Type": "TECH_AP_MEDIEVAL_19", - "Cost": 300, - "UITreeRow": -2, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "TECH_AP_MEDIEVAL_20", - "Cost": 300, - "UITreeRow": 0, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "TECH_AP_MEDIEVAL_21", - "Cost": 300, - "UITreeRow": 4, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "TECH_AP_MEDIEVAL_22", - "Cost": 390, - "UITreeRow": -1, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "TECH_AP_MEDIEVAL_23", - "Cost": 390, - "UITreeRow": 1, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "TECH_AP_MEDIEVAL_24", - "Cost": 390, - "UITreeRow": 2, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "TECH_AP_MEDIEVAL_25", - "Cost": 390, - "UITreeRow": 3, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "TECH_AP_RENAISSANCE_26", - "Cost": 600, - "UITreeRow": -3, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "TECH_AP_RENAISSANCE_27", - "Cost": 600, - "UITreeRow": -2, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "TECH_AP_RENAISSANCE_28", - "Cost": 600, - "UITreeRow": 0, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "TECH_AP_RENAISSANCE_29", - "Cost": 600, - "UITreeRow": 1, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "TECH_AP_RENAISSANCE_30", - "Cost": 600, - "UITreeRow": 4, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "TECH_AP_RENAISSANCE_31", - "Cost": 730, - "UITreeRow": -3, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "TECH_AP_RENAISSANCE_32", - "Cost": 730, - "UITreeRow": -1, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "TECH_AP_RENAISSANCE_33", - "Cost": 730, - "UITreeRow": 1, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "TECH_AP_RENAISSANCE_34", - "Cost": 730, - "UITreeRow": 3, - "EraType": "ERA_RENAISSANCE" - }, - { - "Type": "TECH_AP_INDUSTRIAL_35", - "Cost": 930, - "UITreeRow": -2, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "TECH_AP_INDUSTRIAL_36", - "Cost": 930, - "UITreeRow": -1, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "TECH_AP_INDUSTRIAL_37", - "Cost": 930, - "UITreeRow": 1, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "TECH_AP_INDUSTRIAL_38", - "Cost": 930, - "UITreeRow": 3, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "TECH_AP_INDUSTRIAL_39", - "Cost": 1070, - "UITreeRow": -3, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "TECH_AP_INDUSTRIAL_40", - "Cost": 1070, - "UITreeRow": -1, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "TECH_AP_INDUSTRIAL_41", - "Cost": 1070, - "UITreeRow": 0, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "TECH_AP_INDUSTRIAL_42", - "Cost": 1070, - "UITreeRow": 2, - "EraType": "ERA_INDUSTRIAL" - }, - { - "Type": "TECH_AP_MODERN_43", - "Cost": 1250, - "UITreeRow": -2, - "EraType": "ERA_MODERN" - }, - { - "Type": "TECH_AP_MODERN_44", - "Cost": 1250, - "UITreeRow": 0, - "EraType": "ERA_MODERN" - }, - { - "Type": "TECH_AP_MODERN_45", - "Cost": 1250, - "UITreeRow": 1, - "EraType": "ERA_MODERN" - }, - { - "Type": "TECH_AP_MODERN_46", - "Cost": 1370, - "UITreeRow": -3, - "EraType": "ERA_MODERN" - }, - { - "Type": "TECH_AP_MODERN_47", - "Cost": 1370, - "UITreeRow": -2, - "EraType": "ERA_MODERN" - }, - { - "Type": "TECH_AP_MODERN_48", - "Cost": 1370, - "UITreeRow": -1, - "EraType": "ERA_MODERN" - }, - { - "Type": "TECH_AP_MODERN_49", - "Cost": 1370, - "UITreeRow": 2, - "EraType": "ERA_MODERN" - }, - { - "Type": "TECH_AP_ATOMIC_50", - "Cost": 1480, - "UITreeRow": -2, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "TECH_AP_ATOMIC_51", - "Cost": 1480, - "UITreeRow": -1, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "TECH_AP_ATOMIC_52", - "Cost": 1480, - "UITreeRow": 0, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "TECH_AP_ATOMIC_53", - "Cost": 1480, - "UITreeRow": 1, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "TECH_AP_ATOMIC_54", - "Cost": 1480, - "UITreeRow": 2, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "TECH_AP_ATOMIC_55", - "Cost": 1660, - "UITreeRow": -3, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "TECH_AP_ATOMIC_56", - "Cost": 1660, - "UITreeRow": 1, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "TECH_AP_ATOMIC_57", - "Cost": 1660, - "UITreeRow": 2, - "EraType": "ERA_ATOMIC" - }, - { - "Type": "TECH_AP_INFORMATION_58", - "Cost": 1850, - "UITreeRow": -3, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "TECH_AP_INFORMATION_59", - "Cost": 1850, - "UITreeRow": -1, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "TECH_AP_INFORMATION_60", - "Cost": 1850, - "UITreeRow": 0, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "TECH_AP_INFORMATION_61", - "Cost": 1850, - "UITreeRow": 1, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "TECH_AP_INFORMATION_62", - "Cost": 1850, - "UITreeRow": 2, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "TECH_AP_INFORMATION_63", - "Cost": 1850, - "UITreeRow": 3, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "TECH_AP_INFORMATION_64", - "Cost": 2155, - "UITreeRow": -2, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "TECH_AP_INFORMATION_65", - "Cost": 2155, - "UITreeRow": 2, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "TECH_AP_INFORMATION_66", - "Cost": 2155, - "UITreeRow": 1, - "EraType": "ERA_INFORMATION" - }, - { - "Type": "TECH_AP_MEDIEVAL_67", - "Cost": 300, - "UITreeRow": -3, - "EraType": "ERA_MEDIEVAL" - }, - { - "Type": "TECH_AP_MODERN_68", - "Cost": 1250, - "UITreeRow": 3, - "EraType": "ERA_MODERN" - }, - { - "Type": "TECH_AP_FUTURE_69", - "Cost": 2200, - "UITreeRow": -3, - "EraType": "ERA_FUTURE" - }, - { - "Type": "TECH_AP_FUTURE_70", - "Cost": 2200, - "UITreeRow": -2, - "EraType": "ERA_FUTURE" - }, - { - "Type": "TECH_AP_FUTURE_71", - "Cost": 2200, - "UITreeRow": -1, - "EraType": "ERA_FUTURE" - }, - { - "Type": "TECH_AP_FUTURE_72", - "Cost": 2200, - "UITreeRow": 0, - "EraType": "ERA_FUTURE" - }, - { - "Type": "TECH_AP_FUTURE_73", - "Cost": 2200, - "UITreeRow": 1, - "EraType": "ERA_FUTURE" - }, - { - "Type": "TECH_AP_FUTURE_74", - "Cost": 2200, - "UITreeRow": 2, - "EraType": "ERA_FUTURE" - }, - { - "Type": "TECH_AP_FUTURE_75", - "Cost": 2500, - "UITreeRow": 0, - "EraType": "ERA_FUTURE" - }, - { - "Type": "TECH_AP_FUTURE_76", - "Cost": 2600, - "UITreeRow": 0, - "EraType": "ERA_FUTURE" - } -] diff --git a/worlds/civ_6/data/new_tech.py b/worlds/civ_6/data/new_tech.py new file mode 100644 index 000000000000..2810bd231de6 --- /dev/null +++ b/worlds/civ_6/data/new_tech.py @@ -0,0 +1,468 @@ +from typing import List +from ..ItemData import NewItemData + + +new_tech: List[NewItemData] = [ + { + "Type": "TECH_AP_ANCIENT_00", + "Cost": 25, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_ANCIENT_01", + "Cost": 25, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_ANCIENT_02", + "Cost": 25, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_ANCIENT_03", + "Cost": 50, + "UITreeRow": -3, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_ANCIENT_04", + "Cost": 50, + "UITreeRow": -2, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_ANCIENT_05", + "Cost": 50, + "UITreeRow": -1, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_ANCIENT_06", + "Cost": 50, + "UITreeRow": 1, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_ANCIENT_07", + "Cost": 50, + "UITreeRow": 0, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_ANCIENT_08", + "Cost": 80, + "UITreeRow": 2, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_ANCIENT_09", + "Cost": 80, + "UITreeRow": 3, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_ANCIENT_10", + "Cost": 80, + "UITreeRow": 4, + "EraType": "ERA_ANCIENT", + }, + { + "Type": "TECH_AP_CLASSICAL_11", + "Cost": 120, + "UITreeRow": -2, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "TECH_AP_CLASSICAL_12", + "Cost": 120, + "UITreeRow": 0, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "TECH_AP_CLASSICAL_13", + "Cost": 120, + "UITreeRow": 1, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "TECH_AP_CLASSICAL_14", + "Cost": 120, + "UITreeRow": 3, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "TECH_AP_CLASSICAL_15", + "Cost": 200, + "UITreeRow": -3, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "TECH_AP_CLASSICAL_16", + "Cost": 200, + "UITreeRow": -1, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "TECH_AP_CLASSICAL_17", + "Cost": 200, + "UITreeRow": 2, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "TECH_AP_CLASSICAL_18", + "Cost": 200, + "UITreeRow": 4, + "EraType": "ERA_CLASSICAL", + }, + { + "Type": "TECH_AP_MEDIEVAL_19", + "Cost": 300, + "UITreeRow": -2, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "TECH_AP_MEDIEVAL_20", + "Cost": 300, + "UITreeRow": 0, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "TECH_AP_MEDIEVAL_21", + "Cost": 300, + "UITreeRow": 4, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "TECH_AP_MEDIEVAL_22", + "Cost": 390, + "UITreeRow": -1, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "TECH_AP_MEDIEVAL_23", + "Cost": 390, + "UITreeRow": 1, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "TECH_AP_MEDIEVAL_24", + "Cost": 390, + "UITreeRow": 2, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "TECH_AP_MEDIEVAL_25", + "Cost": 390, + "UITreeRow": 3, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "TECH_AP_RENAISSANCE_26", + "Cost": 600, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "TECH_AP_RENAISSANCE_27", + "Cost": 600, + "UITreeRow": -2, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "TECH_AP_RENAISSANCE_28", + "Cost": 600, + "UITreeRow": 0, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "TECH_AP_RENAISSANCE_29", + "Cost": 600, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "TECH_AP_RENAISSANCE_30", + "Cost": 600, + "UITreeRow": 4, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "TECH_AP_RENAISSANCE_31", + "Cost": 730, + "UITreeRow": -3, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "TECH_AP_RENAISSANCE_32", + "Cost": 730, + "UITreeRow": -1, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "TECH_AP_RENAISSANCE_33", + "Cost": 730, + "UITreeRow": 1, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "TECH_AP_RENAISSANCE_34", + "Cost": 730, + "UITreeRow": 3, + "EraType": "ERA_RENAISSANCE", + }, + { + "Type": "TECH_AP_INDUSTRIAL_35", + "Cost": 930, + "UITreeRow": -2, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "TECH_AP_INDUSTRIAL_36", + "Cost": 930, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "TECH_AP_INDUSTRIAL_37", + "Cost": 930, + "UITreeRow": 1, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "TECH_AP_INDUSTRIAL_38", + "Cost": 930, + "UITreeRow": 3, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "TECH_AP_INDUSTRIAL_39", + "Cost": 1070, + "UITreeRow": -3, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "TECH_AP_INDUSTRIAL_40", + "Cost": 1070, + "UITreeRow": -1, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "TECH_AP_INDUSTRIAL_41", + "Cost": 1070, + "UITreeRow": 0, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "TECH_AP_INDUSTRIAL_42", + "Cost": 1070, + "UITreeRow": 2, + "EraType": "ERA_INDUSTRIAL", + }, + { + "Type": "TECH_AP_MODERN_43", + "Cost": 1250, + "UITreeRow": -2, + "EraType": "ERA_MODERN", + }, + { + "Type": "TECH_AP_MODERN_44", + "Cost": 1250, + "UITreeRow": 0, + "EraType": "ERA_MODERN", + }, + { + "Type": "TECH_AP_MODERN_45", + "Cost": 1250, + "UITreeRow": 1, + "EraType": "ERA_MODERN", + }, + { + "Type": "TECH_AP_MODERN_46", + "Cost": 1370, + "UITreeRow": -3, + "EraType": "ERA_MODERN", + }, + { + "Type": "TECH_AP_MODERN_47", + "Cost": 1370, + "UITreeRow": -2, + "EraType": "ERA_MODERN", + }, + { + "Type": "TECH_AP_MODERN_48", + "Cost": 1370, + "UITreeRow": -1, + "EraType": "ERA_MODERN", + }, + { + "Type": "TECH_AP_MODERN_49", + "Cost": 1370, + "UITreeRow": 2, + "EraType": "ERA_MODERN", + }, + { + "Type": "TECH_AP_ATOMIC_50", + "Cost": 1480, + "UITreeRow": -2, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "TECH_AP_ATOMIC_51", + "Cost": 1480, + "UITreeRow": -1, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "TECH_AP_ATOMIC_52", + "Cost": 1480, + "UITreeRow": 0, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "TECH_AP_ATOMIC_53", + "Cost": 1480, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "TECH_AP_ATOMIC_54", + "Cost": 1480, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "TECH_AP_ATOMIC_55", + "Cost": 1660, + "UITreeRow": -3, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "TECH_AP_ATOMIC_56", + "Cost": 1660, + "UITreeRow": 1, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "TECH_AP_ATOMIC_57", + "Cost": 1660, + "UITreeRow": 2, + "EraType": "ERA_ATOMIC", + }, + { + "Type": "TECH_AP_INFORMATION_58", + "Cost": 1850, + "UITreeRow": -3, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "TECH_AP_INFORMATION_59", + "Cost": 1850, + "UITreeRow": -1, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "TECH_AP_INFORMATION_60", + "Cost": 1850, + "UITreeRow": 0, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "TECH_AP_INFORMATION_61", + "Cost": 1850, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "TECH_AP_INFORMATION_62", + "Cost": 1850, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "TECH_AP_INFORMATION_63", + "Cost": 1850, + "UITreeRow": 3, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "TECH_AP_INFORMATION_64", + "Cost": 2155, + "UITreeRow": -2, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "TECH_AP_INFORMATION_65", + "Cost": 2155, + "UITreeRow": 2, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "TECH_AP_INFORMATION_66", + "Cost": 2155, + "UITreeRow": 1, + "EraType": "ERA_INFORMATION", + }, + { + "Type": "TECH_AP_MEDIEVAL_67", + "Cost": 300, + "UITreeRow": -3, + "EraType": "ERA_MEDIEVAL", + }, + { + "Type": "TECH_AP_MODERN_68", + "Cost": 1250, + "UITreeRow": 3, + "EraType": "ERA_MODERN", + }, + { + "Type": "TECH_AP_FUTURE_69", + "Cost": 2200, + "UITreeRow": -3, + "EraType": "ERA_FUTURE", + }, + { + "Type": "TECH_AP_FUTURE_70", + "Cost": 2200, + "UITreeRow": -2, + "EraType": "ERA_FUTURE", + }, + { + "Type": "TECH_AP_FUTURE_71", + "Cost": 2200, + "UITreeRow": -1, + "EraType": "ERA_FUTURE", + }, + { + "Type": "TECH_AP_FUTURE_72", + "Cost": 2200, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + }, + { + "Type": "TECH_AP_FUTURE_73", + "Cost": 2200, + "UITreeRow": 1, + "EraType": "ERA_FUTURE", + }, + { + "Type": "TECH_AP_FUTURE_74", + "Cost": 2200, + "UITreeRow": 2, + "EraType": "ERA_FUTURE", + }, + { + "Type": "TECH_AP_FUTURE_75", + "Cost": 2500, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + }, + { + "Type": "TECH_AP_FUTURE_76", + "Cost": 2600, + "UITreeRow": 0, + "EraType": "ERA_FUTURE", + }, +] diff --git a/worlds/civ_6/data/new_tech_prereqs.py b/worlds/civ_6/data/new_tech_prereqs.py index 45cc2b454423..222100c2296f 100644 --- a/worlds/civ_6/data/new_tech_prereqs.py +++ b/worlds/civ_6/data/new_tech_prereqs.py @@ -1,6 +1,6 @@ from typing import List -from worlds.civ_6.ItemData import TechPrereqData +from ..ItemData import TechPrereqData new_tech_prereqs: List[TechPrereqData] = [ diff --git a/worlds/civ_6/data/progressive_districts.json b/worlds/civ_6/data/progressive_districts.json deleted file mode 100644 index f02e33a1b0f8..000000000000 --- a/worlds/civ_6/data/progressive_districts.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "PROGRESSIVE_CAMPUS": ["TECH_WRITING", "TECH_EDUCATION", "TECH_CHEMISTRY"], - - "PROGRESSIVE_THEATER": ["CIVIC_DRAMA_POETRY", "CIVIC_HUMANISM", "TECH_RADIO"], - - "PROGRESSIVE_HOLY_SITE": ["TECH_ASTROLOGY", "CIVIC_THEOLOGY"], - - "PROGRESSIVE_ENCAMPMENT": [ - "TECH_BRONZE_WORKING", - "TECH_MILITARY_ENGINEERING", - "TECH_MILITARY_SCIENCE" - ], - - "PROGRESSIVE_COMMERCIAL_HUB": [ - "TECH_CURRENCY", - "TECH_BANKING", - "TECH_ECONOMICS" - ], - - "PROGRESSIVE_HARBOR": ["TECH_CELESTIAL_NAVIGATION", "TECH_MASS_PRODUCTION"], - - "PROGRESSIVE_INDUSTRIAL_ZONE": [ - "TECH_APPRENTICESHIP", - "TECH_INDUSTRIALIZATION", - "TECH_ELECTRICITY", - "TECH_NUCLEAR_FISSION" - ], - - "PROGRESSIVE_PRESERVE": ["CIVIC_MYSTICISM", "CIVIC_CONSERVATION"], - - "PROGRESSIVE_ENTERTAINMENT_COMPLEX": [ - "CIVIC_GAMES_RECREATION", - "CIVIC_NATURAL_HISTORY", - "CIVIC_PROFESSIONAL_SPORTS" - ], - - "PROGRESSIVE_NEIGHBORHOOD": [ - "CIVIC_URBANIZATION", - "TECH_REPLACEABLE_PARTS", - "CIVIC_CAPITALISM" - ], - - "PROGRESSIVE_AERODROME": ["TECH_FLIGHT", "TECH_ADVANCED_FLIGHT"], - - "PROGRESSIVE_DIPLOMATIC_QUARTER": [ - "TECH_MATHEMATICS", - "CIVIC_DIPLOMATIC_SERVICE" - ], - - "PROGRESSIVE_SPACE_PORT": [ - "TECH_ROCKETRY", - "TECH_SATELLITES", - "TECH_NANOTECHNOLOGY", - "TECH_SMART_MATERIALS", - "TECH_OFFWORLD_MISSION" - ] -} diff --git a/worlds/civ_6/data/progressive_districts.py b/worlds/civ_6/data/progressive_districts.py new file mode 100644 index 000000000000..e75fbc616841 --- /dev/null +++ b/worlds/civ_6/data/progressive_districts.py @@ -0,0 +1,41 @@ +from typing import Dict, List + + +progressive_districts: Dict[str, List[str]] = { + "PROGRESSIVE_CAMPUS": ["TECH_WRITING", "TECH_EDUCATION", "TECH_CHEMISTRY"], + "PROGRESSIVE_THEATER": ["CIVIC_DRAMA_POETRY", "CIVIC_HUMANISM", "TECH_RADIO"], + "PROGRESSIVE_HOLY_SITE": ["TECH_ASTROLOGY", "CIVIC_THEOLOGY"], + "PROGRESSIVE_ENCAMPMENT": [ + "TECH_BRONZE_WORKING", + "TECH_MILITARY_ENGINEERING", + "TECH_MILITARY_SCIENCE", + ], + "PROGRESSIVE_COMMERCIAL_HUB": ["TECH_CURRENCY", "TECH_BANKING", "TECH_ECONOMICS"], + "PROGRESSIVE_HARBOR": ["TECH_CELESTIAL_NAVIGATION", "TECH_MASS_PRODUCTION"], + "PROGRESSIVE_INDUSTRIAL_ZONE": [ + "TECH_APPRENTICESHIP", + "TECH_INDUSTRIALIZATION", + "TECH_ELECTRICITY", + "TECH_NUCLEAR_FISSION", + ], + "PROGRESSIVE_PRESERVE": ["CIVIC_MYSTICISM", "CIVIC_CONSERVATION"], + "PROGRESSIVE_ENTERTAINMENT_COMPLEX": [ + "CIVIC_GAMES_RECREATION", + "CIVIC_NATURAL_HISTORY", + "CIVIC_PROFESSIONAL_SPORTS", + ], + "PROGRESSIVE_NEIGHBORHOOD": [ + "CIVIC_URBANIZATION", + "TECH_REPLACEABLE_PARTS", + "CIVIC_CAPITALISM", + ], + "PROGRESSIVE_AERODROME": ["TECH_FLIGHT", "TECH_ADVANCED_FLIGHT"], + "PROGRESSIVE_DIPLOMATIC_QUARTER": ["TECH_MATHEMATICS", "CIVIC_DIPLOMATIC_SERVICE"], + "PROGRESSIVE_SPACE_PORT": [ + "TECH_ROCKETRY", + "TECH_SATELLITES", + "TECH_NANOTECHNOLOGY", + "TECH_SMART_MATERIALS", + "TECH_OFFWORLD_MISSION", + ], +} From 6ff0b6df7f5a7a7909f0e1fe711e48b8fddaf7f6 Mon Sep 17 00:00:00 2001 From: hesto2 Date: Sun, 1 Dec 2024 13:58:33 -0700 Subject: [PATCH 69/69] Remove unused code --- worlds/civ_6/Data.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/worlds/civ_6/Data.py b/worlds/civ_6/Data.py index 7a68494b956a..7c802688341e 100644 --- a/worlds/civ_6/Data.py +++ b/worlds/civ_6/Data.py @@ -1,10 +1,6 @@ -from dataclasses import dataclass -import json -import os -import pkgutil -from typing import Any, Dict, List, TypedDict +from typing import Dict, List -from worlds.civ_6.ItemData import ( +from .ItemData import ( CivVIBoostData, CivicPrereqData, ExistingItemData, @@ -14,20 +10,6 @@ ) -_cache: Dict[Any, Any] = {} - - -def _get_data(key: str) -> Any: - global _cache - if key not in _cache: - path = os.path.join("data", f"{key}.json") - data = pkgutil.get_data(__name__, path) - if data is None: - raise FileNotFoundError(f"Data file not found: {path}") - _cache[key] = json.loads(data.decode()) - return _cache[key] - - def get_boosts_data() -> List[CivVIBoostData]: from .data.boosts import boosts