120 lines
No EOL
3.8 KiB
Python
120 lines
No EOL
3.8 KiB
Python
"""McRogueFace - Health Bar Widget (animated)
|
|
|
|
Documentation: https://mcrogueface.github.io/cookbook/ui_health_bar
|
|
Repository: https://github.com/jmccardle/McRogueFace/blob/master/docs/cookbook/ui/ui_health_bar_animated.py
|
|
|
|
This code is extracted from the McRogueFace documentation and can be
|
|
run directly with: ./mcrogueface path/to/this/file.py
|
|
"""
|
|
|
|
import mcrfpy
|
|
|
|
class AnimatedHealthBar:
|
|
"""Health bar with smooth fill animation."""
|
|
|
|
def __init__(self, x, y, w, h, current, maximum):
|
|
self.x = x
|
|
self.y = y
|
|
self.w = w
|
|
self.h = h
|
|
self.current = current
|
|
self.display_current = current # What's visually shown
|
|
self.maximum = maximum
|
|
self.timer_name = f"hp_anim_{id(self)}"
|
|
|
|
# Background
|
|
self.background = mcrfpy.Frame(x, y, w, h)
|
|
self.background.fill_color = mcrfpy.Color(40, 40, 40)
|
|
self.background.outline = 2
|
|
self.background.outline_color = mcrfpy.Color(60, 60, 60)
|
|
|
|
# Damage preview (shows recent damage in different color)
|
|
self.damage_fill = mcrfpy.Frame(x + 2, y + 2, w - 4, h - 4)
|
|
self.damage_fill.fill_color = mcrfpy.Color(180, 50, 50)
|
|
self.damage_fill.outline = 0
|
|
|
|
# Main fill
|
|
self.fill = mcrfpy.Frame(x + 2, y + 2, w - 4, h - 4)
|
|
self.fill.fill_color = mcrfpy.Color(50, 200, 50)
|
|
self.fill.outline = 0
|
|
|
|
self._update_display()
|
|
|
|
def _update_display(self):
|
|
"""Update the visual fill based on display_current."""
|
|
ratio = max(0, min(1, self.display_current / self.maximum))
|
|
self.fill.w = (self.w - 4) * ratio
|
|
|
|
# Color based on ratio
|
|
if ratio > 0.6:
|
|
self.fill.fill_color = mcrfpy.Color(50, 200, 50)
|
|
elif ratio > 0.3:
|
|
self.fill.fill_color = mcrfpy.Color(230, 180, 30)
|
|
else:
|
|
self.fill.fill_color = mcrfpy.Color(200, 50, 50)
|
|
|
|
def set_health(self, new_current, animate=True):
|
|
"""
|
|
Set health with optional animation.
|
|
|
|
Args:
|
|
new_current: New health value
|
|
animate: Whether to animate the transition
|
|
"""
|
|
old_current = self.current
|
|
self.current = max(0, min(self.maximum, new_current))
|
|
|
|
if not animate:
|
|
self.display_current = self.current
|
|
self._update_display()
|
|
return
|
|
|
|
# Show damage preview immediately
|
|
if self.current < old_current:
|
|
damage_ratio = self.current / self.maximum
|
|
self.damage_fill.w = (self.w - 4) * (old_current / self.maximum)
|
|
|
|
# Animate the fill
|
|
self._start_animation()
|
|
|
|
def _start_animation(self):
|
|
"""Start animating toward target health."""
|
|
mcrfpy.delTimer(self.timer_name)
|
|
|
|
def animate_step(dt):
|
|
# Lerp toward target
|
|
diff = self.current - self.display_current
|
|
if abs(diff) < 0.5:
|
|
self.display_current = self.current
|
|
mcrfpy.delTimer(self.timer_name)
|
|
# Also update damage preview
|
|
self.damage_fill.w = self.fill.w
|
|
else:
|
|
# Move 10% of the way each frame
|
|
self.display_current += diff * 0.1
|
|
|
|
self._update_display()
|
|
|
|
mcrfpy.setTimer(self.timer_name, animate_step, 16)
|
|
|
|
def damage(self, amount):
|
|
"""Apply damage with animation."""
|
|
self.set_health(self.current - amount, animate=True)
|
|
|
|
def heal(self, amount):
|
|
"""Apply healing with animation."""
|
|
self.set_health(self.current + amount, animate=True)
|
|
|
|
def add_to_scene(self, ui):
|
|
"""Add all frames to scene."""
|
|
ui.append(self.background)
|
|
ui.append(self.damage_fill)
|
|
ui.append(self.fill)
|
|
|
|
|
|
# Usage
|
|
hp_bar = AnimatedHealthBar(50, 50, 300, 30, current=100, maximum=100)
|
|
hp_bar.add_to_scene(ui)
|
|
|
|
# Damage will animate smoothly
|
|
hp_bar.damage(40) |