mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-23 15:33:21 -07:00
* APQuest * Add confetti cannon * ID change on enemy drop * nevermind * Write the apworld * Actually implement hard mode * split everything into multiple files * Push out webworld into a file * Comment * Enemy health graphics * more ruff rules * graphics :) * heal player when receiving health upgrade * the dumbest client of all time * Fix typo * You can kinda play it now! Now we just need to render the game... :))) * fix kvui imports again * It's playable. Kind of * oops * Sounds and stuff * exceptions for audio * player sprite stuff * Not attack without sword * Make sure it plays correctly * Collect behavior * ruff * don't need to clear checked_locations, but do need to still clear finished_game * Connect calls disconnect, so this is not necessary * more seemless reconnection * Ok now I think it's correct * Bgm * Bgm * minor adjustment * More refactoring of graphics and sound * add graphics * Item column * Fix enemies not regaining their health * oops * oops * oops * 6 health final boss on hard mode * boss_6.png * Display APQuest items correctly * auto switch tabs * some mypy stuff * Intro song * Confetti Cannon * a bit more confetti work * launcher component * Graphics change * graphics and cleanup * fix apworld * comment out horse and cat for now * add docs * copypasta * ruff made my comment look unhinged * Move that comment * Fix typing and don't import kvui in nogui * lmao that already exists I don't need to do it myself * Must've just copied this from somewhere * order change * Add unit tests * Notes about the client * oops * another intro song case * Write WebWorld and setup guides * Yes description provided * thing * how to play * Music and Volume * Add cat and horse player sprites * updates * Add hammer and breakable wall * TODO * replace wav with ogg * Codeowners and readme * finish unit tests * lint * Todid * Update worlds/apquest/client/ap_quest_client.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/apquest/client/custom_views.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Filler pattern * __future__ annotations * twebhost * Allow wasd and arrow keys * correct wording * oops * just say the website * append instead of += * qwint is onto my favoritism * kitty alias * Add a comment about preplaced items for assertAccessDependency * Use classvar_matrix instead of MultiworldTestBase * actually remove multiworld stuff from those tests * missed one more * Refactor a bit more * Fix getting of the user path * Actually explain components * Meh * Be a bit clearer about what's what * oops * More comments in the regions.py file * Nevermind * clarify regions further * I use too many brackets * Ok I'm done fr * simplify wording * missing . * Add precollected example * add note about precollected advancements * missing s * APQuest sound rework * Volume slider * I forgot I made this * a * fix volume of jingles * Add math trap to game (only works in play_in_console mode so far) * Math trap in apworld and client side * Fix background during math trap * fix leading 0 * Sound and further ui improvements for Math Trap * fix music bug * rename apquest subfolder to game * Move comment to where it belongs * Clear up language around components (hopefully) * Clear up what CommonClient is * Reword some more * Mention Archipelago (the program) explicitly * Update worlds/apquest/docs/en_APQuest.md Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Explain a bit more why you would use classvar matrix * reword the assert raises stuff * the volume slider thing is no longer true * german game page * Be more clear about why we're overriding Item and Location * default item classification * logically considered -> relevant to logic () * Update worlds/apquest/items.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * a word on the ambiguity of the word 'filler' * more rewording * amount -> number * stress the necessity of appending to the multiworld itempool * Update worlds/apquest/locations.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * get_location_names_with_ids * slight rewording of the new helper method * add some words about creating known location+item pairs * Add some more words to worlds/apqeust/options.py * more words in options.py * 120 chars (thanks Ixrec >:((( LOL) * Less confusing wording about rules, hopefully? * victory -> completion * remove the immediate creation of the hammer rule on the option region entrance * access rule performance * Make all imports module-level in world.py * formatting * get rid of noqa RUF012 (and also disable the rule in my local ruff.toml * move comment for docstring closer to docstring in another place * advancement???? * Missing function type annotations * pass mypy again (I don't love this one but all the alternatives are equally bad) * subclass instead of override * I forgor to remove these * Get rid of classvar_matrix and instead talk about some other stuff * protect people a bit from the assertAccessDependency nonsense * reword a bit more * word * More accessdependency text * More accessdependency text * More accessdependency text * More accessdependency text * oops * this is supposed to be absolute * Add some links to docs * that's called game now * Add an archipelago.json and explain what it means * new line who dis * reorganize a bit * ignore instead of skip * Update archipelago.json * She new on my line till I * Update archipelago.json * add controls tab * new ruff rule? idk * WHOOPS * Pack graphics into fewer files * annoying ruff format thing * Cleanup + mypy * relative import * Update worlds/apquest/client/custom_views.py Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com> * Update generate_math_problem.py * Update worlds/apquest/game/player.py Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com> --------- Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> Co-authored-by: Ixrec <ericrhitchcock@gmail.com> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
107 lines
6.5 KiB
Python
107 lines
6.5 KiB
Python
from .bases import APQuestTestBase
|
|
|
|
|
|
# When writing a test, you'll first need to subclass unittest.TestCase.
|
|
# In our case, we'll subclass the APQuestTestBase we defined in bases.py.
|
|
class TestEasyModeLogic(APQuestTestBase):
|
|
# Our test base is a subclass of WorldTestBase.
|
|
# WorldTestBase takes a dict of options and sets up a multiworld for you with a single world of your game.
|
|
# The world will have the options you specified.
|
|
options = {
|
|
"hard_mode": False,
|
|
# Options you don't specify will use their default values.
|
|
# It is good practice to specify every option that has an impact on your test, even when it's the default value.
|
|
# As such, we'll spell out that hard_mode is meant to be False.
|
|
# All other options in APQuest are cosmetic, so we don't need to list them.
|
|
}
|
|
|
|
# At this point, we could stop, and a few default tests would be run on our world.
|
|
# At the time of writing (2025-09-04), this includes the following tests:
|
|
# - If you have every item, every location can be reached
|
|
# - If you have no items, you can still reach something ("Sphere 1" is not empty)
|
|
# - The world successfully generates (Fill does not crash)
|
|
|
|
# This is already useful, but we also want to do our own tests.
|
|
# A test is a function whose name starts with "test".
|
|
def test_easy_mode_access(self) -> None:
|
|
# Inside a test, we can manually collect items, check access rules, etc.
|
|
# For example, we could check that the two early chests are already accessible despite us having no items.
|
|
# For the sake of structure, let's have every test item in its own subtest.
|
|
with self.subTest("Test checks accessible with nothing"):
|
|
bottom_left_chest = self.world.get_location("Bottom Left Chest")
|
|
top_middle_chest = self.world.get_location("Top Middle Chest")
|
|
|
|
# Since access rules have a "state" argument, we must pass our current CollectionState.
|
|
# Helpfully, since we're in a WorldTestBase, we can just use "self.multiworld.state".
|
|
self.assertTrue(bottom_left_chest.can_reach(self.multiworld.state))
|
|
self.assertTrue(top_middle_chest.can_reach(self.multiworld.state))
|
|
|
|
# Next, let's test that the top left room location requires the key to unlock the door.
|
|
with self.subTest("Test key is required to get top left chest"):
|
|
top_left_room_chest = self.world.get_location("Top Left Room Chest")
|
|
|
|
# Right now, this location should *not* be accessible, as we don't have the key yet.
|
|
self.assertFalse(top_left_room_chest.can_reach(self.multiworld.state))
|
|
|
|
# Now, let's collect the Key.
|
|
# For this, there is a handy helper function to collect items from the itempool.
|
|
# Keep in mind that while test functions are sectioned off from one another, subtests are not.
|
|
# Collecting this here means that the state will have the Key for all future subtests in this function.
|
|
self.collect_by_name("Key")
|
|
|
|
# The top left room chest should now be accessible.
|
|
self.assertTrue(top_left_room_chest.can_reach(self.multiworld.state))
|
|
|
|
# Next, let's test that you need the sword to access locations that require it (bush room and enemies).
|
|
with self.subTest("Test sword is required for enemy and bush locations"):
|
|
# Manually checking the dependency in the previous function was a bit of a hassle, wasn't it?
|
|
# Now we are checking four locations. It would be even longer as a result.
|
|
# Well, there is another option. It's the assertAccessDependency function of WorldTestBase.
|
|
self.assertAccessDependency(
|
|
[
|
|
"Bottom Right Room Right Chest",
|
|
"Bottom Right Room Left Chest",
|
|
"Right Room Enemy Drop",
|
|
"Final Boss Defeated", # Reminder: APQuest's victory condition uses this event location
|
|
],
|
|
[["Sword"]],
|
|
)
|
|
|
|
# The assertAccessDependency function is a bit complicated, so let's discuss what it does.
|
|
# By default, the locations argument must contain *every* location that *hard-depends* on the items.
|
|
# So, in our case: If every item except Sword is collected, *exactly* these four locations are unreachable.
|
|
|
|
# The possible_items argument is initially more intuitive, but has some complexity as well.
|
|
# In our case, we only care about one item. But sometimes, we care about multiple items at once.
|
|
# This is why we pass a list of lists. We'll discuss this more when we test hard mode logic.
|
|
|
|
# Let's do one more test: That the key is required for the Button.
|
|
with self.subTest("Test that the Key is required to activate the Button"):
|
|
# The Button is not the only thing that depends on the Key.
|
|
# As explained above, the locations list must be exhaustive.
|
|
# Thus, we would have to add the "Top Left Room Chest" as well.
|
|
# However, we can set "only_check_listed" if we only want the Top Left Room Button location to be checked.
|
|
self.assertAccessDependency(
|
|
["Top Left Room Button"],
|
|
[["Key"]],
|
|
only_check_listed=True,
|
|
)
|
|
|
|
def test_easy_mode_health_upgrades(self) -> None:
|
|
# For our second test, let's make sure that we have two Health Upgrades with the correct classification.
|
|
|
|
# We can find the Health Upgrades in the itempool like this:
|
|
health_upgrades = self.get_items_by_name("Health Upgrade")
|
|
|
|
# First, let's verify there's two of them.
|
|
with self.subTest("Test that there are two Health Upgrades in the pool"):
|
|
self.assertEqual(len(health_upgrades), 2)
|
|
|
|
# Then, let's verify that they have the useful classification and NOT the progression classification.
|
|
with self.subTest("Test that the Health Upgrades in the pool are useful, but not progression."):
|
|
# To check whether an item has a certain classification, you can use the following helper properties:
|
|
# item.filler, item.trap, item.useful and... item.advancement. No, not item.progression...
|
|
# (Just go with it, AP is old and has had many name changes over the years :D)
|
|
self.assertTrue(all(health_upgrade.useful for health_upgrade in health_upgrades))
|
|
self.assertFalse(any(health_upgrade.advancement for health_upgrade in health_upgrades))
|