animations
This commit is contained in:
+27
-16
@@ -20,13 +20,18 @@ pub use crate::action::ScrollLine;
|
||||
|
||||
/// An active scroll overlay opened by a scripted object via `scroll()`.
|
||||
///
|
||||
/// While a `Scroll` is present on [`GameState`], the front-end should pause ticks
|
||||
/// and display the overlay. Closed via [`GameState::close_scroll`].
|
||||
/// While a `Scroll` is present on [`GameState`], the front-end should display
|
||||
/// the overlay and pause ticks. When the player selects a choice (or dismisses),
|
||||
/// the front-end sets [`choice`](Scroll::choice); [`GameState::tick`] calls
|
||||
/// [`GameState::handle_scroll`] at the start of each tick to dispatch and clear it.
|
||||
pub struct Scroll {
|
||||
/// The object whose `scroll()` call opened this overlay.
|
||||
pub source: ObjectId,
|
||||
/// The lines of content to display.
|
||||
pub lines: Vec<ScrollLine>,
|
||||
/// The choice key the player selected, or `None` if dismissed without a choice.
|
||||
/// Set by the front-end; consumed by [`GameState::handle_scroll`].
|
||||
pub choice: Option<String>,
|
||||
}
|
||||
|
||||
/// An active speech bubble emitted by a scripted object via `say()`.
|
||||
@@ -59,7 +64,7 @@ pub struct GameState {
|
||||
/// Active speech bubbles from `say()` calls, displayed by the front-end over the board.
|
||||
pub speech_bubbles: Vec<SpeechBubble>,
|
||||
/// An active scroll overlay opened by `scroll()`. `Some` while the overlay is
|
||||
/// visible; the front-end pauses ticks and closes it via [`close_scroll`](GameState::close_scroll).
|
||||
/// visible; the front-end pauses ticks and sets [`Scroll::choice`] before resuming.
|
||||
pub active_scroll: Option<Scroll>,
|
||||
/// Remaining seconds for the board-entry transition animation, set to `1.0`
|
||||
/// by [`enter_board`](GameState::enter_board). Front-ends tick this down and
|
||||
@@ -160,10 +165,14 @@ impl GameState {
|
||||
}
|
||||
|
||||
/// Advances real-time game state by `dt` (the elapsed time since the last tick).
|
||||
/// Called once per frame by the front-end's game loop. Counts down object
|
||||
/// cooldowns, drives every scripted object's `tick(dt)` hook (pumping each queue),
|
||||
/// then resolves the actions that were promoted onto the board queue.
|
||||
/// Called once per frame by the front-end's game loop. First processes any active
|
||||
/// scroll (dispatching the player's choice if set), then drives object tick hooks
|
||||
/// and resolves queued actions.
|
||||
pub fn tick(&mut self, dt: Duration) {
|
||||
// Process any pending scroll choice before running scripts; ticks are
|
||||
// suppressed by the front-end while a scroll overlay is visible, so
|
||||
// this runs exactly once per player interaction with a scroll.
|
||||
self.handle_scroll();
|
||||
let secs = dt.as_secs_f64();
|
||||
self.scripts.run_tick(secs);
|
||||
// Expire speech bubbles before resolving new actions so a fresh say()
|
||||
@@ -268,7 +277,7 @@ impl GameState {
|
||||
}
|
||||
// Later scrolls overwrite earlier ones from the same tick.
|
||||
Action::Scroll(lines) => {
|
||||
new_scroll = Some(Scroll { source: ba.source, lines });
|
||||
new_scroll = Some(Scroll { source: ba.source, lines, choice: None });
|
||||
}
|
||||
Action::Teleport { x, y } => {
|
||||
if !board.in_bounds((x, y)) {
|
||||
@@ -311,16 +320,18 @@ impl GameState {
|
||||
self.drain_errors();
|
||||
}
|
||||
|
||||
/// Closes the active scroll overlay, optionally dispatching a named choice
|
||||
/// back to the object that opened it via `send`.
|
||||
/// Consumes the active scroll, dispatching the player's choice (if any) back
|
||||
/// to the source object via `send`.
|
||||
///
|
||||
/// Call from the front-end when the player dismisses the scroll or selects a
|
||||
/// choice. If `choice` is `Some`, the source object receives a `send` call with
|
||||
/// that string as the function name (e.g. `"eat"` triggers `fn eat()`).
|
||||
pub fn close_scroll(&mut self, choice: Option<&str>) {
|
||||
let source = self.active_scroll.take().map(|s| s.source);
|
||||
if let (Some(src), Some(ch)) = (source, choice) {
|
||||
self.scripts.run_send(src, ch, None);
|
||||
/// Called automatically by [`tick`](GameState::tick) as its first step. The
|
||||
/// front-end sets [`Scroll::choice`] before resuming ticks; if no choice was
|
||||
/// made (player dismissed), `choice` stays `None` and the scroll is cleared
|
||||
/// without dispatching.
|
||||
pub fn handle_scroll(&mut self) {
|
||||
if let Some(scroll) = self.active_scroll.take()
|
||||
&& let Some(choice) = scroll.choice
|
||||
{
|
||||
self.scripts.run_send(scroll.source, &choice, None);
|
||||
self.drain_errors();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user