Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions projects/Codex/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,43 @@

## Journals per character

Each character has its own journal in `plugins/Codex/characters/<character>.yml`. Codex opens it when the character's session starts and saves it when the session ends, whether by switching, quitting, death or shutdown. It also saves open journals every `player_data_save` seconds. If a journal cannot be written when its session ends, Codex logs an error, keeps it in memory and retries with every save; the character gets it back if it returns first. A new character starts with an empty journal, so it discovers places and lore again and earns their rewards again, such as the Player EXP for a point of interest.
Each character has its own journal in `plugins/Codex/characters/<character>.yml`. Codex opens it when the character's session starts and saves it when the session ends, whether by switching, quitting, death or shutdown. It also saves open journals every `player_data_save` seconds. If a journal cannot be written when its session ends, Codex logs an error, keeps it in memory and retries with every save; the character gets it back if it returns first. A new character starts with an empty journal; exploration its account already did arrives shortly after it starts playing (see "Shared exploration" below), while research it must find again, earning its rewards again.

Between characters, for example in the selection room, a player has no journal. Discoveries and rewards wait until a character is in play, and `/codex` answers `noCharacterInPlay` ("Choose a character to open the Codex.") instead of opening.

## Out-of-character reference

An entry the account found before journals were per character, or that any of its characters has found since (alive or dead), stays unlocked in `/codex`. Its click actions, such as the lore sub-pages on Main, still run, unless the entry sets `click_actions_cooldown`: that cooldown is kept on the character's own entry, so an entry with one runs its click actions only for a character that found it itself. Its hover text then names the living characters who can use it in character, the played character first: "Aldric and Mira can use this in character." If no living character found it itself, the line reads "No character can use this in character." When the played character has not found the entry itself, a further line says "Aldric knows this only out of character." These three lines are `knownInCharacterBy`, `knownInCharacterByNobody` and `knownOutOfCharacterOnly` in `messages.yml`, and Codex adds them to existing files along with `noCharacterInPlay`.
An entry the account found before journals were per character, or that any of its characters has found since (alive or dead), stays unlocked in `/codex`. Its click actions, such as the lore sub-pages on Main, still run, unless the entry sets `click_actions_cooldown`: that cooldown is kept on the character's own entry, so an entry with one runs its click actions only for a character that found it itself. Its hover text then names the living characters who can use it in character, the played character first: "Aldric and Mira can use this in character." If no living character found it itself, the line reads "No character can use this in character." When the played character has not found the entry itself, a further line says "Aldric knows this only out of character." A last line asks players to use common sense: "Use common sense: play Aldric as not knowing this until they learn it in character." With shared exploration, these lines matter mostly for research. They are `knownInCharacterBy`, `knownInCharacterByNobody`, `knownOutOfCharacterOnly` and `knownOutOfCharacterOnlyAdvice` (empty to hide it) in `messages.yml`, and Codex adds them to existing files along with `noCharacterInPlay` and `sharedDiscoveries`.

Codex reads the other journals when a session starts, since they cannot change while the account plays someone else, and again after `/codex resetplayer *`. Category progress, completion rewards and placeholders count only the played character's own discoveries.

A journal file records `owner` (the account UUID), `name` (the account name when last saved) and, for each category, whether it is completed and its discoveries as `id;date;millis`.

## Shared exploration

Exploration belongs to the account. About two seconds after a character's session starts, Codex copies into its journal every discovery that the account's other characters (alive or dead) and its journal from before characters made, except entries kept per character. A copy keeps the date it was first found. This happens on every session start, so it covers switching, a new character after a death, and accounts whose characters explored apart before the change: each gets everything any of them found.

`shared_discoveries` in `config.yml` sets it up; Codex adds this section to an existing file:

```yaml
shared_discoveries:
enabled: true
keep_per_character:
- research
- achievements:5_research
- achievements:10_research
- achievements:25_research
- achievements:all_research
- achievements:full_codex
rewards:
- from: "console_command: mmocore admin exp give %player% main "
to: "console_command: rpcharacter admin carryexp %player% "
```

`keep_per_character` lists categories and `category:discovery` entries that are not shared: research and the achievements that count research. They stay with whoever found them; the other characters see them out of character (see below) and earn them, Player EXP included, by researching them themselves.

A copied discovery runs only its reward lines (the discovery's own `rewards`, else its category's `per_discovery`) that start with a `rewards` `from`, as `to` plus the rest of the line. Its messages, titles and sounds are skipped; the player gets one `sharedDiscoveries` message instead ("3 Codex entries carried over from your other characters, with their Player EXP."). Player EXP goes through RPCharacters' `carryexp`, so a class at its level cap keeps it until the character can level further ([character controls](../RPCharacters/docs/character-controls.md)). A category the copies complete pays its `all_discoveries` lines the same way. The journal is saved before the rewards run, so a crash can lose a reward but never pay it twice. The server log records `Shared <n> Codex discoveries of account <account> with its character <character>`.

## Discoveries from before characters

Upstream Codex kept one journal per account in `plugins/Codex/players/<account>.yml`. As a one-off, at the start of an account's first character session after the change, Codex copies that journal into the journal of the account's most-played living character (else its most-played character), using RPCharacters' playtime at that moment. The choice is pinned in `plugins/TLibs/legacy-recipients/<account>.txt`, which ConditionalEvents reads too, so both give the account's old data to the same character and a retry never picks another. That character knows those entries in character; every other character starts with an empty journal and earns each discovery and its rewards again. If the most-played character already had a journal, the two merge: it keeps its own entries, and a category counts as completed if either side completed it.
Expand Down
20 changes: 19 additions & 1 deletion projects/ConditionalEvents/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ An event that keeps per-character data runs only while a character is in play, s

Keep anything a character learns or earns per character, such as Codex unlocks, since [Codex](../Codex/overview.md) keeps a journal per character. Use `data_scope: account` for supporter perks and other account rewards. The supporter EXP booster is `/tfmc booster` in TFMCCore and is per account.

## Shared one-time events

A per-character `one_time` event with `share_one_time: true` counts as done for every character of an account once one of them has done it. About two seconds after a character's session starts, it gets each such event another of the account's characters (alive or dead) has done and runs the event's `shared` action group instead of its usual actions, for example to carry the event's Player EXP:

```yaml
sf_title_enter_1:
type: custom
one_time: true
share_one_time: true
actions:
default:
- "console_command: mmocore admin exp give %player% main 40"
shared:
- "console_command: rpcharacter admin carryexp %player% 40"
```

The `shared` group has no event variables such as `%title_name%`. An event without a `shared` group is only marked done. If the character leaves before the group finishes, the mark is undone and shared again at its next session. The server log records `Shared <n> one-time events of account <account> with its character <character>`. Account-scoped events ignore the option; their data is the account's already.

## Stored data

Each account's file, `plugins/ConditionalEvents/players/<account>.yml`, keeps account events under `events` and per-character events under `characters.<character>.events`. As a one-off, on an account's first character session after the change, its old account-level entries for events that are now per character move to its most-played living character (else its most-played character), the same character [Codex](../Codex/overview.md) copies the old journal to, pinned in `plugins/TLibs/legacy-recipients/<account>.txt`. An event done once stays done, and the later cooldown start wins. The file records `seeded-to: <character>` so this happens once; if RPCharacters cannot list the account's characters yet, it waits for a later session. Every other character starts these events fresh. Account-scoped events keep their data on the account.
Expand All @@ -30,4 +48,4 @@ Player data stays in memory from an account's first login until the server resta

## Events on Main

Main has 90 events that keep data. The 87 Codex unlocks in `codex.yml` and `codex_research.yml` and the meditation cooldown in `r_lore.yml` are per character by default, so a second character can earn an achievement, duchy title or research entry its account already has. `act1_exp_catchup` and `act1_exp_catchup_retry` set `data_scope: account`, so the one-off catch-up from before characters had their own progress does not arm again for every character.
Main has 90 events that keep data. The 87 Codex unlocks in `codex.yml` and `codex_research.yml` and the meditation cooldown in `r_lore.yml` are per character by default, so a second character can earn an achievement, duchy title or research entry its account already has. `act1_exp_catchup` and `act1_exp_catchup_retry` set `data_scope: account`, so the one-off catch-up from before characters had their own progress does not arm again for every character. On Dev the 18 duchy title events (`sf_title_enter_*`) also set `share_one_time: true` with a `shared` group that carries their EXP, so a duchy is discovered once per account; Main does not have this yet.
9 changes: 9 additions & 0 deletions projects/RPCharacters/docs/character-bodies.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ Changes to experience queue a guarded checkpoint; quit and permanent death save
synchronously, with an additional periodic checkpoint. A sudden process or host
failure can lose changes since the latest completed progression checkpoint.

Professions are the exception: they belong to the account. When a character is
activated, each MMOCore profession is raised to the best level and experience
any of the account's characters, alive or dead, has reached, and its lifetime
profession points to the most any of them holds. Upgrades stay with the
character that bought them, so a character gets the points unspent. Levels only
go up, so accounts whose characters levelled apart before this get the best of
each. The log records `Raised the professions of character <id> of <account> to
its account's best`; the raise is saved with the checkpoint after activation.

The original account progression fields remain unchanged. On an account's first
migration, an immutable baseline records the existing character roster and
legacy progression. Those characters each receive one independent copy. Later
Expand Down
2 changes: 2 additions & 0 deletions projects/RPCharacters/docs/character-controls.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ Paid picks, and paid changes to a locked creation stage, are charged to the char

Staff with `class-selection.reset-permission` (`rpchar.class.reset`) can run `/rpcharacter admin resetclasses confirm` to give every character a fresh class window: class and subclass changes are free for the lock-time again, paid prices start over and the first subclass is free again. Online characters start now, offline ones when they next join. The reset count is kept in `data/class-resets.yml`.

`/rpcharacter admin carryexp <player> <amount>` gives class EXP carried over from the account's other characters; Codex and ConditionalEvents run it for shared discoveries and duchy titles. MMOCore throws away EXP at a class's level cap, so the class takes what it can hold and the rest waits on the character (`pools.carried-experience` in its progress file). It is added level by level once the character can level further: after a class or subclass pick, or at its next activation after a cap rise. Each level-up still claims its exp-table rewards, such as skill points and skill slots. The player is told once per batch how much is waiting, and how much was added when it is. Amounts are raw EXP, before MMOCore's EXP stat and boosters, like a discovery's own reward. While bodies are off it is plain MMOCore EXP.

With separate bodies disabled, the class level is shared by every character and
kept in the player file (`account-class-level`). Enabling bodies gives each
character its own saved class level. A class capped below the saved level, such
Expand Down
8 changes: 4 additions & 4 deletions projects/RPCharacters/docs/professions-system/SYSTEM.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# Professions

RPCharacters provides account-wide lifetime profession points and per-character upgrade loadouts. LuckPerms profession nodes follow the active character.
RPCharacters provides account-wide profession levels and lifetime points with per-character upgrade loadouts. LuckPerms profession nodes follow the active character.

## Data model

| Data | Owner | Storage |
|------|-------|---------|
| MMOCore profession XP/levels | Account (MMOCore) | MMOCore player data |
| MMOCore profession XP/levels | Account: each character's progress file, raised on activation to the best of the account's characters | `data/progress/<account>/characters/<char-id>.yml` (`mmo.professions`) |
| Trait profession XP % | Per character | `RPCharacter` / `AttributeData` |
| Lifetime points earned | Account | `PlayerData.accountProfessionPoints` — `Map<String, Integer>` |
| Lifetime points earned | Account: raised on activation to the most any character holds | `pools.profession-points` in the progress file (`PlayerData.accountProfessionPoints` while bodies are off) |
| Owned upgrades | Per character | `RPCharacter.professionUpgrades` — ordered upgrade id list |
| Free points | Derived | `lifetime[prof] − characterSpentOnProf` |
| LuckPerms `professions.*` | Active character | apply on `activate()`, strip on `deactivate()` |

**Example:** Account has 10 lifetime agriculturist points. Character A spent 7 on perks → 3 free. Character B (new) has 10 free and zero perks. Each character builds independently up to the account lifetime cap.
**Example:** Character A reached Herborist 11, so the account has 10 lifetime herborist points. A spent 7 on perks → 3 free. Character B (new, or after A's death) plays Herborist 11 with 10 free points and zero perks. Each character builds its upgrades independently up to the account lifetime cap; levels gained on any character reach the others when they are next played.

**Remove upgrade (no refund):** drop upgrade id from character list, strip LP nodes — lifetime unchanged, free increases.

Expand Down