mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-22 15:45:04 -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>
201 lines
7.1 KiB
Python
201 lines
7.1 KiB
Python
from __future__ import annotations
|
|
|
|
# isort: off
|
|
from kvui import GameManager, MDNavigationItemBase
|
|
|
|
# isort: on
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from kivy.clock import Clock
|
|
from kivy.uix.gridlayout import GridLayout
|
|
from kivy.uix.image import Image
|
|
from kivy.uix.layout import Layout
|
|
from kivymd.uix.recycleview import MDRecycleView
|
|
|
|
from ..game.game import Game
|
|
from .custom_views import APQuestControlsView, APQuestGameView, APQuestGrid, ConfettiView, VolumeSliderView
|
|
from .graphics import PlayerSprite, get_texture
|
|
from .sounds import SoundManager
|
|
|
|
if TYPE_CHECKING:
|
|
from .ap_quest_client import APQuestContext
|
|
|
|
|
|
class APQuestManager(GameManager):
|
|
base_title = "APQuest for AP version"
|
|
ctx: APQuestContext
|
|
|
|
lower_game_grid: GridLayout
|
|
upper_game_grid: GridLayout
|
|
|
|
game_view: MDRecycleView
|
|
game_view_tab: MDNavigationItemBase
|
|
|
|
sound_manager: SoundManager
|
|
|
|
bottom_image_grid: list[list[Image]]
|
|
top_image_grid: list[list[Image]]
|
|
confetti_view: ConfettiView
|
|
|
|
bottom_grid_is_grass: bool
|
|
|
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
super().__init__(*args, **kwargs)
|
|
self.sound_manager = SoundManager()
|
|
self.sound_manager.allow_intro_to_play = not self.ctx.delay_intro_song
|
|
self.top_image_grid = []
|
|
self.bottom_image_grid = []
|
|
self.bottom_grid_is_grass = False
|
|
|
|
def allow_intro_song(self) -> None:
|
|
self.sound_manager.allow_intro_to_play = True
|
|
|
|
def add_confetti(self, position: tuple[float, float], amount: int) -> None:
|
|
self.confetti_view.add_confetti(position, amount)
|
|
|
|
def play_jingle(self, audio_filename: str) -> None:
|
|
self.sound_manager.play_jingle(audio_filename)
|
|
|
|
def switch_to_tab(self, desired_tab: MDNavigationItemBase) -> None:
|
|
if self.screens.current_tab == desired_tab:
|
|
return
|
|
self.screens.current_tab.active = False
|
|
self.screens.switch_screens(desired_tab)
|
|
desired_tab.active = True
|
|
|
|
def switch_to_game_tab(self) -> None:
|
|
self.switch_to_tab(self.game_view_tab)
|
|
|
|
def switch_to_regular_tab(self) -> None:
|
|
self.switch_to_tab(self.tabs.children[-1])
|
|
|
|
def game_started(self) -> None:
|
|
self.switch_to_game_tab()
|
|
self.sound_manager.game_started = True
|
|
|
|
def render(self, game: Game, player_sprite: PlayerSprite) -> None:
|
|
self.setup_game_grid_if_not_setup(game.gameboard.size)
|
|
|
|
# This calls game.render(), which needs to happen to update the state of math traps
|
|
self.render_gameboard(game, player_sprite)
|
|
# Only now can we check whether a math problem is active
|
|
self.render_background_game_grid(game.gameboard.size, game.active_math_problem is None)
|
|
self.sound_manager.math_trap_active = game.active_math_problem is not None
|
|
|
|
self.render_item_column(game)
|
|
|
|
def render_gameboard(self, game: Game, player_sprite: PlayerSprite) -> None:
|
|
rendered_gameboard = game.render()
|
|
|
|
for gameboard_row, image_row in zip(rendered_gameboard, self.top_image_grid, strict=False):
|
|
for graphic, image in zip(gameboard_row, image_row[:11], strict=False):
|
|
texture = get_texture(graphic, player_sprite)
|
|
|
|
if texture is None:
|
|
image.opacity = 0
|
|
image.texture = None
|
|
continue
|
|
|
|
image.texture = texture
|
|
image.opacity = 1
|
|
|
|
def render_item_column(self, game: Game) -> None:
|
|
rendered_item_column = game.render_health_and_inventory(vertical=True)
|
|
for item_graphic, image_row in zip(rendered_item_column, self.top_image_grid, strict=False):
|
|
image = image_row[-1]
|
|
|
|
texture = get_texture(item_graphic)
|
|
if texture is None:
|
|
image.opacity = 0
|
|
image.texture = None
|
|
continue
|
|
|
|
image.texture = texture
|
|
image.opacity = 1
|
|
|
|
def render_background_game_grid(self, size: tuple[int, int], grass: bool) -> None:
|
|
if grass == self.bottom_grid_is_grass:
|
|
return
|
|
|
|
for row in range(size[1]):
|
|
for column in range(size[0]):
|
|
image = self.bottom_image_grid[row][column]
|
|
|
|
if not grass:
|
|
image.color = (0.3, 0.3, 0.3)
|
|
image.texture = None
|
|
continue
|
|
|
|
boss_room = (row in (0, 1, 2) and (size[1] - column) in (1, 2, 3)) or (row, column) == (3, size[1] - 2)
|
|
if boss_room:
|
|
image.color = (0.45, 0.35, 0.1)
|
|
image.texture = None
|
|
continue
|
|
image.texture = get_texture("Grass")
|
|
image.color = (1.0, 1.0, 1.0)
|
|
|
|
self.bottom_grid_is_grass = grass
|
|
|
|
def setup_game_grid_if_not_setup(self, size: tuple[int, int]) -> None:
|
|
if self.upper_game_grid.children:
|
|
return
|
|
|
|
self.top_image_grid = []
|
|
self.bottom_image_grid = []
|
|
|
|
for _row in range(size[1]):
|
|
self.top_image_grid.append([])
|
|
self.bottom_image_grid.append([])
|
|
|
|
for _column in range(size[0]):
|
|
bottom_image = Image(fit_mode="fill", color=(0.3, 0.3, 0.3))
|
|
self.lower_game_grid.add_widget(bottom_image)
|
|
self.bottom_image_grid[-1].append(bottom_image)
|
|
|
|
top_image = Image(fit_mode="fill")
|
|
self.upper_game_grid.add_widget(top_image)
|
|
self.top_image_grid[-1].append(top_image)
|
|
|
|
# Right side: Inventory
|
|
image = Image(fit_mode="fill", color=(0.3, 0.3, 0.3))
|
|
self.lower_game_grid.add_widget(image)
|
|
|
|
image2 = Image(fit_mode="fill", opacity=0)
|
|
self.upper_game_grid.add_widget(image2)
|
|
|
|
self.top_image_grid[-1].append(image2)
|
|
|
|
def build(self) -> Layout:
|
|
container = super().build()
|
|
|
|
self.game_view = APQuestGameView(self.ctx.input_and_rerender)
|
|
|
|
self.game_view_tab = self.add_client_tab("APQuest", self.game_view)
|
|
|
|
controls = APQuestControlsView()
|
|
|
|
self.add_client_tab("Controls", controls)
|
|
|
|
game_container = self.game_view.ids["game_container"]
|
|
self.lower_game_grid = APQuestGrid()
|
|
self.upper_game_grid = APQuestGrid()
|
|
self.confetti_view = ConfettiView()
|
|
game_container.add_widget(self.lower_game_grid)
|
|
game_container.add_widget(self.upper_game_grid)
|
|
game_container.add_widget(self.confetti_view)
|
|
|
|
game_container.bind(size=self.lower_game_grid.check_resize)
|
|
game_container.bind(size=self.upper_game_grid.check_resize)
|
|
game_container.bind(size=self.confetti_view.check_resize)
|
|
|
|
volume_slider_container = VolumeSliderView()
|
|
volume_slider = volume_slider_container.ids["volume_slider"]
|
|
volume_slider.value = self.sound_manager.volume_percentage
|
|
volume_slider.bind(value=lambda _, new_volume: self.sound_manager.set_volume_percentage(new_volume))
|
|
|
|
self.grid.add_widget(volume_slider_container, index=3)
|
|
|
|
Clock.schedule_interval(lambda dt: self.confetti_view.redraw_confetti(dt), 1 / 60)
|
|
|
|
return container
|