-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit cd7cc24
Showing
8 changed files
with
1,307 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Source game from pianoman373: Air Delivery - https://www.lexaloffle.com/bbs/?tid=52598 | ||
|
||
License inhereted from Air Delivery - https://creativecommons.org/licenses/by-nc-sa/4.0/ | ||
|
||
gpio-listener and webpage template - https://github.com/benwiley4000/pico8-gpio-listener | ||
|
||
archipelago.js - https://www.npmjs.com/package/archipelago.js | ||
|
||
|
||
A tiny metroidvania where you explore the floating island to collect all the missing packages! Extra locations have been added to all deliveries instead of just those who give items in vanilla, one for just chatting and one for getting their package delivered. | ||
The goal is to get back to your air baloon and continue your route (and hopefully deliver all the packages as you go.) | ||
|
||
|
||
The version of Air Delivery shipped with the AP integration has been modified to use the GPIO layer to communicate with archipelago.js, along with the modifications to the Player and NPCs to add the additional randomizable Locations. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
from BaseClasses import Region, Location, Item, ItemClassification, Tutorial, CollectionState | ||
from worlds.AutoWorld import World, WebWorld | ||
#expand this eventually | ||
from typing import * | ||
|
||
json_world = { | ||
"regions": ["Menu", "main", "upper", "side", "lower"], | ||
"region_map": { | ||
"Menu": { | ||
"main": None | ||
}, | ||
"main": { | ||
"upper": ["claw"], | ||
"side": ["fly", "claw"], | ||
"lower": ["key"] | ||
}, | ||
"side": { | ||
"lower": ["fly"] | ||
} | ||
}, | ||
"location_map": { | ||
"main": { | ||
"mayor delivery": ["mayor missive"], #gives key, needs lower claw letter in main | ||
"mayor chat": None, | ||
"friend delivery": ["friend freight"], #gives nothing, needs upper claw letter in main | ||
"friend chat": None, | ||
"climber delivery": ["climber cargo"], #gives claw, needs free letter in main | ||
"climber chat": None, | ||
"climber cargo": None, #all the way to the right | ||
"friend freight": ["claw"], # OR fly #in town, above | ||
"mayor missive": ["claw"], #top path from ladder to under | ||
"buried bento": ["drill", "claw"], #middle path from ladder to under | ||
}, | ||
|
||
"side": { | ||
"grandpa goods": ["fly"], | ||
}, | ||
|
||
"lower": { | ||
"wife word": ["fly", "claw"], #in cave after door | ||
"paper pack": None, #down stairs by door, between bottom and side | ||
"driller delivery": ["driller dispatch"], #gives drill, needs letter in upper | ||
"driller chat": None, | ||
"paper folder delivery": ["paper pack"], #gives fly, needs free letter between bottom/side | ||
"paper folder chat": None, | ||
"mouse delivery": ["buried bento", "fly"], #gives nothing, needs drill letter by ladder to under | ||
"mouse chat": ["fly"], | ||
"history haul": ["fly", "claw"], #left side before old man | ||
"grandpa delivery": ["grandpa goods", "fly", "claw", "drill"], | ||
"grandpa chat": ["fly", "claw", "drill"], | ||
"history buff delivery": ["history haul"], #left side of under-town, needs letter by old man | ||
"history buff chat": None, | ||
"wife delivery": ["wife word"], #gives nothing, middle of under-town, needs letter after keydoor | ||
"wife chat": None, | ||
}, | ||
|
||
"upper": { | ||
"driller dispatch": None, | ||
"victory": ["drill", "fly"], | ||
} | ||
}, | ||
"items": { | ||
"prog_items": [ | ||
"mayor missive", | ||
"friend freight", | ||
"climber cargo", | ||
"driller dispatch", | ||
"paper pack", | ||
"buried bento", | ||
"grandpa goods", | ||
"history haul", | ||
"wife word", | ||
"claw", | ||
"fly", | ||
"key", | ||
"drill", | ||
], | ||
"filler_items": [ | ||
"Feeling of Satisfaction" | ||
] | ||
}, | ||
"base_id": 19827412012, | ||
"game_name": "Air Delivery", | ||
"filler_name": "Feeling of Satisfaction" | ||
} | ||
|
||
|
||
class TemplateItem(Item): | ||
game = json_world["game_name"] | ||
|
||
|
||
class TemplateLocation(Location): | ||
game = json_world["game_name"] | ||
|
||
|
||
class HelloWeb(WebWorld): | ||
setup_en = Tutorial( | ||
"setup", | ||
"description here", | ||
"en", | ||
"setup_en.md", | ||
"setup/en", | ||
["your name here"] | ||
) | ||
tutorials = [setup_en] | ||
|
||
|
||
#flatten lists of locations and items so they are indexed for name_to_id | ||
location_list = [location for locations in json_world["location_map"].values() for location in locations.keys()] | ||
item_list = [item for item_lists in json_world["items"].values() for item in item_lists] | ||
|
||
class HelloWorld(World): | ||
game = json_world["game_name"] | ||
web = HelloWeb() | ||
location_name_to_id = {name: json_world["base_id"]+location_list.index(name) for name in location_list} | ||
item_name_to_id = {name: json_world["base_id"]+item_list.index(name) for name in item_list} | ||
|
||
#basic getters for json_world data, any option based modifications can be done here; may cache these later | ||
#expect authors to modify the return of super() per options, or fully override if their format is different | ||
def get_region_list(self) -> List[str]: | ||
return json_world["regions"] | ||
|
||
def get_connections(self) -> "List[Tuple(str, str, Optional[Any])]": | ||
er = False | ||
if er: | ||
vanilla_connections = [(region1, region2, rule) for region1, connections in json_world["region_map"].items() for region2, rule in connections.items()] | ||
oneways = ["Menu -> main"] | ||
return_connections = vanilla_connections + [(region2, region1, rule) for connection in vanilla_connections for region1, region2, rule in connection if f"{region1} -> {region2}" not in oneways] | ||
# then create unconnected Entrances later | ||
else: | ||
return [(region1, region2, rule) for region1, connections in json_world["region_map"].items() for region2, rule in connections.items()] | ||
|
||
def get_location_map(self) -> "List[Tuple(str, str, Optional[Any])]": | ||
return [(region, location, rule) for region, placements in json_world["location_map"].items() for location, rule in placements.items()] | ||
|
||
# black box methods | ||
def set_victory(self) -> None: | ||
#current black box to set and setup victory condition, run after all region/locations have been created (but currently before items) | ||
victory = self.multiworld.get_location("victory", self.player) | ||
victory.address = None | ||
victory.place_locked_item(TemplateItem("victory", ItemClassification.progression, None, self.player)) | ||
self.multiworld.completion_condition[self.player] = lambda state: state.has("victory", self.player) | ||
#currently finds victory location, adds locked victory event, and requires victory event for completion | ||
|
||
def create_rule(self, rule: Any) -> Callable[[CollectionState], bool]: | ||
#current black box to convert json_world rule format to an access_rule lambda | ||
return lambda state: state.has_all(rule, self.player) | ||
#currently all my rule objects are None or a list of required items | ||
|
||
def get_item_list(self) -> List[str]: | ||
#current black box to creat a list of item names per count that need to be created | ||
return item_list | ||
#currently my items in my datapackage should all be created once, so this list functions | ||
|
||
def get_item_classification(self, name: str) -> ItemClassification: | ||
if name in json_world["items"]["prog_items"]: | ||
return ItemClassification.progression | ||
elif name in json_world["items"]["filler_items"]: | ||
return ItemClassification.filler | ||
else: | ||
return ItemClassification.useful | ||
|
||
def get_filler_item_name(self) -> str: | ||
#filler_name should be a list and this should choose with self.random | ||
return json_world["filler_name"] | ||
|
||
# common methods | ||
def create_regions(self) -> None: | ||
#create a local map of get_region_list names to region object for referencing in create_regions and adding those regions to the multiworld | ||
regions = {region: None for region in self.get_region_list()} | ||
for region in regions.keys(): | ||
regions[region] = Region(region, self.player, self.multiworld) | ||
self.multiworld.regions.append(regions[region]) | ||
|
||
#TODO - add per option GER handling | ||
#loop through get_region_map, adding the rules per self.create_rule(rule) if present to the connections | ||
for region1, region2, rule in self.get_connections(): | ||
if rule: | ||
regions[region1].connect(regions[region2], rule=self.create_rule(rule)) | ||
else: | ||
regions[region1].connect(regions[region2]) | ||
er = False | ||
if er: | ||
for region, connection, rule in self.get_er_entrances(): | ||
cons = [regions[region].create_exit(connection), regions[region].create_en_target(connection)] | ||
for con in cons: | ||
con.er_type = EntranceType.TWO_WAY | ||
# con.er_group = | ||
con.access_rule = self.create_rule(rule) | ||
|
||
|
||
#loop through get_location_map, adding the rules per self.create_rule(rule) if present to the location | ||
for region, location, rule in self.get_location_map(): | ||
loc = TemplateLocation(self.player, location, self.location_name_to_id[location], regions[region]) | ||
if rule: | ||
loc.access_rule = self.create_rule(rule) | ||
regions[region].locations.append(loc) | ||
|
||
self.set_victory() | ||
|
||
def create_items(self) -> None: | ||
#create all items in get_item_list() | ||
itempool = [] | ||
for item in self.get_item_list(): | ||
itempool.append(self.create_item(item)) | ||
|
||
#fill in any difference in itempool with filler item and submit to multiworld | ||
total_locations = len(self.multiworld.get_unfilled_locations(self.player)) | ||
while len(itempool) < total_locations: | ||
itempool.append(self.create_filler()) | ||
self.multiworld.itempool += itempool | ||
|
||
def create_item(self, name: str) -> "Item": | ||
item_class = self.get_item_classification(name) | ||
return TemplateItem(name, item_class, self.item_name_to_id.get(name, None), self.player) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Air Delivery | ||
|
||
## Where is the options page? | ||
|
||
The [player options page for Air Delivery](../player-options) contains all the options you need to configure and export a config file. | ||
|
||
## What is the goal of Air Delivery when randomized? | ||
|
||
Same as vanilla; Either break the Sword Machine and beat the boss, or aquire the broken sword and flush it down the Factory toilet. | ||
|
||
## Which items can be in another player's world? | ||
|
||
All items in Air Delivery are added to the multiworld item pool. This includes the packages for various NPCs, Key items ("claw", "fly", "key", "drill"), and filler items of appreciation for delivering people their mail! | ||
|
||
## What is considered a location check in Air Delivery? | ||
|
||
All of the NPCs that would normally have a packages to be delivered to them have both a Locations after delivering the package and one just for talking to them. Also all of the vanilla Package locations are Locations | ||
|
||
## What does another world's item look like in Air Delivery? | ||
|
||
Item locations have their original sprites. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Air Delivery Randomizer Setup Guide | ||
|
||
## Required Software | ||
|
||
- The Client folder from the release | ||
- A web browser | ||
|
||
## Configuring your YAML file | ||
|
||
There are currently no options for Air Delivery, but a YAML is still required. | ||
|
||
### What is a YAML file and why do I need one? | ||
|
||
See the guide on setting up a basic YAML at the Archipelago setup | ||
guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en) | ||
|
||
### Where do I get a YAML file? | ||
|
||
You can customize your options by visiting the [Air Delivery Player Settings Page](../player-options) | ||
|
||
## Joining a MultiWorld Game | ||
|
||
### Launching the Air Delivery Client | ||
|
||
The index.html file in the Client folder can be opened in any web browser, then submit the Hostname, Port, and Name for the connection | ||
|
||
An example: | ||
* Hostname: archipelago.gg | ||
* Port: 38281 | ||
* Name: Player1 | ||
|
||
You will then see a `Connected` message showing that you have connected properly, and you can continue to play the game. | ||
|
||
### Running the game | ||
|
||
The game can be played before or after Connecting, but make sure if you are switching slots to reset the game so that your inventory and sent locations are not carried over into the new slot |
Oops, something went wrong.