npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

steamsheep-ts-game-engine

v3.2.0

Published

通用游戏引擎框架 - 基于 TypeScript 和 Zustand 构建的文字冒险/RPG 游戏引擎

Readme

SteamSheep TypeScript Game Engine - 完整 API 参考

一个功能强大、类型安全的文字冒险/RPG 游戏引擎框架。

📚 目录


🚀 快速开始

npm install steamsheep-ts-game-engine
import { createGameEngineStore } from 'steamsheep-ts-game-engine';

// 定义游戏类型
type Stats = 'hp' | 'mp' | 'gold';
type Items = 'sword' | 'potion';
type Flags = 'quest_done';

// 创建 Store
const useGameStore = createGameEngineStore(initialState, 'my-game');

📋 核心 API 列表

1. 类型定义 (core/types.ts)

核心接口

  • GameState<S, I, F, X> - 游戏状态
  • ActionDef<S, I, F, X> - 动作定义
  • EffectDef<S, I, F, X> - 效果定义
  • RequirementDef<S, I, F, X> - 需求定义
  • LocationDef<S, I, F, X> - 地点定义
  • RequirementCheckResult - 需求检查结果
  • FlagsBatchOperation<F> - 批量标记操作
  • GameStoreActions<S, I, F, X> - Store 操作方法
  • SnapshotInfo<S, I, F, X> - 快照信息

2. Store API (state/store.ts)

状态操作

  • updateStat(stat, delta) - 更新单个属性
  • setStats(stats) - 批量更新属性
  • setExtra(updater) - 更新扩展数据
  • addItem(item) - 添加物品
  • removeItem(item) - 移除物品
  • setFlag(flag, value) - 设置标记

系统操作

  • addLog(text, type?) - 添加日志(持久化)
  • showToast(text, type?) - 显示飘字(瞬时)
  • showModal(text, type?) - 显示弹窗(瞬时)
  • advanceTime(amount?) - 推进时间
  • teleport(locationId) - 传送
  • reset() - 重置游戏

快照操作

  • saveSnapshot(description?) - 保存匿名快照
  • saveNamedSnapshot(name, description?) - 保存命名快照
  • undo() - 撤销到上一个状态
  • restoreSnapshot(name) - 恢复到命名快照
  • deleteSnapshot(name) - 删除命名快照
  • listSnapshots() - 列出所有快照
  • hasSnapshot(name) - 检查快照是否存在

3. 效果系统 (EffectDef)

静态效果

  • statsChange - 数值属性变更(支持函数)
  • itemsAdd - 添加物品
  • itemsRemove - 移除物品
  • flagsSet - 设置标记(支持函数)
  • flagsBatch - 批量标记操作(支持函数)
  • teleport - 传送
  • timeAdvance - 时间推进(支持函数)
  • onTimeAdvance - 时间推进钩子
  • triggerEvent - 触发自定义事件

动态效果

  • conditionalEffects(state) - 条件性效果
  • custom(draft, state) - 修改 extra 数据
  • customFull(draft, originalState) - 修改完整状态

后置钩子

  • afterEffects(state, originalState, actions) - 后置效果钩子

4. 需求系统 (RequirementDef)

  • stats - 数值属性需求
  • hasItems - 必须拥有的物品
  • noItems - 不能拥有的物品
  • hasFlags - 必须为 true 的标记
  • noFlags - 必须为 false 的标记
  • custom(state) - 自定义需求(支持返回失败原因)

5. 查询系统 (systems/query.ts)

  • QuerySystem.checkRequirements(state, reqs) - 检查需求(boolean)
  • QuerySystem.checkRequirementsWithReason(state, reqs) - 检查需求(带原因)
  • QuerySystem.canAfford(state, costs) - 检查成本

6. 流程系统 (systems/flow.ts)

  • FlowSystem.executeAction(store, action) - 执行动作

7. 事件系统 (systems/events.ts)

  • gameEvents.on(event, handler) - 监听事件
  • gameEvents.emit(event, data) - 发射事件
  • gameEvents.off(event, handler) - 取消监听

预定义事件

  • EngineEvents.STAT_CHANGE - 属性变化
  • EngineEvents.ITEM_ADD - 物品添加
  • EngineEvents.ITEM_REMOVE - 物品移除
  • EngineEvents.FLAG_CHANGE - 标记变化
  • EngineEvents.ACTION_EXECUTED - 动作执行
  • EngineEvents.NOTIFICATION - 通知
  • EngineEvents.CUSTOM - 自定义事件

8. 历史管理 (state/history.ts)

  • history.push(state, description?) - 保存匿名快照
  • history.pushNamed(state, name, description?) - 保存命名快照
  • history.pop() - 撤销
  • history.restoreNamed(name) - 恢复命名快照
  • history.deleteNamed(name) - 删除命名快照
  • history.hasNamed(name) - 检查快照是否存在
  • history.listSnapshots() - 列出所有快照
  • history.listNamedSnapshots() - 列出命名快照
  • history.peek() - 查看最近快照
  • history.clear(includeNamed?) - 清空快照
  • history.size - 匿名快照数量
  • history.namedSize - 命名快照数量
  • history.totalSize - 总快照数量

📖 详细用法

1. 动态 Flag 支持

effects: {
  // 动态生成 flag 名称
  flagsSet: (state) => {
    const day = state.world.day;
    return {
      [`event_day_${day}`]: true,
      [`gazed_stars_day_${day}`]: true
    };
  }
}

2. 完整状态访问 (customFull)

effects: {
  customFull: (draft, original) => {
    // 可以修改所有状态
    draft.stats.sanity -= 10;
    draft.flags.some_flag = true;
    draft.inventory.push('new_item');
    draft.extra.customData = 'value';
  }
}

3. 条件性效果

effects: {
  conditionalEffects: (state) => {
    if (state.flags.some_condition) {
      return {
        statsChange: { sanity: -10 },
        flagsSet: { flag_a: true }
      };
    } else {
      return {
        statsChange: { sanity: -5 },
        flagsSet: { flag_b: true }
      };
    }
  }
}

4. 职责分离 (afterEffects + resultText 双参数)

{
  effects: {
    statsChange: { sanity: -10 }
  },
  
  // 副作用在这里执行
  afterEffects: (state, original, actions) => {
    if (state.stats.sanity < 20) {
      actions.setFlag('going_mad', true);
      actions.showToast('理智危险!', 'warn');
    }
  },
  
  // 文本生成 - 可以比较前后状态
  resultText: (state, original) => {
    const sanityLoss = original.stats.sanity - state.stats.sanity;
    return sanityLoss > 15
      ? `你失去了 ${sanityLoss} 点理智,感到崩溃...`
      : '仪式完成了。';
  }
}

5. 动态 StatsChange

effects: {
  statsChange: (state) => {
    const maxAP = state.stats.max_action_point || 4;
    return {
      action_point: maxAP - state.stats.action_point
    };
  }
}

6. 批量 Flag 操作

effects: {
  // 清除所有 daily_ 开头的 flags
  flagsBatch: {
    clear: /^daily_/,
    set: { new_day: true }
  }
}

// 或使用函数
effects: {
  flagsBatch: (state) => ({
    clear: Object.keys(state.flags).filter(k => k.startsWith('daily_')),
    set: { new_day: true }
  })
}

7. 时间推进副作用

effects: {
  timeAdvance: 1,
  
  onTimeAdvance: (currentDay, previousDay, state, actions) => {
    // 恢复每日资源
    const maxAP = state.stats.max_action_point || 4;
    actions.setStats({ 
      action_point: maxAP 
    });
    
    // 清除每日标记
    Object.keys(state.flags)
      .filter(f => f.startsWith('daily_'))
      .forEach(f => actions.setFlag(f as F, false));
  }
}

8. 灵活的 Requirements

requirements: {
  custom: (state) => {
    if (!state.inventory.includes('key')) {
      return { 
        passed: false, 
        reason: "需要钥匙才能打开" 
      };
    }
    
    if (state.stats.sanity < 20) {
      return { 
        passed: false, 
        reason: "理智太低,无法集中精神" 
      };
    }
    
    return { passed: true };
  }
}

9. 效果执行顺序

effects: {
  // 【阶段 1:静态效果】按以下顺序执行
  statsChange: { sanity: -10 },        // 1
  itemsAdd: ['item'],                  // 2
  itemsRemove: ['old_item'],           // 3
  flagsSet: { flag: true },            // 4
  flagsBatch: { clear: /^temp_/ },     // 5
  teleport: 'new_location',            // 6
  timeAdvance: 1,                      // 7
  onTimeAdvance: (day) => {},          // 7.1
  triggerEvent: 'custom_event',        // 8
  
  // 【阶段 2:条件效果】
  conditionalEffects: (state) => {     // 9
    // 基于阶段1后的状态
  },
  
  // 【阶段 3:自定义效果】
  custom: (draft, state) => {},        // 10
  customFull: (draft, original) => {}, // 11
}

// 【阶段 4:后置钩子】
afterEffects: (state, original, actions) => {}, // 12

// 【阶段 5:结果文本】
resultText: (state) => {}  // 13

10. 命名快照系统

// 保存命名快照
store.saveNamedSnapshot('before_boss_fight', '挑战Boss前');

// 恢复到指定快照
if (store.restoreSnapshot('before_boss_fight')) {
  console.log('已恢复到Boss战前');
}

// 查看所有快照
const snapshots = store.listSnapshots();
snapshots.forEach(snap => {
  console.log(`${snap.name || '匿名'}: ${snap.description}`);
  console.log(`时间: ${new Date(snap.timestamp).toLocaleString()}`);
});

// 检查快照是否存在
if (store.hasSnapshot('before_boss_fight')) {
  // 可以恢复
}

// 删除快照
store.deleteSnapshot('before_boss_fight');

11. ResultText 比较前后状态

// 简化前:需要通过 afterEffects + extra 传递信息
{
  effects: {
    conditionalEffects: () => {
      if (Math.random() < 0.15) {
        return { statsChange: { gnosis: 1 } };
      }
      return null;
    }
  },
  afterEffects: (state, original, actions) => {
    const gainedGnosis = state.stats.gnosis > original.stats.gnosis;
    actions.setExtra({ lastMeditateGainedGnosis: gainedGnosis });
  },
  resultText: (state) => {
    return state.extra.lastMeditateGainedGnosis
      ? "获得了灵知!"
      : "平静地冥想。";
  }
}

// 简化后:直接比较前后状态
{
  effects: {
    conditionalEffects: () => {
      if (Math.random() < 0.15) {
        return { statsChange: { gnosis: 1 } };
      }
      return null;
    }
  },
  resultText: (state, original) => {
    const gainedGnosis = state.stats.gnosis > original.stats.gnosis;
    return gainedGnosis
      ? "获得了灵知!"
      : "平静地冥想。";
  }
}

// 显示具体变化量
resultText: (state, original) => {
  const hpChange = state.stats.hp - original.stats.hp;
  const goldChange = state.stats.gold - original.stats.gold;
  
  const parts = [];
  if (hpChange < 0) parts.push(`失去 ${-hpChange} HP`);
  if (goldChange > 0) parts.push(`获得 ${goldChange} 金币`);
  
  return parts.join(',') || '什么都没发生。';
}

🎮 完整示例

// 克苏鲁风格的仪式动作
const performRitualAction: ActionDef<Stats, Items, Flags, Extra> = {
  id: 'perform_ritual',
  label: '进行禁忌仪式',
  
  costs: { 
    action_point: 2,
    sanity: 5 
  },
  
  requirements: {
    hasItems: ['ancient_book'],
    stats: { 
      knowledge: { min: 10 },
      sanity: { min: 20 }
    },
    custom: (state) => {
      const time = state.world.time;
      if (time < 18 && time > 6) {
        return {
          passed: false,
          reason: '禁忌仪式只能在夜晚进行'
        };
      }
      return { passed: true };
    }
  },
  
  effects: {
    // 动态属性变更
    statsChange: (state) => ({
      knowledge: state.world.time === 0 ? 5 : 3,
      sanity: state.world.time === 0 ? -15 : -10
    }),
    
    // 动态标记
    flagsSet: (state) => ({
      [`ritual_day_${state.world.day}`]: true,
      'has_performed_ritual': true
    }),
    
    // 条件效果
    conditionalEffects: (state) => {
      if (state.stats.sanity < 30) {
        return {
          statsChange: { sanity: -5 },
          flagsSet: { 'going_mad': true },
          itemsAdd: ['cursed_artifact']
        };
      }
      return null;
    },
    
    // 修改扩展数据
    custom: (draft) => {
      draft.lastActionTime = Date.now();
      draft.reputation += 5;
    },
    
    // 完整状态修改
    customFull: (draft, original) => {
      if (original.inventory.includes('protective_charm')) {
        const sanityLoss = original.stats.sanity - draft.stats.sanity;
        draft.stats.sanity = original.stats.sanity - Math.floor(sanityLoss * 0.5);
        draft.inventory = draft.inventory.filter(i => i !== 'protective_charm');
        draft.flags.charm_used = true;
      }
    }
  },
  
  afterEffects: (state, original, actions) => {
    if (state.stats.knowledge >= 100 && !state.flags.master_scholar) {
      actions.setFlag('master_scholar', true);
      actions.showModal('成就解锁:博学大师!', 'success');
    }
    
    if (state.flags.midnight_ritual) {
      actions.saveNamedSnapshot('after_midnight_ritual', '午夜仪式后');
    }
  },
  
  resultText: (state) => {
    const parts = [];
    if (state.flags.midnight_ritual) {
      parts.push('午夜时分,仪式达到了高潮');
    }
    if (state.stats.sanity < 20) {
      parts.push('你的理智岌岌可危');
    }
    if (state.flags.charm_used) {
      parts.push('护符保护了你,但它已经碎裂了');
    }
    return parts.join(',') + '。';
  }
};

📝 总结

本引擎提供了以下增强功能:

  1. 动态 Flag 支持 - flagsSet 支持函数形式
  2. 完整状态访问 - customFull 可修改所有状态
  3. 条件性效果 - conditionalEffects 根据状态决定效果
  4. 职责分离 - afterEffects 和 resultText 分离副作用和文本
  5. 动态 StatsChange - statsChange 支持函数形式
  6. 批量 Flag 操作 - flagsBatch 支持正则、前缀、列表
  7. 时间推进副作用 - onTimeAdvance 自动处理每日重置
  8. 灵活的 Requirements - custom 可返回失败原因
  9. 明确的执行顺序 - 5 个阶段,清晰的文档说明
  10. 命名快照系统 - 支持保存和恢复命名快照
  11. ResultText 双参数 - 可以比较前后状态,无需 extra 传递

所有功能都是类型安全的,并且完全向后兼容!


⚠️ 重要:通知系统使用

如果你使用 showModal()showToast() 但没有看到显示,请确保在你的应用中渲染了 OverlaySystem 组件:

import { OverlaySystem } from 'steamsheep-ts-game-engine';

function App() {
  return (
    <>
      <YourGameUI />
      {/* ⚠️ 必须添加这个组件 */}
      <OverlaySystem />
    </>
  );
}

详细说明请查看:通知系统使用指南


📚 更多文档

📄 许可证

MIT