Skip to main content

buff

Temporary effects system — curses are just shitty buffs

Quellinformationen

Repository
SimHacker/moollm
Letzte Quellaktivität
16. September 2026 um 12:55
Erkannte Sprache von SKILL.md
Englisch
Sterne
60
Forks
5

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
26 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
buff
description
Temporary effects system — curses are just shitty buffs
allowed-tools
["read_file","write_file"]
tier
1
protocol
BUFF-AS-MODIFIER
related
["simulation","time","needs","character","cat","dog","persona","yaml-jazz"]
tags
["moollm","effects","curses","stats","game","modifiers"]
hosts
any
# Buff > *"All effects are buffs. Some are just shitty."* Buffs modify stats, abilities, or behavior. They have durations, can stack, and come from various sources. **Curses are just negative buffs** — no separate system. ## Hosts — a buff attaches to whatever the effect belongs to A Sim gets caffeinated. A tavern gets haunted. A lantern burns down. Silver, as a *material*, harms anyone who wields it. Bob is smitten with Alice, whatever Alice thinks. In Fluxx, the *rules themselves* get a new one. All of those are buffs, and each belongs to a different kind of thing: ```yaml # a character buff: name: "Caffeinated" host: character/mrs-crumplebottom effect: { energy: +2, focus: +1 } expires: { after: 5, unit: turns } # a room — and note it reaches the people inside it, not just the room buff: name: "Poisoned Air" host: room/lower-mine effect: { breathable: false } radiates: { to: occupants, effect: { health: -1 }, while: present } expires: { when: "ventilation restored" } # an object, carrying its own countdown buff: name: "Burning Down" host: object/brass-lantern effect: { light_radius: -1 } tick: { every: 1, effect: { fuel: -1 } } expires: { when: "fuel <= 0" } # a prototype — every instance inherits it, including ones made later buff: name: "Cursed Silver" host: prototype/material/silver effect: { harms_wielder: true } # a relationship — the edge, with a direction buff: name: "Smitten" host: relationship/{ from: bob, to: alice } effect: { romance: +80 } expires: { after: 40, unit: turns } # the ruleset buff: name: "Everyone Draws Two" host: ruleset/current-game effect: { draw_per_turn: 2 } expires: { when: "superseded by another Draw rule" } ``` Nothing in the buff's structure changes with the host. Same fields, same clock, same conflict resolution. ### Rooms and objects can simply *be* characters MOOLLM has no rigid entity classes to work around. Everything is a prototype, and anything can delegate to [`character`](../character/) — so a room that needs to want things, take turns, and narrate becomes animate by saying so: ```yaml room: id: the-forge delegates_to: [room, character] # it is a place AND someone wants: { be_tended: 8, be_admired: 3 } buffs: - { name: "Well Banked", effect: { production_speed: +30 }, expires: { after: 12, unit: turns } } ``` Rooms already [`ADVERTISE`](../room/) in the same auction characters bid into, so an animate room is a small step rather than a special case. A forge that resents neglect is a *character who is a place*, and it holds its buffs itself. This is also why the host list above is not a type system. It is a list of things that turn out to want modifiers, and `host:` accepts any of them — plus whatever else a world invents. ### Restricting a buff to certain hosts Most buffs are host-agnostic and should say nothing. Where a buff genuinely only makes sense somewhere, it says so: ```yaml buff: name: "Haunted" hosts: [room, object] # a place or a thing, not a mood someone has buff: name: "Waterlogged" # nothing declared — anything can get wet effect: { weight: +1, flammable: false } ``` `hosts:` omitted means **any**; be liberal in what you accept ([postel](../postel/)). A world may also narrow it for a whole game, which is where a ruleset's own restrictions belong: ```yaml world: buff_policy: allowed_hosts: [character] # this game keeps effects on people on_violation: "refuse and explain" ``` ### Three things a host has to answer The structure is the same everywhere, but every host must have an answer to who narrates, when it ends, and how far it reaches: | Host | Who narrates its tick | Ends when | |---|---|---| | **character** | the character | death, scene end, dispel | | **room** | the room if it is animate; otherwise nobody, and it ticks silently | an explicit condition — see below | | **object** | the object if animate, else its holder | destruction, or leaving play | | **prototype** | nothing — a prototype does not tick; its instances do | the buff is removed from the prototype | | **relationship** | either party, or the narrator | either party leaves play, or dispel | | **group** | any member, or the narrator | per `on_member_leave:` — dissolve, shrink, or persist | | **ruleset** | the narrator | repeal, or supersession by a later rule | **Places and materials outlive everything.** A timer keyed to the host's own death never fires on a room, a material, or a ruleset. Those hosts need a real end condition — a turn count, an event, or a predicate: ```yaml expires: { after: 12, unit: turns } # good expires: { when: "ventilation restored" } # good expires: { with: host } # never fires on a room ``` **`radiates:` is how a buff reaches beyond its host.** Buffing a room and buffing everyone in it are different claims, and a buff that means both says so: ```yaml buff: name: "Sweltering" host: room/the-forge effect: { comfort: -10 } # the room's own property radiates: to: occupants effect: { energy: -1, mood: -2 } # everyone inside while: present # leaving ends it ``` The same field carries auras from a character, contagion from an object, and region effects from a place. `to:` accepts `occupants`, `holder`, `wearer`, `nearby: <radius>`, or a query. ### Relationship hosts — the matrix cell is the host Some effects belong to neither party. Cupid, in Don's SimProv playset for The Sims 1, makes Bob love Alice — and whether Alice loves Bob back is a *separate* value the player chooses to set or not. The effect is not on Bob and not on Alice. It is on the edge, and the edge has a direction. **The relationship matrix already is that host.** A character's [`relationships:`](../character/SKILL.md) map is keyed by target and holds an open mapping per target, so a pair-hosted buff needs no new storage — it goes in the cell: ```yaml # in bob.yml relationships: alice: feeling: "Can't stop thinking about her. This is new." daily: 45 lifetime: 30 buffs: - name: "Smitten" source: "object/cupid" effect: { romance: +80 } expires: { after: 40, unit: turns } tick: "he finds a reason to walk past her" ``` `host: relationship/{ from: bob, to: alice }` names that cell. The direction is not a field on the buff — it is *where the buff is stored*, which is why the asymmetry maintains itself. Bob's file says he is smitten; Alice's file is untouched, and stays untouched unless someone sets it. Why the cell, rather than two coordinated buffs on two characters: - **Nothing keeps two halves consistent.** A buff on Bob referencing Alice plus another on Alice referencing Bob gives two objects that expire, dispel, and get edited independently, with no invariant tying them. - **Dispel wants to name the edge.** "End whatever is between those two" is one operation on one host, not a search for buffs whose payload mentions the other party. - **Asymmetry is the normal case.** Unrequited feelings are the interesting ones, which is why the storage is per-direction in the first place. #### The matrix is YAML Jazz, so the dimensions are open The Sims 1 matrix holds a fixed numeric tuple per directed pair. MOOLLM's holds whatever the world needs, in whatever notation fits, with comments carrying meaning the way they do everywhere else. So a relationship buff is not confined to the dimensions someone declared in advance: ```yaml relationships: marieke: owes_her: "three coffees and an apology" # invented for this world, needs no schema buffs: - name: "Awkward Since the Thing" effect: eye_contact: -3 # not a stat anybody registered — the guard reads the prose guard: "while neither of them has mentioned it" expires: { when: "one of them mentions it" } ``` Two consequences worth naming. The `daily`/`lifetime` pair inherited from the Sims chunk format is **one convention among many**, not the schema — it stays for [beaming to and from The Sims](../character/CARD.yml), and a world that does not care about it simply omits it. And an effect can be *purely semantic*, with no numeric footprint at all, because the cell is prose-capable and the guard is compiled from English ([BUFF-IN-TIME-COMPILER.md](BUFF-IN-TIME-COMPILER.md)). #### Pairs of anything, including yourself The relationships map already points at characters, objects, and places — so pair-hosted buffs come along for free: ```yaml # a person and an object relationships: object/grandfathers-watch: buffs: - { name: "Can't Bear to Sell It", effect: { will_trade: false } } # a person and a place room/the-old-library: buffs: - { name: "Homesick For It", effect: { pull: +4 }, expires: { when: "she visits" } } # a person and themselves — `self:` is a relationship like any other self: buffs: - name: "Ashamed" effect: { confidence: -20 } expires: { when: "she tells someone what happened" } ``` That last one is the case a character-hosted buff models badly and a relationship-hosted one models exactly: shame is not a property of a person, it is a stance toward oneself, and it ends when the stance changes. The same shape covers grudges, trust, debts, jealousy, and disguises — a disguise's effect lives in the observer-wearer pair and belongs to neither ([`buffopedia/systems/equipment-inventory/`](buffopedia/systems/equipment-inventory/SYSTEM.yml)). In Korz terms this is a slot whose coordinates include both parties, which is why [`SELF-KORZ.md`](SELF-KORZ.md) wants the receiver dropped. ⚠ **Reciprocity is about existence, not values.** If A has a cell for B, B should have a cell for A — but the *contents* must be free to disagree, or Cupid is unimplementable. See the note in [`../coherence-engine/`](../coherence-engine/GLANCE.yml). ### Bulk operations over the matrix Cupid edits one directed cell. **Super Cupid** — make everyone love everyone, or hate, or go neutral — operates on a whole region of the matrix at once, and it needs three fields: ```yaml operation: scope: { among: everyone } # or: a lot, a family, a named set topology: complete_symmetric # who ends up pointing at whom — see below write: { romance: 100 } ``` **Neutral is a different kind of operation from love and hate,** and the difference is the base/effective split ([EFFECTIVE-VALUES.md](EFFECTIVE-VALUES.md)): | Layer written | Behavior | What "neutral" means there | |---|---|---| | **base** | permanent; prior values are gone | social amnesia — history destroyed, nobody remembers the feud | | **modifier** (a buff per cell) | temporary; originals return on expiry | a truce — everyone is civil for an hour, then remembers | That is one object with two wildly different characters, and the buff version is only possible *because* base values are preserved underneath. A party where everyone adores each other for an hour and then the grudges come back is the whole reason the split exists. ```yaml buff: name: "Universal Adoration" host: relationship/{ among: everyone } # applied per cell in scope effect: { romance: +100 } expires: { after: 60, unit: minutes } # base values return, feuds intact ``` **Topology is the interesting parameter,** because it turns one object into several: | Topology | Shape | What it makes | |---|---|---| | `complete_symmetric` | everyone ↔ everyone | a commune | | `complete_asymmetric` | random per direction | unrequited chaos | | `star_inbound` | everyone → one | a cult — adoration flows one way | | `star_outbound` | one → everyone | the doting leader | | `perfect_matching` | disjoint pairs | mass pairing, the guru assigning couples | | `clique_partition` | love inside, hate across | **factions — a schism generator** | `star_inbound` and `clique_partition` are worth calling out: one is how a cult's affection graph actually looks, and the other manufactures rival factions in a single gesture, which is otherwise hours of hand-editing. ### Group hosts — one buff over a set, not a mesh of pairs Some bonds are held by three or more parties at once: a marriage of three, a band, a crew, a conspiracy, a cult. The host is the **set**: ```yaml buff: name: "Married" host: group/{ members: [bob, alice, chandra] } effect: { household: shared, inheritance: equal } buff: name: "In On It" host: group/{ members: [marieke, henk] } # two is just the common case effect: { may_discuss: the-thing } guard: "only when no one else is present" ``` A group host is a *hyperedge*, and it is not reducible to the pairwise cells in the relationship matrix: - **The bond is one thing, not its pairs.** Three people in one marriage is a single commitment among three, not three couples. Storing it as pairs asserts something false about its structure. - **Dissolution is one operation.** Ending a group bond means removing one buff from one host. Pairwise, it is *n(n−1)/2* removals that can partly fail, leaving a marriage that half exists. - **Membership can change without the bond ending.** Members join and leave a crew that persists. A pair bond, by contrast, dies when either party leaves — which is correct for pairs and wrong for groups. So use a **relationship host** when the asymmetry matters (Bob's feelings for Alice, which Alice need not return) and a **group host** when the *togetherness* is the thing being modified. They coexist: three people can share one marriage while each holding a different private opinion of the others. Two questions a group-hosted buff must answer, because the pair case answered them implicitly: | Question | Options | |---|---| | A member leaves | dissolve · shrink · block the departure | | Membership is edited | new buff · amend in place (a Fluxx New Rule played onto the host) | ```yaml buff: name: "The Crew" host: group/{ members: [a, b, c] } on_member_leave: shrink # dissolve | shrink | block min_members: 2 # dissolve if it would drop below ``` Worked example — an officiant that summons a participant list and marries it, whoever and however many they are: [`life-events-playset.md`](../../../WillWrightShowForFood/designs/orchestrator-playsets/life-events-playset.md). Prior art, in Don's own code, is written up at [`buffopedia/systems/simprov/`](buffopedia/systems/simprov/SYSTEM.yml). What The Sims itself eventually did about relationship-hosted effects is in [`buffopedia/systems/sims-4/`](buffopedia/systems/sims-4/SYSTEM.yml). ### Prototype hosts inherit for free Attaching a buff to a prototype means everything delegating to it is affected, because that is what delegation does: ```yaml buff: name: "Cursed Silver" hosts: [prototype] attached_to: material/silver effect: { harms_wielder: true } # every object made of silver now harms its wielder, including ones made later ``` Most engines cannot do this at all; Dwarf Fortress is the notable exception, and MOOLLM gets it from the object model rather than as a feature. See [`buffopedia/registry.yml`](buffopedia/registry.yml) under the `syndrome` family. ## Structure ```yaml buff: name: "Caffeinated" source: "Espresso" effect: { energy: +2, focus: +1 } duration: 5 # simulation turns stacks: false ``` | Field | Purpose | |-------|---------| | `name` | Display name | | `source` | What granted this buff | | `effect` | Stat mods OR semantic prompt | | `duration` | How long it lasts | | `stacks` | Can multiple instances exist? | | `max_stacks` | If stacking, limit | | `decay` | How it ends (time, action, condition) | ## Buff Types ### Numeric Traditional stat modifiers: ```yaml buff: name: "Caffeinated" effect: { energy: +2, focus: +1 } duration: 5 ``` ### Semantic Arbitrary effect prompts interpreted by the LLM — not predefined stats, just vibes: - "feeling lucky" - "cats seem to like you today" - "slightly cursed" - "radiating calm energy" - "shadows feel watchful" **How it works:** ``` Buff: "cats seem to like you today" Action: PAT TERPIE LLM: Gives bonus, narrates extra warmth ``` ### Mixed Combine numeric and semantic: ```yaml buff: name: "Terpie's Blessing" effect: calm: +2 vibe: "cats trust you more" duration: "a while"
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen