Wargroove: Implement New Game (#1401)

This adds Wargroove to the list of supported games. Wargroove uses a custom non-linear campaign over the vanilla and double trouble campaigns. A Wargroove client has been added which does a lot of heavy lifting for the Wargroove implementation and must be always on during gameplay. The mod source files can be found here: https://github.com/FlySniper/WargrooveArchipelagoMod
This commit is contained in:
FlySniper
2023-02-24 01:35:09 -05:00
committed by GitHub
parent 7c68e91d4a
commit 5966aa5327
18 changed files with 1251 additions and 0 deletions

104
worlds/wargroove/Items.py Normal file
View File

@@ -0,0 +1,104 @@
import typing
from BaseClasses import Item, ItemClassification
from typing import Dict, List
PROGRESSION = ItemClassification.progression
PROGRESSION_SKIP_BALANCING = ItemClassification.progression_skip_balancing
USEFUL = ItemClassification.useful
FILLER = ItemClassification.filler
class ItemData(typing.NamedTuple):
code: typing.Optional[int]
type: str
classification: ItemClassification = PROGRESSION
item_table: Dict[str, ItemData] = {
# Units
'Spearman': ItemData(52000, 'Unit'),
'Wagon': ItemData(52001, 'Unit', USEFUL),
'Mage': ItemData(52002, 'Unit'),
'Archer': ItemData(52003, 'Unit'),
'Knight': ItemData(52004, 'Unit'),
'Ballista': ItemData(52005, 'Unit'),
'Golem': ItemData(52006, 'Unit', USEFUL),
'Harpy': ItemData(52007, 'Unit'),
'Witch': ItemData(52008, 'Unit', USEFUL),
'Dragon': ItemData(52009, 'Unit'),
'Balloon': ItemData(52010, 'Unit', USEFUL),
'Barge': ItemData(52011, 'Unit'),
'Merfolk': ItemData(52012, 'Unit'),
'Turtle': ItemData(52013, 'Unit'),
'Harpoon Ship': ItemData(52014, 'Unit'),
'Warship': ItemData(52015, 'Unit'),
'Thief': ItemData(52016, 'Unit'),
'Rifleman': ItemData(52017, 'Unit'),
# Map Triggers
'Eastern Bridges': ItemData(52018, 'Trigger'),
'Southern Walls': ItemData(52019, 'Trigger'),
'Final Bridges': ItemData(52020, 'Trigger', PROGRESSION_SKIP_BALANCING),
'Final Walls': ItemData(52021, 'Trigger', PROGRESSION_SKIP_BALANCING),
'Final Sickle': ItemData(52022, 'Trigger', PROGRESSION_SKIP_BALANCING),
# Player Buffs
'Income Boost': ItemData(52023, 'Boost', FILLER),
'Commander Defense Boost': ItemData(52024, 'Boost', FILLER),
# Factions
'Cherrystone Commanders': ItemData(52025, 'Faction', USEFUL),
'Felheim Commanders': ItemData(52026, 'Faction', USEFUL),
'Floran Commanders': ItemData(52027, 'Faction', USEFUL),
'Heavensong Commanders': ItemData(52028, 'Faction', USEFUL),
'Requiem Commanders': ItemData(52029, 'Faction', USEFUL),
'Outlaw Commanders': ItemData(52030, 'Faction', USEFUL),
# Event Items
'Wargroove Victory': ItemData(None, 'Goal')
}
class CommanderData(typing.NamedTuple):
name: str
internal_name: str
alt_name: str = None
faction_table: Dict[str, List[CommanderData]] = {
'Starter': [
CommanderData('Mercival', 'commander_mercival')
],
'Cherrystone': [
CommanderData('Mercia', 'commander_mercia'),
CommanderData('Emeric', 'commander_emeric'),
CommanderData('Caesar', 'commander_caesar'),
],
'Felheim': [
CommanderData('Valder', 'commander_valder'),
CommanderData('Ragna', 'commander_ragna'),
CommanderData('Sigrid', 'commander_sigrid')
],
'Floran': [
CommanderData('Greenfinger', 'commander_greenfinger'),
CommanderData('Sedge', 'commander_sedge'),
CommanderData('Nuru', 'commander_nuru')
],
'Heavensong': [
CommanderData('Tenri', 'commander_tenri'),
CommanderData('Koji', 'commander_koji'),
CommanderData('Ryota', 'commander_ryota')
],
'Requiem': [
CommanderData('Elodie', 'commander_elodie'),
CommanderData('Dark Mercia', 'commander_darkmercia')
],
'Outlaw': [
CommanderData('Wulfar', 'commander_wulfar'),
CommanderData('Twins', 'commander_twins', 'Errol & Orla'),
CommanderData('Vesper', 'commander_vesper')
]
}

View File

@@ -0,0 +1,41 @@
location_table = {
'Humble Beginnings: Caesar': 53001,
'Humble Beginnings: Chest 1': 53002,
'Humble Beginnings: Chest 2': 53003,
'Humble Beginnings: Victory': 53004,
'Best Friendssss: Find Sedge': 53005,
'Best Friendssss: Victory': 53006,
'A Knight\'s Folly: Caesar': 53007,
'A Knight\'s Folly: Victory': 53008,
'Denrunaway: Chest': 53009,
'Denrunaway: Victory': 53010,
'Dragon Freeway: Victory': 53011,
'Deep Thicket: Find Sedge': 53012,
'Deep Thicket: Victory': 53013,
'Corrupted Inlet: Victory': 53014,
'Mage Mayhem: Caesar': 53015,
'Mage Mayhem: Victory': 53016,
'Endless Knight: Victory': 53017,
'Ambushed in the Middle: Victory (Blue)': 53018,
'Ambushed in the Middle: Victory (Green)': 53019,
'The Churning Sea: Victory': 53020,
'Frigid Archery: Light the Torch': 53021,
'Frigid Archery: Victory': 53022,
'Archery Lessons: Chest': 53023,
'Archery Lessons: Victory': 53024,
'Surrounded: Caesar': 53025,
'Surrounded: Victory': 53026,
'Darkest Knight: Victory': 53027,
'Robbed: Victory': 53028,
'Open Season: Caesar': 53029,
'Open Season: Victory': 53030,
'Doggo Mountain: Find all the Dogs': 53031,
'Doggo Mountain: Victory': 53032,
'Tenri\'s Fall: Victory': 53033,
'Master of the Lake: Victory': 53034,
'A Ballista\'s Revenge: Victory': 53035,
'Rebel Village: Victory (Pink)': 53036,
'Rebel Village: Victory (Red)': 53037,
'Foolish Canal: Victory': 53038,
'Wargroove Finale: Victory': None,
}

View File

@@ -0,0 +1,38 @@
import typing
from Options import Choice, Option, Range
class IncomeBoost(Range):
"""How much extra income the player gets per turn per boost received."""
display_name = "Income Boost"
range_start = 0
range_end = 100
default = 25
class CommanderDefenseBoost(Range):
"""How much extra defense the player's commander gets per boost received."""
display_name = "Commander Defense Boost"
range_start = 0
range_end = 8
default = 2
class CommanderChoice(Choice):
"""How the player's commander is selected for missions.
Locked Random: The player's commander is randomly predetermined for each level.
Unlockable Factions: The player starts with Mercival and can unlock playable factions.
Random Starting Faction: The player starts with a random starting faction and can unlock the rest.
When playing with unlockable factions, faction items are added to the pool.
Extra faction items after the first also reward starting Groove charge."""
display_name = "Commander Choice"
option_locked_random = 0
option_unlockable_factions = 1
option_random_starting_faction = 2
wargroove_options: typing.Dict[str, type(Option)] = {
"income_boost": IncomeBoost,
"commander_defense_boost": CommanderDefenseBoost,
"commander_choice": CommanderChoice
}

169
worlds/wargroove/Regions.py Normal file
View File

@@ -0,0 +1,169 @@
def create_regions(world, player: int):
from . import create_region
from .Locations import location_table
world.regions += [
create_region(world, player, 'Menu', None, ['Humble Beginnings']),
# Level 1
create_region(world, player, 'Humble Beginnings', [
'Humble Beginnings: Caesar',
'Humble Beginnings: Chest 1',
'Humble Beginnings: Chest 2',
'Humble Beginnings: Victory',
], ['Best Friendssss', 'A Knight\'s Folly', 'Denrunaway', 'Wargroove Finale']),
# Levels 2A-2C
create_region(world, player, 'Best Friendssss', [
'Best Friendssss: Find Sedge',
'Best Friendssss: Victory'
], ['Dragon Freeway', 'Deep Thicket', 'Corrupted Inlet']),
create_region(world, player, 'A Knight\'s Folly', [
'A Knight\'s Folly: Caesar',
'A Knight\'s Folly: Victory'
], ['Mage Mayhem', 'Endless Knight', 'Ambushed in the Middle']),
create_region(world, player, 'Denrunaway', [
'Denrunaway: Chest',
'Denrunaway: Victory'
], ['The Churning Sea', 'Frigid Archery', 'Archery Lessons']),
# Levels 3AA-3AC
create_region(world, player, 'Dragon Freeway', [
'Dragon Freeway: Victory',
], ['Surrounded']),
create_region(world, player, 'Deep Thicket', [
'Deep Thicket: Find Sedge',
'Deep Thicket: Victory',
], ['Darkest Knight']),
create_region(world, player, 'Corrupted Inlet', [
'Corrupted Inlet: Victory',
], ['Robbed']),
# Levels 3BA-3BC
create_region(world, player, 'Mage Mayhem', [
'Mage Mayhem: Caesar',
'Mage Mayhem: Victory',
], ['Open Season', 'Foolish Canal: Mage Mayhem Entrance']),
create_region(world, player, 'Endless Knight', [
'Endless Knight: Victory',
], ['Doggo Mountain', 'Foolish Canal: Endless Knight Entrance']),
create_region(world, player, 'Ambushed in the Middle', [
'Ambushed in the Middle: Victory (Blue)',
'Ambushed in the Middle: Victory (Green)',
], ['Tenri\'s Fall']),
# Levels 3CA-3CC
create_region(world, player, 'The Churning Sea', [
'The Churning Sea: Victory',
], ['Rebel Village']),
create_region(world, player, 'Frigid Archery', [
'Frigid Archery: Light the Torch',
'Frigid Archery: Victory',
], ['A Ballista\'s Revenge']),
create_region(world, player, 'Archery Lessons', [
'Archery Lessons: Chest',
'Archery Lessons: Victory',
], ['Master of the Lake']),
# Levels 4AA-4AC
create_region(world, player, 'Surrounded', [
'Surrounded: Caesar',
'Surrounded: Victory',
]),
create_region(world, player, 'Darkest Knight', [
'Darkest Knight: Victory',
]),
create_region(world, player, 'Robbed', [
'Robbed: Victory',
]),
# Levels 4BAA-4BCA
create_region(world, player, 'Open Season', [
'Open Season: Caesar',
'Open Season: Victory',
]),
create_region(world, player, 'Doggo Mountain', [
'Doggo Mountain: Find all the Dogs',
'Doggo Mountain: Victory',
]),
create_region(world, player, 'Tenri\'s Fall', [
'Tenri\'s Fall: Victory',
]),
# Level 4BAB
create_region(world, player, 'Foolish Canal', [
'Foolish Canal: Victory',
]),
# Levels 4CA-4CC
create_region(world, player, 'Master of the Lake', [
'Master of the Lake: Victory',
]),
create_region(world, player, 'A Ballista\'s Revenge', [
'A Ballista\'s Revenge: Victory',
]),
create_region(world, player, 'Rebel Village', [
'Rebel Village: Victory (Pink)',
'Rebel Village: Victory (Red)',
]),
# Final Level
create_region(world, player, 'Wargroove Finale', [
'Wargroove Finale: Victory'
]),
]
# link up our regions with the entrances
world.get_entrance('Humble Beginnings', player).connect(world.get_region('Humble Beginnings', player))
world.get_entrance('Best Friendssss', player).connect(world.get_region('Best Friendssss', player))
world.get_entrance('A Knight\'s Folly', player).connect(world.get_region('A Knight\'s Folly', player))
world.get_entrance('Denrunaway', player).connect(world.get_region('Denrunaway', player))
world.get_entrance('Wargroove Finale', player).connect(world.get_region('Wargroove Finale', player))
world.get_entrance('Dragon Freeway', player).connect(world.get_region('Dragon Freeway', player))
world.get_entrance('Deep Thicket', player).connect(world.get_region('Deep Thicket', player))
world.get_entrance('Corrupted Inlet', player).connect(world.get_region('Corrupted Inlet', player))
world.get_entrance('Mage Mayhem', player).connect(world.get_region('Mage Mayhem', player))
world.get_entrance('Endless Knight', player).connect(world.get_region('Endless Knight', player))
world.get_entrance('Ambushed in the Middle', player).connect(world.get_region('Ambushed in the Middle', player))
world.get_entrance('The Churning Sea', player).connect(world.get_region('The Churning Sea', player))
world.get_entrance('Frigid Archery', player).connect(world.get_region('Frigid Archery', player))
world.get_entrance('Archery Lessons', player).connect(world.get_region('Archery Lessons', player))
world.get_entrance('Surrounded', player).connect(world.get_region('Surrounded', player))
world.get_entrance('Darkest Knight', player).connect(world.get_region('Darkest Knight', player))
world.get_entrance('Robbed', player).connect(world.get_region('Robbed', player))
world.get_entrance('Open Season', player).connect(world.get_region('Open Season', player))
world.get_entrance('Doggo Mountain', player).connect(world.get_region('Doggo Mountain', player))
world.get_entrance('Tenri\'s Fall', player).connect(world.get_region('Tenri\'s Fall', player))
world.get_entrance('Foolish Canal: Mage Mayhem Entrance', player).connect(world.get_region('Foolish Canal', player))
world.get_entrance('Foolish Canal: Endless Knight Entrance', player).connect(
world.get_region('Foolish Canal', player)
)
world.get_entrance('Master of the Lake', player).connect(world.get_region('Master of the Lake', player))
world.get_entrance('A Ballista\'s Revenge', player).connect(world.get_region('A Ballista\'s Revenge', player))
world.get_entrance('Rebel Village', player).connect(world.get_region('Rebel Village', player))

161
worlds/wargroove/Rules.py Normal file
View File

@@ -0,0 +1,161 @@
from typing import List
from BaseClasses import MultiWorld, Region, Location
from ..AutoWorld import LogicMixin
from ..generic.Rules import set_rule
class WargrooveLogic(LogicMixin):
def _wargroove_has_item(self, player: int, item: str) -> bool:
return self.has(item, player)
def _wargroove_has_region(self, player: int, region: str) -> bool:
return self.can_reach(region, 'Region', player)
def _wargroove_has_item_and_region(self, player: int, item: str, region: str) -> bool:
return self.can_reach(region, 'Region', player) and self.has(item, player)
def set_rules(world: MultiWorld, player: int):
# Final Level
set_rule(world.get_location('Wargroove Finale: Victory', player),
lambda state: state._wargroove_has_item(player, "Final Bridges") and
state._wargroove_has_item(player, "Final Walls") and
state._wargroove_has_item(player, "Final Sickle"))
# Level 1
set_rule(world.get_location('Humble Beginnings: Caesar', player), lambda state: True)
set_rule(world.get_location('Humble Beginnings: Chest 1', player), lambda state: True)
set_rule(world.get_location('Humble Beginnings: Chest 2', player), lambda state: True)
set_rule(world.get_location('Humble Beginnings: Victory', player), lambda state: True)
set_region_exit_rules(world.get_region('Humble Beginnings', player),
[world.get_location('Humble Beginnings: Victory', player)])
# Levels 2A-2C
set_rule(world.get_location('Best Friendssss: Find Sedge', player), lambda state: True)
set_rule(world.get_location('Best Friendssss: Victory', player), lambda state: True)
set_region_exit_rules(world.get_region('Best Friendssss', player),
[world.get_location('Best Friendssss: Victory', player)])
set_rule(world.get_location('A Knight\'s Folly: Caesar', player), lambda state: True)
set_rule(world.get_location('A Knight\'s Folly: Victory', player), lambda state: True)
set_region_exit_rules(world.get_region('A Knight\'s Folly', player),
[world.get_location('A Knight\'s Folly: Victory', player)])
set_rule(world.get_location('Denrunaway: Chest', player), lambda state: True)
set_rule(world.get_location('Denrunaway: Victory', player), lambda state: True)
set_region_exit_rules(world.get_region('Denrunaway', player), [world.get_location('Denrunaway: Victory', player)])
# Levels 3AA-3AC
set_rule(world.get_location('Dragon Freeway: Victory', player),
lambda state: state._wargroove_has_item(player, 'Mage'))
set_region_exit_rules(world.get_region('Dragon Freeway', player),
[world.get_location('Dragon Freeway: Victory', player)])
set_rule(world.get_location('Deep Thicket: Find Sedge', player),
lambda state: state._wargroove_has_item(player, 'Mage'))
set_rule(world.get_location('Deep Thicket: Victory', player),
lambda state: state._wargroove_has_item(player, 'Mage'))
set_region_exit_rules(world.get_region('Deep Thicket', player),
[world.get_location('Deep Thicket: Victory', player)])
set_rule(world.get_location('Corrupted Inlet: Victory', player),
lambda state: state._wargroove_has_item(player, 'Barge') or
state._wargroove_has_item(player, 'Merfolk') or
state._wargroove_has_item(player, 'Warship'))
set_region_exit_rules(world.get_region('Corrupted Inlet', player),
[world.get_location('Corrupted Inlet: Victory', player)])
# Levels 3BA-3BC
set_rule(world.get_location('Mage Mayhem: Caesar', player),
lambda state: state._wargroove_has_item(player, 'Harpy') or state._wargroove_has_item(player, 'Dragon'))
set_rule(world.get_location('Mage Mayhem: Victory', player),
lambda state: state._wargroove_has_item(player, 'Harpy') or state._wargroove_has_item(player, 'Dragon'))
set_region_exit_rules(world.get_region('Mage Mayhem', player), [world.get_location('Mage Mayhem: Victory', player)])
set_rule(world.get_location('Endless Knight: Victory', player),
lambda state: state._wargroove_has_item(player, 'Eastern Bridges') and (
state._wargroove_has_item(player, 'Spearman') or
state._wargroove_has_item(player, 'Harpy') or
state._wargroove_has_item(player, 'Dragon')))
set_region_exit_rules(world.get_region('Endless Knight', player),
[world.get_location('Endless Knight: Victory', player)])
set_rule(world.get_location('Ambushed in the Middle: Victory (Blue)', player),
lambda state: state._wargroove_has_item(player, 'Spearman'))
set_rule(world.get_location('Ambushed in the Middle: Victory (Green)', player),
lambda state: state._wargroove_has_item(player, 'Spearman'))
set_region_exit_rules(world.get_region('Ambushed in the Middle', player),
[world.get_location('Ambushed in the Middle: Victory (Blue)', player),
world.get_location('Ambushed in the Middle: Victory (Green)', player)])
# Levels 3CA-3CC
set_rule(world.get_location('The Churning Sea: Victory', player),
lambda state: (state._wargroove_has_item(player, 'Merfolk') or state._wargroove_has_item(player, 'Turtle'))
and state._wargroove_has_item(player, 'Harpoon Ship'))
set_region_exit_rules(world.get_region('The Churning Sea', player),
[world.get_location('The Churning Sea: Victory', player)])
set_rule(world.get_location('Frigid Archery: Light the Torch', player),
lambda state: state._wargroove_has_item(player, 'Archer') and
state._wargroove_has_item(player, 'Southern Walls'))
set_rule(world.get_location('Frigid Archery: Victory', player),
lambda state: state._wargroove_has_item(player, 'Archer'))
set_region_exit_rules(world.get_region('Frigid Archery', player),
[world.get_location('Frigid Archery: Victory', player)])
set_rule(world.get_location('Archery Lessons: Chest', player),
lambda state: state._wargroove_has_item(player, 'Knight') and
state._wargroove_has_item(player, 'Southern Walls'))
set_rule(world.get_location('Archery Lessons: Victory', player),
lambda state: state._wargroove_has_item(player, 'Knight') and
state._wargroove_has_item(player, 'Southern Walls'))
set_region_exit_rules(world.get_region('Archery Lessons', player),
[world.get_location('Archery Lessons: Victory', player)])
# Levels 4AA-4AC
set_rule(world.get_location('Surrounded: Caesar', player),
lambda state: state._wargroove_has_item_and_region(player, 'Knight', 'Surrounded'))
set_rule(world.get_location('Surrounded: Victory', player),
lambda state: state._wargroove_has_item_and_region(player, 'Knight', 'Surrounded'))
set_rule(world.get_location('Darkest Knight: Victory', player),
lambda state: state._wargroove_has_item_and_region(player, 'Spearman', 'Darkest Knight'))
set_rule(world.get_location('Robbed: Victory', player),
lambda state: state._wargroove_has_item_and_region(player, 'Thief', 'Robbed') and
state._wargroove_has_item(player, 'Rifleman'))
# Levels 4BA-4BC
set_rule(world.get_location('Open Season: Caesar', player),
lambda state: state._wargroove_has_item_and_region(player, 'Mage', 'Open Season') and
state._wargroove_has_item(player, 'Knight'))
set_rule(world.get_location('Open Season: Victory', player),
lambda state: state._wargroove_has_item_and_region(player, 'Mage', 'Open Season') and
state._wargroove_has_item(player, 'Knight'))
set_rule(world.get_location('Doggo Mountain: Find all the Dogs', player),
lambda state: state._wargroove_has_item_and_region(player, 'Knight', 'Doggo Mountain'))
set_rule(world.get_location('Doggo Mountain: Victory', player),
lambda state: state._wargroove_has_item_and_region(player, 'Knight', 'Doggo Mountain'))
set_rule(world.get_location('Tenri\'s Fall: Victory', player),
lambda state: state._wargroove_has_item_and_region(player, 'Mage', 'Tenri\'s Fall') and
state._wargroove_has_item(player, 'Thief'))
set_rule(world.get_location('Foolish Canal: Victory', player),
lambda state: state._wargroove_has_item_and_region(player, 'Mage', 'Foolish Canal') and
state._wargroove_has_item(player, 'Spearman'))
# Levels 4CA-4CC
set_rule(world.get_location('Master of the Lake: Victory', player),
lambda state: state._wargroove_has_item_and_region(player, 'Warship', 'Master of the Lake'))
set_rule(world.get_location('A Ballista\'s Revenge: Victory', player),
lambda state: state._wargroove_has_item_and_region(player, 'Ballista', 'A Ballista\'s Revenge'))
set_rule(world.get_location('Rebel Village: Victory (Pink)', player),
lambda state: state._wargroove_has_item_and_region(player, 'Spearman', 'Rebel Village'))
set_rule(world.get_location('Rebel Village: Victory (Red)', player),
lambda state: state._wargroove_has_item_and_region(player, 'Spearman', 'Rebel Village'))
def set_region_exit_rules(region: Region, locations: List[Location], operator: str = "or"):
if operator == "or":
exit_rule = lambda state: any(location.access_rule(state) for location in locations)
else:
exit_rule = lambda state: all(location.access_rule(state) for location in locations)
for region_exit in region.exits:
region_exit.access_rule = exit_rule

View File

@@ -0,0 +1,28 @@
<FactionBox>:
orientation: 'vertical'
padding: [10,5,10,5]
size_hint_y: 0.14
<CommanderGroup>:
orientation: 'horizontal'
<CommanderButton>:
text_size: self.size
size_hint: (None, 0.8)
width: 100
markup: True
halign: 'center'
valign: 'middle'
padding_x: 5
outline_width: 1
disabled: True
on_release: setattr(self, 'state', 'down')
<ItemTracker>:
orientation: 'horizontal'
padding_y: 5
<ItemLabel>:
size_hint_x: None
size: self.texture_size
pos_hint: {'left': 1}

View File

@@ -0,0 +1,139 @@
import os
import string
import json
from BaseClasses import Item, MultiWorld, Region, Location, Entrance, Tutorial, ItemClassification
from .Items import item_table, faction_table
from .Locations import location_table
from .Regions import create_regions
from .Rules import set_rules
from ..AutoWorld import World, WebWorld
from .Options import wargroove_options
class WargrooveWeb(WebWorld):
tutorials = [Tutorial(
"Multiworld Setup Guide",
"A guide to setting up Wargroove for Archipelago.",
"English",
"wargroove_en.md",
"wargroove/en",
["Fly Sniper"]
)]
class WargrooveWorld(World):
"""
Command an army, in this retro style turn based strategy game!
"""
option_definitions = wargroove_options
game = "Wargroove"
topology_present = True
data_version = 1
web = WargrooveWeb()
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = location_table
def _get_slot_data(self):
return {
'seed': "".join(self.multiworld.per_slot_randoms[self.player].choice(string.ascii_letters) for i in range(16)),
'income_boost': self.multiworld.income_boost[self.player],
'commander_defense_boost': self.multiworld.commander_defense_boost[self.player],
'can_choose_commander': self.multiworld.commander_choice[self.player] != 0,
'starting_groove_multiplier': 20 # Backwards compatibility in case this ever becomes an option
}
def generate_early(self):
# Selecting a random starting faction
if self.multiworld.commander_choice[self.player] == 2:
factions = [faction for faction in faction_table.keys() if faction != "Starter"]
starting_faction = WargrooveItem(self.multiworld.random.choice(factions) + ' Commanders', self.player)
self.multiworld.push_precollected(starting_faction)
def generate_basic(self):
# Fill out our pool with our items from the item table
pool = []
precollected_item_names = {item.name for item in self.multiworld.precollected_items[self.player]}
ignore_faction_items = self.multiworld.commander_choice[self.player] == 0
for name, data in item_table.items():
if data.code is not None and name not in precollected_item_names and not data.classification == ItemClassification.filler:
if name.endswith(' Commanders') and ignore_faction_items:
continue
item = WargrooveItem(name, self.player)
pool.append(item)
# Matching number of unfilled locations with filler items
locations_remaining = len(location_table) - 1 - len(pool)
while locations_remaining > 0:
# Filling the pool equally with both types of filler items
pool.append(WargrooveItem("Commander Defense Boost", self.player))
locations_remaining -= 1
if locations_remaining > 0:
pool.append(WargrooveItem("Income Boost", self.player))
locations_remaining -= 1
self.multiworld.itempool += pool
# Placing victory event at final location
victory = WargrooveItem("Wargroove Victory", self.player)
self.multiworld.get_location("Wargroove Finale: Victory", self.player).place_locked_item(victory)
self.multiworld.completion_condition[self.player] = lambda state: state.has("Wargroove Victory", self.player)
def set_rules(self):
set_rules(self.multiworld, self.player)
def create_item(self, name: str) -> Item:
return WargrooveItem(name, self.player)
def create_regions(self):
create_regions(self.multiworld, self.player)
def fill_slot_data(self) -> dict:
slot_data = self._get_slot_data()
for option_name in wargroove_options:
option = getattr(self.multiworld, option_name)[self.player]
slot_data[option_name] = int(option.value)
return slot_data
def get_filler_item_name(self) -> str:
return self.multiworld.random.choice(["Commander Defense Boost", "Income Boost"])
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
ret = Region(name, player, world)
if locations:
for location in locations:
loc_id = location_table.get(location, 0)
location = WargrooveLocation(player, location, loc_id, ret)
ret.locations.append(location)
if exits:
for exit in exits:
ret.exits.append(Entrance(player, exit, ret))
return ret
class WargrooveLocation(Location):
game: str = "Wargroove"
def __init__(self, player: int, name: str, address=None, parent=None):
super(WargrooveLocation, self).__init__(player, name, address, parent)
if address is None:
self.event = True
self.locked = True
class WargrooveItem(Item):
game = "Wargroove"
def __init__(self, name, player: int = None):
item_data = item_table[name]
super(WargrooveItem, self).__init__(
name,
item_data.classification,
item_data.code,
player
)

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,34 @@
# Wargroove (Steam, Windows)
## 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?
This randomizer shuffles units, map events, factions and boosts. It features a custom, non-linear campaign where the
final level and 3 branching paths are all available to the player from the start. The player cannot beat the final level
without specific items scattered throughout the branching paths. Certain levels on these paths may require
specific units or items in order to progress.
## What items and locations get shuffled?
1. Every buildable unit in the game (except for soldiers and dogs, which are free).
2. Commanders available to certain factions. If the player acquires the Floran Commanders, they can select any commander
from that faction.
3. Income and Commander Defense boosts that provide the player with extra income or extra commander defense.
4. Special map events like the Eastern Bridges or the Southern Walls, which unlock certain locations in certain levels.
## Which items can be in another player's world?
Any of the above items can be in another player's world.
## When the player receives an item, what happens?
When the player receives an item, a message will appear in Wargroove with the item name and sender name, once an action
is taken in game.
## What is the goal of this game when randomized?
The goal is to beat the level titled `The End` by finding the `Final Bridges`, `Final Walls`, and `Final Sickle`.

View File

@@ -0,0 +1,83 @@
# Wargroove Setup Guide
## Required Files
- Wargroove with the Double Trouble DLC installed through Steam on Windows
- Only the Steam Windows version is supported. MAC, Switch, Xbox, and Playstation are not supported.
- [The most recent Archipelago release](https://github.com/ArchipelagoMW/Archipelago/releases)
## Backup playerProgress files
`playerProgress` and `playerProgress.bak` contain save data for all of your Wargroove campaigns. Backing up these files
is strongly recommended in case they become corrupted.
1. Type `%appdata%\Chucklefish\Wargroove\save` in the file browser and hit enter.
2. Copy the `playerProgress` and `playerProgress.bak` files and paste them into a backup directory.
## Update host.yaml to include the Wargroove root directory
1. Look for your Archipelago install files. By default, the installer puts them in `C:\ProgramData\Archipelago`.
2. Open the `host.yaml` file in your favorite text editor (Notepad will work).
3. Put your Wargroove root directory in the `root_directory:` under the `wargroove_options:` section.
- The Wargroove root directory can be found by going to
`Steam->Right Click Wargroove->Properties->Local Files->Browse Local Files` and copying the path in the address bar.
- Paste the path in between the quotes next to `root_directory:` in the `host.yaml`.
- You may have to replace all single \\ with \\\\.
4. Start the Wargroove client.
## Installing the Archipelago Wargroove Mod and Campaign files
1. Shut down Wargroove if it is open.
2. Start the ArchipelagoWargrooveClient.exe from the Archipelago installation.
This should install the mod and campaign for you.
3. Start Wargroove.
## Verify the campaign can be loaded
1. Start Wargroove from Steam.
2. Go to `Story->Campaign->Custom->Archipelago` and click play. You should see the first level.
## Starting a Multiworld game
1. Start the Wargroove Client and connect to the server. Enter your username from your
[settings file.](/games/Wargroove/player-settings)
2. Start Wargroove and play the Archipelago campaign by going to `Story->Campaign->Custom->Archipelago`.
## Ending a Multiworld game
It is strongly recommended that you delete your campaign progress after finishing a multiworld game.
This can be done by going to the level selection screen in the Archipelago campaign, hitting `ESC` and clicking the
`Delete Progress` button. The main menu should now be visible.
## Updating to a new version of the Wargroove mod or downloading new campaign files
First, delete your campaign progress by going to the level selection screen in the Archipelago campaign,
hitting `ESC` and clicking the `Delete Progress` button.
Follow the `Installing the Archipelago Wargroove Mod and Campaign files` steps again, but look for the latest version
to download. In addition, follow the steps outlined in `Wargroove crashes when trying to run the Archipelago campaign`
when attempting to update the campaign files and the mod.
## Troubleshooting
### The game is too hard
`Go to the campaign overview screen->Hit escape on the keyboard->Click adjust difficulty->Adjust the setttings`
### The mod doesn't load
Double-check the mod installation under `%appdata%\Chucklefish\Wargroove\mods`. There should be 3 `.dat` files in
`%appdata%\Chucklefish\Wargroove\mods\ArchipelagoMod`. Otherwise, follow
`Installing the Archipelago Wargroove Mod and Campaign files` steps once more.
### Wargroove crashes or there is a lua error
Wargroove is finicky, but there could be several causes for this. If it happens often or can be reproduced,
please submit a bug report in the tech-support channel on the [discord](https://discord.gg/archipelago).
### Wargroove crashes when trying to run the Archipelago campaign
This is caused by not deleting campaign progress before updating the mod and campaign files.
1. Go to `Custom Content->Create->Campaign->Archipelago->Edit` and attempt to update the mod.
2. Wargroove will give an error message.
3. Go back to `Custom Content->Create->Campaign->Archipelago->Edit` and attempt to update the mod again.
4. Wargroove crashes.
5. Go back to `Custom Content->Create->Campaign->Archipelago->Edit` and attempt to update the mod again.
6. In the edit menu, hit `ESC` and click `Delete Progress`.
7. If the above steps do not allow you to start the campaign from `Story->Campaign->Custom->Archipelago` replace
`playerProgress` and `playerProgress.bak` with your previously backed up files.
### Mod is out of date when trying to run the Archipelago campaign
Please follow the above steps in `Wargroove crashes when trying to run the Archipelago campaign`.