mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Starcraft 2 Wings of Liberty AP Implementation (#528)
This commit is contained in:
169
worlds/sc2wol/Items.py
Normal file
169
worlds/sc2wol/Items.py
Normal file
@@ -0,0 +1,169 @@
|
||||
from BaseClasses import Item
|
||||
import typing
|
||||
|
||||
class ItemData(typing.NamedTuple):
|
||||
code: typing.Optional[int]
|
||||
type: typing.Optional[str]
|
||||
number: typing.Optional[int]
|
||||
progression: bool = False
|
||||
never_exclude: bool = True
|
||||
quantity: int = 1
|
||||
|
||||
|
||||
class StarcraftWoLItem(Item):
|
||||
game: str = "Starcraft2WoL"
|
||||
|
||||
def __init__(self, name, advancement: bool = False, code: int = None, player: int = None):
|
||||
super(StarcraftWoLItem, self).__init__(name, advancement, code, player)
|
||||
|
||||
|
||||
def get_full_item_list():
|
||||
return item_table
|
||||
|
||||
|
||||
SC2WOL_ITEM_ID_OFFSET = 1000
|
||||
|
||||
item_table = {
|
||||
"Marine": ItemData(0+SC2WOL_ITEM_ID_OFFSET, "Unit", 0, progression=True),
|
||||
"Medic": ItemData(1+SC2WOL_ITEM_ID_OFFSET, "Unit", 1, progression=True),
|
||||
"Firebat": ItemData(2+SC2WOL_ITEM_ID_OFFSET, "Unit", 2, progression=True),
|
||||
"Marauder": ItemData(3+SC2WOL_ITEM_ID_OFFSET, "Unit", 3, progression=True),
|
||||
"Reaper": ItemData(4+SC2WOL_ITEM_ID_OFFSET, "Unit", 4, progression=True),
|
||||
"Hellion": ItemData(5+SC2WOL_ITEM_ID_OFFSET, "Unit", 5, progression=True),
|
||||
"Vulture": ItemData(6+SC2WOL_ITEM_ID_OFFSET, "Unit", 6, progression=True),
|
||||
"Goliath": ItemData(7+SC2WOL_ITEM_ID_OFFSET, "Unit", 7, progression=True),
|
||||
"Diamondback": ItemData(8+SC2WOL_ITEM_ID_OFFSET, "Unit", 8, progression=True),
|
||||
"Siege Tank": ItemData(9+SC2WOL_ITEM_ID_OFFSET, "Unit", 9, progression=True),
|
||||
"Medivac": ItemData(10+SC2WOL_ITEM_ID_OFFSET, "Unit", 10, progression=True),
|
||||
"Wraith": ItemData(11+SC2WOL_ITEM_ID_OFFSET, "Unit", 11, progression=True),
|
||||
"Viking": ItemData(12+SC2WOL_ITEM_ID_OFFSET, "Unit", 12, progression=True),
|
||||
"Banshee": ItemData(13+SC2WOL_ITEM_ID_OFFSET, "Unit", 13, progression=True),
|
||||
"Battlecruiser": ItemData(14+SC2WOL_ITEM_ID_OFFSET, "Unit", 14, progression=True),
|
||||
"Ghost": ItemData(15+SC2WOL_ITEM_ID_OFFSET, "Unit", 15, progression=True),
|
||||
"Spectre": ItemData(16+SC2WOL_ITEM_ID_OFFSET, "Unit", 16, progression=True),
|
||||
"Thor": ItemData(17+SC2WOL_ITEM_ID_OFFSET, "Unit", 17, progression=True),
|
||||
|
||||
"Progressive Infantry Weapon": ItemData (100+SC2WOL_ITEM_ID_OFFSET, "Upgrade", 0, quantity=3),
|
||||
"Progressive Infantry Armor": ItemData (102+SC2WOL_ITEM_ID_OFFSET, "Upgrade", 2, quantity=3),
|
||||
"Progressive Vehicle Weapon": ItemData (103+SC2WOL_ITEM_ID_OFFSET, "Upgrade", 4, quantity=3),
|
||||
"Progressive Vehicle Armor": ItemData (104+SC2WOL_ITEM_ID_OFFSET, "Upgrade", 6, quantity=3),
|
||||
"Progressive Ship Weapon": ItemData (105+SC2WOL_ITEM_ID_OFFSET, "Upgrade", 8, quantity=3),
|
||||
"Progressive Ship Armor": ItemData (106+SC2WOL_ITEM_ID_OFFSET, "Upgrade", 10, quantity=3),
|
||||
|
||||
"Projectile Accelerator (Bunker)": ItemData (200+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 0),
|
||||
"Neosteel Bunker (Bunker)": ItemData (201+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 1),
|
||||
"Titanium Housing (Missile Turret)": ItemData (202+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 2),
|
||||
"Hellstorm Batteries (Missile Turret)": ItemData (203+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 3),
|
||||
"Advanced Construction (SCV)": ItemData (204+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 4),
|
||||
"Dual-Fusion Welders (SCV)": ItemData (205+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 5),
|
||||
"Fire-Suppression System (Building)": ItemData (206+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 6),
|
||||
"Orbital Command (Building)": ItemData (207+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 7),
|
||||
"Stimpack (Marine)": ItemData (208+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 8),
|
||||
"Combat Shield (Marine)": ItemData (209+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 9),
|
||||
"Advanced Medic Facilities (Medic)": ItemData (210+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 10),
|
||||
"Stabilizer Medpacks (Medic)": ItemData (211+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 11),
|
||||
"Incinerator Gauntlets (Firebat)": ItemData (212+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 12),
|
||||
"Juggernaut Plating (Firebat)": ItemData (213+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 13),
|
||||
"Concussive Shells (Marauder)": ItemData (214+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 14),
|
||||
"Kinetic Foam (Marauder)": ItemData (215+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 15),
|
||||
"U-238 Rounds (Reaper)": ItemData (216+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 16),
|
||||
"G-4 Clusterbomb (Reaper)": ItemData (217+SC2WOL_ITEM_ID_OFFSET, "Armory 1", 17),
|
||||
|
||||
"Twin-Linked Flamethrower (Hellion)": ItemData(300+SC2WOL_ITEM_ID_OFFSET, "Armory 2", 0),
|
||||
"Thermite Filaments (Hellion)": ItemData(301+SC2WOL_ITEM_ID_OFFSET, "Armory 2", 1),
|
||||
"Cerberus Mine (Vulture)": ItemData(302 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 2),
|
||||
"Replenishable Magazine (Vulture)": ItemData(303 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 3),
|
||||
"Multi-Lock Weapons System (Goliath)": ItemData(304 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 4),
|
||||
"Ares-Class Targeting System (Goliath)": ItemData(305 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 5),
|
||||
"Tri-Lithium Power Cell (Diamondback)": ItemData(306 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 6),
|
||||
"Shaped Hull (Diamondback)": ItemData(307 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 7),
|
||||
"Maelstrom Rounds (Siege Tank)": ItemData(308 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 8),
|
||||
"Shaped Blast (Siege Tank)": ItemData(309 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 9),
|
||||
"Rapid Deployment Tube (Medivac)": ItemData(310 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 10),
|
||||
"Advanced Healing AI (Medivac)": ItemData(311 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 11),
|
||||
"Tomahawk Power Cells (Wraith)": ItemData(312 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 12),
|
||||
"Displacement Field (Wraith)": ItemData(313 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 13),
|
||||
"Ripwave Missiles (Viking)": ItemData(314 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 14),
|
||||
"Phobos-Class Weapons System (Viking)": ItemData(315 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 15),
|
||||
"Cross-Spectrum Dampeners (Banshee)": ItemData(316 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 16),
|
||||
"Shockwave Missile Battery (Banshee)": ItemData(317 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 17),
|
||||
"Missile Pods (Battlecruiser)": ItemData(318 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 18),
|
||||
"Defensive Matrix (Battlecruiser)": ItemData(319 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 19),
|
||||
"Ocular Implants (Ghost)": ItemData(320 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 20),
|
||||
"Crius Suit (Ghost)": ItemData(321 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 21),
|
||||
"Psionic Lash (Spectre)": ItemData(322 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 22),
|
||||
"Nyx-Class Cloaking Module (Spectre)": ItemData(323 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 23),
|
||||
"330mm Barrage Cannon (Thor)": ItemData(324 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 24),
|
||||
"Immortality Protocol (Thor)": ItemData(325 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 25),
|
||||
|
||||
"Bunker": ItemData (400+SC2WOL_ITEM_ID_OFFSET, "Building", 0, progression=True),
|
||||
"Missile Turret": ItemData (401+SC2WOL_ITEM_ID_OFFSET, "Building", 1, progression=True),
|
||||
"Sensor Tower": ItemData (402+SC2WOL_ITEM_ID_OFFSET, "Building", 2),
|
||||
|
||||
"War Pigs": ItemData (500 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 0),
|
||||
"Devil Dogs": ItemData(501 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 1),
|
||||
"Hammer Securities": ItemData(502 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 2),
|
||||
"Spartan Company": ItemData(503 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 3),
|
||||
"Siege Breakers": ItemData(504 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 4),
|
||||
"Hel's Angel": ItemData(505 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 5),
|
||||
"Dusk Wings": ItemData(506 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 6),
|
||||
"Jackson's Revenge": ItemData(507 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 7),
|
||||
|
||||
"Ultra-Capacitors": ItemData(600 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 0),
|
||||
"Vanadium Plating": ItemData(601 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 1),
|
||||
"Orbital Depots": ItemData(602 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 2),
|
||||
"Micro-Filtering": ItemData(603 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 3),
|
||||
"Automated Refinery": ItemData(604 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 4),
|
||||
"Command Center Reactor": ItemData(605 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 5),
|
||||
"Raven": ItemData(606 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 6),
|
||||
"Science Vessel": ItemData(607 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 7),
|
||||
"Tech Reactor": ItemData(608 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 8),
|
||||
"Orbital Strike": ItemData(609 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 9),
|
||||
"Shrike Turret": ItemData(610 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 10),
|
||||
"Fortified Bunker": ItemData(611 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 11),
|
||||
"Planetary Fortress": ItemData(612 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 12),
|
||||
"Perdition Turret": ItemData(613 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 13),
|
||||
"Predator": ItemData(614 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 14),
|
||||
"Hercules": ItemData(615 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 15, progression=True),
|
||||
"Cellular Reactor": ItemData(616 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 16),
|
||||
"Regenerative Bio-Steel": ItemData(617 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 17),
|
||||
"Hive Mind Emulator": ItemData(618 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 18),
|
||||
"Psi Disrupter": ItemData(619 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 19),
|
||||
|
||||
"Zealot": ItemData (700 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 0, progression=True),
|
||||
"Stalker": ItemData (701 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 1, progression=True),
|
||||
"High Templar": ItemData (702 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 2, progression=True),
|
||||
"Dark Templar": ItemData (703 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 3, progression=True),
|
||||
"Immortal": ItemData (704 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 4, progression=True),
|
||||
"Colossus": ItemData (705 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 5, progression=True),
|
||||
"Phoenix": ItemData (706 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 6, progression=True),
|
||||
"Void Ray": ItemData (707 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 7, progression=True),
|
||||
"Carrier": ItemData (708 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 8, progression=True),
|
||||
|
||||
"+5 Starting Minerals": ItemData(800+SC2WOL_ITEM_ID_OFFSET, "Minerals", 5, quantity=0),
|
||||
"+5 Starting Vespene": ItemData(801+SC2WOL_ITEM_ID_OFFSET, "Vespene", 5, quantity=0)
|
||||
}
|
||||
|
||||
basic_unit: typing.Tuple[str, ...] = (
|
||||
'Marine',
|
||||
'Marauder',
|
||||
'Firebat',
|
||||
'Hellion',
|
||||
'Vulture'
|
||||
)
|
||||
|
||||
|
||||
item_name_groups = {"Missions":
|
||||
{"Beat Liberation Day", "Beat The Outlaws", "Beat Zero Hour", "Beat Evacuation",
|
||||
"None Outbreak", "Beat Safe Haven", "Beat Haven's Fall", "Beat Smash and Grab", "Beat The Dig",
|
||||
"Beat The Moebius Factor", "Beat Supernova", "Beat Maw of the Void", "Beat Devil's Playground",
|
||||
"Beat Welcome to the Jungle", "Beat Breakout", "Beat Ghost of a Chance",
|
||||
"Beat The Great Train Robbery", "Beat Cutthroat", "Beat Engine of Destruction",
|
||||
"Beat Media Blitz", "Beat Piercing the Shroud"}}
|
||||
|
||||
filler_items: typing.Tuple[str, ...] = (
|
||||
'+5 Starting Minerals',
|
||||
'+5 Starting Vespene'
|
||||
)
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in get_full_item_list().items() if data.code}
|
||||
186
worlds/sc2wol/Locations.py
Normal file
186
worlds/sc2wol/Locations.py
Normal file
@@ -0,0 +1,186 @@
|
||||
from typing import List, Tuple, Optional, Callable, NamedTuple
|
||||
from BaseClasses import MultiWorld
|
||||
|
||||
from BaseClasses import Location
|
||||
|
||||
SC2WOL_LOC_ID_OFFSET = 1000
|
||||
|
||||
class SC2WoLLocation(Location):
|
||||
game: str = "Starcraft2WoL"
|
||||
|
||||
|
||||
class LocationData(NamedTuple):
|
||||
region: str
|
||||
name: str
|
||||
code: Optional[int]
|
||||
rule: Callable = lambda state: True
|
||||
|
||||
|
||||
def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[LocationData, ...]:
|
||||
location_table: List[LocationData] = [
|
||||
LocationData("Liberation Day", "Liberation Day: Victory", SC2WOL_LOC_ID_OFFSET + 100),
|
||||
LocationData("Liberation Day", "Liberation Day: First Statue", SC2WOL_LOC_ID_OFFSET + 101),
|
||||
LocationData("Liberation Day", "Liberation Day: Second Statue", SC2WOL_LOC_ID_OFFSET + 102),
|
||||
LocationData("Liberation Day", "Liberation Day: Third Statue", SC2WOL_LOC_ID_OFFSET + 103),
|
||||
LocationData("Liberation Day", "Liberation Day: Fourth Statue", SC2WOL_LOC_ID_OFFSET + 104),
|
||||
LocationData("Liberation Day", "Liberation Day: Fifth Statue", SC2WOL_LOC_ID_OFFSET + 105),
|
||||
LocationData("Liberation Day", "Liberation Day: Sixth Statue", SC2WOL_LOC_ID_OFFSET + 106),
|
||||
LocationData("Liberation Day", "Beat Liberation Day", None),
|
||||
LocationData("The Outlaws", "The Outlaws: Victory", SC2WOL_LOC_ID_OFFSET + 200),
|
||||
LocationData("The Outlaws", "The Outlaws: Rebel Base", SC2WOL_LOC_ID_OFFSET + 201),
|
||||
LocationData("The Outlaws", "Beat The Outlaws", None),
|
||||
LocationData("Zero Hour", "Zero Hour: Victory", SC2WOL_LOC_ID_OFFSET + 300),
|
||||
LocationData("Zero Hour", "Zero Hour: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 301),
|
||||
LocationData("Zero Hour", "Zero Hour: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 302),
|
||||
LocationData("Zero Hour", "Zero Hour: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 303),
|
||||
LocationData("Zero Hour", "Beat Zero Hour", None),
|
||||
LocationData("Evacuation", "Evacuation: Victory", SC2WOL_LOC_ID_OFFSET + 400),
|
||||
LocationData("Evacuation", "Evacuation: First Chysalis", SC2WOL_LOC_ID_OFFSET + 401),
|
||||
LocationData("Evacuation", "Evacuation: Second Chysalis", SC2WOL_LOC_ID_OFFSET + 402),
|
||||
LocationData("Evacuation", "Evacuation: Third Chysalis", SC2WOL_LOC_ID_OFFSET + 403),
|
||||
LocationData("Evacuation", "Beat Evacuation", None),
|
||||
LocationData("Outbreak", "Outbreak: Victory", SC2WOL_LOC_ID_OFFSET + 500),
|
||||
LocationData("Outbreak", "Outbreak: Left Infestor", SC2WOL_LOC_ID_OFFSET + 501),
|
||||
LocationData("Outbreak", "Outbreak: Right Infestor", SC2WOL_LOC_ID_OFFSET + 502),
|
||||
LocationData("Outbreak", "Beat Outbreak", None),
|
||||
LocationData("Safe Haven", "Safe Haven: Victory", SC2WOL_LOC_ID_OFFSET + 600),
|
||||
LocationData("Safe Haven", "Beat Safe Haven", None),
|
||||
LocationData("Haven's Fall", "Haven's Fall: Victory", SC2WOL_LOC_ID_OFFSET + 700),
|
||||
LocationData("Haven's Fall", "Beat Haven's Fall", None),
|
||||
LocationData("Smash and Grab", "Smash and Grab: Victory", SC2WOL_LOC_ID_OFFSET + 800),
|
||||
LocationData("Smash and Grab", "Smash and Grab: First Relic", SC2WOL_LOC_ID_OFFSET + 801),
|
||||
LocationData("Smash and Grab", "Smash and Grab: Second Relic", SC2WOL_LOC_ID_OFFSET + 802),
|
||||
LocationData("Smash and Grab", "Smash and Grab: Third Relic", SC2WOL_LOC_ID_OFFSET + 803),
|
||||
LocationData("Smash and Grab", "Smash and Grab: Fourth Relic", SC2WOL_LOC_ID_OFFSET + 804),
|
||||
LocationData("Smash and Grab", "Beat Smash and Grab", None),
|
||||
LocationData("The Dig", "The Dig: Victory", SC2WOL_LOC_ID_OFFSET + 900),
|
||||
LocationData("The Dig", "The Dig: Left Relic", SC2WOL_LOC_ID_OFFSET + 901),
|
||||
LocationData("The Dig", "The Dig: Right Ground Relic", SC2WOL_LOC_ID_OFFSET + 902),
|
||||
LocationData("The Dig", "The Dig: Right Cliff Relic", SC2WOL_LOC_ID_OFFSET + 903),
|
||||
LocationData("The Dig", "Beat The Dig", None),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: 3rd Data Core", SC2WOL_LOC_ID_OFFSET + 1000),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: 1st Data Core ", SC2WOL_LOC_ID_OFFSET + 1001),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: 2nd Data Core", SC2WOL_LOC_ID_OFFSET + 1002),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: South Rescue", SC2WOL_LOC_ID_OFFSET + 1003,
|
||||
lambda state: state._sc2wol_able_to_rescue(world, player) or True),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: Wall Rescue", SC2WOL_LOC_ID_OFFSET + 1004,
|
||||
lambda state: state._sc2wol_able_to_rescue(world, player) or True),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: Mid Rescue", SC2WOL_LOC_ID_OFFSET + 1005,
|
||||
lambda state: state._sc2wol_able_to_rescue(world, player) or True),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: Nydus Roof Rescue", SC2WOL_LOC_ID_OFFSET + 1006,
|
||||
lambda state: state._sc2wol_able_to_rescue(world, player) or True),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: Alive Inside Rescue", SC2WOL_LOC_ID_OFFSET + 1007,
|
||||
lambda state: state._sc2wol_able_to_rescue(world, player) or True),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1008),
|
||||
LocationData("The Moebius Factor", "Beat The Moebius Factor", None),
|
||||
LocationData("Supernova", "Supernova: Victory", SC2WOL_LOC_ID_OFFSET + 1100),
|
||||
LocationData("Supernova", "Supernova: West Relic", SC2WOL_LOC_ID_OFFSET + 1101),
|
||||
LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102),
|
||||
LocationData("Supernova", "Supernova: South Relic", SC2WOL_LOC_ID_OFFSET + 1103),
|
||||
LocationData("Supernova", "Supernova: East Relic", SC2WOL_LOC_ID_OFFSET + 1104),
|
||||
LocationData("Supernova", "Beat Supernova", None),
|
||||
LocationData("Maw of the Void", "Maw of the Void: Xel'Naga Vault", SC2WOL_LOC_ID_OFFSET + 1200),
|
||||
LocationData("Maw of the Void", "Maw of the Void: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1201),
|
||||
LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202),
|
||||
LocationData("Maw of the Void", "Maw of the Void: South Close Prisoners", SC2WOL_LOC_ID_OFFSET + 1203),
|
||||
LocationData("Maw of the Void", "Maw of the Void: South Far Prisoners", SC2WOL_LOC_ID_OFFSET + 1204),
|
||||
LocationData("Maw of the Void", "Maw of the Void: North Prisoners", SC2WOL_LOC_ID_OFFSET + 1205),
|
||||
LocationData("Maw of the Void", "Beat Maw of the Void", None),
|
||||
LocationData("Devil's Playground", "Devil's Playground: 8000 Minerals", SC2WOL_LOC_ID_OFFSET + 1300),
|
||||
LocationData("Devil's Playground", "Devil's Playground: Tosh's Miners", SC2WOL_LOC_ID_OFFSET + 1301),
|
||||
LocationData("Devil's Playground", "Devil's Playground: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1302),
|
||||
LocationData("Devil's Playground", "Beat Devil's Playground", None),
|
||||
LocationData("Welcome to the Jungle", "Welcome to the Jungle: 7 Canisters", SC2WOL_LOC_ID_OFFSET + 1400),
|
||||
LocationData("Welcome to the Jungle", "Welcome to the Jungle: Close Relic", SC2WOL_LOC_ID_OFFSET + 1401),
|
||||
LocationData("Welcome to the Jungle", "Welcome to the Jungle: West Relic", SC2WOL_LOC_ID_OFFSET + 1402),
|
||||
LocationData("Welcome to the Jungle", "Welcome to the Jungle: North-East Relic", SC2WOL_LOC_ID_OFFSET + 1403),
|
||||
LocationData("Welcome to the Jungle", "Beat Welcome to the Jungle", None),
|
||||
LocationData("Breakout", "Breakout: Main Prison", SC2WOL_LOC_ID_OFFSET + 1500),
|
||||
LocationData("Breakout", "Breakout: Diamondback Prison", SC2WOL_LOC_ID_OFFSET + 1501),
|
||||
LocationData("Breakout", "Breakout: Siegetank Prison", SC2WOL_LOC_ID_OFFSET + 1502),
|
||||
LocationData("Breakout", "Beat Breakout", None),
|
||||
LocationData("Ghost of a Chance", "Ghost of a Chance: Psi-Indoctrinator", SC2WOL_LOC_ID_OFFSET + 1600),
|
||||
LocationData("Ghost of a Chance", "Ghost of a Chance: Terrazine Tank", SC2WOL_LOC_ID_OFFSET + 1601),
|
||||
LocationData("Ghost of a Chance", "Ghost of a Chance: Jorium Stockpile", SC2WOL_LOC_ID_OFFSET + 1602),
|
||||
LocationData("Ghost of a Chance", "Ghost of a Chance: First Island Spectres", SC2WOL_LOC_ID_OFFSET + 1603),
|
||||
LocationData("Ghost of a Chance", "Ghost of a Chance: Second Island Spectres", SC2WOL_LOC_ID_OFFSET + 1604),
|
||||
LocationData("Ghost of a Chance", "Ghost of a Chance: Third Island Spectres", SC2WOL_LOC_ID_OFFSET + 1605),
|
||||
LocationData("Ghost of a Chance", "Beat Ghost of a Chance", None),
|
||||
LocationData("The Great Train Robbery", "The Great Train Robbery: 8 Trains", SC2WOL_LOC_ID_OFFSET + 1700, lambda state: state._sc2wol_has_train_killers(world, player)),
|
||||
LocationData("The Great Train Robbery", "The Great Train Robbery: North Defiler", SC2WOL_LOC_ID_OFFSET + 1701),
|
||||
LocationData("The Great Train Robbery", "The Great Train Robbery: Mid Defiler", SC2WOL_LOC_ID_OFFSET + 1702),
|
||||
LocationData("The Great Train Robbery", "The Great Train Robbery: South Defiler", SC2WOL_LOC_ID_OFFSET + 1703),
|
||||
LocationData("The Great Train Robbery", "Beat The Great Train Robbery", None,
|
||||
lambda state: state._sc2wol_has_train_killers(world, player)),
|
||||
LocationData("Cutthroat", "Cutthroat: Orlan's Planetary", SC2WOL_LOC_ID_OFFSET + 1800),
|
||||
LocationData("Cutthroat", "Cutthroat: Mira Han", SC2WOL_LOC_ID_OFFSET + 1801),
|
||||
LocationData("Cutthroat", "Cutthroat: North Relic", SC2WOL_LOC_ID_OFFSET + 1802),
|
||||
LocationData("Cutthroat", "Cutthroat: Mid Relic", SC2WOL_LOC_ID_OFFSET + 1803),
|
||||
LocationData("Cutthroat", "Cutthroat: Southwest Relic", SC2WOL_LOC_ID_OFFSET + 1804),
|
||||
LocationData("Cutthroat", "Beat Cutthroat", None),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: Dominion Bases", SC2WOL_LOC_ID_OFFSET + 1900,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: Odin", SC2WOL_LOC_ID_OFFSET + 1901),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: Loki", SC2WOL_LOC_ID_OFFSET + 1902,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: Lab Devourer", SC2WOL_LOC_ID_OFFSET + 1903),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: North Devourer", SC2WOL_LOC_ID_OFFSET + 1904,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: Southeast Devourer", SC2WOL_LOC_ID_OFFSET + 1905,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
LocationData("Engine of Destruction", "Beat Engine of Destruction", None,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
LocationData("Media Blitz", "Media Blitz: Full Upload", SC2WOL_LOC_ID_OFFSET + 2000),
|
||||
LocationData("Media Blitz", "Media Blitz: Tower 1", SC2WOL_LOC_ID_OFFSET + 2001),
|
||||
LocationData("Media Blitz", "Media Blitz: Tower 2", SC2WOL_LOC_ID_OFFSET + 2002),
|
||||
LocationData("Media Blitz", "Media Blitz: Tower 3", SC2WOL_LOC_ID_OFFSET + 2003),
|
||||
LocationData("Media Blitz", "Media Blitz: Science Facility", SC2WOL_LOC_ID_OFFSET + 2004),
|
||||
LocationData("Media Blitz", "Beat Media Blitz", None),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Facility Escape", SC2WOL_LOC_ID_OFFSET + 2100),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Holding Cell Relic", SC2WOL_LOC_ID_OFFSET + 2101),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk ", SC2WOL_LOC_ID_OFFSET + 2105),
|
||||
LocationData("Piercing the Shroud", "Beat Piercing the Shroud", None),
|
||||
LocationData("Whispers of Doom", "Whispers of Doom: Void Seeker Escape", SC2WOL_LOC_ID_OFFSET + 2200),
|
||||
LocationData("Whispers of Doom", "Whispers of Doom: First Hatchery", SC2WOL_LOC_ID_OFFSET + 2201),
|
||||
LocationData("Whispers of Doom", "Whispers of Doom: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 2202),
|
||||
LocationData("Whispers of Doom", "Whispers of Doom: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 2203),
|
||||
LocationData("Whispers of Doom", "Beat Whispers of Doom", None),
|
||||
LocationData("A Sinister Turn", "A Sinister Turn: Preservers Freed", SC2WOL_LOC_ID_OFFSET + 2300,
|
||||
lambda state: state._sc2wol_has_protoss_medium_units(world, player)),
|
||||
LocationData("A Sinister Turn", "A Sinister Turn: Robotics Facility", SC2WOL_LOC_ID_OFFSET + 2301),
|
||||
LocationData("A Sinister Turn", "A Sinister Turn: Dark Shrine", SC2WOL_LOC_ID_OFFSET + 2302),
|
||||
LocationData("A Sinister Turn", "A Sinister Turn: Templar Archives", SC2WOL_LOC_ID_OFFSET + 2303,
|
||||
lambda state: state._sc2wol_has_protoss_common_units(world, player)),
|
||||
LocationData("A Sinister Turn", "Beat A Sinister Turn", None,
|
||||
lambda state: state._sc2wol_has_protoss_medium_units(world, player)),
|
||||
LocationData("Echoes of the Future", "Echoes of the Future: Overmind", SC2WOL_LOC_ID_OFFSET + 2400),
|
||||
LocationData("Echoes of the Future", "Echoes of the Future: Close Obelisk", SC2WOL_LOC_ID_OFFSET + 2401),
|
||||
LocationData("Echoes of the Future", "Echoes of the Future: West Obelisk", SC2WOL_LOC_ID_OFFSET + 2402),
|
||||
LocationData("Echoes of the Future", "Beat Echoes of the Future", None),
|
||||
LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2500),
|
||||
LocationData("In Utter Darkness", "In Utter Darkness: Protoss Archive", SC2WOL_LOC_ID_OFFSET + 2501),
|
||||
LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2502),
|
||||
LocationData("In Utter Darkness", "Beat In Utter Darkness", None),
|
||||
LocationData("Gates of Hell", "Gates of Hell: Nydus Worms", SC2WOL_LOC_ID_OFFSET + 2600),
|
||||
LocationData("Gates of Hell", "Gates of Hell: Large Army", SC2WOL_LOC_ID_OFFSET + 2601),
|
||||
LocationData("Gates of Hell", "Beat Gates of Hell", None),
|
||||
LocationData("Belly of the Beast", "Belly of the Beast: Extract", SC2WOL_LOC_ID_OFFSET + 2700),
|
||||
LocationData("Belly of the Beast", "Belly of the Beast: First Charge", SC2WOL_LOC_ID_OFFSET + 2701),
|
||||
LocationData("Belly of the Beast", "Belly of the Beast: Second Charge", SC2WOL_LOC_ID_OFFSET + 2702),
|
||||
LocationData("Belly of the Beast", "Belly of the Beast: Third Charge", SC2WOL_LOC_ID_OFFSET + 2703),
|
||||
LocationData("Belly of the Beast", "Beat Belly of the Beast", None),
|
||||
LocationData("Shatter the Sky", "Shatter the Sky: Platform Destroyed", SC2WOL_LOC_ID_OFFSET + 2800),
|
||||
LocationData("Shatter the Sky", "Shatter the Sky: Close Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2801),
|
||||
LocationData("Shatter the Sky", "Shatter the Sky: Northwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2802),
|
||||
LocationData("Shatter the Sky", "Shatter the Sky: Southeast Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2803),
|
||||
LocationData("Shatter the Sky", "Shatter the Sky: Southwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2804),
|
||||
LocationData("Shatter the Sky", "Shatter the Sky: Leviathan", SC2WOL_LOC_ID_OFFSET + 2805),
|
||||
LocationData("Shatter the Sky", "Beat Shatter the Sky", None),
|
||||
LocationData("All-In", "All-In: Victory", None)
|
||||
]
|
||||
|
||||
return tuple(location_table)
|
||||
|
||||
49
worlds/sc2wol/LogicMixin.py
Normal file
49
worlds/sc2wol/LogicMixin.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from BaseClasses import MultiWorld
|
||||
from ..AutoWorld import LogicMixin
|
||||
|
||||
|
||||
class SC2WoLLogic(LogicMixin):
|
||||
def _sc2wol_has_common_unit(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Marine', 'Marauder', 'Firebat', 'Hellion', 'Vulture'}, player)
|
||||
|
||||
def _sc2wol_has_bunker_unit(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Marine', 'Marauder'}, player)
|
||||
|
||||
def _sc2wol_has_air(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Viking', 'Wraith', 'Medivac', 'Banshee', 'Hercules'}, player)
|
||||
|
||||
def _sc2wol_has_battlecruiser(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has('Battlecruiser', player)
|
||||
|
||||
def _sc2wol_has_air_anti_air(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Viking', 'Wraith'}, player)
|
||||
|
||||
def _sc2wol_has_mobile_anti_air(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Marine', 'Goliath'}, player) or self._sc2wol_has_air_anti_air(world, player)
|
||||
|
||||
def _sc2wol_has_anti_air(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has('Missile Turret', player) or self._sc2wol_has_mobile_anti_air(world, player)
|
||||
|
||||
def _sc2wol_has_heavy_defense(self, world: MultiWorld, player: int) -> bool:
|
||||
return (self.has_any({'Siege Tank', 'Vulture'}, player) or
|
||||
self.has('Bunker', player) and self._sc2wol_has_bunker_unit(world, player)) and \
|
||||
self._sc2wol_has_anti_air(world, player)
|
||||
|
||||
def _sc2wol_has_train_killers(self, world: MultiWorld, player: int) -> bool:
|
||||
return (self.has_any({'Siege Tank', 'Diamondback'}, player) or
|
||||
self.has_all({'Reaper', "G-4 Clusterbomb"}, player) or self.has_all({'Spectre', 'Psionic Lash'}, player))
|
||||
|
||||
def _sc2wol_able_to_rescue(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Medivac', 'Hercules', 'Raven', 'Orbital Strike'}, player)
|
||||
|
||||
def _sc2wol_has_protoss_common_units(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Zealot', 'Immortal', 'Stalker', 'Dark Templar'}, player)
|
||||
|
||||
def _sc2wol_has_protoss_medium_units(self, world: MultiWorld, player: int) -> bool:
|
||||
return self._sc2wol_has_protoss_common_units(world, player) and \
|
||||
self.has_any({'Stalker', 'Void Ray', 'Phoenix', 'Carrier'}, player)
|
||||
|
||||
def _sc2wol_cleared_missions(self, world: MultiWorld, player: int, mission_count: int) -> bool:
|
||||
return self.has_group("Missions", player, mission_count)
|
||||
|
||||
|
||||
48
worlds/sc2wol/Options.py
Normal file
48
worlds/sc2wol/Options.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from typing import Dict
|
||||
from BaseClasses import MultiWorld
|
||||
from Options import Choice, Option
|
||||
|
||||
class GameDifficulty(Choice):
|
||||
"""The difficulty of the campaign, affects enemy AI, starting units, and game speed."""
|
||||
display_name = "Game Difficulty"
|
||||
option_casual = 0
|
||||
option_normal = 1
|
||||
option_hard = 2
|
||||
option_brutal = 3
|
||||
|
||||
class UpgradeBonus(Choice):
|
||||
"""Determines what lab upgrade to use, whether it is Ultra-Capacitors which boost attack speed with every weapon upgrade
|
||||
or Vanadium Plating which boosts life with every armor upgrade."""
|
||||
display_name = "Upgrade Bonus"
|
||||
option_ultra_capacitors = 0
|
||||
option_vanadium_plating = 1
|
||||
|
||||
class BunkerUpgrade(Choice):
|
||||
"""Determines what bunker lab upgrade to use, whether it is Shrike Turret which outfits bunkers with an automated turret or
|
||||
Fortified Bunker which boosts the life of bunkers."""
|
||||
display_name = "Bunker Upgrade"
|
||||
option_shrike_turret = 0
|
||||
option_fortified_bunker = 1
|
||||
|
||||
class AllInMap(Choice):
|
||||
"""Determines what verion of All-In (final map) that will be generated for the campaign."""
|
||||
display_name = "All In Map"
|
||||
option_ground = 0
|
||||
option_air = 1
|
||||
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
sc2wol_options: Dict[str, Option] = {
|
||||
"game_difficulty": GameDifficulty,
|
||||
"upgrade_bonus": UpgradeBonus,
|
||||
"bunker_upgrade": BunkerUpgrade,
|
||||
"all_in_map": AllInMap,
|
||||
}
|
||||
|
||||
def get_option_value(world: MultiWorld, player: int, name: str) -> int:
|
||||
option = getattr(world, name, None)
|
||||
|
||||
if option == None:
|
||||
return 0
|
||||
|
||||
return int(option[player].value)
|
||||
162
worlds/sc2wol/Regions.py
Normal file
162
worlds/sc2wol/Regions.py
Normal file
@@ -0,0 +1,162 @@
|
||||
from typing import List, Set, Dict, Tuple, Optional, Callable
|
||||
from BaseClasses import MultiWorld, Region, Entrance, Location, RegionType
|
||||
from .Locations import LocationData
|
||||
|
||||
|
||||
def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location]):
|
||||
locations_per_region = get_locations_per_region(locations)
|
||||
|
||||
regions = [
|
||||
create_region(world, player, locations_per_region, location_cache, "Menu"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Liberation Day"),
|
||||
create_region(world, player, locations_per_region, location_cache, "The Outlaws"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Zero Hour"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Evacuation"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Outbreak"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Safe Haven"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Haven's Fall"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Smash and Grab"),
|
||||
create_region(world, player, locations_per_region, location_cache, "The Dig"),
|
||||
create_region(world, player, locations_per_region, location_cache, "The Moebius Factor"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Supernova"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Maw of the Void"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Devil's Playground"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Welcome to the Jungle"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Breakout"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Ghost of a Chance"),
|
||||
create_region(world, player, locations_per_region, location_cache, "The Great Train Robbery"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Cutthroat"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Engine of Destruction"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Media Blitz"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Piercing the Shroud"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Whispers of Doom"),
|
||||
create_region(world, player, locations_per_region, location_cache, "A Sinister Turn"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Echoes of the Future"),
|
||||
create_region(world, player, locations_per_region, location_cache, "In Utter Darkness"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Gates of Hell"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Belly of the Beast"),
|
||||
create_region(world, player, locations_per_region, location_cache, "Shatter the Sky"),
|
||||
create_region(world, player, locations_per_region, location_cache, "All-In")
|
||||
]
|
||||
|
||||
if __debug__:
|
||||
throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys())
|
||||
|
||||
world.regions += regions
|
||||
|
||||
names: Dict[str, int] = {}
|
||||
|
||||
connect(world, player, names, 'Menu', 'Liberation Day'),
|
||||
connect(world, player, names, 'Liberation Day', 'The Outlaws',
|
||||
lambda state: state._sc2wol_has_common_unit(world, player)),
|
||||
connect(world, player, names, 'The Outlaws', 'Zero Hour'),
|
||||
connect(world, player, names, 'Zero Hour', 'Evacuation',
|
||||
lambda state: state._sc2wol_has_anti_air(world, player)),
|
||||
connect(world, player, names, 'Evacuation', 'Outbreak'),
|
||||
connect(world, player, names, "Outbreak", "Safe Haven",
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player) and
|
||||
state._sc2wol_cleared_missions(world, player, 7)),
|
||||
connect(world, player, names, "Outbreak", "Haven's Fall",
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player) and
|
||||
state._sc2wol_cleared_missions(world, player, 7)),
|
||||
connect(world, player, names, 'Zero Hour', 'Smash and Grab',
|
||||
lambda state: state._sc2wol_has_anti_air(world, player)),
|
||||
connect(world, player, names, 'Smash and Grab', 'The Dig',
|
||||
lambda state: state._sc2wol_cleared_missions(world, player, 8) and
|
||||
state._sc2wol_has_heavy_defense(world, player)),
|
||||
connect(world, player, names, 'The Dig', 'The Moebius Factor',
|
||||
lambda state: state._sc2wol_cleared_missions(world, player, 11) and
|
||||
state._sc2wol_has_air(world, player)),
|
||||
connect(world, player, names, 'The Moebius Factor', 'Supernova',
|
||||
lambda state: state._sc2wol_cleared_missions(world, player, 14)),
|
||||
connect(world, player, names, 'Supernova', 'Maw of the Void'),
|
||||
connect(world, player, names, 'Zero Hour', "Devil's Playground",
|
||||
lambda state: state._sc2wol_cleared_missions(world, player, 4)),
|
||||
connect(world, player, names, "Devil's Playground", 'Welcome to the Jungle'),
|
||||
connect(world, player, names, "Welcome to the Jungle", 'Breakout',
|
||||
lambda state: state._sc2wol_cleared_missions(world, player, 8)),
|
||||
connect(world, player, names, "Welcome to the Jungle", 'Ghost of a Chance',
|
||||
lambda state: state._sc2wol_cleared_missions(world, player, 8)),
|
||||
connect(world, player, names, "Zero Hour", 'The Great Train Robbery',
|
||||
lambda state: state._sc2wol_cleared_missions(world, player, 6)),
|
||||
connect(world, player, names, 'The Great Train Robbery', 'Cutthroat',
|
||||
lambda state: state.has("Beat The Great Train Robbery", player)),
|
||||
connect(world, player, names, 'Cutthroat', 'Engine of Destruction',
|
||||
lambda state: state.has("Beat The Great Train Robbery", player)),
|
||||
connect(world, player, names, 'Engine of Destruction', 'Media Blitz',
|
||||
lambda state: state.has("Beat Engine of Destruction", player)),
|
||||
connect(world, player, names, 'Media Blitz', 'Piercing the Shroud'),
|
||||
connect(world, player, names, 'The Dig', 'Whispers of Doom',),
|
||||
connect(world, player, names, 'Whispers of Doom', 'A Sinister Turn'),
|
||||
connect(world, player, names, 'A Sinister Turn', 'Echoes of the Future',
|
||||
lambda state: state.has("Beat A Sinister Turn", player)),
|
||||
connect(world, player, names, 'Echoes of the Future', 'In Utter Darkness'),
|
||||
connect(world, player, names, 'Maw of the Void', 'Gates of Hell'),
|
||||
connect(world, player, names, 'Gates of Hell', 'Belly of the Beast'),
|
||||
connect(world, player, names, 'Gates of Hell', 'Shatter the Sky'),
|
||||
connect(world, player, names, 'Gates of Hell', 'All-In',
|
||||
lambda state: state.has('Beat Gates of Hell', player) or state.has('Beat Shatter the Sky', player))
|
||||
|
||||
|
||||
def throwIfAnyLocationIsNotAssignedToARegion(regions: List[Region], regionNames: Set[str]):
|
||||
existingRegions = set()
|
||||
|
||||
for region in regions:
|
||||
existingRegions.add(region.name)
|
||||
|
||||
if (regionNames - existingRegions):
|
||||
raise Exception("Starcraft: the following regions are used in locations: {}, but no such region exists".format(regionNames - existingRegions))
|
||||
|
||||
|
||||
def create_location(player: int, location_data: LocationData, region: Region, location_cache: List[Location]) -> Location:
|
||||
location = Location(player, location_data.name, location_data.code, region)
|
||||
location.access_rule = location_data.rule
|
||||
|
||||
if id is None:
|
||||
location.event = True
|
||||
location.locked = True
|
||||
|
||||
location_cache.append(location)
|
||||
|
||||
return location
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], location_cache: List[Location], name: str) -> Region:
|
||||
region = Region(name, RegionType.Generic, name, player)
|
||||
region.world = world
|
||||
|
||||
if name in locations_per_region:
|
||||
for location_data in locations_per_region[name]:
|
||||
location = create_location(player, location_data, region, location_cache)
|
||||
region.locations.append(location)
|
||||
|
||||
return region
|
||||
|
||||
|
||||
def connect(world: MultiWorld, player: int, used_names: Dict[str, int], source: str, target: str, rule: Optional[Callable] = None):
|
||||
sourceRegion = world.get_region(source, player)
|
||||
targetRegion = world.get_region(target, player)
|
||||
|
||||
if target not in used_names:
|
||||
used_names[target] = 1
|
||||
name = target
|
||||
else:
|
||||
used_names[target] += 1
|
||||
name = target + (' ' * used_names[target])
|
||||
|
||||
connection = Entrance(player, name, sourceRegion)
|
||||
|
||||
if rule:
|
||||
connection.access_rule = rule
|
||||
|
||||
sourceRegion.exits.append(connection)
|
||||
connection.connect(targetRegion)
|
||||
|
||||
|
||||
def get_locations_per_region(locations: Tuple[LocationData, ...]) -> Dict[str, List[LocationData]]:
|
||||
per_region: Dict[str, List[LocationData]] = {}
|
||||
|
||||
for location in locations:
|
||||
per_region.setdefault(location.region, []).append(location)
|
||||
|
||||
return per_region
|
||||
173
worlds/sc2wol/__init__.py
Normal file
173
worlds/sc2wol/__init__.py
Normal file
@@ -0,0 +1,173 @@
|
||||
import typing
|
||||
|
||||
from typing import List, Set, Tuple
|
||||
from BaseClasses import Item, MultiWorld, Location, Tutorial
|
||||
from ..AutoWorld import World, WebWorld
|
||||
from .Items import StarcraftWoLItem, item_table, filler_items, item_name_groups, get_full_item_list, \
|
||||
basic_unit
|
||||
from .Locations import get_locations
|
||||
from .Regions import create_regions
|
||||
from .Options import sc2wol_options, get_option_value
|
||||
from .LogicMixin import SC2WoLLogic
|
||||
from ..AutoWorld import World
|
||||
|
||||
|
||||
class Starcraft2WoLWebWorld(WebWorld):
|
||||
setup = Tutorial(
|
||||
"Multiworld Setup Guide",
|
||||
"A guide to setting up the Starcraft 2 randomizer connected to an Archipelago Multiworld",
|
||||
"English",
|
||||
"setup_en.md",
|
||||
"setup/en",
|
||||
["TheCondor"]
|
||||
)
|
||||
|
||||
tutorials = [setup]
|
||||
|
||||
class SC2WoLWorld(World):
|
||||
"""
|
||||
StarCraft II: Wings of Liberty is a science fiction real-time strategy video game developed and published by Blizzard Entertainment.
|
||||
Command Raynor's Raiders in collecting pieces of the Keystone in order to stop the zerg threat posed by the Queen of Blades.
|
||||
"""
|
||||
|
||||
game = "Starcraft 2 Wings of Liberty"
|
||||
web = Starcraft2WoLWebWorld()
|
||||
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = {location.name: location.code for location in get_locations(None, None)}
|
||||
options = sc2wol_options
|
||||
|
||||
item_name_groups = item_name_groups
|
||||
locked_locations: typing.List[str]
|
||||
location_cache: typing.List[Location]
|
||||
|
||||
def _get_sc2wol_data(self):
|
||||
return {}
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
super(SC2WoLWorld, self).__init__(world, player)
|
||||
self.location_cache = []
|
||||
self.locked_locations = []
|
||||
|
||||
def _create_items(self, name: str):
|
||||
data = get_full_item_list()[name]
|
||||
return [self.create_item(name)] * data.quantity
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
data = get_full_item_list()[name]
|
||||
return StarcraftWoLItem(name, data.progression, data.code, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player, get_locations(self.world, self.player),
|
||||
self.location_cache)
|
||||
|
||||
def generate_basic(self):
|
||||
excluded_items = get_excluded_items(self, self.world, self.player)
|
||||
|
||||
assign_starter_items(self.world, self.player, excluded_items, self.locked_locations)
|
||||
|
||||
pool = get_item_pool(self.world, self.player, excluded_items)
|
||||
|
||||
fill_item_pool_with_dummy_items(self, self.world, self.player, self.locked_locations, self.location_cache, pool)
|
||||
|
||||
self.world.itempool += pool
|
||||
|
||||
def set_rules(self):
|
||||
setup_events(self.world, self.player, self.locked_locations, self.location_cache)
|
||||
|
||||
self.world.completion_condition[self.player] = lambda state: state.has('All-In: Victory', self.player)
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return self.world.random.choice(filler_items)
|
||||
|
||||
def fill_slot_data(self):
|
||||
slot_data = self._get_sc2wol_data()
|
||||
for option_name in sc2wol_options:
|
||||
option = getattr(self.world, option_name)[self.player]
|
||||
if slot_data.get(option_name, None) is None and type(option.value) in {str, int}:
|
||||
slot_data[option_name] = int(option.value)
|
||||
return slot_data
|
||||
|
||||
|
||||
def setup_events(world: MultiWorld, player: int, locked_locations: typing.List[str], location_cache: typing.List[Location]):
|
||||
for location in location_cache:
|
||||
if location.address == None:
|
||||
item = Item(location.name, True, None, player)
|
||||
|
||||
locked_locations.append(location.name)
|
||||
|
||||
location.place_locked_item(item)
|
||||
|
||||
|
||||
def get_excluded_items(self: SC2WoLWorld, world: MultiWorld, player: int) -> Set[str]:
|
||||
excluded_items: Set[str] = set()
|
||||
|
||||
if get_option_value(world, player, "upgrade_bonus") == 1:
|
||||
excluded_items.add("Ultra-Capacitors")
|
||||
else:
|
||||
excluded_items.add("Vanadium Plating")
|
||||
|
||||
if get_option_value(world, player, "bunker_upgrade") == 1:
|
||||
excluded_items.add("Shrike Turret")
|
||||
else:
|
||||
excluded_items.add("Fortified Bunker")
|
||||
|
||||
for item in world.precollected_items[player]:
|
||||
excluded_items.add(item.name)
|
||||
|
||||
return excluded_items
|
||||
|
||||
|
||||
def assign_starter_items(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str]):
|
||||
non_local_items = world.non_local_items[player].value
|
||||
|
||||
local_basic_unit = tuple(item for item in basic_unit if item not in non_local_items)
|
||||
if not local_basic_unit:
|
||||
raise Exception("At least one basic unit must be local")
|
||||
|
||||
assign_starter_item(world, player, excluded_items, locked_locations, 'Liberation Day: First Statue',
|
||||
local_basic_unit)
|
||||
|
||||
|
||||
def assign_starter_item(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str],
|
||||
location: str, item_list: Tuple[str, ...]):
|
||||
|
||||
item_name = world.random.choice(item_list)
|
||||
|
||||
excluded_items.add(item_name)
|
||||
|
||||
item = create_item_with_correct_settings(world, player, item_name)
|
||||
|
||||
world.get_location(location, player).place_locked_item(item)
|
||||
|
||||
locked_locations.append(location)
|
||||
|
||||
|
||||
def get_item_pool(world: MultiWorld, player: int, excluded_items: Set[str]) -> List[Item]:
|
||||
pool: List[Item] = []
|
||||
|
||||
for name, data in item_table.items():
|
||||
if name not in excluded_items:
|
||||
for _ in range(data.quantity):
|
||||
item = create_item_with_correct_settings(world, player, name)
|
||||
pool.append(item)
|
||||
|
||||
return pool
|
||||
|
||||
def fill_item_pool_with_dummy_items(self: SC2WoLWorld, world: MultiWorld, player: int, locked_locations: List[str],
|
||||
location_cache: List[Location], pool: List[Item]):
|
||||
for _ in range(len(location_cache) - len(locked_locations) - len(pool)):
|
||||
item = create_item_with_correct_settings(world, player, self.get_filler_item_name())
|
||||
pool.append(item)
|
||||
|
||||
|
||||
def create_item_with_correct_settings(world: MultiWorld, player: int, name: str) -> Item:
|
||||
data = item_table[name]
|
||||
|
||||
item = Item(name, data.progression, data.code, player)
|
||||
item.never_exclude = data.never_exclude
|
||||
|
||||
if not item.advancement:
|
||||
return item
|
||||
|
||||
return item
|
||||
33
worlds/sc2wol/docs/en_Starcraft 2 Wings of Liberty.md
Normal file
33
worlds/sc2wol/docs/en_Starcraft 2 Wings of Liberty.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Starcraft 2 Wings of Liberty
|
||||
|
||||
## Where is the settings page?
|
||||
|
||||
The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
|
||||
config file.
|
||||
|
||||
## What does randomization do to this game?
|
||||
|
||||
Items which the player would normally acquire throughout the game have been moved around. Logic remains, so the game is
|
||||
always able to be completed, but because of the item shuffle the player may need to access certain areas before they
|
||||
would in the vanilla game. All rings and spells are also randomized into those item locations, therefore you can no
|
||||
longer craft them at the alchemist
|
||||
|
||||
## What is the goal of Starcraft 2 when randomized?
|
||||
|
||||
The goal remains unchanged. Beat the final mission All In.
|
||||
|
||||
## What items and locations get shuffled?
|
||||
|
||||
Unit unlocks, upgrade unlocks, armory upgrades, laboratory researches, and mercenary unlocks can be shuffled, and all
|
||||
bonus objectives, side missions, mission completions are now locations that can contain these items.
|
||||
|
||||
## Which items can be in another player's world?
|
||||
|
||||
Any of the items which can be shuffled may also be placed into another player's world. It is possible to choose to limit
|
||||
certain items to your own world.
|
||||
|
||||
## When the player receives an item, what happens?
|
||||
|
||||
When the player receives an item, they will receive a message through their text client and in game if currently playing
|
||||
a mission. They will immediately be able to use that unlock/upgrade.
|
||||
|
||||
33
worlds/sc2wol/docs/setup_en.md
Normal file
33
worlds/sc2wol/docs/setup_en.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Starcraft 2 Wings of Liberty Randomizer Setup Guide
|
||||
|
||||
## Required Software
|
||||
|
||||
- [Starcraft 2](https://starcraft2.com/en-us/)
|
||||
- [Starcraft 2 AP Client](https://github.com/ArchipelagoMW/Archipelago)
|
||||
- [Starcraft 2 AP Maps and Data](https://github.com/TheCondor07/Starcraft2ArchipelagoData)
|
||||
|
||||
## General Concept
|
||||
|
||||
Starcraft 2 AP Client launches a custom version of Starcraft 2 running modified Wings of Liberty campaign maps
|
||||
to allow for randomization of the items
|
||||
|
||||
## Installation Procedures
|
||||
|
||||
Download latest release on [Starcraft 2 Archipelago Data Releases](https://github.com/TheCondor07/Starcraft2ArchipelagoData/releases) you
|
||||
can find the .zip files on the releases page. Download the zip then extract the zip to the
|
||||
folder where your Starcraft 2 game is installed. The just run ArchipelagoStarcraftClient.exe to start the client to
|
||||
connect to a Multiworld Game.
|
||||
|
||||
## Joining a MultiWorld Game
|
||||
|
||||
1. Run ArchipelagoStarcraftClient.exe
|
||||
2. Type in /connect [sever ip]
|
||||
3. Insert slot name and password as prompted
|
||||
4. Once connected, use /unfinished to find what missions you can play and '/play [mission id]' to launch a mission. For
|
||||
new games under default settings the first mission available will always be Liberation Day[1] playable using the command
|
||||
'/play 1'
|
||||
|
||||
## Where do I get a config file?
|
||||
|
||||
The [Player Settings](https://archipelago.gg/games/Starcraft%202%20Wings%20of%20Liberty/player-settings) page on the website allows you to
|
||||
configure your personal settings and export them into a config file
|
||||
3
worlds/sc2wol/requirements.txt
Normal file
3
worlds/sc2wol/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
nest-asyncio >= 1.5.5
|
||||
six >= 1.16.0
|
||||
apsc2 >= 5.5
|
||||
Reference in New Issue
Block a user