Initial scaffold: Nakama backend, Godot client, content schema
Sets up the Step 1 (Project Foundation) and Step 2 (Content Schemas) scaffold from the implementation sequence. Includes Docker Compose stack, Nakama TypeScript module stubs, PostgreSQL migration, Godot 4 project shell with scene stubs and NakamaClient singleton, YAML content schemas for all entity types, and example data for 3 characters, 2 jobs, and all 7 core resources. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
fb00954954
37 changed files with 2730 additions and 0 deletions
131
nakama/modules/village.ts
Normal file
131
nakama/modules/village.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// village.ts — village state, assignment loop, and manual micro-management
|
||||
//
|
||||
// This is the highest-priority system to validate in the PoC. The core design
|
||||
// question: does the manual work loop feel meaningfully better than passive
|
||||
// waiting, without becoming mandatory misery?
|
||||
//
|
||||
// Passive resource generation rules:
|
||||
// - Each job slot generates resources while characters are assigned.
|
||||
// - Output is calculated at collection time (lazy evaluation), not on a tick.
|
||||
// - Passive output caps after MAX_PASSIVE_HOURS hours to discourage long
|
||||
// check-in skips from breaking the economy.
|
||||
// - A character's profession suitability bonus multiplies their slot output.
|
||||
//
|
||||
// Manual micro-management rules:
|
||||
// - The player can spend MANUAL_WORK_COST_MANA to trigger a burst gain on
|
||||
// one scarce resource (default: wood — defined as the first bottleneck).
|
||||
// - Burst gain is 3–5x the passive rate per action but subject to a daily cap.
|
||||
// - Each successive manual action within the same day gives diminishing returns
|
||||
// (50% yield per extra action after the third).
|
||||
// - Server tracks manual_work_count_today and manual_work_reset_at per player.
|
||||
//
|
||||
// These constants are PoC defaults. Production values must come from content.
|
||||
|
||||
const MAX_PASSIVE_HOURS = 8; // cap passive accumulation at 8 hours
|
||||
const MANUAL_WORK_COST_MANA = 5;
|
||||
const MANUAL_WORK_DAILY_FULL_CAP = 3; // first 3 actions give full burst yield
|
||||
const MANUAL_WORK_BURST_MULTIPLIER = 4; // 4x passive rate per burst action
|
||||
const MANUAL_WORK_DIMINISHING_FACTOR = 0.5; // 50% per extra action after cap
|
||||
|
||||
export function rpcGetVillageState(
|
||||
ctx: nkruntime.Context,
|
||||
logger: nkruntime.Logger,
|
||||
nk: nkruntime.Nakama,
|
||||
_payload: string
|
||||
): string {
|
||||
// TODO PR 8: read village state, assignment slots, and pending passive yield
|
||||
logger.info(`village/state stub called by ${ctx.userId}`);
|
||||
|
||||
return JSON.stringify({
|
||||
slots: [],
|
||||
pending_resources: {},
|
||||
message: "Village state is stubbed. Implement in PR 8.",
|
||||
});
|
||||
}
|
||||
|
||||
export function rpcAssignCharacter(
|
||||
ctx: nkruntime.Context,
|
||||
logger: nkruntime.Logger,
|
||||
nk: nkruntime.Nakama,
|
||||
payload: string
|
||||
): string {
|
||||
// TODO PR 8: validate character ownership, job compatibility, write assignment
|
||||
const req = JSON.parse(payload || "{}");
|
||||
logger.info(`village/assign stub: char=${req.character_id} job=${req.job_id} user=${ctx.userId}`);
|
||||
|
||||
return JSON.stringify({
|
||||
ok: false,
|
||||
error: "not_implemented",
|
||||
message: "Assignment is stubbed. Implement in PR 8.",
|
||||
});
|
||||
}
|
||||
|
||||
export function rpcUnassignCharacter(
|
||||
ctx: nkruntime.Context,
|
||||
logger: nkruntime.Logger,
|
||||
nk: nkruntime.Nakama,
|
||||
payload: string
|
||||
): string {
|
||||
const req = JSON.parse(payload || "{}");
|
||||
logger.info(`village/unassign stub: char=${req.character_id} user=${ctx.userId}`);
|
||||
|
||||
return JSON.stringify({
|
||||
ok: false,
|
||||
error: "not_implemented",
|
||||
message: "Unassignment is stubbed. Implement in PR 8.",
|
||||
});
|
||||
}
|
||||
|
||||
export function rpcCollectPassiveResources(
|
||||
ctx: nkruntime.Context,
|
||||
logger: nkruntime.Logger,
|
||||
nk: nkruntime.Nakama,
|
||||
_payload: string
|
||||
): string {
|
||||
// TODO PR 8:
|
||||
// 1. Read assignment state and last_collected_at timestamp.
|
||||
// 2. Calculate elapsed time (capped at MAX_PASSIVE_HOURS).
|
||||
// 3. Sum per-slot output adjusted by suitability bonuses.
|
||||
// 4. Grant resources via wallet update with ledger entry.
|
||||
// 5. Write new last_collected_at.
|
||||
logger.info(`village/collect stub called by ${ctx.userId}`);
|
||||
|
||||
return JSON.stringify({
|
||||
ok: false,
|
||||
granted: {},
|
||||
error: "not_implemented",
|
||||
message: "Passive collection is stubbed. Implement in PR 8.",
|
||||
});
|
||||
}
|
||||
|
||||
export function rpcManualWork(
|
||||
ctx: nkruntime.Context,
|
||||
logger: nkruntime.Logger,
|
||||
nk: nkruntime.Nakama,
|
||||
payload: string
|
||||
): string {
|
||||
// TODO PR 5 (priority — this is the core PoC differentiator):
|
||||
// 1. Read player manual_work_count_today; reset if a new day.
|
||||
// 2. Check mana balance >= MANUAL_WORK_COST_MANA.
|
||||
// 3. Deduct mana.
|
||||
// 4. Calculate burst yield:
|
||||
// - If count < MANUAL_WORK_DAILY_FULL_CAP: full burst
|
||||
// - Else: apply MANUAL_WORK_DIMINISHING_FACTOR per extra action
|
||||
// 5. Grant scarce resource (wood by default in PoC).
|
||||
// 6. Increment manual_work_count_today.
|
||||
// 7. Return new balance and yield so the client can show the comparison.
|
||||
const req = JSON.parse(payload || "{}");
|
||||
const targetResource: string = req.resource ?? "wood";
|
||||
logger.info(`village/manual_work stub: resource=${targetResource} user=${ctx.userId}`);
|
||||
|
||||
return JSON.stringify({
|
||||
ok: false,
|
||||
error: "not_implemented",
|
||||
resource: targetResource,
|
||||
burst_yield: 0,
|
||||
passive_equivalent: 0,
|
||||
daily_actions_used: 0,
|
||||
daily_actions_cap: MANUAL_WORK_DAILY_FULL_CAP,
|
||||
message: "Manual work is stubbed. This is the highest-priority PoC feature — implement in PR 5.",
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue