Skip to content

Commit

Permalink
feat: items menu state WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Ajinkya committed Nov 14, 2023
1 parent 9121692 commit 8bb7d2e
Show file tree
Hide file tree
Showing 20 changed files with 315 additions and 121 deletions.
Binary file added assets/ui/cursor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/ui/gradient_panel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/ui/inventory_icons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion explore_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from game import Game
from map_definitions import MapDefinition
from state_stack import StateStack
from world import World


class ExploreState:
Expand All @@ -25,6 +26,7 @@ def __init__(self, map_def: MapDefinition, start_tile_pos: Tuple[int, int], disp
self.start_pos = start_tile_pos
self.game = Game(display=display)
self.game.setup(map_def, ACTIONS)
self.game.world = World(display)
self.hero = Character(characters["hero"], self.game)
self.hero.entity.set_tile_pos(*start_tile_pos, self.game)
self.game.camera.set_follow(self.hero.entity)
Expand Down Expand Up @@ -57,6 +59,6 @@ def process_event(self, event: pygame.event.Event):
if isinstance(self.stack.top(), InGameMenuState):
return
self.stack.push(
InGameMenuState(self.display, self.manager, self.stack)
InGameMenuState(self.game.world, self.display, self.manager, self.stack)
)

3 changes: 3 additions & 0 deletions game.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
from map_definitions import MapDefinition, Trigger, create_map_triggers
from map_utils import Camera, CameraGroup, TmxMap
from sprite_utils import Tile, Circle, Rectangle
from world import World


class Game:
world: World
tmx_map: TmxMap = None
map_group: CameraGroup
entity_group: CameraGroup
Expand Down Expand Up @@ -88,6 +90,7 @@ def setup(self, map_def: MapDefinition, actions: Dict[str, Callable] = None):

def update(self, dt: float = None):
dt = dt if dt is not None else self.dt
self.world.update(dt)
self.map_group.update()
self.entity_group.update(game=self)
for npc in self.npcs:
Expand Down
14 changes: 8 additions & 6 deletions ingame_menu_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,31 @@
from state_stack import StateStack
from statemachine import StateMachine
from ui_states import create_state
from world import World


# stack state
class InGameMenuState:
should_exit = False

def __init__(self, display: pygame.Surface, manager: pygame_gui.UIManager, stack: StateStack = None):
def __init__(self, world: World, display: pygame.Surface, manager: pygame_gui.UIManager, stack: StateStack = None):
self.stack = stack
self.manager = manager
self.display = display
self.world = world

# create state machine for in game menu
# controller = ("frontmenu", "items", "magic", "equip", "status")
self.menu_options = [
self.menu_options = (
"Item",
"Magic",
"Equip",
"Status",
"Exit"
]
controller = ("frontmenu",)
)
self.class_names = ("front", "item",)
state_classes = {} # This will store state classes, not instances.
for state_name in controller:
for state_name in self.class_names:
state_class = create_state(state_name)
assert state_class is not None, f"State {state_name} not found"
assert state_name not in state_classes, f"State {state_name} already exists"
Expand All @@ -37,7 +39,7 @@ def __init__(self, display: pygame.Surface, manager: pygame_gui.UIManager, stack
self.state_machine.add(state_name, state_class(self, self.manager, self.display))

def enter(self) -> None:
self.state_machine.change("frontmenu")
self.state_machine.change(self.class_names[0])

def exit(self) -> None:
pass
Expand Down
2 changes: 1 addition & 1 deletion sprite_utils/spritesheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

def load_sprite_sheet(sprite_sheet_path: str, frame_width: int, frame_height: int, rows: int, columns: int):
# Load the sprite sheet
sprite_sheet = pygame.image.load(sprite_sheet_path)
sprite_sheet = pygame.image.load(sprite_sheet_path).convert_alpha()
# Create a list of frames
frames = []
# Split the sprite sheet into frames
Expand Down
19 changes: 0 additions & 19 deletions state.py

This file was deleted.

28 changes: 27 additions & 1 deletion statemachine.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
from state import State
"""
A simple state machine implementation.
example: A state machine for a game menu, characters, etc.
A Main Menu can be in State Stack, while its submenus are in State Machine.
in State machine current state is always active, while in State Stack, only the top state is active.
in state machine, you can change state, while in state stack, you can push and pop state.
"""

from typing import Protocol, runtime_checkable


@runtime_checkable
class State(Protocol):
def enter(self, **kwargs) -> None:
...

def exit(self) -> None:
...

def render(self, **kwargs) -> None:
...

def update(self, dt) -> None:
...

def process_event(self, event) -> None:
...


class StateMachine:
Expand Down
5 changes: 5 additions & 0 deletions ui/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import os

ASSETS_PATH = os.path.abspath('.') + '/assets/'

from .panel import * # noqa: F403

Check failure on line 5 in ui/__init__.py

View workflow job for this annotation

GitHub Actions / build (3.11)

Ruff (E402)

ui/__init__.py:5:1: E402 Module level import not at top of file
from .dialogue_panel import * # noqa: F403

Check failure on line 6 in ui/__init__.py

View workflow job for this annotation

GitHub Actions / build (3.11)

Ruff (E402)

ui/__init__.py:6:1: E402 Module level import not at top of file
from .selections import * # noqa: F403

Check failure on line 7 in ui/__init__.py

View workflow job for this annotation

GitHub Actions / build (3.11)

Ruff (E402)

ui/__init__.py:7:1: E402 Module level import not at top of file
from .select_item import * # noqa: F403

Check failure on line 8 in ui/__init__.py

View workflow job for this annotation

GitHub Actions / build (3.11)

Ruff (E402)

ui/__init__.py:8:1: E402 Module level import not at top of file
from .progress_bar import * # noqa: F403

Check failure on line 9 in ui/__init__.py

View workflow job for this annotation

GitHub Actions / build (3.11)

Ruff (E402)

ui/__init__.py:9:1: E402 Module level import not at top of file
from .textbox import * # noqa: F403

Check failure on line 10 in ui/__init__.py

View workflow job for this annotation

GitHub Actions / build (3.11)

Ruff (E402)

ui/__init__.py:10:1: E402 Module level import not at top of file
from .text import * # noqa: F403

Check failure on line 11 in ui/__init__.py

View workflow job for this annotation

GitHub Actions / build (3.11)

Ruff (E402)

ui/__init__.py:11:1: E402 Module level import not at top of file
from .icons import * # noqa: F403

Check failure on line 12 in ui/__init__.py

View workflow job for this annotation

GitHub Actions / build (3.11)

Ruff (E402)

ui/__init__.py:12:1: E402 Module level import not at top of file
16 changes: 3 additions & 13 deletions ui/dialogue_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import pygame
import pygame_gui
from pygame_gui.core import IContainerLikeInterface

from globals import ASSETS_PATH
from .chunk_message import chunk_message
Expand All @@ -25,7 +24,6 @@ def __init__(self, hero_image: str, hero_name: str, message: str,
)
self.end_callback = end_callback
self.should_exit = False
self.elements = []
self.message_chunks = []
self.current_chunk = 0
self.text_box = None
Expand Down Expand Up @@ -69,13 +67,12 @@ def add_image(self, image_path: str, pos: Tuple[int, int] = (5, 5)):

image_surface = pygame.transform.scale(image_surface, (new_width, new_height))

avatar = pygame_gui.elements.UIImage(
_avatar = pygame_gui.elements.UIImage(
pygame.Rect(pos, (new_width, new_height)),
image_surface,
manager=self.ui_manager,
container=self
)
self.elements.append(avatar)

def add_title_and_message(self, title: str, message: str):
pos: Tuple[int, int] = (self.rect.width * AVATAR_WIDTH_RATIO + 10, 5)
Expand All @@ -89,7 +86,6 @@ def add_title_and_message(self, title: str, message: str):
container=self,
object_id='@dialog_title'
)
self.elements.append(title)

pos = (pos[0], pos[1] + line_height)
size = (size[0], self.rect.height * 0.50)
Expand All @@ -101,7 +97,6 @@ def add_title_and_message(self, title: str, message: str):
object_id='@dialog_message',
wrap_to_height=False,
)
self.elements.append(self.text_box)

if len(self.message_chunks) > 1:
# If there are multiple chunks, add a down arrow indicator
Expand All @@ -113,7 +108,6 @@ def add_title_and_message(self, title: str, message: str):
manager=self.ui_manager,
container=self
)
self.elements.append(self.arrow_indicator)

def update(self, dt: float):
pass
Expand All @@ -122,14 +116,10 @@ def render(self):
pass

def enter(self):
self.visible = 1
for element in self.elements:
element.visible = 1
self.show()

def exit(self):
self.visible = 0
for element in self.elements:
element.visible = 0
self.hide()

def process_event(self, event: pygame.event.Event):
if event.type == pygame.KEYDOWN:
Expand Down
43 changes: 43 additions & 0 deletions ui/icons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
The icon texture is 180x180 pixels and each icon is 18x18 pixels. Therefore, the texture
sheet can contain up to 100 icons. That’s more than enough for our needs! What we
want to do is take this texture, split it up into 18 x 18 pixels chunks, and create a sprite
for each icon we want to use. Each icon is given a simple id.
Icon Category:
usable = 1,
accessory = 2,
weapon = 3,
armor = 4,
up_arrow = 5,
down_arrow = 6
"""
from enum import Enum, auto

Check failure on line 15 in ui/icons.py

View workflow job for this annotation

GitHub Actions / build (3.11)

Ruff (F401)

ui/icons.py:15:18: F401 `enum.Enum` imported but unused
from typing import Dict

import pygame

from sprite_utils import load_sprite_sheet


class Icons:
def __init__(self, spritesheet_path: str):
self.spritesheet = load_sprite_sheet(spritesheet_path, 18, 18, 2, 10)
self.icon_category = {
"usable": 1,
"accessory": 2,
"weapon": 3,
"armor": 4,
"up_arrow": 5,
"down_arrow": 6
}
self.icons: Dict[str, pygame.Surface] = {}
self.create_icons()

def create_icons(self):
for category, idx in self.icon_category.items():
self.icons[category] = self.spritesheet[idx]

def get_icon(self, category: str) -> pygame.Surface:
assert category in self.icons, f"Icon id {category} not found"
return self.icons[category]
6 changes: 6 additions & 0 deletions ui/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ def create_panel(self, panel_name: str) -> pygame_gui.elements.UIPanel:
def kill_layout(self) -> None:
self.kill()

def hide_layout(self) -> None:
self.hide()

def show_layout(self) -> None:
self.show()

def render(self):
for p in self.panels.keys():
panel = self.panels[p]
Expand Down
24 changes: 20 additions & 4 deletions ui/select_item.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
from typing import Tuple

import pygame
from pygame import Rect
from pygame_gui import UIManager
from pygame_gui.elements import UILabel
from pygame_gui.elements import UILabel, UIImage
from pygame_gui.core import IContainerLikeInterface


class SelectItem(UILabel):
def __init__(self, relative_rect: Rect, text: str, manager: UIManager, container=None,
def __init__(self, relative_rect: Rect, text: str,
manager: UIManager, container: IContainerLikeInterface = None,
icon_img: pygame.Surface = None, icon_size: Tuple[int, int] = (18, 18),
base_color=(255, 255, 255), highlight_color=(204, 255, 0), active_color=(255, 0, 0)):

# Initialize the UILabel parent class
super().__init__(relative_rect, text, manager, container)
self.parent = container
super().__init__(relative_rect, text, manager, self.parent)

self.base_color = base_color
self.highlight_color = highlight_color
Expand All @@ -18,6 +24,17 @@ def __init__(self, relative_rect: Rect, text: str, manager: UIManager, container
self.is_active = False
self.update_colors()

if icon_img is not None:
self.icon_surf = icon_img.convert_alpha()
pos = (relative_rect.x-icon_size[0], relative_rect.y)
self.icon = UIImage(
relative_rect=Rect(pos, icon_size),
image_surface=self.icon_surf,
manager=manager,
container=self.parent,
parent_element=self.parent
)

def update_colors(self):
if self.is_active:
self.text_colour = self.active_color
Expand Down Expand Up @@ -56,6 +73,5 @@ def handle_event(self, event):
if self.rect.collidepoint(event.pos):
self.on_select()


def on_select(self):
self.set_active()
Loading

0 comments on commit 8bb7d2e

Please sign in to comment.