0.1.37 天人感应·因果织锦:功德系统+飞升试炼+世界奇观+MOD扩展口
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/** WorldSim —— 世界自进化引擎(game/engine/sim/WorldSim.ts) */
|
||||
import { World } from '../runtime/World'
|
||||
import { WorldSimState, NpcDynamics, WORLDSIM, ERA_CONF, makeWorldSimState, NpcStance, REGION_TIDE, eraDuraOf, regionTideOf, calamityNames, calamityFamilyOf, calamityEffectOf, CalamityName, POOL_BASE, worldNum } from './worldsim-data'
|
||||
import { WorldSimState, NpcDynamics, WORLDSIM, ERA_CONF, makeWorldSimState, NpcStance, REGION_TIDE, eraDuraOf, regionTideOf, calamityNames, calamityFamilyOf, calamityEffectOf, CalamityName, POOL_BASE, worldNum, WONDER_CHANCE, WONDER_NAMES, WONDER_DESCS, WONDER_DURATION, WorldWonderKind, WorldWonder, allWonderKinds, wonderDefOf } from './worldsim-data'
|
||||
import { pack } from '../../data/registry'
|
||||
import { ITEMS } from '../../data/items'
|
||||
import { npcById, getNpcDefs, registerNpcDef, unregisterNpcDef } from '../../data/npcs'
|
||||
@@ -85,10 +85,108 @@ export class WorldSim {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- D-0.8 功德因果年首回馈(0.1.37) ----
|
||||
if (this.w.state.month === 1) {
|
||||
const fam = this.w.state.family
|
||||
// karma 年首向中性衰减(0.1.37:衰减率可经 worldNum('karmaDecayRate') 覆写)
|
||||
const decayRate = worldNum('karmaDecayRate')
|
||||
if (fam.karma !== 0) {
|
||||
const decay = Math.round(fam.karma * decayRate * 10) / 10
|
||||
fam.karma = Math.round((fam.karma - decay) * 10) / 10
|
||||
if (Math.abs(fam.karma) < 0.1) fam.karma = 0
|
||||
}
|
||||
// 因果回馈:高 karma 年首广播福泽
|
||||
const karmaHigh = worldNum('karmaHighThreshold')
|
||||
if (fam.karma > karmaHigh && !fam.flag['karmaBlessed-' + this.w.state.year]) {
|
||||
fam.flag['karmaBlessed-' + this.w.state.year] = true
|
||||
this.w.log('good', `积善之家必有余庆——功德深厚(${Math.round(fam.karma)}),天地福泽暗佑。`)
|
||||
}
|
||||
// 因果回馈:低 karma 年首天谴预警
|
||||
const karmaLow = worldNum('karmaLowThreshold')
|
||||
if (fam.karma < karmaLow && !fam.flag['karmaWarned-' + this.w.state.year]) {
|
||||
fam.flag['karmaWarned-' + this.w.state.year] = true
|
||||
this.w.log('bad', `因果缠身——功德亏缺(${Math.round(fam.karma)}),灾祸或至。`)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- D-0.9 世界奇观年首判定(0.1.37 种子派生——零 rng 消耗) ----
|
||||
if (this.w.state.month === 1) {
|
||||
// 上一年奇观到期清理
|
||||
if (s.wonder && s.wonder.duration && s.wonder.duration > 0) {
|
||||
s.wonder.elapsed = (s.wonder.elapsed ?? 0) + 12
|
||||
if (s.wonder.elapsed >= s.wonder.duration) {
|
||||
const wdefEnd = wonderDefOf(s.wonder.kind)
|
||||
const name = wdefEnd?.name ?? s.wonder.kind
|
||||
s.newsFeed.push({ year: this.w.state.year, month: 1, src: '天道', text: `${name}消散——天地复常。`, kind: 'annal' })
|
||||
this.w.log('info', `【天下】${name}消散,天地复常。`)
|
||||
s.wonder = undefined
|
||||
}
|
||||
}
|
||||
// 年首种子派生判定(不消耗 rng 主序列)
|
||||
if (!s.wonder) {
|
||||
let sh = 0
|
||||
for (let i = 0; i < this.w.state.seed.length; i++) sh = (sh * 31 + this.w.state.seed.charCodeAt(i)) >>> 0
|
||||
const wonderRoll = ((sh ^ (this.w.state.year * 16777619) ^ (s.npcSuccessions * 2654435761)) >>> 0) % 1000 / 1000
|
||||
if (wonderRoll < worldNum('wonderChance')) {
|
||||
// 0.1.37 P5:奇观类型从聚合池选取(内置 + MOD 扩展——零 rng 消耗)
|
||||
const allKinds = allWonderKinds()
|
||||
const kindIdx = ((sh + this.w.state.year * 40503) >>> 0) % allKinds.length
|
||||
const wdef = allKinds[kindIdx]!
|
||||
const kind = wdef.kind as WorldWonderKind
|
||||
s.wonder = {
|
||||
kind,
|
||||
year: this.w.state.year,
|
||||
duration: wdef.duration,
|
||||
elapsed: 0
|
||||
}
|
||||
const name = wdef.name
|
||||
const desc = wdef.desc
|
||||
s.newsFeed.push({ year: this.w.state.year, month: 1, src: '天机', text: `${name}——${desc}`, kind: 'annal' })
|
||||
this.w.log('good', `【天下异变】${name}:${desc}`)
|
||||
this.w.emitFx('pulse', `wonder:${kind}`)
|
||||
// 一次性奇观效果(0.1.37 P5:通过 wonderDefOf 识别扩展奇观的 oneshot 类型)
|
||||
const wdef2 = wonderDefOf(kind)
|
||||
if (wdef2?.oneshot === 'bless') {
|
||||
// 天降祥瑞:灾年消弭
|
||||
if (s.calamity) {
|
||||
s.calamity = undefined
|
||||
s.calamityLeft = 0
|
||||
this.w.log('good', '祥瑞降世,灾厄消弭——灾云散尽。')
|
||||
}
|
||||
// 声望 +5
|
||||
this.w.state.family.reputation += 5
|
||||
} else if (wdef2?.oneshot === 'loot') {
|
||||
// 古遗迹/宝物型奇观:获得随机资源
|
||||
const loot = ['lingcao', 'lingkuang', 'beastcore', 'lingyu'][((sh + this.w.state.year * 7919) >>> 0) % 4]
|
||||
const qty = 10 + ((sh + this.w.state.year * 13) >>> 0) % 15
|
||||
this.w.state.family.inventory[loot] = (this.w.state.family.inventory[loot] ?? 0) + qty
|
||||
const lootName = { lingcao: '灵草', lingkuang: '灵矿', beastcore: '兽核', lingyu: '灵玉' }[loot] ?? loot
|
||||
this.w.log('good', `${name}中得${lootName}×${qty}——前世遗珍。`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 0.1.37 奇观持续效果(月度——通过 wonderDefOf 识别扩展奇观的 effect 类型) ----
|
||||
if (s.wonder) {
|
||||
const wdefCont = wonderDefOf(s.wonder.kind)
|
||||
if (wdefCont?.effect === 'tide_up') {
|
||||
// 灵气增益:tide 缓升(修炼加成)
|
||||
s.tide = clamp(s.tide + 0.003, WORLDSIM.tideMin - 0.05, WORLDSIM.tideMax + 0.2)
|
||||
} else if (wdefCont?.effect === 'tide_down') {
|
||||
// 灵气衰减:tide 缓降(修行衰减)
|
||||
s.tide = clamp(s.tide - 0.003, WORLDSIM.tideMin - 0.2, WORLDSIM.tideMax + 0.05)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- D. 灾年(年签一掷 + 持续渐退;B12) ----
|
||||
// 剩数递减
|
||||
if ((s.calamityLeft ?? 0) > 0) s.calamityLeft = (s.calamityLeft ?? 0) - 1
|
||||
const calamityP = worldNum('calamityChance') * ERA_CONF[s.era ?? 'pingshi'].calamityMult
|
||||
// 0.1.37 因果调制:karma 高于阈值灾年概率 -15%,低于阈值 +15%(阈值可经 worldNum 覆写)
|
||||
const kHigh = worldNum('karmaHighThreshold')
|
||||
const kLow = worldNum('karmaLowThreshold')
|
||||
const karmaCalamityMod = this.w.state.family.karma > kHigh ? 0.85 : this.w.state.family.karma < kLow ? 1.15 : 1
|
||||
const calamityP = worldNum('calamityChance') * ERA_CONF[s.era ?? 'pingshi'].calamityMult * karmaCalamityMod
|
||||
if (this.w.state.month === 1 && rng.chance(calamityP)) {
|
||||
const cl = rng.pick(calamityNames())
|
||||
s.calamity = cl
|
||||
@@ -720,13 +818,24 @@ function assessStance(w: World, id: string, dyn: NpcDynamics): NpcStance {
|
||||
const prosperity = dyn.prosperity ?? 50
|
||||
const power = npc?.power ?? 100
|
||||
const relation = npc?.relation ?? 0
|
||||
// 0.1.37 因果调制:玩家家族 karma 影响 NPC 姿态倾向(阈值可经 worldNum 覆写)
|
||||
const karma = w.state.family.karma ?? 0
|
||||
const kHighS = worldNum('karmaHighThreshold')
|
||||
const kLowS = worldNum('karmaLowThreshold')
|
||||
const karmaAllyMod = karma > kHighS ? 2 : karma < kLowS ? -1 : 0
|
||||
const karmaExpandMod = karma < kLowS ? 2 : karma > kHighS ? -1 : 0
|
||||
// 硬判据优先
|
||||
if (prosperity < 30 || power < 45) return 'endure'
|
||||
if (relation >= 50) return 'ally'
|
||||
if (era === 'luanshi' && power >= 120) return 'expand'
|
||||
if (era === 'mofa') return 'endure'
|
||||
// 软权重:乱世偏扩张、盛世偏守成、平世均衡
|
||||
const W: Record<NpcStance, number> = { guardian: 3, expand: era === 'luanshi' ? 6 : 2, endure: era === 'mofa' ? 5 : 2, ally: era === 'shengshi' || era === 'pingshi' ? 4 : 1 }
|
||||
// 软权重:乱世偏扩张、盛世偏守成、平世均衡 + karma 调制
|
||||
const W: Record<NpcStance, number> = {
|
||||
guardian: 3,
|
||||
expand: Math.max(0, (era === 'luanshi' ? 6 : 2) + karmaExpandMod),
|
||||
endure: era === 'mofa' ? 5 : 2,
|
||||
ally: Math.max(0, (era === 'shengshi' || era === 'pingshi' ? 4 : 1) + karmaAllyMod)
|
||||
}
|
||||
const pick = (Object.keys(W) as NpcStance[]).sort((a, b) => W[b] - W[a])
|
||||
// 用幂等 rng 抽取(先取最高权)+ 30% 软翻转
|
||||
const top = pick[0]!
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { World } from '../runtime/World'
|
||||
import { WONDER_NAMES } from './worldsim-data'
|
||||
|
||||
export function worldSimBrief(w: World): { tideLabel: string; priceLabel: string; latest: string } | null {
|
||||
export function worldSimBrief(w: World): { tideLabel: string; priceLabel: string; latest: string; wonderLabel?: string } | null {
|
||||
const ws = w.state.worldSim
|
||||
if (!ws) return null
|
||||
const tide = ws.tide ?? 0.5
|
||||
@@ -9,5 +10,8 @@ export function worldSimBrief(w: World): { tideLabel: string; priceLabel: string
|
||||
const ratio = (pool['lingcao'] ?? 600) / 600
|
||||
const priceLabel = ratio > 1.15 ? '物腾' : ratio < 0.85 ? '物贱' : '平稳'
|
||||
const latest = ws.newsFeed?.[ws.newsFeed.length - 1]?.text ?? ''
|
||||
return { tideLabel, priceLabel, latest }
|
||||
// 0.1.37 世界奇观标签
|
||||
const wonder = (ws as { wonder?: { kind: string } }).wonder
|
||||
const wonderLabel = wonder ? WONDER_NAMES[wonder.kind as keyof typeof WONDER_NAMES] : undefined
|
||||
return { tideLabel, priceLabel, latest, wonderLabel }
|
||||
}
|
||||
|
||||
@@ -64,6 +64,8 @@ export interface WorldSimState {
|
||||
worldAnnals?: { year: number; text: string }[]
|
||||
/** 世鉴(50/100 年宏观:开局 vs 现在/era 路径/最强家更替——独立数组零漂) */
|
||||
sagaAnnals?: { year: number; kind: 'quinquagenary' | 'centennial'; text: string }[]
|
||||
/** 0.1.37 世界奇观(当前活跃奇观;年首种子派生判定——零 rng 消耗) */
|
||||
wonder?: WorldWonder
|
||||
}
|
||||
|
||||
export function makeWorldSimState(): WorldSimState {
|
||||
@@ -225,11 +227,57 @@ export const WORLDSIM = {
|
||||
shortageLine: 0.7, // 断供警戒线:池/基准 < 此值起计连续月
|
||||
shortageMonths: 3,
|
||||
shortagePrice: 1.5, // 断供价乘
|
||||
newSecretChance: 0.04 // 年首新秘境现身概率(乱世 ×2)
|
||||
newSecretChance: 0.04, // 年首新秘境现身概率(乱世 ×2)
|
||||
// —— 0.1.37 因果/奇观 ——
|
||||
wonderChance: 0.08, // 世界奇观年首现身概率(种子派生——零 rng 消耗)
|
||||
karmaDecayRate: 0.1, // 功德年首衰减率(向中性靠拢——善行需持续)
|
||||
karmaHighThreshold: 30, // 功德深厚阈值(触发灾年 -15%/渡劫 +5%)
|
||||
karmaLowThreshold: -20 // 功德亏缺阈值(触发灾年 +15%/渡劫 -5%)
|
||||
}
|
||||
|
||||
export type CalamityName = string
|
||||
|
||||
// ==================== 0.1.37 世界奇观 ====================
|
||||
|
||||
/** 世界奇观类型(年首种子派生判定——零 rng 消耗) */
|
||||
export type WorldWonderKind = 'tide_surge' | 'ancient_ruin' | 'heaven_bless' | 'qi_wane' | string
|
||||
|
||||
export interface WorldWonder {
|
||||
kind: WorldWonderKind
|
||||
year: number
|
||||
/** 持续月数(灵气涌潮/灵脉枯竭有持续效果;古遗迹/天降祥瑞一次性) */
|
||||
duration?: number
|
||||
/** 已过月数 */
|
||||
elapsed?: number
|
||||
}
|
||||
|
||||
/** 世界奇观年首现身概率(聚合读口——MOD 可经 worldNum('wonderChance') 覆写) */
|
||||
export const WONDER_CHANCE = 0.08
|
||||
|
||||
/** 奇观名称映射 */
|
||||
export const WONDER_NAMES: Record<WorldWonderKind, string> = {
|
||||
tide_surge: '灵气涌潮',
|
||||
ancient_ruin: '古遗迹现身',
|
||||
heaven_bless: '天降祥瑞',
|
||||
qi_wane: '灵脉枯竭'
|
||||
}
|
||||
|
||||
/** 奇观描述 */
|
||||
export const WONDER_DESCS: Record<WorldWonderKind, string> = {
|
||||
tide_surge: '天地灵气如潮涌至,万灵共鸣——修炼事半功倍。',
|
||||
ancient_ruin: '远古修士洞府现世——其中或藏功法丹材之珍。',
|
||||
heaven_bless: '祥瑞降世——风调雨顺,灾厄消弭。',
|
||||
qi_wane: '灵脉逆行——天地灵气骤衰,修行维艰。'
|
||||
}
|
||||
|
||||
/** 奇观持续月数(灵气涌潮/灵脉枯竭持续 12 月;古遗迹/天降祥瑞一次性) */
|
||||
export const WONDER_DURATION: Record<WorldWonderKind, number> = {
|
||||
tide_surge: 12,
|
||||
ancient_ruin: 0,
|
||||
heaven_bless: 0,
|
||||
qi_wane: 12
|
||||
}
|
||||
|
||||
/** 默认灾因集(聚合读口:默认+MOD/插件扩展——默认表和不变 → 基准世界指纹不漂) */
|
||||
export const DEFAULT_CALAMITIES = ['旱灾', '涝灾', '蝗灾', '疫病', '兽潮', '寒潮'] as const
|
||||
|
||||
@@ -292,3 +340,61 @@ export function calamityEffectOf(name: string): Partial<Record<string, number>>
|
||||
export function calamityDescOf(name: string): string {
|
||||
return EXTENDED_BRIEF[name] ?? `${name}——灵植减产,市价将行。`
|
||||
}
|
||||
|
||||
// ==================== 0.1.37 奇观类型扩展注册表(MOD 口) ====================
|
||||
|
||||
/** 扩展奇观类型定义(MOD 可注入自定义奇观——零 rng 消耗,种子派生选取) */
|
||||
export interface ExtendedWonderDef {
|
||||
/** 奇观类型 id(唯一——与内置不冲突) */
|
||||
kind: string
|
||||
/** 中文名称 */
|
||||
name: string
|
||||
/** 描述文本 */
|
||||
desc: string
|
||||
/** 持续月数(0 = 一次性) */
|
||||
duration: number
|
||||
/** 持续效果类型:tide_up = 灵气增益 / tide_down = 灵气衰减 / none = 无持续效果 */
|
||||
effect: 'tide_up' | 'tide_down' | 'none'
|
||||
/** 一次性效果:bless = 灾年消弭+声望 / loot = 资源获取 / none = 无 */
|
||||
oneshot: 'bless' | 'loot' | 'none'
|
||||
}
|
||||
|
||||
const EXTENDED_WONDERS: ExtendedWonderDef[] = []
|
||||
|
||||
/** 内置奇观定义(供聚合读口统一访问) */
|
||||
const BUILTIN_WONDERS: ExtendedWonderDef[] = [
|
||||
{ kind: 'tide_surge', name: '灵气涌潮', desc: '天地灵气如潮涌至,万灵共鸣——修炼事半功倍。', duration: 12, effect: 'tide_up', oneshot: 'none' },
|
||||
{ kind: 'ancient_ruin', name: '古遗迹现身', desc: '远古修士洞府现世——其中或藏功法丹材之珍。', duration: 0, effect: 'none', oneshot: 'loot' },
|
||||
{ kind: 'heaven_bless', name: '天降祥瑞', desc: '祥瑞降世——风调雨顺,灾厄消弭。', duration: 0, effect: 'none', oneshot: 'bless' },
|
||||
{ kind: 'qi_wane', name: '灵脉枯竭', desc: '灵脉逆行——天地灵气骤衰,修行维艰。', duration: 12, effect: 'tide_down', oneshot: 'none' }
|
||||
]
|
||||
|
||||
/** MOD/插件注册奇观类型(kind 唯一——与内置+已注册不冲突) */
|
||||
export function addWonderType(def: Omit<ExtendedWonderDef, 'name' | 'desc' | 'duration' | 'effect' | 'oneshot'> & Partial<Omit<ExtendedWonderDef, 'kind'>>): boolean {
|
||||
if (!def.kind || BUILTIN_WONDERS.some((w) => w.kind === def.kind) || EXTENDED_WONDERS.some((w) => w.kind === def.kind)) return false
|
||||
EXTENDED_WONDERS.push({
|
||||
kind: def.kind,
|
||||
name: def.name ?? '异变',
|
||||
desc: def.desc ?? '天地异变,灵机暗涌。',
|
||||
duration: def.duration ?? 0,
|
||||
effect: def.effect ?? 'none',
|
||||
oneshot: def.oneshot ?? 'none'
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
/** 卸载时按 kind 摘除扩展奇观 */
|
||||
export function removeWonderType(kind: string): void {
|
||||
const idx = EXTENDED_WONDERS.findIndex((w) => w.kind === kind)
|
||||
if (idx >= 0) EXTENDED_WONDERS.splice(idx, 1)
|
||||
}
|
||||
|
||||
/** 聚合读口:全部奇观类型(内置 + 扩展) */
|
||||
export function allWonderKinds(): ExtendedWonderDef[] {
|
||||
return [...BUILTIN_WONDERS, ...EXTENDED_WONDERS]
|
||||
}
|
||||
|
||||
/** 聚合读口:按 kind 查奇观定义 */
|
||||
export function wonderDefOf(kind: string): ExtendedWonderDef | undefined {
|
||||
return BUILTIN_WONDERS.find((w) => w.kind === kind) ?? EXTENDED_WONDERS.find((w) => w.kind === kind)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user