Skip to content

Commit

Permalink
Add initial NCO implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Ziktofel committed Jan 7, 2024
1 parent e411da5 commit 1c6158c
Show file tree
Hide file tree
Showing 10 changed files with 644 additions and 43 deletions.
52 changes: 33 additions & 19 deletions worlds/sc2/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,18 @@

loop = asyncio.get_event_loop_policy().new_event_loop()
nest_asyncio.apply(loop)
max_bonus: int = 13
victory_modulo: int = 100
MAX_BONUS: int = 28
VICTORY_MODULO: int = 100

# GitHub repo where the Map/mod data is hosted for /download_data command
DATA_REPO_OWNER = "Ziktofel"
DATA_REPO_NAME = "Archipelago-SC2-data"
DATA_API_VERSION = "API3"

# Bot controller
CONTROLLER_HEALTH: int = 38281
CONTROLLER2_HEALTH: int = 38282


# Data version file path.
# This file is used to tell if the downloaded data are outdated
Expand Down Expand Up @@ -323,6 +327,7 @@ class SC2Context(CommonContext):
minerals_per_item = 15
vespene_per_item = 15
starting_supply_per_item = 2
nova_covert_ops_only = False

def __init__(self, *args, **kwargs) -> None:
super(SC2Context, self).__init__(*args, **kwargs)
Expand Down Expand Up @@ -386,6 +391,7 @@ def on_package(self, cmd: str, args: dict) -> None:
self.minerals_per_item = args["slot_data"].get("minerals_per_item", 15)
self.vespene_per_item = args["slot_data"].get("vespene_per_item", 15)
self.starting_supply_per_item = args["slot_data"].get("starting_supply_per_item", 2)
self.nova_covert_ops_only = args["slot_data"].get("nova_covert_ops_only", False)

if self.required_tactics == RequiredTactics.option_no_logic:
# Locking Grant Story Tech if no logic
Expand Down Expand Up @@ -488,8 +494,8 @@ def build_location_to_mission_mapping(self) -> None:

for loc in self.server_locations:
offset = SC2WOL_LOC_ID_OFFSET if loc < SC2HOTS_LOC_ID_OFFSET \
else (SC2HOTS_LOC_ID_OFFSET - SC2Mission.ALL_IN.id * victory_modulo)
mission_id, objective = divmod(loc - offset, victory_modulo)
else (SC2HOTS_LOC_ID_OFFSET - SC2Mission.ALL_IN.id * VICTORY_MODULO)
mission_id, objective = divmod(loc - offset, VICTORY_MODULO)
mission_id_to_location_ids[mission_id].add(objective)
self.mission_id_to_location_ids = {mission_id: sorted(objectives) for mission_id, objectives in
mission_id_to_location_ids.items()}
Expand All @@ -499,7 +505,7 @@ def locations_for_mission(self, mission_name: str):
mission_id: int = mission.id
objectives = self.mission_id_to_location_ids[mission_id]
for objective in objectives:
yield get_location_offset(mission_id) + mission_id * victory_modulo + objective
yield get_location_offset(mission_id) + mission_id * VICTORY_MODULO + objective


class CompatItemHolder(typing.NamedTuple):
Expand Down Expand Up @@ -625,7 +631,7 @@ def calculate_items(ctx: SC2Context) -> typing.Dict[SC2Race, typing.List[int]]:
3 * num_missions // 100
]
upgrade_count = 0
completed = len([id for id in ctx.mission_id_to_location_ids if get_location_offset(id) + victory_modulo * id in ctx.checked_locations])
completed = len([id for id in ctx.mission_id_to_location_ids if get_location_offset(id) + VICTORY_MODULO * id in ctx.checked_locations])
for amount in amounts:
if completed >= amount:
upgrade_count += 1
Expand Down Expand Up @@ -719,7 +725,7 @@ def kerrigan_primal(ctx: SC2Context, items: typing.Dict[SC2Race, typing.List[int
return items[SC2Race.ZERG][type_flaggroups[SC2Race.ZERG]["Level"]] >= 35
elif ctx.kerrigan_primal_status == KerriganPrimalStatus.option_half_completion:
total_missions = len(ctx.mission_id_to_location_ids)
completed = len([(mission_id * victory_modulo + get_location_offset(mission_id)) in ctx.checked_locations
completed = len([(mission_id * VICTORY_MODULO + get_location_offset(mission_id)) in ctx.checked_locations
for mission_id in ctx.mission_id_to_location_ids])
return completed >= (total_missions / 2)
elif ctx.kerrigan_primal_status == KerriganPrimalStatus.option_item:
Expand Down Expand Up @@ -752,7 +758,7 @@ def __init__(self, ctx: SC2Context, mission_id):
self.ctx = ctx
self.ctx.last_bot = self
self.mission_id = mission_id
self.boni = [False for _ in range(max_bonus)]
self.boni = [False for _ in range(MAX_BONUS)]

super(ArchipelagoBot, self).__init__()

Expand All @@ -776,7 +782,7 @@ async def on_step(self, iteration: int):
game_speed = self.ctx.game_speed_override
else:
game_speed = self.ctx.game_speed
await self.chat_send("?SetOptions {} {} {} {} {} {} {} {} {} {}".format(
await self.chat_send("?SetOptions {} {} {} {} {} {} {} {} {} {} {} {}".format(
difficulty,
self.ctx.generic_upgrade_research,
self.ctx.all_in_choice,
Expand All @@ -786,7 +792,9 @@ async def on_step(self, iteration: int):
kerrigan_options,
self.ctx.grant_story_tech,
self.ctx.take_over_ai_allies,
soa_options
soa_options,
self.ctx.mission_order,
1 if self.ctx.nova_covert_ops_only else 0
))
await self.chat_send("?GiveResources {} {} {}".format(
start_items[SC2Race.ANY][0],
Expand All @@ -809,10 +817,16 @@ async def on_step(self, iteration: int):
self.ctx.announcements.task_done()

# Archipelago reads the health
controller1_state = 0
controller2_state = 0
for unit in self.all_own_units():
if unit.health_max == 38281:
game_state = int(38281 - unit.health)
if unit.health_max == CONTROLLER_HEALTH:
controller1_state = int(CONTROLLER_HEALTH - unit.health)
self.can_read_game = True
elif unit.health_max == CONTROLLER2_HEALTH:
controller2_state = int(CONTROLLER2_HEALTH - unit.health)
self.can_read_game = True
game_state = controller1_state + (controller2_state << 15)

if iteration == 160 and not game_state & 1:
await self.chat_send("?SendMessage Warning: Archipelago unable to connect or has lost connection to " +
Expand All @@ -836,7 +850,7 @@ async def on_step(self, iteration: int):
print("Mission Completed")
await self.ctx.send_msgs(
[{"cmd": 'LocationChecks',
"locations": [get_location_offset(self.mission_id) + victory_modulo * self.mission_id]}])
"locations": [get_location_offset(self.mission_id) + VICTORY_MODULO * self.mission_id]}])
self.mission_completed = True
else:
print("Game Complete")
Expand All @@ -849,7 +863,7 @@ async def on_step(self, iteration: int):
checks = len(self.ctx.checked_locations)
await self.ctx.send_msgs(
[{"cmd": 'LocationChecks',
"locations": [get_location_offset(self.mission_id) + victory_modulo * self.mission_id + x + 1]}])
"locations": [get_location_offset(self.mission_id) + VICTORY_MODULO * self.mission_id + x + 1]}])
self.boni[x] = True
# Kerrigan level needs manual updating if the check's receiver isn't the local player
if self.ctx.levels_per_check > 0 and self.last_received_update == len(self.ctx.items_received):
Expand All @@ -865,10 +879,10 @@ async def on_step(self, iteration: int):

async def updateTerranTech(self, current_items):
terran_items = current_items[SC2Race.TERRAN]
await self.chat_send("?GiveTerranTech {} {} {} {} {} {} {} {} {} {} {} {}".format(
await self.chat_send("?GiveTerranTech {} {} {} {} {} {} {} {} {} {} {} {} {} {}".format(
terran_items[0], terran_items[1], terran_items[2], terran_items[3], terran_items[4],
terran_items[5], terran_items[6], terran_items[7], terran_items[8], terran_items[9], terran_items[10],
terran_items[11]))
terran_items[11], terran_items[12], terran_items[13]))

async def updateZergTech(self, current_items):
zerg_items = current_items[SC2Race.ZERG]
Expand Down Expand Up @@ -1013,7 +1027,7 @@ def calc_available_missions(ctx: SC2Context, unlocks: typing.Optional[dict] = No

# Get number of missions completed
for loc in ctx.checked_locations:
if loc % victory_modulo == 0:
if loc % VICTORY_MODULO == 0:
missions_complete += 1

for campaign in ctx.mission_req_table:
Expand Down Expand Up @@ -1068,7 +1082,7 @@ def mission_reqs_completed(ctx: SC2Context, mission_name: str, missions_complete
# Check if required mission has been completed
mission_id = ctx.mission_req_table[parsed_req_mission.campaign][
list(ctx.mission_req_table[parsed_req_mission.campaign])[parsed_req_mission.connect_to - 1]].mission.id
if not (mission_id * victory_modulo + get_location_offset(mission_id)) in ctx.checked_locations:
if not (mission_id * VICTORY_MODULO + get_location_offset(mission_id)) in ctx.checked_locations:
if not ctx.mission_req_table[campaign][mission_name].or_requirements:
return False
else:
Expand Down Expand Up @@ -1331,7 +1345,7 @@ def is_mod_update_available(owner: str, repo: str, api_version: str, metadata: s

def get_location_offset(mission_id):
return SC2WOL_LOC_ID_OFFSET if mission_id <= SC2Mission.ALL_IN.id \
else (SC2HOTS_LOC_ID_OFFSET - SC2Mission.ALL_IN.id * victory_modulo)
else (SC2HOTS_LOC_ID_OFFSET - SC2Mission.ALL_IN.id * VICTORY_MODULO)


def launch():
Expand Down
23 changes: 23 additions & 0 deletions worlds/sc2/ItemNames.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
BATTLECRUISER_MISSILE_PODS = "Missile Pods (Battlecruiser)"
BATTLECRUISER_OPTIMIZED_LOGISTICS = "Optimized Logistics (Battlecruiser)"
BATTLECRUISER_TACTICAL_JUMP = "Tactical Jump (Battlecruiser)"
BATTLECRUISER_BEHEMOTH_PLATING = "Behemoth Plating (Battlecruiser)"
BATTLECRUISER_COVERT_OPS_ENGINES = "Covert Ops Engines (Battlecruiser)"
BUNKER_NEOSTEEL_BUNKER = "Neosteel Bunker (Bunker)"
BUNKER_PROJECTILE_ACCELERATOR = "Projectile Accelerator (Bunker)"
BUNKER_SHRIKE_TURRET = "Shrike Turret (Bunker)"
Expand Down Expand Up @@ -273,6 +275,27 @@
WRAITH_INTERNAL_TECH_MODULE = "Internal Tech Module (Wraith)"
WRAITH_RESOURCE_EFFICIENCY = "Resource Efficiency (Wraith)"

# Nova
NOVA_GHOST_VISOR = "Ghost Visor (Nova Equipment)"
NOVA_RANGEFINDER_OCULUS = "Rangefinder Oculus (Nova Equipment)"
NOVA_DOMINATION = "Domination (Nova Ability)"
NOVA_BLINK = "Blink (Nova Ability)"
NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE = "Progressive Stealth Suit Module (Nova Suit Module)"
NOVA_ENERGY_SUIT_MODULE = "Energy Suit Module (Nova Suit Module)"
NOVA_ARMORED_SUIT_MODULE = "Armored Suit Module (Nova Suit Module)"
NOVA_JUMP_SUIT_MODULE = "Jump Suit Module (Nova Suit Module)"
NOVA_C20A_CANISTER_RIFLE = "C20A Canister Rifle (Nova Weapon)"
NOVA_HELLFIRE_SHOTGUN = "Hellfire Shotgun (Nova Weapon)"
NOVA_PLASMA_RIFLE = "Plasma Rifle (Nova Weapon)"
NOVA_MONOMOLECULAR_BLADE = "Monomolecular Blade (Nova Weapon)"
NOVA_BLAZEFIRE_GUNBLADE = "Blazefire Gunblade (Nova Weapon)"
NOVA_STIM_INFUSION = "Stim Infusion (Nova Gadget)"
NOVA_PULSE_GRENADES = "Pulse Grenades (Nova Gadget)"
NOVA_FLASHBANG_GRENADES = "Flashbang Grenades (Nova Gadget)"
NOVA_IONIC_FORCE_FIELD = "Ionic Force Field (Nova Gadget)"
NOVA_HOLO_DECOY = "Holo Decoy (Nova Gadget)"
NOVA_NUKE = "Tac Nuke Strike (Nova Ability)"

# Zerg Units
ZERGLING = "Zergling"
SWARM_QUEEN = "Swarm Queen"
Expand Down
Loading

0 comments on commit 1c6158c

Please sign in to comment.