Skip to content

Commit

Permalink
Add Bumper Stickers (ArchipelagoMW#811)
Browse files Browse the repository at this point in the history
* bumpstik: initial commit

* bumpstik: fix game name in location obj

* bumpstik: specified offset

* bumpstik: forgot to call create_regions

* bumpstik: fix entrance generation

* bumpstik: fix completion definition

* bumpstik: treasure bumper, LttP text

* bumpstik: add more score-based locations

* bumpstik: adjust regions

* bumpstik: fill with Treasure Bumpers

* bumpstik: force Treasure Bumper on last location

* bumpstik: don't require Hazard Bumpers for level 4

* bumpstik: treasure bumper locations

* bumpstik: formatting

* bumpstik: refactor to 0.3.5

* bumpstik: Treasure bumpers are now progression

* bumpstik: complete reimplementation of locations

* bumpstik: implement Nothing as item

* bumpstik: level 3 and 4 locations

* bumpstik: correct a goal value

* bumpstik: region defs need one extra treasure

* bumpstik: add more starting paint cans

* bumpstik: toned down final score goal

* bumpstik: changing items, Hazards no longer traps

* bumpstik: remove item groups

* bumpstik: update self.world to self.multiworld

* bumpstik: clean up item types and classes

* bumpstik: add options
also add traps to item pool

* bumpstik: update docs

* bumpstik: oops

* bumpstik: add to master game list on readme

* bumpstik: renaming Task Skip to Task Advance
because "Task Skip" is surprisingly hard to say

* bumpstik: fill with score on item gen
instead of nothing (nothing is still the default filler)

* bumpstik: add 18 checks

* bumpstik: bump ap ver

* bumpstik: add item groups

* bumpstik: make helper items and traps configurable

* bumpstik: make Hazard Bumper progression

* bumpstik: tone final score goal down to 50K

* bumpstik: 0.4.0 region update

* bumpstik: clean up docs
also final goal is now 50K or your score + 5000, whichever is higher

* bumpstik: take datapackage out of testing mode

* bumpstik: Apply suggestions from code review

code changes for .apworld support

Co-authored-by: Zach Parks <[email protected]>

---------

Co-authored-by: Zach Parks <[email protected]>
  • Loading branch information
FelicitusNeko and ThePhar authored Jun 27, 2023
1 parent a4e485e commit 36b5b12
Show file tree
Hide file tree
Showing 8 changed files with 530 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Currently, the following games are supported:
* DLC Quest
* Noita
* Undertale
* Bumper Stickers

For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled
Expand Down
129 changes: 129 additions & 0 deletions worlds/bumpstik/Items.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Copyright (c) 2022 FelicitusNeko
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT

import typing

from BaseClasses import Item, ItemClassification
from worlds.alttp import ALTTPWorld


class BumpStikLttPText(typing.NamedTuple):
pedestal: typing.Optional[str]
sickkid: typing.Optional[str]
magicshop: typing.Optional[str]
zora: typing.Optional[str]
fluteboy: typing.Optional[str]


LttPCreditsText = {
"Nothing": BumpStikLttPText("blank space",
"Forgot it at home again",
"Hallucinating again",
"Bucket o' Nothing for 9999.99",
"King Nothing"),
"Score Bonus": BumpStikLttPText("helpful hand",
"Busy kid gets the point...s",
"Variable conversion rate",
"Stonks",
"Catchy ad jingle"),
"Task Advance": BumpStikLttPText("hall pass",
"Faker kid skips again",
"I know a way around it",
"Money can fix it",
"Quick! A distraction"),
"Starting Turner": BumpStikLttPText("fidget spinner",
"Spinning kid turns heads",
"This turns things around",
"Your turn to turn",
"Turn turn turn"),
"Reserved": BumpStikLttPText("... wuh?",
"Why's this here?",
"Why's this here?",
"Why's this here?",
"Why's this here?"),
"Starting Paint Can": BumpStikLttPText("paint bucket",
"Artsy kid paints again",
"Your rainbow destiny",
"Rainbow for sale",
"Let me paint a picture"),
"Booster Bumper": BumpStikLttPText("multiplier",
"Math kid multiplies again",
"Growing shrooms",
"Investment opportunity",
"In harmony with themself"),
"Hazard Bumper": BumpStikLttPText("dull stone",
"...I got better",
"Mischief Maker",
"Whoops for sale",
"Stuck in a moment"),
"Treasure Bumper": BumpStikLttPText("odd treasure box",
"Interdimensional treasure",
"Shrooms for ???",
"Who knows what this is",
"You get what you give"),
"Rainbow Trap": BumpStikLttPText("chaos prism",
"Roy G Biv in disguise",
"The colors Duke! The colors",
"Paint overstock",
"Raise a little hell"),
"Spinner Trap": BumpStikLttPText("whirlwind",
"Vertigo kid gets dizzy",
"The room is spinning Dave",
"International sabotage",
"You spin me right round"),
"Killer Trap": BumpStikLttPText("broken board",
"Thank you Mr Coffey",
"Lethal dosage",
"Assassin for hire",
"Killer Queen"),
}


item_groups = {
"Helpers": ["Task Advance", "Starting Turner", "Starting Paint Can"],
"Targets": ["Treasure Bumper", "Booster Bumper", "Hazard Bumper"],
"Traps": ["Rainbow Trap", "Spinner Trap", "Killer Trap"]
}


class BumpStikItem(Item):
game = "Bumper Stickers"
type: str

def __init__(self, name, classification, code, player):
super(BumpStikItem, self).__init__(
name, classification, code, player)

if code is None:
self.type = "Event"
elif name in item_groups["Traps"]:
self.type = "Trap"
self.classification = ItemClassification.trap
elif name in item_groups["Targets"]:
self.type = "Target"
self.classification = ItemClassification.progression
elif name in item_groups["Helpers"]:
self.type = "Helper"
self.classification = ItemClassification.useful
else:
self.type = "Other"


offset = 595_000

item_table = {
item: offset + x for x, item in enumerate(LttPCreditsText.keys())
}

ALTTPWorld.pedestal_credit_texts.update({item_table[name]: f"and the {texts.pedestal}"
for name, texts in LttPCreditsText.items()})
ALTTPWorld.sickkid_credit_texts.update(
{item_table[name]: texts.sickkid for name, texts in LttPCreditsText.items()})
ALTTPWorld.magicshop_credit_texts.update(
{item_table[name]: texts.magicshop for name, texts in LttPCreditsText.items()})
ALTTPWorld.zora_credit_texts.update(
{item_table[name]: texts.zora for name, texts in LttPCreditsText.items()})
ALTTPWorld.fluteboy_credit_texts.update(
{item_table[name]: texts.fluteboy for name, texts in LttPCreditsText.items()})
49 changes: 49 additions & 0 deletions worlds/bumpstik/Locations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright (c) 2022 FelicitusNeko
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT

from BaseClasses import Location


class BumpStikLocation(Location):
game = "Bumper Stickers"


offset = 595_000

level1_locs = [f"{(i + 1) * 250} Points" for i in range(4)] + \
[f"{(i + 1) * 500} Level Points" for i in range(4)] + \
[f"{(i + 1) * 25} Level Bumpers" for i in range(3)] + \
["Combo 5"]

level2_locs = [f"{(i + 1) * 500} Points" for i in range(4)] + \
[f"{(i + 1) * 1000} Level Points" for i in range(4)] + \
[f"{(i + 1) * 25} Level Bumpers" for i in range(4)] + \
["Combo 5"] + ["Chain x2"]

level3_locs = [f"{(i + 1) * 800} Points" for i in range(4)] + \
[f"{(i + 1) * 2000} Level Points" for i in range(4)] + \
[f"{(i + 1) * 25} Level Bumpers" for i in range(5)] + \
["Combo 5", "Combo 7"] + ["Chain x2"] + \
["All Clear, 3 colors"]

level4_locs = [f"{(i + 1) * 1500} Points" for i in range(4)] + \
[f"{(i + 1) * 3000} Level Points" for i in range(4)] + \
[f"{(i + 1) * 25} Level Bumpers" for i in range(6)] + \
["Combo 5", "Combo 7"] + ["Chain x2", "Chain x3"]

level5_locs = ["50,000+ Total Points", "Cleared all Hazards"]

for x, loc_list in enumerate([level1_locs, level2_locs, level3_locs, level4_locs, level5_locs]):
for y, loc in enumerate(loc_list):
loc_list[y] = f"Level {x + 1} - {loc}"

extra_locs = [f"Bonus Booster {i+1}" for i in range(5)] + \
[f"Treasure Bumper {i+1}" for i in range(32)]

all_locs = level1_locs + level2_locs + level3_locs + level4_locs + level5_locs + extra_locs

location_table = {
loc: offset + i for i, loc in enumerate(all_locs)
}
80 changes: 80 additions & 0 deletions worlds/bumpstik/Options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright (c) 2022 FelicitusNeko
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT

import typing
from Options import Option, Range


class TaskAdvances(Range):
"""Task Advances allow you to skip one step of a level task. They do not restock, so use them sparingly."""
display_name = "Task Advances"
range_start = 0
range_end = 5
default = 4


class Turners(Range):
"""Turners allow you to change the direction of a Bumper. These restock when the board resets."""
display_name = "Turners"
range_start = 0
range_end = 5
default = 3


class PaintCans(Range):
"""
Paint Cans allow you to change the color of a Bumper.
The ones you get from the multiworld restock when the board resets; you also get one-time ones from score.
"""
display_name = "Paint Cans"
range_start = 0
range_end = 5
default = 3


class Traps(Range):
"""
Traps affect the board in various ways.
This number indicates how many total traps will be added to the item pool.
"""
display_name = "Trap Count"
range_start = 0
range_end = 15
default = 5


class RainbowTrapWeight(Range):
"""Rainbow Traps change the color of every bumper on the field."""
display_name = "Rainbow Trap weight"
range_start = 0
range_end = 100
default = 50


class SpinnerTrapWeight(Range):
"""Spinner Traps change the direction of every bumper on the field."""
display_name = "Spinner Trap weight"
range_start = 0
range_end = 100
default = 50


class KillerTrapWeight(Range):
"""Killer Traps end the current board immediately."""
display_name = "Killer Trap weight"
range_start = 0
range_end = 100
default = 0


bumpstik_options: typing.Dict[str, type(Option)] = {
"task_advances": TaskAdvances,
"turners": Turners,
"paint_cans": PaintCans,
"trap_count": Traps,
"rainbow_trap_weight": RainbowTrapWeight,
"spinner_trap_weight": SpinnerTrapWeight,
"killer_trap_weight": KillerTrapWeight
}
50 changes: 50 additions & 0 deletions worlds/bumpstik/Regions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) 2022 FelicitusNeko
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT

from BaseClasses import MultiWorld, Region, Entrance
from .Locations import BumpStikLocation, level1_locs, level2_locs, level3_locs, level4_locs, level5_locs, location_table


def _generate_entrances(player: int, entrance_list: [str], parent: Region):
return [Entrance(player, entrance, parent) for entrance in entrance_list]


def create_regions(world: MultiWorld, player: int):
region_map = {
"Menu": level1_locs + ["Bonus Booster 1"] + [f"Treasure Bumper {i + 1}" for i in range(8)],
"Level 1": level2_locs + ["Bonus Booster 2"] + [f"Treasure Bumper {i + 9}" for i in range(8)],
"Level 2": level3_locs + ["Bonus Booster 3"] + [f"Treasure Bumper {i + 17}" for i in range(8)],
"Level 3": level4_locs + [f"Bonus Booster {i + 4}" for i in range(2)] +
[f"Treasure Bumper {i + 25}" for i in range(8)],
"Level 4": level5_locs
}

entrance_map = {
"Level 1": lambda state:
state.has("Booster Bumper", player, 2) and state.has("Treasure Bumper", player, 9),
"Level 2": lambda state:
state.has("Booster Bumper", player, 3) and state.has("Treasure Bumper", player, 17),
"Level 3": lambda state:
state.has("Booster Bumper", player, 4) and state.has("Treasure Bumper", player, 25),
"Level 4": lambda state:
state.has("Booster Bumper", player, 5) and state.has("Treasure Bumper", player, 33)
}

for x, region_name in enumerate(region_map):
region_list = region_map[region_name]
region = Region(region_name, player, world)
for location_name in region_list:
region.locations += [BumpStikLocation(
player, location_name, location_table[location_name], region)]
if x < 4:
region.exits += _generate_entrances(player,
[f"To Level {x + 1}"], region)

world.regions += [region]

for entrance in entrance_map:
connection = world.get_entrance(f"To {entrance}", player)
connection.access_rule = entrance_map[entrance]
connection.connect(world.get_region(entrance, player))
Loading

0 comments on commit 36b5b12

Please sign in to comment.