HP Bar works, msg logs work, WIP part 7
This commit is contained in:
parent
83f7723c08
commit
7685d57286
@ -1,5 +1,7 @@
|
||||
from typing import Any
|
||||
|
||||
import colors
|
||||
|
||||
class BaseAction:
|
||||
entity: Any
|
||||
|
||||
@ -57,14 +59,27 @@ class MeleeAction(DirectionAction):
|
||||
|
||||
if not target:
|
||||
return False
|
||||
|
||||
|
||||
#apply damage
|
||||
damage = self.entity.fighter.attack - target.fighter.defense
|
||||
target.fighter.current_hp -= damage
|
||||
|
||||
#calculate output
|
||||
engine = self.entity.floor_map.engine
|
||||
msg_text = f"{self.entity.name} attacked {target.name}"
|
||||
msg_color = colors.white
|
||||
|
||||
if self.entity is engine.player:
|
||||
msg_color = colors.player_atk
|
||||
else:
|
||||
msg_color = colors.enemy_atk
|
||||
|
||||
if damage > 0:
|
||||
print(f"{self.entity.name} attacked {target.name} for {damage} damage")
|
||||
target.fighter.current_hp -= damage
|
||||
msg_text += f" for {damage} damage"
|
||||
else:
|
||||
print(f"{self.entity.name} attacked {target.name} but was ineffective")
|
||||
msg_text += f" but was ineffective"
|
||||
|
||||
engine.message_log.add_message(text = msg_text, fg=msg_color)
|
||||
|
||||
return True
|
||||
|
||||
|
15
source/colors.py
Normal file
15
source/colors.py
Normal file
@ -0,0 +1,15 @@
|
||||
#copy/pasted, because reasons
|
||||
white = (0xFF, 0xFF, 0xFF)
|
||||
black = (0x0, 0x0, 0x0)
|
||||
|
||||
player_atk = (0xE0, 0xE0, 0xE0)
|
||||
enemy_atk = (0xFF, 0xC0, 0xC0)
|
||||
|
||||
player_die = (0xFF, 0x30, 0x30)
|
||||
enemy_die = (0xFF, 0xA0, 0x30)
|
||||
|
||||
welcome_text = (0x20, 0xA0, 0xFF)
|
||||
|
||||
bar_text = white
|
||||
bar_filled = (0x0, 0x60, 0x0)
|
||||
bar_empty = (0x40, 0x10, 0x10)
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import colors
|
||||
from components.base_component import BaseComponent
|
||||
|
||||
class Fighter(BaseComponent):
|
||||
@ -29,13 +30,15 @@ class Fighter(BaseComponent):
|
||||
|
||||
|
||||
def die_and_despawn(self) -> None:
|
||||
if self.entity is self.entity.floor_map.engine.player and self.entity.ai:
|
||||
engine = self.entity.floor_map.engine
|
||||
|
||||
if self.entity is engine.player and self.entity.ai:
|
||||
from event_handler import GameOverEventHandler
|
||||
self.entity.floor_map.engine.event_handler = GameOverEventHandler(self.entity.floor_map.engine)
|
||||
print("You died")
|
||||
engine.message_log.add_message("You died.", colors.player_die)
|
||||
|
||||
else:
|
||||
print(f"The {self.entity.name} died")
|
||||
engine.message_log.add_message(f"The {self.entity.name} died", colors.enemy_die)
|
||||
|
||||
self.entity.char = "%"
|
||||
self.entity.color = (191, 0, 0)
|
||||
|
@ -2,16 +2,23 @@ from tcod.context import Context
|
||||
from tcod.console import Console
|
||||
from tcod.map import compute_fov
|
||||
|
||||
from message_log import Message, MessageLog
|
||||
from render_functions import render_hp_bar
|
||||
|
||||
import entity_types
|
||||
from floor_map import FloorMap #TODO: replace with "DungeonMap"
|
||||
|
||||
class Engine:
|
||||
def __init__(self, floor_map: FloorMap):
|
||||
def __init__(self, floor_map: FloorMap, intro_msg: Message = None):
|
||||
from event_handler import InGameEventHandler
|
||||
self.event_handler = InGameEventHandler(self)
|
||||
self.floor_map = floor_map
|
||||
self.floor_map.engine = self #references everywhere!
|
||||
|
||||
self.message_log = MessageLog()
|
||||
if intro_msg:
|
||||
self.message_log.push_message(intro_msg)
|
||||
|
||||
#grab the player object
|
||||
self.player = self.floor_map.player
|
||||
|
||||
@ -38,9 +45,18 @@ class Engine:
|
||||
self.floor_map.render(console)
|
||||
|
||||
#UI
|
||||
console.print(
|
||||
x=1, y=47,
|
||||
string=f"HP: {self.player.fighter.current_hp}/{self.player.fighter.current_hp}",
|
||||
render_hp_bar(
|
||||
console = console,
|
||||
current_value = self.player.fighter.current_hp,
|
||||
max_value = self.player.fighter.maximum_hp,
|
||||
total_width = 20
|
||||
)
|
||||
self.message_log.render(
|
||||
console=console,
|
||||
x=21,
|
||||
y=45 - 5,
|
||||
width = 40,
|
||||
height = 5
|
||||
)
|
||||
|
||||
#send to the screen
|
||||
|
@ -8,7 +8,7 @@ player = Actor(
|
||||
name = "Player",
|
||||
walkable = False,
|
||||
ai_class = BaseAI,
|
||||
fighter = Fighter(hp = 10, attack = 2, defense = 0),
|
||||
fighter = Fighter(hp = 10, attack = 2, defense = 1),
|
||||
)
|
||||
|
||||
#gobbos
|
||||
@ -18,7 +18,7 @@ gobbo = Actor(
|
||||
name = "Gobbo",
|
||||
walkable = False,
|
||||
ai_class = AttackOnSight,
|
||||
fighter = Fighter(hp = 5, attack = 2, defense = 0),
|
||||
fighter = Fighter(hp = 5, attack = 1, defense = 0),
|
||||
)
|
||||
|
||||
gobbo_red = Actor(
|
||||
|
@ -3,6 +3,8 @@ import tcod
|
||||
|
||||
from engine import Engine
|
||||
from procgen import generate_floor_map
|
||||
from message_log import Message
|
||||
import colors
|
||||
|
||||
def main() -> None:
|
||||
#tcod stuff
|
||||
@ -16,6 +18,8 @@ def main() -> None:
|
||||
|
||||
w, h = context.recommended_console_size(min_columns=10, min_rows=10)
|
||||
|
||||
print (w,h)
|
||||
|
||||
console = tcod.console.Console(
|
||||
width = w,
|
||||
height = h + 5,
|
||||
@ -24,7 +28,8 @@ def main() -> None:
|
||||
|
||||
engine = Engine(
|
||||
#is created externally, because
|
||||
floor_map = generate_floor_map(80, 45, 10, 10)
|
||||
floor_map = generate_floor_map(80, 45, 10, 10),
|
||||
intro_msg = Message("Welcome to the Cave of Gobbos!", colors.welcome_text)
|
||||
)
|
||||
|
||||
#game loop that never returns
|
||||
|
49
source/message_log.py
Normal file
49
source/message_log.py
Normal file
@ -0,0 +1,49 @@
|
||||
from typing import List, Reversible, Tuple
|
||||
from textwrap import TextWrapper
|
||||
|
||||
from tcod.console import Console
|
||||
|
||||
import colors
|
||||
|
||||
|
||||
class Message:
|
||||
def __init__(self, text: str, fg: Tuple[int, int, int] = colors.white, count: int = 1):
|
||||
self.raw_text = text
|
||||
self.fg = fg
|
||||
self.count = count
|
||||
|
||||
@property
|
||||
def full_text(self) -> str:
|
||||
if self.count > 1:
|
||||
return f"{self.raw_text} (x{self.count})"
|
||||
return self.raw_text
|
||||
|
||||
|
||||
class MessageLog:
|
||||
def __init__(self):
|
||||
self.messages: List[Message] = []
|
||||
|
||||
def add_message(self, text: str, fg: Tuple[int, int, int] = colors.white, *, stack: bool = True) -> None:
|
||||
if stack and self.messages and text == self.messages[-1].raw_text:
|
||||
self.messages[-1].count += 1
|
||||
else:
|
||||
self.messages.append(Message(text, fg))
|
||||
|
||||
def push_message(self, msg: Message) -> None:
|
||||
self.messages.append(msg)
|
||||
|
||||
def render(self, console: Console, x: int, y: int, width: int, height: int) -> None:
|
||||
self.render_messages(console, x, y, width, height, self.messages)
|
||||
|
||||
@staticmethod
|
||||
def render_messages(console: Console, x: int, y: int, width: int, height: int, messages: Reversible[Message]) -> None:
|
||||
y_offset = height - 1
|
||||
|
||||
wrapper = TextWrapper(width=width, subsequent_indent = " ")
|
||||
|
||||
for message in reversed(messages):
|
||||
for line in reversed(wrapper.wrap(message.full_text)): #oh, neat
|
||||
console.print(x=x,y=y + y_offset,string=line,fg=message.fg)
|
||||
y_offset -= 1
|
||||
if y_offset < 0:
|
||||
return
|
15
source/render_functions.py
Normal file
15
source/render_functions.py
Normal file
@ -0,0 +1,15 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import colors
|
||||
|
||||
from tcod.console import Console
|
||||
|
||||
def render_hp_bar(console: Console, current_value: int, max_value: int, total_width: int) -> None:
|
||||
bar_width = int(float(current_value) / max_value * total_width)
|
||||
|
||||
console.draw_rect(x=0, y=45, width=total_width, height=1, ch=1, bg=colors.bar_empty)
|
||||
|
||||
if bar_width > 0:
|
||||
console.draw_rect(x=0, y=45, width=bar_width, height=1, ch=1, bg=colors.bar_filled)
|
||||
|
||||
console.print(x=1, y=45, string=f"HP: {current_value}/{max_value}", fg=colors.bar_text)
|
Loading…
x
Reference in New Issue
Block a user