diff --git a/help.json b/help.json index f0021ea..a9115fb 100644 --- a/help.json +++ b/help.json @@ -18,6 +18,7 @@ "panel_merge": "Use #panel_merge followed by the frame path to merge panels. For example, #panel_merge {} will merge the current panel with the top-level split.", "send_multiple_commands": "To send several commands in quick succession, separate them with a semicolon. For example, use 'n;e' to go north and then east.", "tick": "Use #tick to set a recurring command to run at a specified time interval (in seconds).\r\nFor example, #tick 60 {#echo Cockadoodledoo!} will repeat every 60 seconds.\r\nOptionally give it a name with a third argument.\r\nUse #tick by itself to list active ticks.\r\nSee also: #untick", + "slow": "To send commands at a controlled rate, try #slow {command;command;command} 0.5, where 0.5 is replaced with the time between commands in seconds" "unact": "Use #unact followed by the trigger pattern to delete a trigger. Example: #unact {^The (.*) attacks you.}.", "unalias": "Use #unalias followed by the alias pattern to delete it. For example, #unalias {^hi}.", "undelay": "Use #undelay followed by the delay name to cancel it. Example: #undelay ding to cancel a delay named 'ding'.", diff --git a/src/lua_engine.rs b/src/lua_engine.rs index 2fe999e..39ca4b4 100644 --- a/src/lua_engine.rs +++ b/src/lua_engine.rs @@ -234,6 +234,7 @@ pub fn install_lua_globals( register_command!(panel_merge); register_command!(panel_swap); register_command!(sendmud_raw); + register_command!(slow); register_stateless_command!(storage); register_command!(tick); register_command!(tsplit); diff --git a/src/lua_engine/frames.rs b/src/lua_engine/frames.rs index 347a8ed..102d118 100644 --- a/src/lua_engine/frames.rs +++ b/src/lua_engine/frames.rs @@ -2,6 +2,7 @@ use crate::{ echo_to_term_frame, id_intern::intern_id, match_table::{create_match_table, match_table_add, match_table_remove}, + parsing::parse_commands, timer_host::TimerHostAccessContext, FrameId, FrameViewType, GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, }; @@ -875,6 +876,67 @@ pub(super) fn untick<'gc>( }) } +pub(super) fn slow<'gc>( + ctx: Context<'gc>, + global_memo: &GlobalMemoCell, + _global_layout: &UseStateSetter, +) -> Callback<'gc> { + let global_memo = global_memo.clone(); + Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { + let cur_frame_id = try_unwrap_frame( + ctx, + &ctx.get_global::("info")? + .get(ctx, ctx.intern_static(b"current_frame"))?, + )?; + let cmds = piccolo::String::from_value( + ctx, + stack + .pop_front() + .ok_or_else(|| anyhow::Error::msg("Missing commands to slowly queue"))?, + )? + .to_str()?; + let delay: f64 = f64::from_value( + ctx, + stack + .pop_front() + .ok_or_else(|| anyhow::Error::msg("Missing slow delay time"))?, + )?; + if !stack.is_empty() { + Err(anyhow::Error::msg( + "Extra arguments to slow command. Try wrapping the action in {}", + ))?; + } + + let cmds = parse_commands(cmds).commands; + let delay_fn: Function = ctx.get_global::
("commands")?.get(ctx, "delay")?; + + let seq = async_sequence(&ctx, |locals, mut seq| { + let global_memo = global_memo.clone(); + let delay_fn = locals.stash(&ctx, delay_fn); + async move { + for (idx, cmd) in cmds.iter().enumerate() { + if idx == 0 { + global_memo + .command_queue + .borrow_mut() + .push_front((cur_frame_id.clone(), cmd.clone())); + } else { + seq.try_enter(|ctx, _locals, _exec, mut stack| { + stack.consume::<()>(ctx)?; + stack.into_back(ctx, delay * (idx as f64)); + stack.into_back(ctx, cmd.to_string()); + Ok(()) + })?; + seq.call(&delay_fn, 0).await?; + } + } + Ok(SequenceReturn::Return) + } + }); + Ok(piccolo::CallbackReturn::Sequence(seq)) + }) +} + pub(super) fn editor<'gc>( ctx: Context<'gc>, global_memo: &GlobalMemoCell,