来源信息
- 仓库
- SimHacker/moollm
- 最近来源活动
- 2026年9月16日 12:55
- 检测到的 SKILL.md 语言
- 英语
- 星标
- 60
- 分支
- 5
安装方式
默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。
检查来源文件
决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。
文件资源管理器
26 个文件正在显示 SKILL.md
SKILL.md
来源说明 · 只读预览- 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"
在 GitHub 查看这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看