feat: mothership character store

This commit is contained in:
hypercross 2026-03-23 01:13:19 +08:00
parent c5e091da08
commit 8f6a6b96e2
3 changed files with 476 additions and 0 deletions

View File

@ -0,0 +1,334 @@
import { createStore } from "solid-js/store";
import type {
CharacterStats,
CharacterSaves,
InventoryItem,
MothershipCharacter,
MothershipStoreState,
VitalValue,
StressValue,
} from "./types";
/**
*
*/
export function createDefaultCharacter(): MothershipCharacter {
return {
stats: {
strength: 50,
agility: 50,
combat: 50,
intellect: 50,
},
saves: {
fear: 50,
sanity: 50,
body: 50,
},
skills: [],
inventory: [],
status: [],
hp: { current: 0, max: 0 },
stress: { current: 0, min: 0 },
wounds: { current: 0, max: 0 },
};
}
/**
* 0-99
*/
function clampStat(value: number): number {
return Math.max(0, Math.min(99, value));
}
/**
*
*/
function clampValue(value: number, min: number, max: number): number {
return Math.max(min, Math.min(max, value));
}
const [store, setStore] = createStore<MothershipStoreState>({
character: createDefaultCharacter(),
});
/**
*
*/
export function setStat<K extends keyof CharacterStats>(
key: K,
value: number
): void {
setStore("character", "stats", key, clampStat(value));
}
/**
*
*/
export function setStats(stats: Partial<CharacterStats>): void {
setStore("character", "stats", (prev) => ({
...prev,
...Object.fromEntries(
Object.entries(stats).map(([key, value]) => [key, clampStat(value)])
),
}));
}
/**
*
*/
export function setSave<K extends keyof CharacterSaves>(
key: K,
value: number
): void {
setStore("character", "saves", key, clampStat(value));
}
/**
*
*/
export function setSaves(saves: Partial<CharacterSaves>): void {
setStore("character", "saves", (prev) => ({
...prev,
...Object.fromEntries(
Object.entries(saves).map(([key, value]) => [key, clampStat(value)])
),
}));
}
/**
*
*/
export function addSkill(skill: string): void {
setStore("character", "skills", (prev) => [...prev, skill]);
}
/**
*
*/
export function removeSkill(skill: string): void {
setStore("character", "skills", (prev) =>
prev.filter((s) => s !== skill)
);
}
/**
*
*/
export function setSkills(skills: string[]): void {
setStore("character", "skills", [...skills]);
}
/**
*
*/
export function addInventoryItem(
name: string,
quantity: number = 1,
attributes?: Record<string, any>
): void {
setStore("character", "inventory", (prev) => [
...prev,
{ name, quantity: Math.max(1, quantity), attributes },
]);
}
/**
*
*/
export function removeInventoryItem(name: string): void {
setStore("character", "inventory", (prev) =>
prev.filter((item) => item.name !== name)
);
}
/**
*
*/
export function updateInventoryItemQuantity(
name: string,
quantity: number
): void {
setStore("character", "inventory", (prev) =>
prev.map((item) =>
item.name === name
? { ...item, quantity: Math.max(0, quantity) }
: item
).filter((item) => item.quantity > 0)
);
}
/**
*
*/
export function updateInventoryItemAttributes(
name: string,
attributes: Record<string, any>
): void {
setStore("character", "inventory", (prev) =>
prev.map((item) =>
item.name === name
? { ...item, attributes }
: item
)
);
}
/**
*
*/
export function addStatus(
name: string,
quantity: number = 1,
attributes?: Record<string, any>
): void {
setStore("character", "status", (prev) => [
...prev,
{ name, quantity: Math.max(1, quantity), attributes },
]);
}
/**
*
*/
export function removeStatus(name: string): void {
setStore("character", "status", (prev) =>
prev.filter((item) => item.name !== name)
);
}
/**
* HP
*/
export function setHP(value: Partial<VitalValue>): void {
setStore("character", "hp", (prev) => ({
...prev,
...value,
current: value.current !== undefined
? clampValue(value.current, 0, value.max ?? prev.max)
: prev.current,
max: value.max !== undefined
? Math.max(0, value.max)
: prev.max,
}));
}
/**
*
*/
export function takeDamage(amount: number): void {
setStore("character", "hp", (prev) => ({
...prev,
current: Math.max(0, prev.current - amount),
}));
}
/**
* HP
*/
export function healHP(amount: number): void {
setStore("character", "hp", (prev) => ({
...prev,
current: Math.min(prev.max, prev.current + amount),
}));
}
/**
*
*/
export function setStress(value: Partial<StressValue>): void {
setStore("character", "stress", (prev) => ({
...prev,
...value,
current: value.current !== undefined
? clampValue(value.current, value.min ?? prev.min, Number.MAX_SAFE_INTEGER)
: prev.current,
min: value.min !== undefined ? Math.max(0, value.min) : prev.min,
}));
}
/**
*
*/
export function addStress(amount: number): void {
setStore("character", "stress", (prev) => ({
...prev,
current: prev.current + amount,
}));
}
/**
*
*/
export function reduceStress(amount: number): void {
setStore("character", "stress", (prev) => ({
...prev,
current: Math.max(prev.min, prev.current - amount),
}));
}
/**
*
*/
export function setWounds(value: Partial<VitalValue>): void {
setStore("character", "wounds", (prev) => ({
...prev,
...value,
current: value.current !== undefined
? clampValue(value.current, 0, value.max ?? prev.max)
: prev.current,
max: value.max !== undefined
? Math.max(0, value.max)
: prev.max,
}));
}
/**
*
*/
export function addWound(amount: number = 1): void {
setStore("character", "wounds", (prev) => ({
...prev,
current: Math.min(prev.max, prev.current + amount),
}));
}
/**
*
*/
export function healWound(amount: number = 1): void {
setStore("character", "wounds", (prev) => ({
...prev,
current: Math.max(0, prev.current - amount),
}));
}
/**
*
*/
export function resetCharacter(): void {
setStore("character", createDefaultCharacter());
}
/**
*
*/
export function setCharacter(character: MothershipCharacter): void {
setStore("character", character);
}
/**
*
*/
export function getCharacter(): MothershipCharacter {
return store.character;
}
/**
* Store SolidJS
*/
export function useCharacterStore() {
return store;
}
export { store, setStore };

View File

@ -0,0 +1,66 @@
/**
* Mothership TRPG Store
*
* @module journals/mothership
*/
export type {
CharacterStats,
CharacterSaves,
InventoryItem,
VitalValue,
StressValue,
MothershipCharacter,
MothershipStoreState,
} from "./types";
export {
// Store 核心
store,
setStore,
useCharacterStore,
getCharacter,
// 初始化
createDefaultCharacter,
resetCharacter,
setCharacter,
// Stats 操作
setStat,
setStats,
// Saves 操作
setSave,
setSaves,
// Skills 操作
addSkill,
removeSkill,
setSkills,
// Inventory 操作
addInventoryItem,
removeInventoryItem,
updateInventoryItemQuantity,
updateInventoryItemAttributes,
// Status 操作
addStatus,
removeStatus,
// HP 操作
setHP,
takeDamage,
healHP,
// Stress 操作
setStress,
addStress,
reduceStress,
// Wounds 操作
setWounds,
addWound,
healWound,
} from "./characterStore";

View File

@ -0,0 +1,76 @@
/**
* Mothership TRPG
* 0-99
*/
export interface CharacterStats {
/** 力量 */
strength: number;
/** 敏捷 */
agility: number;
/** 战斗 */
combat: number;
/** 智力 */
intellect: number;
}
/**
* Mothership TRPG
* 0-99
*/
export interface CharacterSaves {
/** 恐惧豁免 */
fear: number;
/** 理智豁免 */
sanity: number;
/** 体质豁免 */
body: number;
}
/**
*
*/
export interface InventoryItem {
/** 物品名称 */
name: string;
/** 数量 */
quantity: number;
/** 自定义属性,如护甲值 { ap: 3 } */
attributes?: Record<string, any>;
}
/**
* /
*/
export interface VitalValue {
current: number;
max: number;
}
/**
*
*/
export interface StressValue {
current: number;
min: number;
}
/**
* Mothership
*/
export interface MothershipCharacter {
stats: CharacterStats;
saves: CharacterSaves;
skills: string[];
inventory: InventoryItem[];
status: InventoryItem[];
hp: VitalValue;
stress: StressValue;
wounds: VitalValue;
}
/**
* Store
*/
export type MothershipStoreState = {
character: MothershipCharacter;
};