Commit a854789c authored by Toast Engineer's avatar Toast Engineer

Implement DoubleLayerMusicPlayer, tinker with combat trying to get it to work

parent ba32ba2e
......@@ -30,6 +30,8 @@ class PrintToNotificationBox:
def write(self, text: str):
if not text.isspace():
text_menu.pop_up_notification(text)
import gfx
gfx.main_window.draw()
self.old_stdout.write(text)
def flush(self):
......
......@@ -2,6 +2,7 @@ import functools
import itertools
import pprint
import random
import sys
import typing
import minigames
......@@ -23,6 +24,7 @@ class LimitedList(list):
class StatusEffect:
incapacitating = False
abbreviation = ''
def __init__(self, turns_left):
self.turns_left = turns_left
......@@ -30,7 +32,8 @@ class StatusEffect:
@classmethod
def apply(cls, character, *args, **kwargs):
"""Initializes the StatusEffect as normal and immediately applies it to a character."""
character.status_effects.append(cls(*args, **kwargs))
if not any(isinstance(theeffect, cls) for theeffect in character.status_effects):
character.status_effects.append(cls(*args, **kwargs))
def each_turn(self, character):
"""Return True to have time_up called and get removed from the status effects list"""
......@@ -41,9 +44,13 @@ class StatusEffect:
pass
class BleedingOut(StatusEffect):
abbreviation = "BLEED"
def __init__(self):
super().__init__(25)
def each_turn(self, character):
print(f"{character} is bleeding to death!")
def time_up(self, character):
character.kill()
......@@ -68,7 +75,7 @@ class UseItem(BattleAction):
if not should_remain:
try:
self.user.inventory.remove(self.item)
except IndexError:
except ValueError:
pass
class RunAway(BattleAction):
......@@ -101,7 +108,7 @@ class Character:
is_pc = False
def __init__(self, name = "noname", strength = 1, agility = 1, magic = 1, tech = 1, max_physical_stamina = 2, max_mental_stamina = 2):
def __init__(self, name = "noname", strength = 10, agility = 10, magic = 10, tech = 10, max_physical_stamina = 20, max_mental_stamina = 20):
self.name = name
self.strength = strength
......@@ -144,6 +151,7 @@ ST: {self.strength:.1f} AG: {self.agility:.1f}
MG: {self.magic:.1f} TH: {self.tech:.1f}
Phys Stam: {self.physical_stamina:.1f} / {self.max_physical_stamina:.1f}
Mntl Stam: {self.mental_stamina:.1f} / {self.max_mental_stamina:.1f}
{' '.join(eff.abbreviation for eff in self.status_effects)}
""" )
def process_status_effects(self):
......@@ -272,11 +280,11 @@ class Item:
if battle.ESTIMATING_FUTURE:
return max(0, min(1, difficulty))
#if character.is_enemy_npc():
# effectiveness = minigame(1 / difficulty)
return max(0, min(1, difficulty))
#effectiveness = minigame(difficulty)
if character.is_pc:
effectiveness = minigame(difficulty)
setattr(character, self.skill, getattr(character, self.skill) + (effectiveness / 4))
else:
effectiveness = minigame(1 / difficulty)
return effectiveness
......@@ -371,9 +379,13 @@ class MeleeWeapon(Weapon):
def str(self):
return f"{self.name} ({self.base_power})"
def determine_effectiveness(self, character: Character, target: Character):
return super().determine_effectiveness(character, target, minigames.test_your_strength)
def use_in_combat(self, character: Character, target: Character):
effectiveness = self.determine_effectiveness(character, target)
stamina_cost = self.base_power * (1 / effectiveness) / self.sharpness
print(effectiveness, file=sys.__stdout__)
stamina_cost = self.base_power * (1 - effectiveness) / self.sharpness
if character.physical_stamina <= stamina_cost:
print(f"{character} attacks {target} with {self}, while collapsing from exhausion.")
effectiveness = character.physical_stamina
......@@ -407,6 +419,13 @@ class Stick(MeleeWeapon):
class PointyStick(MeleeWeapon):
sharpness = 1.2
class Axe(MeleeWeapon):
base_power = 3
sharpness = 1.1
class RangedWeapon(Weapon):
pass
if __name__ == "__main__":
w = Wizard()
for t in range(6):
......@@ -427,4 +446,4 @@ if __name__ == "__main__":
print(w)
shop_items = [FirstAidKit, CombatFirstAidKit, Stick, PointyStick, LazarusPump]
\ No newline at end of file
shop_items = [FirstAidKit, CombatFirstAidKit, Stick, PointyStick]
\ No newline at end of file
......@@ -3,6 +3,7 @@ import random
import sys
import traceback
import typing
import time
import gfx
import text_menu
......@@ -94,7 +95,9 @@ class GameState:
self.mainwin = gfx.MainWindow(self.camera)
self.world_renderer = self.mainwin.world_view
self.current_level = None #None represents town
self.party = []
self.party = [character.Warrior()] * 6
for the_character in self.party:
the_character.inventory.append(character.PointyStick())
self.battle = None
self.partyfunds = 900
......@@ -130,6 +133,11 @@ class GameState:
self.camera.update(self.current_level.map)
self.current_level.update(self)
if math.isclose(time.perf_counter() % 15, 0):
for the_character in self.party:
the_character.process_status_effects()
if all(c.incapacitated for c in self.party):
text_menu.pop_up_notification("Your party has fallen.")
self.party.clear()
......@@ -143,7 +151,7 @@ class GameState:
self.mainwin.middle_textbox_active = self.battle is not None
self.mainwin.draw()
if random.random() > 0.9995 and self.current_level:
if random.random() > 0.95 and self.current_level:
self.start_battle()
except Exception:
......
......@@ -66,8 +66,7 @@ def any_button():
class OutOfTime(BaseException):
"""You should never actually get this exception - it's used in some minigames to implement the timer."""
def test_your_strength(self, difficulty):
print(self, difficulty)
def test_your_strength(difficulty):
s = get_surface((300, 100))
time_between_lights = min(1, 1 / difficulty)
light1_time = time_between_lights
......
......@@ -69,18 +69,16 @@ class ChunkedMusicPlayer:
def stop(self, fadeout = 0):
self.stop_thread = True
if self.thread:
self.thread.join()
if fadeout:
self.channel.fadeout(fadeout)
else:
if not self.thread:
self.channel.fadeout(1000)
self.channel.stop()
self.next_chunk_idx = 0
def _thread_target(self):
while not self.stop_thread:
time.sleep(self.queue_chunk() / 2)
self.channel.fadeout(1000)
self.channel.stop()
def spawn_thread(self):
"""Spawn a thread to queue up the next segment automatically."""
......@@ -89,8 +87,56 @@ class ChunkedMusicPlayer:
self.thread = threading.Thread(target=self._thread_target, daemon=True)
self.thread.start()
class DoubleLayerMusicPlayer:
"""For playing music where, for example, you have an "explore" theme, and then
when you get in to a battle, a separate track plays over it to create the "battle"
theme, smoothly fading in and out."""
def __init__(self, a_path, b_path):
self.a = pygame.mixer.Sound(str(a_path))
self.b = pygame.mixer.Sound(str(b_path))
self.b_should_be_playing = False
self.b_volume = 0.0001
self.stop = False
self.b_fader_thread = threading.Thread(target=self.b_fader, daemon=True).start()
def b_fader(self):
while not self.stop:
if self.b_should_be_playing:
self.b_volume += 0.01
else:
self.b_volume -= 0.01
self.b_volume = max(0, min(1, self.b_volume))
self.b.set_volume(self.b_volume)
time.sleep(0.01)
def play(self):
self.a.play(-1)
self.b.play(-1)
self.b.set_volume(0.0001)
def stop(self):
self.stop = True
self.a.stop()
self.b.stop()
self.b_fader_thread.join()
if __name__ == "__main__":
pygame.init()
pygame.display.set_mode((10,10))
p = DoubleLayerMusicPlayer(Path("music", "lv1", "a.ogg"), Path("music", "lv1", "b.ogg"))
p.play()
clock = pygame.time.Clock()
while True:
for theevent in pygame.event.get():
if theevent.type == pygame.KEYUP:
p.b_should_be_playing = not p.b_should_be_playing
print(p.b.get_volume())
clock.tick(60)
w = pygcurse.PygcurseWindow(font=pygame.font.Font("FSEX300.ttf", 16))
m = ChunkedMusicPlayer()
m.play()
......
......@@ -69,6 +69,10 @@ def item_shop(gamestate):
select_recipient["Never mind"] = None
recipient = select_recipient()
if recipient is None:
continue
recipient.inventory.append(item)
print(f"Bought a(n) {item} for {recipient} for ${item.sale_price}")
gamestate.partyfunds -= item.sale_price
......@@ -78,16 +82,18 @@ class TownMenu(PopUpMenu):
def __call__(self, gamestate):
music = musicplayer.ChunkedMusicPlayer(Path("music", "preparation"), lambda: len(gamestate.party))
music.spawn_thread()
if gamestate.party:
for thecharacter in gamestate.party:
thecharacter.physical_stamina = thecharacter.max_physical_stamina
thecharacter.mental_stamina = thecharacter.max_mental_stamina
print("All characters restored to maximum stamina.")
super().__call__(gamestate)
music.stop(fadeout=1)
def enter_tower(gamestate):
testman = characters.Warrior("Testman")
testman.inventory.append(characters.PointyStick())
gamestate.party.append(testman)
#testman = characters.Warrior("Testman")
#testman.inventory.append(characters.PointyStick())
#gamestate.party.append(testman)
if len(gamestate.party) == 0:
pop_up_notification("You need to assemble a party before you can begin the adventure.")
return
......@@ -101,10 +107,10 @@ def enter_tower(gamestate):
town_menu = TownMenu({
"The Tower's Shadow Inn" : manage_party,
"Adventurer's Supply Store" : item_shop,
"Alliterative Arnold's Affordable Adventuring Accessories": (lambda x: None),
"A Terrible-Smelling Back Alley" : (lambda x: None),
"Public Workshop" : (lambda x: None),
"Manage party inventory": (lambda x: None),
#"Alliterative Arnold's Affordable Adventuring Accessories": (lambda x: None),
#"A Terrible-Smelling Back Alley" : (lambda x: None),
#"Public Workshop" : (lambda x: None),
#"Manage party inventory": (lambda x: None),
"Enter the Tower" : enter_tower,
"Quit the Game" : quit_confirmation
}, loop_until_non_callable = True,)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment