From valkyrie-mom
Complex event flow patterns for Valkyrie MoM scenarios. Use when creating event loops, multi-question dialogues, silent events, token swaps, random events, or variable-controlled branching.
npx claudepluginhub thijs-hakkenberg/valkyriemcp --plugin valkyrie-momThis skill uses the workspace's default tool permissions.
Advanced event patterns for Mansions of Madness scenarios. Each pattern includes structure, rules, and MCP tool call examples.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Advanced event patterns for Mansions of Madness scenarios. Each pattern includes structure, rules, and MCP tool call examples.
Silent events (display=false) auto-advance without showing dialog. Used for logic branching, cleanup, and chaining.
Critical rules:
event1 set, it MUST have buttons>=1. Valkyrie only parses event1..eventN up to the buttons count.buttons=0 is only safe when it has NO eventN fields (e.g., a terminal remove-only event).vartests branch based on variable state without player interaction.Example — Silent cleanup event:
upsert_event("EventCleanup", {
display: "false",
buttons: "1",
remove: "TokenOldClue SpawnGuard",
event1: "EventNextPhase"
})
Example — Terminal silent event (no chaining):
upsert_event("EventRemoveInvestigators", {
display: "false",
buttons: "0",
remove: "TokenInvestigators"
})
Loops repeat a block of events until an exit condition is met. Structure: Controller → Body → Exit.
# Initialize
upsert_event("EventLoopInit", {
display: "false",
buttons: "1",
operations: "loopCount,=,0",
event1: "EventLoopController"
})
# Controller — check if done
upsert_event("EventLoopController", {
display: "false",
buttons: "2",
vartests: "VarOperation:loopCount,>=,3",
event1: "EventLoopBody", # button1 = test FAILS (keep looping)
event2: "EventLoopExit" # button2 = test PASSES (exit)
})
# Body — do work, increment, return to controller
upsert_event("EventLoopBody", {
display: "true",
buttons: "1",
operations: "loopCount,+,1",
event1: "EventLoopController"
})
# Exit
upsert_event("EventLoopExit", {
display: "true",
buttons: "1",
event1: "EventNextScene"
})
Important — vartests button mapping:
button1 / event1 = test FAILS (condition not met)button2 / event2 = test PASSES (condition met)>= not == to avoid off-by-one infinite loopsA dialogue where each of N questions has its own pass/fail outcome, requiring 2^N permutation events for all combinations. For 3 questions, that's 8 final outcome events.
# Question 1 — skill test
upsert_event("EventQ1", {
buttons: "2",
quota: "1",
event1: "EventQ2fromQ1Pass", # Q1 passed → Q2
event2: "EventQ2fromQ1Fail" # Q1 failed → Q2
})
# Question 2 — after Q1 pass
upsert_event("EventQ2fromQ1Pass", {
buttons: "2",
quota: "1",
event1: "EventQ3_PP", # Q1 pass, Q2 pass → Q3
event2: "EventQ3_PF" # Q1 pass, Q2 fail → Q3
})
# Question 2 — after Q1 fail
upsert_event("EventQ2fromQ1Fail", {
buttons: "2",
quota: "1",
event1: "EventQ3_FP", # Q1 fail, Q2 pass → Q3
event2: "EventQ3_FF" # Q1 fail, Q2 fail → Q3
})
# Question 3 — four variants (PP, PF, FP, FF from Q1+Q2)
# Each has 2 outcomes → 8 total final events
upsert_event("EventQ3_PP", {
buttons: "2",
quota: "1",
event1: "EventOutcome_PPP",
event2: "EventOutcome_PPF"
})
# ... (EventQ3_PF, EventQ3_FP, EventQ3_FF similarly)
# 8 Outcome events: PPP, PPF, PFP, PFF, FPP, FPF, FFP, FFF
# Each shows narrative based on the combination of successes/failures
For more than 3 questions, use variables instead of permutation events:
# Track successes with a variable
upsert_event("EventQ1", {
buttons: "2",
quota: "1",
operations: "successes,=,0",
event1: "EventQ1Pass",
event2: "EventQ1Fail"
})
upsert_event("EventQ1Pass", {
display: "false",
buttons: "1",
operations: "successes,+,1",
event1: "EventQ2"
})
# ... then check successes threshold at the end
Replace one token with another in a single event. Used to update the map state (e.g., locked door → open door).
upsert_event("EventUnlockDoor", {
display: "false",
buttons: "1",
remove: "TokenLockedDoor",
add: "TokenOpenDoor",
event1: "EventDoorOpened"
})
When to use:
Use randomevents=true to randomly pick one event from the event list instead of showing all buttons.
upsert_event("EventRandomEncounter", {
display: "false",
buttons: "3",
randomevents: "true",
event1: "EventEncounterGhost",
event2: "EventEncounterCultist",
event3: "EventEncounterHorror"
})
Use a flag variable per outcome to skip already-seen events:
# Random selector with conditions on sub-events
upsert_event("EventRandomEncounter", {
display: "false",
buttons: "3",
randomevents: "true",
event1: "EventEncA",
event2: "EventEncB",
event3: "EventEncC"
})
# Each encounter sets a "seen" flag
upsert_event("EventEncA", {
buttons: "1",
conditions: "seenA,==,0", # skip if already seen
operations: "seenA,=,1",
event1: "EventContinue"
})
Use vartests to route events based on game state. Events are evaluated in button order.
upsert_event("EventCheckProgress", {
display: "false",
buttons: "2",
vartests: "VarOperation:cluesFound,>=,3",
event1: "EventNotEnoughClues", # test FAILS (< 3 clues)
event2: "EventEnoughClues" # test PASSES (>= 3 clues)
})
| Feature | conditions | vartests |
|---|---|---|
| Check timing | Before event displays | After event displays |
| If false | Event silently skipped | Routes to button1/event1 |
| If true | Event proceeds normally | Routes to button2/event2 |
| Format | var,comparator,value (space-separated, AND logic) | VarOperation:var,comparator,value |
| Use case | Gate tokens, spawns, events | Branch event flow |
upsert_event("EventFinalCheck", {
display: "false",
buttons: "2",
vartests: "VarOperation:key1Found,>=,1 VarOperation:key2Found,>=,1 VarTestsLogicalOperator:AND",
event1: "EventMissingKeys",
event2: "EventBothKeysFound"
})
Use consistent naming to keep complex event flows readable:
Event<Location><Action> — e.g., EventStudyExplore, EventHallwayFightEvent<Name>Pass / Event<Name>Fail — skill test outcomesEvent<Name>Loop / Event<Name>LoopBody / Event<Name>LoopExit — loop componentsEventQ1, EventQ2, etc. — dialogue questions0_<Name> / 1_<Name> prefixes for loop init/body events within a tile context