Planning

Character & Skills Reference

Full design detail for the Character/Stats/Skills plugins (see the plan entry) — every skill node, both equipment systems, the UI notification spec, and the currency design. Extracted from a standalone reference document that isn't tracked anywhere else; this page is now the source of truth for it.

Not yet built. Nothing on this page reflects live code — it's implementation-ready design, API-verified against Rising World's Plugin API and definitions.db at the time it was written, not re-verified since. Confirm hooks are still current before implementing.

Progression model

A single character-level XP track (from combat, gathering, and quests) grants both stat points and skill points. Skills don't level independently through use — all growth comes from spending points earned at the character level.

ProsOne XP system to balance instead of two competing ones (character level vs. per-skill practice leveling).
ConsPlayers who love a specific skill can't grind that skill directly to get better at it — progress is gated by overall character level regardless of playstyle.
Ranked node mechanic

Each node holds multiple ranks. A node needs rank ≥1 in the node before it to unlock, and its own rank can never exceed that prior node's current rank — forcing balanced investment up the chain rather than rushing straight to the strongest node.

ProsPrevents a min-max strategy of ignoring early nodes entirely to rush a powerful late node.
ConsCan feel restrictive to players who want to specialize hard into one specific effect early.
Professions as labels

There is no separate profession system or profession point pool. "Carpenter," "miner," etc. are just descriptive labels for whatever skill combination a player has actually invested in.

ProsZero extra system to design, balance, or sync — professions fall out of the skill data for free.
ConsNo profession-specific perks or titles exist unless built as a separate display-only feature layered on top later.
API-verified roster. Every node across all 12 skills below traces to a real, confirmed method — either a Plugin API event setter, an Inventory/Item/Npc/Player object mutator, or a genuine field in definitions.db. Speed and cost-reduction nodes without any supporting hook were cut rather than kept as aspirational placeholders. Animal taming/riding was cut entirely (saddle systems are the separate saddle & saddle bag system below) — the roster is smaller than the original ambition (Crafting skills lost half their nodes) since the API doesn't expose everything that was hoped for.

Extraction

Mining

Break stone and pull ore from the world.

Swing power
PlayerHitTerrainEvent → setDamage()

Reads the event's current value and boosts it by the rank's percentage before handing the event back.

ProsDirect and precise — the bonus is guaranteed every hit, no RNG involved.
ConsPurely additive on top of whatever tool the player is holding; needs testing against steel/drill tools so it doesn't trivialize late-game mining.
Ore yield
Inventory → addItem() on hit

Rolls a chance sized to the rank; on success, grants the bonus item straight to the player's inventory.

ProsDoesn't touch the base drop table at all — purely additive, so it can't break existing yield balance.
ConsRNG-based; a player can hit a bad streak and feel like the skill "isn't working" even at high rank.
Prospecting
regions_ores depth-weight table + addItem()

Reads the real per-depth ore-weight table (definitions.db) for the player's region, then rolls a bonus toward whichever ore is genuinely rarer at that depth — not a guessed curve.

ProsGrounded in the game's actual ore rarity per depth/region, so the bonus always makes sense in context.
ConsNeeds the region lookup to run efficiently on every hit — worth caching per-chunk rather than querying per swing.
Endurance
Player → setStamina() (custom drain)

Deducts a small stamina cost on every qualifying hit — a cost the plugin introduces itself — then reduces that deduction by the rank's percentage.

ProsGives the node a real cost to offset instead of a bonus with nothing behind it.
ConsThe cost is entirely invented by the plugin — needs careful tuning so it reads as a meaningful tradeoff, not an arbitrary tax.

Felling

Cut down trees for lumber and fruit.

Axe power
PlayerHitVegetationEvent → setDamage()

Reads the event's current value and boosts it by the rank's percentage before handing the event back.

ProsSame reliable, guaranteed mechanism as Mining's Swing power.
ConsShares the same late-game-trivialization risk once players have steel axes.
Timber yield
PlayerDestroyVegetationEvent + addItem()

Rolls a chance sized to the rank; on success, grants the bonus item straight to the player's inventory.

ProsAdditive bonus log, no base yield changes needed.
ConsRNG-based — same bad-streak perception risk as Ore yield.
Orchard bonus
plants.type='Tree' AND pickupitem IS NOT NULL + addItem()

Checks the target's type ID against a maintained category list, then rolls a bonus-item chance if it matches.

ProsBacked by a real structural field (pickupitem), not a maintained name list — new fruit trees added in future updates are caught automatically.
ConsOnly distinguishes "has a pickup fruit" — doesn't rank fruits by rarity, so all fruit trees get equal treatment regardless of how rare the fruit is.
Sapling recovery
PlayerDestroyVegetationEvent + addItem()

Rolls a chance sized to the rank; on success, grants the bonus item straight to the player's inventory.

ProsEncourages sustainable chopping without forcing it — purely a bonus.
ConsRNG-based; players replanting for a tree farm may want this guaranteed rather than chance-based.
Endurance
Player → setStamina() (custom drain)

Deducts a small stamina cost on every qualifying hit — a cost the plugin introduces itself — then reduces that deduction by the rank's percentage.

ProsConsistent with Mining's Endurance — one shared drain system, easy to reason about.
ConsSame invented-cost tuning risk as Mining's version.

Gathering

Forage wild plants, berries, and loose materials.

Quick hands
PlayerHitVegetationEvent → setDamage()

Reads the event's current value and boosts it by the rank's percentage before handing the event back.

ProsReliable and guaranteed, same mechanism family as Felling/Mining.
ConsFraming as "damage" feels odd for foraging berries — might read strangely to players even though the underlying mechanism is identical to chopping.
Bountiful find
PlayerDestroyVegetationEvent + addItem()

Rolls a chance sized to the rank; on success, grants the bonus item straight to the player's inventory.

ProsSimple, additive, safe.
ConsRNG-based, same bad-streak risk as other yield nodes.
Keen eye
plants.type='Plant' + addItem()

Checks the target's type ID against a maintained category list, then rolls a bonus-item chance if it matches.

ProsBacked by the real plants.type='Plant' field — no maintenance needed as content is added.
Cons"Plant" type is broad (covers all non-tree, non-crop vegetation) — may need finer filtering if some wild plants should be excluded.

Production

Plant farming

Grow and harvest cultivated crops.

Bumper crop
PlayerDestroyVegetationEvent + addItem()

Rolls a chance sized to the rank; on success, grants the bonus item straight to the player's inventory.

ProsAdditive, doesn't interfere with base farming balance.
ConsRNG-based.
Quick growth
PlantGrowthEvent → setNextGrowthTime()

Directly shortens the plant's next-growth-stage timer by the rank's percentage.

ProsDirectly and reliably shortens wait time — no RNG, the benefit is always felt.
ConsNeeds testing against very short base growth times so the reduction isn't imperceptible at low crop-growth durations.
Crop quality
addItem() quality-tagged variant

Rolls a chance sized to the rank; on success, grants the bonus item straight to the player's inventory.

ProsLets high-rank farmers produce visibly better goods, good for the currency/trading economy.
ConsRequires a "quality-tagged" item variant to actually exist and be meaningfully different — depends on downstream systems (cooking, trading) actually valuing it.
Land efficiency
Inventory → removeItem() skipped

Rolls a chance sized to the rank; on success, bumps the resulting item stack instead of consuming the input.

ProsEffectively a resource discount without needing a cost-reduction setter the API doesn't provide.
ConsOnly applies at the moment of planting — can't partially refund.

Animal husbandry

Breed, raise, and collect from livestock.

npcs_breeding shows identical pregnancy timing (1800s duration, 20000s cooldown) across every animal — the real variation is litter size (pig/shewolf up to 3, most animals locked to 1) and which animals have milk fields populated at all (only cow, in this dataset).
Product yield
PlayerNpcInteractionEvent + addItem()

Cow's real milkduration (8000s) and milkcooldown (600s) define the base collection window — this node grants a bonus item on top of the normal yield, scaled by rank.

ProsGrounded in the real collection window rather than an assumed one.
ConsOnly cow has populated milk fields in this dataset — the node may need per-animal conditional logic rather than a blanket rule if other animals get milk data later.
Breeding success
NpcTransformEvent (Interaction)

For animals whose real maxchilds exceeds 1 (pig, shewolf, arcticshewolf), biases the litter roll toward that maximum rather than the minimum, scaled by rank.

ProsTargets exactly the animals where it matters — no wasted effect on animals locked to a single offspring.
ConsHas zero effect on most animals (locked at 1 child), so the node reads as "sometimes does nothing" depending on which animal a player breeds.
Growth speed
Npc → setAge()

Directly advances the animal's age value, scaled by rank, to speed up maturation.

ProsSimple, direct, and guaranteed — no roll involved.
ConsJumping age forward abruptly might skip visual growth stages the player would otherwise see happen naturally.
Taming ease
PlayerNpcInteractionEvent → setCancelled()

Rolls a chance sized to the rank; on failure only, cancels the event to represent a missed attempt.

ProsCheap to implement — reuses an event the game already fires.
ConsA cancelled interaction may look like a bug without clear failure feedback (sound/message on miss).

Fishing

Cast a line and reel in a catch.

Real spawnchance ranges from 5% (blobfish) to 100% (common fish like herring/perch/plaice) — tier alone doesn't encode rarity in this dataset, spawnchance does.
Faster bite
PlayerFishingStartEvent → setWaitTime()

Reads the event's current value and boosts it by the rank's percentage before handing the event back.

ProsDirectly reduces dead time, always felt, no RNG.
ConsToo strong a reduction removes the game's fishing pacing entirely — worth capping the minimum wait time.
Catch quality
setFishingDefinitionID()

Rerolls toward a low-spawnchance entry (e.g. blobfish at 5%, tuna at 20%) instead of the default weighted pool, scaled by rank.

ProsGrounded in real spawnchance values — the bonus is meaningfully rare, not an arbitrary reroll.
ConsAt high rank, could make rare fish common enough to undercut their value in the currency/trading system.
Bait efficiency
Inventory → addItem() refund

Applies only to species with requiresbait=1 (salmon, tuna, koi, carp, shrimp, blobfish) — rolls a refund chance on those catches specifically, scaled by rank.

ProsCorrectly scoped to only the fish that actually consume bait — no wasted logic on bait-free catches.
ConsLow visibility — a refunded bait item might go unnoticed by the player compared to a more visible bonus.
Deep water bonus
getWaterDepth()

Compares against real minwaterdepth thresholds (e.g. salmon 40, tuna 50) — grants a bonus only once the player is actually deep enough for that species to spawn.

ProsRewards genuinely difficult positioning (deep water), tied to real spawn thresholds.
ConsRequires accurate water-depth detection at the exact moment of casting, not just player position.

Combat

Hunting

Track and take down wild animals.

Tracker's strike
PlayerHitNpcEvent (wildlife) → setDamage()

Reads the event's current value and boosts it by the rank's percentage before handing the event back.

ProsReliable, guaranteed bonus against wildlife specifically.
ConsNeeds a solid wildlife-vs-hostile NPC filter, or it bleeds into Combat's territory.
Hide yield
NpcDeathEvent → getStorageID() + addItem()

Rolls a chance sized to the rank; on success, grants the bonus item straight to the player's inventory.

ProsAdding to the corpse's own storage (rather than the player's inventory directly) keeps the normal looting flow intact.
ConsRNG-based, and depends on the corpse storage still existing when the bonus roll happens.
Trophy chance
addItem() rare-material roll

Rolls a chance sized to the rank; on success, grants the bonus item straight to the player's inventory.

ProsGives a real reason to hunt dangerous animals (bears, tigers) specifically.
ConsNeeds a maintained "tough animal" list, since the API has no native difficulty/tier field for NPCs.
Endurance
Player → setStamina() (native sprint drain)

Reduces the native stamina cost of sprinting while tracking, scaled by rank.

ProsOffsets a cost that genuinely already exists in the base game — no invented system needed, unlike Mining/Felling's Endurance.
ConsOverlaps with any other skill that also touches sprint stamina — worth checking Combat/Taming for conflicts if those are ever added back.

Combat

Fight hostile NPCs and other players.

Real weapon baselines from items/weapondata: huntingknife 70 dmg / 800 durability, combatknife 70 dmg / 1000 durability, javelinspear 100 dmg / 180 durability.
Melee power
PlayerHitNpcEvent / PlayerHitPlayerEvent → setDamage()

Boosts the event's damage value by the rank's percentage — e.g. rank 5 on a 70-damage huntingknife adds roughly 8.75 damage.

ProsScales naturally with whatever weapon the player is using — real baseline numbers confirm the bonus stays proportional.
ConsPvP-relevant — needs careful balancing since it directly affects player-vs-player outcomes, not just PvE.
Marksmanship
PlayerWeaponFireEvent → setFireRotation()

Nudges the fired shot's rotation toward the target to reduce spread, scaled by rank; damage bonus applies at the eventual hit event.

ProsSplits cleanly into two real, confirmed hooks (rotation at fire-time, damage at hit-time) rather than one fragile mechanism.
ConsTwo separate events need to agree on the same player/rank lookup — more moving parts than a single-event node.
Toughness
PlayerDamageEvent → setDamage()

Reads the incoming damage value and reduces it by the rank's percentage before it's applied.

ProsDirectly confirmed setter, works identically regardless of damage source.
ConsStacking defensive bonuses across multiple systems (armor, this skill, potential future buffs) risks making a maxed player nearly unkillable — worth capping total reduction somewhere.

Crafting

Smithing

Smelt ore and forge tools and weapons.

Real recipe costs confirmed: axe/pickaxe = 2 stick + 1 iron ingot, requiring an anvil. Ore efficiency (cost reduction) was cut earlier since no live setter exists — this recipe data is reference only.
Ingot yield
ItemProcessEvent → setNewStack()

Rolls a chance sized to the rank; on success, bumps the resulting item stack instead of consuming the input.

ProsConfirmed directly on the actual smelt-completion event — not a workaround.
ConsOnly affects the smelt step — doesn't reduce the ore cost that went in, so it's a yield bonus, not a cost discount.
Craftsmanship
Item → setModifier() / setValue()

Applies a stat or durability modifier to the crafted item object, scaled by rank.

ProsApplies directly to the crafted item, so the benefit is visible and persistent on that object.
ConsExact meaning of setModifier() isn't documented — needs an in-game test to confirm what it actually changes before relying on it.

Cooking

Prepare ingredients into meals.

Meal quality
Item → setModifier() / setValue()

Applies a stat or durability modifier to the crafted item object, scaled by rank.

ProsSame direct item-level application as Smithing's Craftsmanship.
ConsSame setModifier() semantics caveat — verify in-game before finalizing.
Ingredient efficiency
PlayerProcessItemEvent → setNewStack()

Rolls a chance sized to the rank; on success, bumps the resulting item stack instead of consuming the input.

ProsA genuine resource-cost discount, confirmed directly on the processing event.
ConsOnly works at the moment of processing — can't apply retroactively.

Construction

Build with wood or stone, and specialize in either.

basestrength varies by shape, not material: block/cylinder=100, ramp=60, plank=50, quartercylinder=30. Material comes from the live texture (blockids.type), not a static per-shape field.
Sturdy build
ConstructionElement → setStrength()

Boosts the placed element's real basestrength (which already varies 30–100 by shape) by the rank's percentage.

ProsScales proportionally with the shape's own real baseline, so thin shapes (quartercylinder) and bulky ones (block) both get sensible bonuses.
ConsApplies equally to every material — doesn't reward material choice, that's what the two branches below are for.
Woodworking
getTexture() → blockids.type='wood' + setStrength()

Checks the target's type ID against a maintained category list, then rolls a bonus-item chance if it matches.

ProsCorrected to use the real texture-based material field — an earlier version of this logic used a broken join and would never have fired.
ConsReads the live texture at placement time, so re-texturing an existing structure after the fact won't retroactively apply or remove the bonus.
Stonework
getTexture() → blockids.type='stone' + setStrength()

Checks the target's type ID against a maintained category list, then rolls a bonus-item chance if it matches.

ProsSame corrected mechanism as Woodworking.
ConsSame re-texturing caveat as Woodworking.

Tailoring

Craft clothing and armor.

Fine stitching
Item → setModifier() / setValue()

Applies a stat or durability modifier to the crafted item object, scaled by rank.

ProsSame direct item-level mechanism as other crafting quality nodes.
ConsSame setModifier() verification caveat.
Durability
Item → setDurability()

Applies a stat or durability modifier to the crafted item object, scaled by rank.

ProssetDurability() is unambiguous — unlike setModifier(), there's no semantic uncertainty about what this changes.
ConsNeeds a sensible cap — unlimited durability scaling could make crafted gear never need replacing.

Type lookup table

Confirmed against the real definitions.db schema. Correction from an earlier pass: constructions.relateditem is a fixed placeholder on every row, not a real link — construction material actually comes from the live element's texture ID, cross-referenced against blockids.type.

Sync flow
  1. Home server calls Definitions.getAllPlantDefinitions() and getAllConstructionDefinitions() at startup. No other server ever runs this scan.
  2. Each definition's real type/material/pickupitem fields are read directly — no name matching required.
  3. Home pushes the finished typeID → category table to the backend over the existing persistent socket.
  4. Backend caches it (write-through, same pattern as player data) and serves it to every server — including Home — on connect.
Home-only discovery assumes every linked server runs the same plugin and content set. If a spoke server ever installs unique custom plants or construction types Home doesn't have, they won't appear in the table.
CategoryReal field checkUsed by
felling.lumberplants.type = 'Tree' AND pickupitem IS NULLFelling — Axe power, Timber yield, Sapling recovery
felling.fruitplants.type = 'Tree' AND pickupitem IS NOT NULLFelling — Orchard bonus
gathering.wildplants.type = 'Plant'Gathering — Keen eye
farming.cropplants.type = 'Crop'Plant farming — Bumper crop, Crop quality, Land efficiency
construction.woodConstructionElement.getTexture() → blockids.type = 'wood'Construction — Woodworking
construction.stoneConstructionElement.getTexture() → blockids.type = 'stone'Construction — Stonework

Unmatched plant or construction types fall into no category and receive no bonus — worth logging a startup warning for anything that doesn't match, so new game content doesn't silently miss out on skill support.

Backpack system

A modular, stat-scalable storage container, synced across every server.

A backpack cannot be respec'd (its slot count changed) while it contains items — it must be emptied first. This avoids ever needing to decide what happens to overflow items when capacity shrinks.
Storage container

World.createStorage() creates a plugin-owned container tagged StorageType.API — distinct from vanilla Chest/Vehicle/Npc storages. Player.showStorage(storageID) / hideStorage() open and close it exactly like a native chest.

ProsA genuine native storage object, not a UI-panel workaround — the game's own inventory-drag-and-drop, stacking, and slot logic all work for free.
ConsThe storage ID is server-local — it does not carry across a transfer, only its contents do (see Cross-server sync below).
Carrying capacity stat

Storage.setSlotCount() is a plain integer, so a "Carrying capacity" stat — paid for with character-level stat points, not skill points — can drive it directly: base 9 slots + 1 per point invested, recalculated whenever the storage is (re)created.

ProsReuses the stat-point system that already exists for character leveling — no new progression currency needed.
ConsNeeds the respec-lock rule above to stay coherent, since shrinking a live storage with items in high slot indices has no defined native behavior.
Cross-server sync

On disconnect/transfer, serialize the storage's contents (Storage.getItems()) into the player data package as its own module, not folded into the core inventory blob — keeps it toggleable and extensible later. On the destination server, call World.createStorage() fresh and replay the item list back in via addItemToSlot().

This is the same force-sync-on-transfer pattern already used for inventory — a second, independent payload riding alongside it, not a new sync mechanism.

Saddle & saddle bag system

Custom-crafted saddles and saddle bags with hide-socketed stats, synced and persistent through trades.

Item has no generic setAttribute()/getAttribute() like Npc, Player, ConstructionElement, and Plant do — stat storage has to be encoded into Item.setValue() instead, and that field's native meaning (if any) needs a quick in-game confirmation before relying on it.
Custom slot encoding

Each socketed hide is stored as a small ID packed into Item.setValue(): 6 bits per slot supports up to 63 hide types. Saddles use 4 slots (24 bits total — health, speed, whistle distance, whistle cooldown); saddle bags use 2 slots (12 bits — both extra capacity). The actual bonus amounts are never stored on the item at all.

ProsOne native, already-synced Item field carries the full socket configuration — no per-item backend record to maintain.
ConssetValue() may already carry a native meaning (e.g. vendor sell price) that this would collide with — needs an in-game test before committing.
Backend-configurable hide stats

The bonus each hide grants per slot lives in backend config, not on the item — synced to every server on connect, the same handshake pattern already used for currency, skill trees, and the type lookup table. Adjusting a hide's power is a config edit, not a code change.

Hide (item, variant)Health slotSpeed slotWhistle distanceWhistle cooldown
penguinhide+50 HP+3%+10m−2s
foxpelt+50 HP+5%+10m−2s
sheeppelt+50 HP+5%+10m−2s
goatpelt+50 HP+5%+10m−2s
pighide+50 HP+4%+10m−2s
cowhide+100 HP+8%+20m−4s
deerpelt+100 HP+8%+20m−4s
boarhide+150 HP+7%+25m−5s
wolfpelt+100 HP+15%+40m−8s
deerredpelt+150 HP+9%+30m−6s
lionpelt+150 HP+9%+30m−6s
moosepelt+150 HP+9%+30m−6s
bearpelt+150 HP+7%+25m−5s
polarbearpelt+150 HP+7%+25m−5s
horsehide (v0–3)+100 HP+15%+40m−8s
zebrahide+150 HP+10%+30m−6s
rhinohide+250 HP+10%+35m−7s
elephanthide+350 HP+3%+25m−5s
horsehide (v4, unicorn)+500 HP+10%+60m−15s
Saddle bag capacity table

Saddle bags use the same hide roster but a different rationale: bonus scales with hide surface area/animal size rather than speed, since carrying capacity is about material, not agility. Same backend-config sync pattern as the saddle table above.

Hide (item, variant)Extra storage slots
penguinhide / foxpelt / sheeppelt / goatpelt / pighide+1
cowhide / deerpelt+2
boarhide / wolfpelt / deerredpelt / lionpelt / moosepelt / bearpelt / polarbearpelt / horsehide (v0–3) / zebrahide+3
rhinohide+4
elephanthide+5
horsehide (v4, unicorn)+6
Unicorn variant

Unicorn is not a separate item — it's horsehide with itemtexture/variant=4, dropped by horse-variant NPCs 14 and 15 in npcs_variants (800 HP source, 1.0% and 0.1% spawn chance respectively — the rarest, toughest entry in the whole dataset). Item.getVariant() already distinguishes it natively from common horsehide.

ProsNo extra tracking needed to know a hide came from a unicorn — the game's own variant field does it for free.
ConsDeliberately kept slightly behind Wolf/Horse/Zebra on raw speed percentage so common hides stay relevant even after a unicorn drop — worth revisiting if playtesting shows nobody bothers with the common speed hides once unicorn exists.
Whistle-to-mount teleport

Npc.setPosition() is confirmed directly — whistling reads the player's current position and calls setPosition() on the mounted Npc. The cooldown itself isn't an API concept: plain plugin-side timestamp tracking, compared against now plus whatever the socketed cooldown-reduction stat subtracts.

Cross-server sync

Since the socket configuration lives inside Item.setValue(), it rides along automatically with whatever serialization already happens for inventory/equipped items in the player data package — no new sync mechanism, same as the backpack.

ProsStats survive a sale or trade for free, since they travel with the item object itself, not a separate per-player record.
ConsIf the item is ever destroyed by something outside normal inventory flow (e.g. a bug that clears an item without going through the usual path), the encoded stats are gone with it — there's no backend backup of an individual item's configuration.

Toast notification system

One shared, modular popup function every other system calls into. First concrete spec for the parked "UI system" feature idea.

No transition/animation system exists anywhere in the UI toolkit — confirmed by checking the Style class and the full ui package for animate/transition/tween methods and finding none. Fade effects would need manual per-tick opacity stepping; instant dismiss (as decided) avoids that entirely.
Shared function signature

One function — showToast(player, text, backgroundColor, borderColor, duration) — callable from any system. Skill rank-ups, currency changes, backpack-respec-blocked messages, and saddle stat notifications all call the same function instead of each building its own popup.

ProsA single implementation to maintain and style consistently — no drift between how different systems present feedback.
ConsEvery caller has to agree on the same visual language (box + text, no per-system custom layout) — not the right tool for anything more complex than a short message.
Independent, always-visible UI

Toasts are their own top-level UIElement, added directly via player.addUIElement() — not nested under the Skills/Currency/Map page-switching root. This makes them always visible: they render regardless of which page is open, or even if the whole page UI is hidden entirely.

This is the one real architectural decision this system forces: two separate UI roots per player, not one — the page root and the always-visible toast root, each managed independently.

Stack behavior

Capped at 4 visible toasts. A 5th incoming toast immediately dismisses the oldest (instant removal, no fade) to make room, then the new one appears. Dismiss is likewise instant on timeout — no animation.

ProsZero animation cost, simplest possible implementation, and a bounded UI footprint no matter how many notifications fire in a short window.
ConsFast-firing notifications (e.g. rapid skill rank-ups) could feel abrupt with no fade cushioning — worth watching in practice, easy to revisit later since dismiss behavior is isolated to one code path.
Dismiss timing

Each toast stores its own creation timestamp. A shared tick (via UpdateEvent, checked periodically rather than every frame to avoid overhead) walks the active toast list per player and instantly removes anything past its duration.

Currency system

Backend-defined, multiple currencies, physical exchange markets in each city.

Currency definition

Backend config defines each currency's value type (int/double/float), icon, and is-tradable flag — synced to every server plugin on connect.

Base currency is Gold Coin; more currencies can be added later purely through config, no code changes required.

Exchange market

Physical location in each main city; leaning toward dynamic (supply/demand) exchange rates, mechanics still undecided.

ConsDynamic rates need a real design pass before implementation — currently a direction, not a spec.

Remaining build order

The original phased plan had 11 phases (0–10), sequenced by dependency. Phases 0, 1, 2, 5, and 6 covered backend skeleton, single-plugin socket connection, single-server player data sync, multi-server topology, and Home-only systems (weather/time/chat/routing) — all effectively satisfied already by the real framework rework, TransferManager's grid-transfer system, and WeatherChangeListener/TimeSyncManager, though built independently rather than by following this exact plan. What's left, in dependency order:

One skill, fully working (originally Phase 3)

Pick Mining — the most-verified skill with the cleanest hooks. Build the ranked-node data model and the unlock-gate/rank-ceiling enforcement server-side. Wire PlayerHitTerrainEvent to Swing power and Ore yield. Skip Prospecting and Endurance initially — they carry extra dependencies (region data, a custom stamina system).

Done when: a player can spend a point, see Swing power's damage bonus actually apply in-game, and the rank persists through a disconnect/reconnect.

Currency (originally Phase 4)

Balances just live inside the player data package, so this is mostly data-model work: add currency fields, a basic admin/debug command to grant currency, confirm it round-trips through sync like everything else in the package.

Done when: currency balance survives a disconnect/reconnect cycle.

Type lookup table (originally Phase 7)

Add the Definitions.getAllPlantDefinitions()/getAllConstructionDefinitions() scan on Home, build the typeID → category table, push it through the existing config-handshake pattern.

Done when: a spoke server logs the received category table on connect and it matches what Home actually scanned.

Remaining skills, one at a time (originally Phase 8)

With the pattern proven on Mining, the rest go faster: Felling and Gathering reuse the lookup table immediately. Then Plant Farming, Hunting, Combat, Fishing, Husbandry, and finally the thinner Crafting skills (Smithing, Cooking, Construction, Tailoring).

Extraction and combat skills only need player/hit events already built for Mining; Construction specifically needs the lookup table's wood/stone split.

Cities & portals (originally Phase 9)

Define the in-world Area per city, wire PlayerEnterAreaEvent to the per-player discovery flag, and connect discovered-portal teleportation into the existing transfer flow — it's a trigger, not a new system. See the plan entry.

Polish (originally Phase 10)

Exchange market rate mechanics, cache eviction tuning under real load (see TODO), TLS if moving beyond local testing, an admin re-scan command for the lookup table.