koishi-plugin-satori-ai 1.3.3 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ import { Context, Session } from 'koishi';
2
+ import { Sat } from './types';
3
+ export declare class BroadcastManager {
4
+ private ctx;
5
+ private config;
6
+ private idCounter;
7
+ private broadcastMessages;
8
+ constructor(ctx: Context, cfg: Sat.Config);
9
+ createBroadcast(message: string, img: boolean): Promise<string>;
10
+ deleteBroadcast(id: string): Promise<string>;
11
+ seedBroadcast(session: Session): Promise<void>;
12
+ getBroadcastList(): Promise<string>;
13
+ }
14
+ export declare class broadcastMessage {
15
+ private ctx;
16
+ private config;
17
+ message: string;
18
+ private alreadySentChannels;
19
+ setTime: Date;
20
+ private img;
21
+ constructor(ctx: Context, cfg: Sat.Config, message: string, img: boolean);
22
+ sendMessage(session: Session): Promise<void>;
23
+ getAlreadySentChannels(): string;
24
+ }
@@ -3,8 +3,7 @@ import { abstractGame, abstractGameSingleGame, gameResult } from './abstractGame
3
3
  import { winFlag } from './game';
4
4
  export interface OneTouchResult extends gameResult {
5
5
  win: winFlag;
6
- message: string;
7
- playerId: number;
6
+ bonus: number;
8
7
  }
9
8
  declare class OneTouchSingleGame extends abstractGameSingleGame {
10
9
  private player;
@@ -16,22 +15,22 @@ declare class OneTouchSingleGame extends abstractGameSingleGame {
16
15
  private playerLevelHP;
17
16
  private aiLevelHp;
18
17
  private lastScore;
18
+ private bonus;
19
+ private singleBonus;
20
+ private singleBonusMultiplier;
21
+ private comboCombos;
19
22
  constructor(disposeListener: () => boolean, session: Session);
20
23
  startGame(): Promise<string>;
21
- endGame: () => Promise<{
22
- message: string;
23
- win: winFlag;
24
- gameName: string;
25
- playerID: string;
26
- }>;
24
+ endGame: () => Promise<OneTouchResult>;
27
25
  private initState;
28
26
  processInput(input: string): Promise<string>;
29
27
  private buildTurnResult;
30
28
  private judgeEnd;
31
29
  private processPlayerTurn;
30
+ private buildMyTurnBonusMessage;
32
31
  private processAiTurn;
33
- private applyEffectToEnemy;
34
- private applyEffectToSelf;
32
+ private buildAiTurnBonusMessage;
33
+ private applyEffect;
35
34
  private checkCombo;
36
35
  private buildResultMessage;
37
36
  private aiSearchEntrance;
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Context, Session } from 'koishi';
2
2
  import { Sat, User } from './types';
3
3
  import Puppeteer from 'koishi-plugin-puppeteer';
4
+ import { BroadcastManager } from './broadcast';
4
5
  export declare let puppeteer: Puppeteer | null;
5
6
  export declare function refreshPuppeteer(ctx: Context): void;
6
7
  export declare class SAT extends Sat {
@@ -11,6 +12,7 @@ export declare class SAT extends Sat {
11
12
  private ChannelParallelCount;
12
13
  private onlineUsers;
13
14
  private moodManager;
15
+ broadcastManager: BroadcastManager;
14
16
  private usersToWarn;
15
17
  private game;
16
18
  constructor(ctx: Context, config: Sat.Config);
package/lib/index.js CHANGED
@@ -33,7 +33,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
33
33
  // src/locales/zh.yml
34
34
  var require_zh = __commonJS({
35
35
  "src/locales/zh.yml"(exports, module2) {
36
- module2.exports = { commands: { sat: { description: "AI聊天", messages: { "no-prompt": "你好。使用说明可以看我的空间哦。", tooManyEnglishLetters: "请不要用这么多英文......", err: "寄!{0}", tooLong: "你的话太多了。", content_tooLong: "这个问题有点复杂,你还是去问别人吧。", "duplicate-dialogue": "这个刚刚说过了吧......", online: "思考中,请再等一会!", "no-response": "我不知道你在说什么……", update_level_succeed: "用户等级已更新为{0}", exceeds: "已经差不多够了吧?请明天再来。", usage: "今日次数{0}/{1}", hatMan: "叮!帽子先生保护了你的好感度,快说谢谢帽子先生吧!", rockBottom: "叮!谷底小石保护了你的好感度,快说谢谢谷底小石吧!", not_good_mood: "别得寸进尺……(好感↓)", pocket_money: "心情不错,给你点零花钱吧~(获得了{0}p点)", warning: "<at id={0}/>", block1: "我讨厌你!", block2: "我讨厌你们!" } }, "sat.clear": { description: "清空当前频道会话", messages: { clean: "已清空当前会话。", Allclean: "已清空所有会话。" } }, "sat.common_sense": { description: "添加常识", messages: { "no-prompt": "你想让我知道什么?", succeed: "我知道了,{0}" } } } };
36
+ module2.exports = { commands: { sat: { description: "AI聊天", messages: { "no-prompt": "你好。使用说明可以看我的空间哦。", tooManyEnglishLetters: "请不要用这么多英文......", err: "寄!{0}", tooLong: "你的话太多了。", content_tooLong: "这个问题有点复杂,你还是去问别人吧。", "duplicate-dialogue": "这个刚刚说过了吧......", online: "思考中,请再等一会!", "no-response": "我不知道你在说什么……", update_level_succeed: "用户等级已更新为{0}", exceeds: "已经差不多够了吧?请明天再来。", usage: "今日次数{0}/{1}", hatMan: "叮!帽子先生保护了你的好感度,快说谢谢帽子先生吧!", rockBottom: "叮!谷底小石保护了你的好感度,快说谢谢谷底小石吧!", not_good_mood: "别得寸进尺……(好感↓)", pocket_money: "心情不错,给你点零花钱吧~(获得了{0}p点)", warning: "<at id={0}/>", block1: "我讨厌你!", block2: "我讨厌你们!" } }, "sat.clear": { description: "清空当前频道会话", messages: { clean: "已清空当前会话。", Allclean: "已清空所有会话。" } }, "sat.common_sense": { description: "添加常识", messages: { "no-prompt": "你想让我知道什么?", succeed: "我知道了,{0}" } }, "sat.broadcast": { description: "广播一条消息,在每个生效群收到首条消息时会推送", options: { delete: "删除一条广播,参数为广播的ID", list: "查看当前所有广播", img: "使用puppeteer图片化广播消息" } } } };
37
37
  }
38
38
  });
39
39
 
@@ -46,7 +46,7 @@ __export(src_exports, {
46
46
  refreshPuppeteer: () => refreshPuppeteer
47
47
  });
48
48
  module.exports = __toCommonJS(src_exports);
49
- var import_koishi13 = require("koishi");
49
+ var import_koishi14 = require("koishi");
50
50
  var path7 = __toESM(require("path"));
51
51
 
52
52
  // src/api.ts
@@ -255,7 +255,7 @@ function processPrompt(prompt) {
255
255
  prompt = prompt.replace(/\*\*/g, "");
256
256
  if (!prompt)
257
257
  return "**";
258
- return prompt;
258
+ return prompt.trim();
259
259
  }
260
260
  __name(processPrompt, "processPrompt");
261
261
  function filterResponse(prompt, words) {
@@ -474,7 +474,7 @@ var APIClient = class {
474
474
  continue;
475
475
  }
476
476
  }
477
- return { content: "模型发生复读行为,建议重置对话", error: true };
477
+ return { content: "unreachable", error: true };
478
478
  }
479
479
  // 生成请求头
480
480
  createHeaders(keys) {
@@ -1028,6 +1028,7 @@ function createMiddleware(ctx, sat, config) {
1028
1028
  return async (session, next) => {
1029
1029
  if (config.enable_favorability && config.enable_warning && session.channelId === config.warning_group)
1030
1030
  sat.getWarningList(session);
1031
+ sat.broadcastManager.seedBroadcast(session);
1031
1032
  if (!isSpecialMessage(session))
1032
1033
  await sat.handleChannelMemoryManager(session);
1033
1034
  if (config.private && isPrivateSession(session)) {
@@ -1217,6 +1218,10 @@ var Sat = class extends import_koishi5.Service {
1217
1218
  max_pocket_money: import_koishi5.Schema.number().default(400).description("随机p点最大值"),
1218
1219
  pocket_money_cost: import_koishi5.Schema.number().default(2).description("消耗心情换p点的心情值")
1219
1220
  }).description("好感度拓展:心情设置(需要开启好感度模块才能生效)"),
1221
+ import_koishi5.Schema.object({
1222
+ Broadcast_block_channel: import_koishi5.Schema.array(String).description("不提供广播的频道id"),
1223
+ waiting_time: import_koishi5.Schema.number().default(1440).description("广播在缓存中等待的最大时间(分钟)(超过时间后没有触发会不再推送)")
1224
+ }).description("广播设置(发出后在每个生效群收到首条消息时会推送)"),
1220
1225
  import_koishi5.Schema.object({
1221
1226
  enable_game: import_koishi5.Schema.boolean().default(false).description("是否开启游戏模块"),
1222
1227
  game_block_channel: import_koishi5.Schema.array(String).description("不提供游戏的频道id"),
@@ -2171,17 +2176,27 @@ var import_koishi11 = require("koishi");
2171
2176
  var logger10 = new import_koishi11.Logger("satori-game-onetouch");
2172
2177
  var SKILL_MAP = {
2173
2178
  // 基础技能
2174
- "-1": { damage: 0, name: "无效" },
2179
+ "-1": { name: "无效" },
2175
2180
  "1": { pierceDamage: 1, bleed: 3, name: "锥刺" },
2181
+ //特性:流血
2176
2182
  "2": { pierceDamage: 1, stun: true, name: "点穴" },
2183
+ //特性:眩晕
2177
2184
  "3": { damage: 6, counterAttack: 3, name: "爪击" },
2185
+ //特性:反击
2178
2186
  "4": { shield: 1, name: "护盾" },
2187
+ //特性:护盾
2179
2188
  "5": { damage: 4, weakStun: true, name: "巴掌" },
2189
+ //特性:普通伤害+弱眩晕
2180
2190
  "6": { heal: 6, selfbleed: -2, name: "酒" },
2191
+ //特性:回血
2181
2192
  "7": { pierceDamage: 1, destroyShield: 2, name: "钻击" },
2193
+ //特性:破盾
2182
2194
  "8": { damage: 13, selfStun: true, name: "枪击" },
2195
+ //特性:高伤+自眩晕
2183
2196
  "9": { pierceDamage: 1, bleed: 2, strengthChange: -1, name: "钩" },
2197
+ //特性:削弱
2184
2198
  "0": { selfstrengthChange: 2, name: "蓄力" },
2199
+ //特性:蓄力
2185
2200
  // 组合技
2186
2201
  "1+8": { damage: 15, bleed: 3, selfStun: true, name: "空尖弹" },
2187
2202
  "5+5": { damage: 5, stun: true, name: "镇压" },
@@ -2193,12 +2208,13 @@ var SKILL_MAP = {
2193
2208
  "4+6": { shield: 2, heal: 5, selfbleed: -3, name: "固守" },
2194
2209
  "8+8": { damage: 13, damageTimes: 2, selfStun: true, name: "双持" },
2195
2210
  "1+2": { pierceDamage: 3, vulnerablility: 3, stun: true, name: "弱点刺击" },
2196
- "2+8": { damage: 8, stun: true, selfStun: true, addLeft: 1, addRight: 9, name: "点射" },
2211
+ "2+8": { damage: 12, stun: true, selfStun: true, addLeft: 1, addRight: 9, name: "点射" },
2197
2212
  "3+4": { name: "防御反击", shield: 1, counterAttack: 15 },
2198
2213
  "7+7": { pierceDamage: 7, destroyShield: 5, name: "穿龙枪" },
2199
2214
  "7+8": { pierceDamage: 13, destroyShield: 3, selfStun: true, name: "穿甲弹" },
2200
2215
  "2+6": { damage: 5, heal: 6, selfbleed: -1, selfstrengthChange: 1, stun: true, name: "点辰" },
2201
2216
  "3+7": { name: "混沌", counterAttack: 7, destroyShield: 2, addLeft: 9, addRight: 1 },
2217
+ "0+9": { name: "将大局逆转吧", revwersalOfStrength: true, selfstrengthChange: 2, strengthChange: -1 },
2202
2218
  "0+0": { name: "华丽收场", bleed: 5, selfstrengthChange: -99, magnificentEnd: true }
2203
2219
  };
2204
2220
  var OneTouchSingleGame = class extends abstractGameSingleGame {
@@ -2214,18 +2230,28 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2214
2230
  // 当前回合数
2215
2231
  baseHP = 40;
2216
2232
  // 初始血量
2217
- playerLevelHP = 10;
2233
+ playerLevelHP = 8;
2218
2234
  // 每级增加的血量
2219
2235
  aiLevelHp = 10;
2220
2236
  // AI每级增加的血量
2221
2237
  lastScore = 0;
2222
2238
  // 上一回合的分数
2239
+ bonus = 0;
2240
+ // 奖励分数
2241
+ singleBonus = 0;
2242
+ // 单回合的分数
2243
+ singleBonusMultiplier = 1;
2244
+ // 单回合的分数倍率
2245
+ comboCombos = 0;
2246
+ // 组合技连击次数
2223
2247
  constructor(disposeListener, session) {
2224
2248
  super(disposeListener, session);
2225
2249
  }
2226
2250
  async startGame() {
2227
2251
  super.startGame();
2228
2252
  this.turnCount = 0;
2253
+ this.bonus = 0;
2254
+ this.comboCombos = 0;
2229
2255
  this.player = {
2230
2256
  left: Math.round(Math.random() * 8) + 1,
2231
2257
  right: Math.round(Math.random() * 8) + 1,
@@ -2249,16 +2275,25 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2249
2275
  }
2250
2276
  // 结束游戏,返回结果
2251
2277
  endGame = async () => {
2278
+ if (this.winningFlag === 4 /* pending */ || this.winningFlag === 2 /* lose */) {
2279
+ this.bonus = Math.abs(Math.floor(this.level * 0.2 * this.bonus));
2280
+ this.bonus -= Math.floor(this.level * this.level * (Math.random() * 2 + 2) + 50);
2281
+ this.bonus = Math.min(this.bonus, 0);
2282
+ }
2283
+ if (this.winningFlag === 1 /* win */) {
2284
+ this.bonus = Math.floor(this.level * this.bonus * 0.1 * (Math.random() * 1 + 1));
2285
+ }
2286
+ const finalBonus = this.bonus;
2252
2287
  super.endGame();
2253
- return { message: `${this.level}`, win: this.winningFlag, gameName: "一碰一", playerID: this.session.userId };
2288
+ return { win: this.winningFlag, gameName: "一碰一", playerID: this.session.userId, bonus: finalBonus, message: this.level.toString() };
2254
2289
  };
2255
2290
  initState(level) {
2256
2291
  this.player = {
2257
2292
  left: this.player.left,
2258
2293
  right: this.player.right,
2259
2294
  hp: this.baseHP + level * this.playerLevelHP,
2260
- shield: Math.round(level / 3),
2261
- strength: Math.round(level / 5),
2295
+ shield: Math.round(5 - level / 2),
2296
+ strength: 0,
2262
2297
  bleed: 0,
2263
2298
  counterAttack: 0,
2264
2299
  vulnerablility: 0,
@@ -2282,17 +2317,17 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2282
2317
  if (this.turnCount === 0)
2283
2318
  this.initState(this.level);
2284
2319
  this.turnCount++;
2285
- if (this.winningFlag !== 4 /* pending */)
2286
- return "游戏已结束";
2287
2320
  const [handA, handB] = input.split(" ");
2288
2321
  if (handA !== "左" && handA !== "右" || handB !== "左" && handB !== "右")
2289
2322
  return;
2323
+ if (this.winningFlag !== 4 /* pending */)
2324
+ return "游戏已结束";
2290
2325
  const numberA = this.player[handA === "左" ? "left" : "right"];
2291
2326
  const numberB = this.ai[handB === "左" ? "left" : "right"];
2292
2327
  const result = this.processPlayerTurn(numberA, numberB);
2293
2328
  const bestMove = this.ai.status === 1 /* Stunned */ ? [0, 0] : this.aiSearchEntrance();
2294
2329
  const aiResult = this.processAiTurn(bestMove[0], bestMove[1]);
2295
- if (this.player.status === 1 /* Stunned */)
2330
+ if (this.player.status === 1 /* Stunned */ && this.player.hp > 0 && this.ai.hp > 0)
2296
2331
  setTimeout(async () => {
2297
2332
  this.session.send(await this.processInput(input));
2298
2333
  }, 1e3);
@@ -2302,7 +2337,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2302
2337
  const createStatusBar = /* @__PURE__ */ __name((value, max, width) => `[${"■".repeat(Math.max(Math.ceil(value / max * width), 0))}${"□".repeat(width - Math.max(Math.ceil(value / max * width)))}]`, "createStatusBar");
2303
2338
  const statusIcon = /* @__PURE__ */ __name((value, icon) => value != 0 ? `${icon}×${value}` : "", "statusIcon");
2304
2339
  const aiStatusDisplay = [
2305
- `❤️${createStatusBar(this.ai.hp, Math.max(this.baseHP + this.level * this.aiLevelHp, this.ai.hp), Math.round(this.baseHP / 10 + this.level * this.aiLevelHp / this.playerLevelHP))} ${this.ai.hp}HP`,
2340
+ `❤️${createStatusBar(this.ai.hp, Math.max(this.baseHP + this.level * this.aiLevelHp, this.ai.hp), Math.round(this.baseHP / 8 + this.level * this.aiLevelHp / this.playerLevelHP))} ${this.ai.hp}HP`,
2306
2341
  `🛡️${this.ai.shield}`,
2307
2342
  statusIcon(this.ai.bleed, "🩸"),
2308
2343
  statusIcon(this.ai.strength, "💪"),
@@ -2311,7 +2346,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2311
2346
  this.ai.status === 1 /* Stunned */ ? "💫 眩晕" : ""
2312
2347
  ].filter(Boolean).join(" | ");
2313
2348
  const playerStatusDisplay = [
2314
- `❤️${createStatusBar(this.player.hp, Math.max(this.baseHP + this.level * this.playerLevelHP, this.player.hp), Math.round(this.baseHP / 10 + this.level))} ${this.player.hp}HP`,
2349
+ `❤️${createStatusBar(this.player.hp, Math.max(this.baseHP + this.level * this.playerLevelHP, this.player.hp), Math.round(this.baseHP / 8 + this.level))} ${this.player.hp}HP`,
2315
2350
  `🛡️${this.player.shield}`,
2316
2351
  statusIcon(this.player.bleed, "🩸"),
2317
2352
  statusIcon(this.player.strength, "💪"),
@@ -2319,9 +2354,9 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2319
2354
  statusIcon(this.player.vulnerablility, "💔"),
2320
2355
  this.player.status === 1 /* Stunned */ ? "💫 眩晕" : ""
2321
2356
  ].filter(Boolean).join(" | ");
2322
- return wrapInHTML2(`你的行动:${result}
2357
+ return wrapInHTML2(`▶️你的行动:${result}
2323
2358
 
2324
- 我的行动:${aiResult}
2359
+ ▶️我的行动:${aiResult}
2325
2360
 
2326
2361
  我的当前手势:左${this.ai.left} 右${this.ai.right}
2327
2362
  你的当前手势:左${this.player.left} 右${this.player.right}
@@ -2339,12 +2374,10 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2339
2374
  judgeEnd() {
2340
2375
  if (this.ai.hp <= 0) {
2341
2376
  this.winningFlag = 1 /* win */;
2342
- this.endGame();
2343
2377
  return `你赢了!发送结束游戏退出`;
2344
2378
  }
2345
2379
  if (this.player.hp <= 0) {
2346
2380
  this.winningFlag = 2 /* lose */;
2347
- this.endGame();
2348
2381
  return `你输了!发送结束游戏退出`;
2349
2382
  }
2350
2383
  }
@@ -2354,35 +2387,141 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2354
2387
  }
2355
2388
  if (this.player.status === 1 /* Stunned */) {
2356
2389
  this.player.status = 2 /* lastStunned */;
2357
- this.player = this.applyEffectToSelf(this.ai, this.player, SKILL_MAP["-1"]);
2390
+ this.applyEffect(this.player, this.ai, SKILL_MAP["-1"], false);
2358
2391
  return "你被眩晕,跳过回合";
2359
2392
  }
2360
- const playerState = this.player;
2361
- const aiState = this.ai;
2362
2393
  const sum = (handA + handB) % 10;
2363
- this.player[handA === playerState.left ? "left" : "right"] = sum;
2394
+ this.player[handA === this.player.left ? "left" : "right"] = sum;
2364
2395
  let effect = SKILL_MAP[sum.toString()] || {};
2365
2396
  let isCombo = false;
2366
2397
  const combo = this.checkCombo(this.player["left"], this.player["right"]);
2367
2398
  if (combo) {
2368
2399
  isCombo = true;
2400
+ this.comboCombos++;
2369
2401
  effect = { ...combo };
2402
+ } else {
2403
+ this.comboCombos = 0;
2404
+ }
2405
+ const bonusMessage = this.buildMyTurnBonusMessage(effect, isCombo);
2406
+ this.applyEffect(this.player, this.ai, effect, !!combo);
2407
+ return this.buildResultMessage(effect, isCombo, this.player, this.ai) + (bonusMessage ? `
2408
+
2409
+ ${bonusMessage}` : "");
2410
+ }
2411
+ // 生成玩家回合奖励信息
2412
+ buildMyTurnBonusMessage(effect, isCombo) {
2413
+ let bonusMessage = "";
2414
+ this.singleBonus = 0;
2415
+ this.singleBonusMultiplier = 1;
2416
+ if (isCombo) {
2417
+ this.singleBonus += 10;
2418
+ bonusMessage += `触发组合技,获得10点分数!
2419
+ `;
2420
+ }
2421
+ if ((effect.damage || 0) * (effect.damageTimes || 1) >= 20 && this.ai.shield === 0) {
2422
+ const effectBonus = Math.round((effect.damage || 0) * (effect.damageTimes || 1) * 0.8);
2423
+ this.singleBonus += effectBonus;
2424
+ bonusMessage += `沉重一击!获得${effectBonus}点分数!
2425
+ `;
2426
+ }
2427
+ if ((effect.pierceDamage || 0) >= 15) {
2428
+ const effectBonus = Math.round(effect.pierceDamage * 0.5);
2429
+ this.singleBonus += effectBonus;
2430
+ bonusMessage += `穿刺一击!获得${effectBonus}点分数!
2431
+ `;
2432
+ }
2433
+ if ((effect.destroyShield || 0) >= Math.min(this.ai.shield, 3) && this.ai.shield > 1) {
2434
+ const effectBonus = Math.round(this.ai.shield * 5);
2435
+ this.singleBonus += effectBonus;
2436
+ bonusMessage += `快速破盾!获得${effectBonus}点分数!
2437
+ `;
2370
2438
  }
2371
- this.ai = this.applyEffectToEnemy(playerState, aiState, effect, !!combo);
2372
- this.player = this.applyEffectToSelf(aiState, playerState, effect);
2373
- return this.buildResultMessage(effect, isCombo, this.player, this.ai);
2439
+ if ((effect.bleed || 0) + this.ai.bleed > 9) {
2440
+ const effectBonus = Math.round(this.ai.bleed);
2441
+ this.singleBonus += effectBonus;
2442
+ bonusMessage += `流血打击!获得${effectBonus}点分数!
2443
+ `;
2444
+ }
2445
+ if (effect.shield && (effect.shield || 0) + this.player.shield >= 5) {
2446
+ const effectBonus = Math.round(((effect.shield || 0) + this.player.shield) * 2);
2447
+ this.singleBonus += effectBonus;
2448
+ bonusMessage += `强效护盾!获得${effectBonus}点分数!
2449
+ `;
2450
+ }
2451
+ if ((effect.selfbleed || 0) < -2 && this.player.bleed > -effect.selfbleed) {
2452
+ const effectBonus = Math.round(this.player.bleed * 2);
2453
+ this.singleBonus += effectBonus;
2454
+ bonusMessage += `关键治疗!获得${effectBonus}点分数!
2455
+ `;
2456
+ }
2457
+ if ((effect.stun || effect.weakStun && this.ai.shield === 0) && this.ai.status === 0 /* Normal */ && this.ai.bleed > 5) {
2458
+ const effectBonus = Math.round(this.ai.bleed * 2);
2459
+ this.singleBonus += effectBonus;
2460
+ bonusMessage += `流血眩晕!获得${effectBonus}点分数!
2461
+ `;
2462
+ }
2463
+ if ((effect.strengthChange || 0) < -1 && this.ai.strength < 0) {
2464
+ const effectBonus = Math.round(effect.strengthChange * -5);
2465
+ this.singleBonus += effectBonus;
2466
+ bonusMessage += `强效削弱!获得${effectBonus}点分数!
2467
+ `;
2468
+ }
2469
+ if (this.ai.counterAttack > 0 && !effect.damage && !effect.pierceDamage) {
2470
+ const effectBonus = Math.round(this.ai.counterAttack);
2471
+ this.singleBonus += effectBonus;
2472
+ bonusMessage += `反击规避!获得${effectBonus}点分数!
2473
+ `;
2474
+ }
2475
+ if (this.player.hp < 10 && !this.player.shield && (effect.damage || effect.pierceDamage)) {
2476
+ const effectBonus = (10 - this.player.hp) * 2;
2477
+ this.singleBonus += effectBonus;
2478
+ bonusMessage += `濒死反击!获得${effectBonus}点分数!
2479
+ `;
2480
+ }
2481
+ if (effect.revwersalOfStrength && this.player.strength <= this.ai.strength - 5) {
2482
+ const effectBonus = Math.min((this.ai.strength - this.player.strength) * 3, 100);
2483
+ this.singleBonus += effectBonus;
2484
+ bonusMessage += `逆转大局!获得${effectBonus}点分数!
2485
+ `;
2486
+ }
2487
+ if ((effect.damage || 0) + (effect.pierceDamage || 0) >= 10 && this.ai.vulnerablility > 0) {
2488
+ const effectBonus = this.ai.vulnerablility * 0.1;
2489
+ this.singleBonusMultiplier += effectBonus;
2490
+ bonusMessage += `易伤打击!本回合分数增加${100 * effectBonus}%!
2491
+ `;
2492
+ }
2493
+ if (this.turnCount === 4 + Math.round(this.level / 3) && this.player.hp > this.ai.hp * 1.2 && this.player.shield > this.ai.shield && this.player.strength > this.ai.strength && this.player.bleed < this.ai.bleed) {
2494
+ const effectBonus = Math.round(this.player.hp * 0.1 + this.player.shield + this.player.strength * 0.5);
2495
+ this.singleBonus += effectBonus;
2496
+ this.singleBonusMultiplier *= 2;
2497
+ bonusMessage += `压倒性的优势!获得${effectBonus}点分数!本回合分数*2!
2498
+ `;
2499
+ }
2500
+ if (effect.magnificentEnd && this.ai.bleed > 1) {
2501
+ const effectBonus = Math.round(this.ai.bleed * 1.5);
2502
+ this.singleBonusMultiplier *= effectBonus;
2503
+ this.bonus *= 2;
2504
+ bonusMessage += `华丽收场!当前总分数*2,本回合分数*${effectBonus}!
2505
+ `;
2506
+ }
2507
+ if (this.comboCombos >= 1) {
2508
+ this.singleBonusMultiplier += this.comboCombos / 5;
2509
+ bonusMessage += `组合技连击${this.comboCombos}次!本回合分数+${20 * this.comboCombos}%!`;
2510
+ }
2511
+ return bonusMessage;
2374
2512
  }
2375
2513
  processAiTurn(handA, handB) {
2376
- if (this.ai.hp <= 0) {
2377
- return `已经结束游戏`;
2378
- }
2514
+ if (this.ai.hp <= 0)
2515
+ return `已经结束游戏
2516
+
2517
+ ` + this.buildAiTurnBonusMessage(SKILL_MAP["-1"]);
2379
2518
  if (this.ai.status === 2 /* lastStunned */) {
2380
2519
  this.ai.status = 0 /* Normal */;
2381
2520
  }
2382
2521
  if (this.ai.status === 1 /* Stunned */) {
2383
2522
  this.ai.status = 2 /* lastStunned */;
2384
- this.ai = this.applyEffectToSelf(this.player, this.ai, SKILL_MAP["-1"]);
2385
- return "被眩晕,跳过回合";
2523
+ this.applyEffect(this.ai, this.player, SKILL_MAP["-1"], false);
2524
+ return "被眩晕,跳过回合,本回合分数奖励继承到下一回合";
2386
2525
  }
2387
2526
  const sum = (handA + handB) % 10;
2388
2527
  this.ai[handA === this.ai.left ? "left" : "right"] = sum;
@@ -2393,9 +2532,51 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2393
2532
  isCombo = true;
2394
2533
  effect = { ...combo };
2395
2534
  }
2396
- this.player = this.applyEffectToEnemy(this.ai, this.player, effect, !!combo);
2397
- this.ai = this.applyEffectToSelf(this.player, this.ai, effect);
2398
- return handA + "碰" + handB + "," + this.buildResultMessage(effect, isCombo, this.ai, this.player);
2535
+ const bonusMessage = this.buildAiTurnBonusMessage(effect);
2536
+ this.applyEffect(this.ai, this.player, effect, !!combo);
2537
+ return handA + "碰" + handB + "," + this.buildResultMessage(effect, isCombo, this.ai, this.player) + (bonusMessage ? `
2538
+
2539
+ ${bonusMessage}` : "");
2540
+ }
2541
+ buildAiTurnBonusMessage(effect) {
2542
+ let bonusMessage = "";
2543
+ if ((effect.damage || effect.pierceDamage) && this.player.counterAttack > 0) {
2544
+ const effectBonus = Math.max(this.player.counterAttack + this.player.strength, 0);
2545
+ this.singleBonus += effectBonus;
2546
+ bonusMessage += `反击!获得${effectBonus}点分数!
2547
+ `;
2548
+ }
2549
+ if ((effect.damage || 0) > 12 && this.player.shield > 0) {
2550
+ const effectBonus = Math.round(effect.damage);
2551
+ this.singleBonus += effectBonus;
2552
+ bonusMessage += `关键格挡!获得${effectBonus}点分数!
2553
+ `;
2554
+ }
2555
+ if (this.player.shield > 0 && effect.damage && this.player.counterAttack > 0) {
2556
+ const effectBonus = Math.round(this.player.shield * 2 + this.player.counterAttack);
2557
+ this.singleBonus += effectBonus;
2558
+ bonusMessage += `盾反!获得${effectBonus}点分数!
2559
+ `;
2560
+ }
2561
+ if (effect.damage && this.player.vulnerablility > 0 && this.player.shield > 0) {
2562
+ const effectBonus = 0.5;
2563
+ this.singleBonusMultiplier += effectBonus;
2564
+ bonusMessage += `易伤保护!本回合分数增加${100 * effectBonus}%!
2565
+ `;
2566
+ }
2567
+ if (effect.weakStun && this.player.shield > 0) {
2568
+ const effectBonus = 0.3;
2569
+ this.singleBonusMultiplier += effectBonus;
2570
+ bonusMessage += `眩晕格挡!本回合分数增加${100 * effectBonus}%!
2571
+ `;
2572
+ }
2573
+ const turnBonus = Math.round(this.singleBonus * this.singleBonusMultiplier);
2574
+ this.bonus += turnBonus;
2575
+ bonusMessage += `本回合总计获得${this.singleBonus}*${this.singleBonusMultiplier}=${turnBonus}点分数
2576
+ 当前总分数:${this.bonus}`;
2577
+ this.singleBonus = 0;
2578
+ this.singleBonusMultiplier = 1;
2579
+ return bonusMessage;
2399
2580
  }
2400
2581
  // damage?: number // 普通伤害
2401
2582
  // damageTimes?: number // 伤害次数
@@ -2405,48 +2586,31 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2405
2586
  // destroyShield?: number // 破坏护盾
2406
2587
  // weakStun?: boolean // 弱眩晕
2407
2588
  // strengthChange?: number // 力量变化
2408
- applyEffectToEnemy(self, target, effect, isCombo) {
2589
+ // heal?: number // 治疗
2590
+ // shield?: number // 护盾
2591
+ // selfbleed?: number // 自身流血
2592
+ // selfStun?: boolean // 自身眩晕
2593
+ // selfstrengthChange?: number // 自身力量变化
2594
+ applyEffect(self, enemy, effect, isCombo) {
2595
+ if (effect.revwersalOfStrength) {
2596
+ const temp = self.strength;
2597
+ self.strength = enemy.strength;
2598
+ enemy.strength = temp;
2599
+ }
2409
2600
  if (effect.pierceDamage)
2410
- target.hp -= Math.max(effect.pierceDamage + (self.strength || 0), 0);
2601
+ enemy.hp -= Math.max(effect.pierceDamage + (self.strength || 0), 0);
2411
2602
  if (effect.destroyShield)
2412
- target.shield = Math.max(0, target.shield - effect.destroyShield);
2603
+ enemy.shield = Math.max(0, enemy.shield - effect.destroyShield);
2413
2604
  if (effect.bleed)
2414
- target.bleed += effect.bleed;
2605
+ enemy.bleed += effect.bleed;
2415
2606
  if (effect.strengthChange)
2416
- target.strength = target.strength + effect.strengthChange;
2607
+ enemy.strength = enemy.strength + effect.strengthChange;
2417
2608
  if (effect.addLeft)
2418
- target.left = (target.left + effect.addLeft) % 10;
2609
+ enemy.left = (enemy.left + effect.addLeft) % 10;
2419
2610
  if (effect.addRight)
2420
- target.right = (target.right + effect.addRight) % 10;
2611
+ enemy.right = (enemy.right + effect.addRight) % 10;
2421
2612
  if (effect.vulnerablility)
2422
- target.vulnerablility = (target.vulnerablility || 0) + effect.vulnerablility;
2423
- if (effect.damage) {
2424
- effect.damageTimes = effect.damageTimes || 1;
2425
- for (let i = 0; i < effect.damageTimes; i++) {
2426
- const blocked = target.shield > 0;
2427
- target.shield = Math.max(0, target.shield - (isCombo ? 2 : 1));
2428
- if (!blocked) {
2429
- target.hp -= Math.round(Math.max(0, effect.damage + (self.strength || 0)) * (target.vulnerablility ? 1.5 : 1));
2430
- if (effect.weakStun && target.status !== 2 /* lastStunned */) {
2431
- target.status = 1 /* Stunned */;
2432
- }
2433
- }
2434
- }
2435
- }
2436
- if (effect.magnificentEnd) {
2437
- target.hp -= target.bleed * (target.bleed + 1) / 2;
2438
- target.bleed = 0;
2439
- }
2440
- if (effect.stun && target.status !== 2 /* lastStunned */)
2441
- target.status = 1 /* Stunned */;
2442
- return target;
2443
- }
2444
- // heal?: number // 治疗
2445
- // shield?: number // 护盾
2446
- // selfbleed?: number // 自身流血
2447
- // selfStun?: boolean // 自身眩晕
2448
- // selfstrengthChange?: number // 自身力量变化
2449
- applyEffectToSelf(enemy, self, effect) {
2613
+ enemy.vulnerablility = (enemy.vulnerablility || 0) + effect.vulnerablility;
2450
2614
  if (effect.heal)
2451
2615
  self.hp += effect.heal;
2452
2616
  if (effect.shield)
@@ -2457,22 +2621,41 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2457
2621
  self.left = (self.left + effect.addSelfLeft) % 10;
2458
2622
  if (effect.addSelfRight)
2459
2623
  self.right = (self.right + effect.addSelfRight) % 10;
2624
+ if (effect.damage) {
2625
+ effect.damageTimes = effect.damageTimes || 1;
2626
+ for (let i = 0; i < effect.damageTimes; i++) {
2627
+ const blocked = enemy.shield > 0;
2628
+ enemy.shield = Math.max(0, enemy.shield - (isCombo ? 2 : 1));
2629
+ if (!blocked) {
2630
+ enemy.hp -= Math.round(Math.max(0, effect.damage + (self.strength || 0)) * (enemy.vulnerablility ? 1.5 : 1));
2631
+ if (effect.weakStun && enemy.status !== 2 /* lastStunned */) {
2632
+ enemy.status = 1 /* Stunned */;
2633
+ }
2634
+ }
2635
+ }
2636
+ }
2460
2637
  if (self.counterAttack)
2461
2638
  self.counterAttack = 0;
2462
2639
  if (effect.counterAttack)
2463
2640
  self.counterAttack = effect.counterAttack;
2464
2641
  if (enemy.counterAttack > 0 && (effect.damage > 0 || effect.pierceDamage > 0))
2465
2642
  self.hp -= Math.max(enemy.counterAttack + (enemy.strength || 0), 0);
2466
- if (effect.selfStun && self.status !== 2 /* lastStunned */)
2467
- self.status = 1 /* Stunned */;
2468
2643
  if (effect.selfbleed)
2469
- self.bleed = self.bleed + effect.selfbleed;
2644
+ self.bleed += effect.selfbleed;
2470
2645
  if (self.bleed > 0)
2471
2646
  self.hp -= self.bleed;
2472
2647
  self.bleed = Math.max(0, self.bleed - 1);
2648
+ if (effect.magnificentEnd) {
2649
+ enemy.hp -= enemy.bleed * (enemy.bleed + 1) / 2;
2650
+ enemy.bleed = 0;
2651
+ }
2473
2652
  if (self.vulnerablility)
2474
2653
  self.vulnerablility = Math.max(0, self.vulnerablility - 1);
2475
- return self;
2654
+ if (effect.stun && enemy.status !== 2 /* lastStunned */)
2655
+ enemy.status = 1 /* Stunned */;
2656
+ if (effect.selfStun && self.status !== 2 /* lastStunned */)
2657
+ self.status = 1 /* Stunned */;
2658
+ return;
2476
2659
  }
2477
2660
  checkCombo(a, b) {
2478
2661
  const key = `${Math.min(a, b)}+${Math.max(a, b)}`;
@@ -2482,6 +2665,8 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2482
2665
  let msg = [];
2483
2666
  if (effect.name)
2484
2667
  msg.push(`${effect.name}!`);
2668
+ if (effect.revwersalOfStrength)
2669
+ msg.push(`双方力量反转!`);
2485
2670
  if (effect.pierceDamage)
2486
2671
  msg.push(`对对方造成穿刺伤害${Math.max(0, effect.pierceDamage + (self.strength || 0))}`);
2487
2672
  if (effect.damage)
@@ -2543,7 +2728,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2543
2728
  ];
2544
2729
  const newState = this.simulateMove(move, simulatedState, 1);
2545
2730
  const score = this.aiSearch(
2546
- this.level,
2731
+ Math.round(this.level / 2),
2547
2732
  false,
2548
2733
  newState,
2549
2734
  -Infinity,
@@ -2602,7 +2787,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2602
2787
  }
2603
2788
  return moves;
2604
2789
  }
2605
- // 深度克隆玩家状态
2790
+ // 克隆玩家状态
2606
2791
  cloneState(state) {
2607
2792
  return {
2608
2793
  left: state.left,
@@ -2611,6 +2796,8 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2611
2796
  shield: state.shield,
2612
2797
  strength: state.strength,
2613
2798
  bleed: state.bleed,
2799
+ counterAttack: state.counterAttack,
2800
+ vulnerablility: state.vulnerablility,
2614
2801
  status: state.status
2615
2802
  };
2616
2803
  }
@@ -2623,14 +2810,13 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2623
2810
  attacker.status = 0 /* Normal */;
2624
2811
  if (attacker.status === 1 /* Stunned */) {
2625
2812
  attacker.status = 2 /* lastStunned */;
2626
- attacker = this.applyEffectToSelf(defender, attacker, SKILL_MAP["-1"]);
2813
+ this.applyEffect(attacker, defender, SKILL_MAP["-1"], false);
2627
2814
  } else {
2628
2815
  const sum = (handA + handB) % 10;
2629
2816
  attacker[handA === attacker.left ? "left" : "right"] = sum;
2630
2817
  const combo = this.checkCombo(attacker.left, attacker.right);
2631
2818
  const effect = combo ? combo : SKILL_MAP[sum.toString()] || {};
2632
- defender = this.applyEffectToEnemy(attacker, defender, effect, !!combo);
2633
- attacker = this.applyEffectToSelf(defender, attacker, effect);
2819
+ this.applyEffect(attacker, defender, effect, !!combo);
2634
2820
  }
2635
2821
  if (attackerIndex === 1) {
2636
2822
  const temp = attacker;
@@ -2644,13 +2830,39 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2644
2830
  evaluateState(state) {
2645
2831
  const ai = state[1];
2646
2832
  const player = state[0];
2833
+ const aiBaseHP = this.baseHP + this.level * this.aiLevelHp;
2834
+ const playerBaseHP = this.baseHP + this.level * this.playerLevelHP;
2647
2835
  let score = ai.hp - player.hp;
2648
- score += Math.min(ai.strength * 5, 50);
2649
- score -= Math.min(player.strength * 5, 50);
2836
+ if (ai.hp < aiBaseHP / 2)
2837
+ score -= Math.round(aiBaseHP / 4);
2838
+ if (player.hp < playerBaseHP / 2)
2839
+ score += Math.round(playerBaseHP / 4);
2840
+ if (ai.hp < aiBaseHP / 4)
2841
+ score -= Math.round(aiBaseHP / 4);
2842
+ if (player.hp < playerBaseHP / 4)
2843
+ score += Math.round(playerBaseHP / 4);
2844
+ if (ai.hp < aiBaseHP / 8)
2845
+ score -= Math.round(aiBaseHP / 4);
2846
+ if (player.hp < playerBaseHP / 8)
2847
+ score += Math.round(playerBaseHP / 4);
2848
+ score += Math.min(ai.strength * 5, 100);
2849
+ score -= Math.max(player.strength * 5, -100);
2650
2850
  score += ai.shield * 8;
2651
2851
  score -= player.shield * 8;
2652
- score -= Math.min(Math.round(ai.bleed * ai.bleed / 2.5), 900);
2653
- score += Math.min(Math.round(player.bleed * player.bleed / 2.5), 900);
2852
+ score -= Math.min(Math.round(ai.bleed * ai.bleed / 2.5));
2853
+ score += Math.min(Math.round(player.bleed * player.bleed / 2.5));
2854
+ if (ai.status === 1 /* Stunned */)
2855
+ score -= 10;
2856
+ if (player.status === 1 /* Stunned */)
2857
+ score += 10;
2858
+ if (ai.counterAttack > 0)
2859
+ score += Math.max(ai.counterAttack + (ai.strength || 0), 0);
2860
+ if (player.counterAttack > 0)
2861
+ score -= Math.max(player.counterAttack + (player.strength || 0), 0);
2862
+ if (ai.vulnerablility > 0)
2863
+ score -= Math.round(ai.vulnerablility * 4);
2864
+ if (player.vulnerablility > 0)
2865
+ score += Math.round(player.vulnerablility * 4);
2654
2866
  if (player.hp <= 0)
2655
2867
  score += 1e5;
2656
2868
  if (ai.hp <= 0)
@@ -2658,26 +2870,26 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2658
2870
  return score;
2659
2871
  }
2660
2872
  generateChat(Score) {
2661
- if (this.lastScore < 1e5 && Score > 1e5) {
2873
+ if (this.lastScore < 9e4 && Score > 9e4) {
2662
2874
  return "我觉得你要输了哦~";
2663
2875
  }
2664
2876
  if (this.lastScore < 0 && Score > 0) {
2665
2877
  return "局势发生变化了呢~";
2666
2878
  }
2667
- if (this.lastScore > -1e5 && Score < -1e5) {
2668
- return "感觉我要输了呢~";
2669
- }
2879
+ if (Score - this.lastScore > 10 && Math.random() > 0.5)
2880
+ return "看招!";
2670
2881
  }
2671
2882
  instuction = `游戏说明:
2672
2883
  这个游戏的基本玩法是:
2673
2884
  两个人玩,两只手分别可以做出"一到十"的手势,每一种手势代表一个招式。
2674
2885
  例如"三"是${SKILL_MAP["3"].name},"四"是${SKILL_MAP["4"].name}等;当两只手符合特定组合时还可以触发组合技;
2675
2886
  每个人在自己的回合可以选择用自己的一只手碰对方的另一只手,两者数值相加,如果超过十仅保留个位。
2676
- 例如自己的"一"碰对方的"二"变成"三"可以触发技能"${SKILL_MAP["3"].name}"对对方造成伤害。
2887
+ 例如自己的"一"碰对方的"二"变成"三"同时触发技能"${SKILL_MAP["3"].name}"对对方造成伤害。
2677
2888
  每次开局两人初始手势随机,由玩家先手,双方轮流行动。
2889
+ 当自身两手手势相同时,无论选择左手还是右手最终发生变化的都是左手。
2678
2890
  玩家初始有"${this.baseHP} + ${this.playerLevelHP} * 难度"血量。
2679
2891
  ai初始有"${this.baseHP} + ${this.aiLevelHp} * 难度"血量。
2680
- 玩家初始有"难度 / 3"护盾,"难度 / 5"力量,四舍五入取整。
2892
+ 玩家初始有"5 - 难度 / 2"护盾,四舍五入取整。
2681
2893
  血量没有上限,率先将对方血量减到零的人获胜:
2682
2894
  具体的技能设计如下:{
2683
2895
  一:${SKILL_MAP["1"].name}:造成${SKILL_MAP["1"].pierceDamage}穿刺伤害,${SKILL_MAP["1"].bleed}流血;
@@ -2707,6 +2919,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2707
2919
  七+八:${SKILL_MAP["7+8"].name}:造成${SKILL_MAP["7+8"].pierceDamage}穿刺伤害,破坏对方${SKILL_MAP["7+8"].destroyShield}层护盾,眩晕自己
2708
2920
  六+二:${SKILL_MAP["2+6"].name}:造成${SKILL_MAP["2+6"].damage}伤害,恢复${SKILL_MAP["2+6"].heal}生命,自身${SKILL_MAP["2+6"].selfbleed}流血,自身增加${SKILL_MAP["2+6"].selfstrengthChange}力量,眩晕对方
2709
2921
  三+七:${SKILL_MAP["3+7"].name}:获得${SKILL_MAP["3+7"].counterAttack}层反击,破坏对方${SKILL_MAP["3+7"].destroyShield}层护盾,对方左手数值增加${SKILL_MAP["3+7"].addLeft},右手数值增加${SKILL_MAP["3+7"].addRight}
2922
+ 九+十:${SKILL_MAP["0+9"].name}:逆转双方力量,然后增加自身${SKILL_MAP["0+9"].selfstrengthChange}力量,对方${SKILL_MAP["0+9"].strengthChange}力量
2710
2923
  十+十:${SKILL_MAP["0+0"].name}:对对方造成${SKILL_MAP["0+0"].bleed}流血,自身${SKILL_MAP["0+0"].selfstrengthChange}力量,然后立即结算对方全部流血
2711
2924
  }
2712
2925
  注:
@@ -2714,11 +2927,12 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2714
2927
  护盾效果:每一层护盾可阻挡下一次受到的普通伤害,如果伤害来源是组合技则消耗两层护盾(若仅有一层则消耗一层)。护盾上限为五层。
2715
2928
  眩晕效果:跳过自己的下一个回合,若上回合已经被眩晕,则本回合不受眩晕影响(即不可被连续眩晕)
2716
2929
  弱眩晕效果:若对方没有护盾,则眩晕对方一回合
2717
- 反击:下一回合若对方行动中有攻击,则对方受到反击层数点穿刺伤害,此效果受力量加成,且仅持续一回合
2930
+ 反击:下一回合若对方行动中有攻击,则对方攻击前受到反击层数点穿刺伤害,此效果受力量加成,且仅持续一回合
2718
2931
  穿刺伤害:不被护盾影响的伤害(不会消耗护盾)
2719
2932
  力量效果:每有一点力量,每次造成的伤害+1,若为负数则减一
2720
2933
  易伤效果:受到的普通伤害+50%,自己的回合结束时减少一层
2721
2934
  组合技:两只手势符合组合技条件时触发组合技,组合技的效果会覆盖普通技能的效果,组合无序
2935
+ bonus: 当行动导致关键效果时,获得额外奖励,在ai回合结束时结算,若ai被眩晕则奖励继承到下一回合
2722
2936
  `;
2723
2937
  };
2724
2938
  var OneTouchGame = class extends abstractGame {
@@ -2734,9 +2948,9 @@ var OneTouchGame = class extends abstractGame {
2734
2948
  level = parseInt(args[0]);
2735
2949
  else {
2736
2950
  setTimeout(() => {
2737
- session.send("未输入难度等级(2-10),默认设为5");
2951
+ session.send("未输入难度等级(2-10),默认设为3");
2738
2952
  }, 500);
2739
- level = 5;
2953
+ level = 3;
2740
2954
  }
2741
2955
  if (level < 2 || level > 10) {
2742
2956
  level = level < 2 ? 2 : 10;
@@ -2895,20 +3109,17 @@ var Game = class {
2895
3109
  return;
2896
3110
  case "一碰一":
2897
3111
  const oneTouchResult = result;
2898
- const oneTouchLevel = parseInt(oneTouchResult.message);
2899
- const oneTouchBonus = Math.floor(oneTouchLevel * (Math.random() * 2 + 9));
3112
+ const oneTouchBonus = oneTouchResult.bonus;
2900
3113
  if (oneTouchResult.win === 1 /* win */) {
2901
- updateUserP(ctx, user, oneTouchBonus * 2);
2902
- updateFavorability(ctx, user, oneTouchLevel * 2);
2903
- session.send("真厉害,奖励你" + oneTouchBonus * 2 + "p点,好感度+" + Math.round(oneTouchLevel * 0.2));
3114
+ updateUserP(ctx, user, oneTouchBonus);
3115
+ updateFavorability(ctx, user, Math.round(oneTouchBonus * 0.02));
3116
+ session.send("真厉害,奖励你" + oneTouchBonus + "p点,好感度+" + Math.round(oneTouchBonus * 0.02));
2904
3117
  } else if (oneTouchResult.win === 2 /* lose */) {
2905
- updateUserP(ctx, user, -oneTouchBonus);
2906
- session.send("真可惜,你输了" + oneTouchBonus + "p点");
2907
- } else if (oneTouchResult.win === 3 /* draw */)
2908
- session.send("平局,稍后再战吧");
2909
- else {
2910
- updateUserP(ctx, user, -oneTouchBonus);
2911
- session.send("游戏中断,你输了" + oneTouchBonus + "p点");
3118
+ updateUserP(ctx, user, oneTouchBonus);
3119
+ session.send("真可惜,你输了" + -oneTouchBonus + "p点");
3120
+ } else {
3121
+ updateUserP(ctx, user, oneTouchBonus);
3122
+ session.send("游戏中断,你输了" + -oneTouchBonus + "p点");
2912
3123
  }
2913
3124
  return;
2914
3125
  default:
@@ -2917,14 +3128,152 @@ var Game = class {
2917
3128
  }
2918
3129
  };
2919
3130
 
3131
+ // src/broadcast.ts
3132
+ var import_koishi13 = require("koishi");
3133
+ var logger12 = new import_koishi13.Logger("satori-ai-broadcast");
3134
+ var BroadcastManager = class {
3135
+ static {
3136
+ __name(this, "BroadcastManager");
3137
+ }
3138
+ ctx;
3139
+ config;
3140
+ idCounter = 0;
3141
+ broadcastMessages = /* @__PURE__ */ new Map();
3142
+ constructor(ctx, cfg) {
3143
+ this.ctx = ctx;
3144
+ this.config = cfg;
3145
+ this.idCounter = 0;
3146
+ ctx.command("sat.broadcast <text:text>", "推送广播", { authority: 4 }).alias("广播").option("delete", "-d <id:string>", { authority: 4 }).option("list", "-l", { authority: 4 }).option("img", "-i", { authority: 4 }).action(async ({ options }, prompt) => {
3147
+ refreshPuppeteer(this.ctx);
3148
+ if (options.list) {
3149
+ const list = this.broadcastMessages.size > 0 ? await this.getBroadcastList() : null;
3150
+ return list ? `${list}` : "当前没有广播";
3151
+ }
3152
+ if (options.delete) {
3153
+ return await this.deleteBroadcast(options.delete);
3154
+ } else {
3155
+ return await this.createBroadcast(prompt, options.img);
3156
+ }
3157
+ });
3158
+ }
3159
+ async createBroadcast(message, img) {
3160
+ const id = `broadcast-${this.idCounter++}`;
3161
+ const broadcast = new broadcastMessage(this.ctx, this.config, message, img);
3162
+ this.broadcastMessages.set(id, broadcast);
3163
+ logger12.info(`广播已创建 ID: ${id}`);
3164
+ return `广播已创建 ID: ${id}`;
3165
+ }
3166
+ async deleteBroadcast(id) {
3167
+ if (this.broadcastMessages.has(id)) {
3168
+ this.broadcastMessages.delete(id);
3169
+ logger12.info(`广播已删除 ID: ${id}`);
3170
+ return "已删除广播:" + id;
3171
+ } else {
3172
+ logger12.warn(`未找到广播 ID: ${id}`);
3173
+ return "未找到该广播";
3174
+ }
3175
+ }
3176
+ async seedBroadcast(session) {
3177
+ for (const [id, broadcast] of this.broadcastMessages.entries()) {
3178
+ if (broadcast.setTime.getTime() + 1e3 * 60 * this.config.waiting_time < Date.now()) {
3179
+ this.broadcastMessages.delete(id);
3180
+ logger12.info(`广播已过期 ID: ${id}`);
3181
+ continue;
3182
+ }
3183
+ await broadcast.sendMessage(session);
3184
+ }
3185
+ return;
3186
+ }
3187
+ async getBroadcastList() {
3188
+ const idList = Array.from(this.broadcastMessages.keys());
3189
+ const list = [];
3190
+ list.push("当前广播列表:");
3191
+ for (const id of idList) {
3192
+ const broadcast = this.broadcastMessages.get(id);
3193
+ if (broadcast) {
3194
+ const time = broadcast.setTime.toLocaleString("zh-CN", { hour12: false });
3195
+ const ddl = new Date(broadcast.setTime.getTime() + 1e3 * 60 * this.config.waiting_time).toLocaleString("zh-CN", { hour12: false });
3196
+ const message = broadcast.message;
3197
+ const alreadySentChannels = broadcast.getAlreadySentChannels();
3198
+ list.push(`ID: ${id}
3199
+ 消息: ${message}
3200
+ 已发送频道:
3201
+ ${alreadySentChannels}
3202
+ 创建时间: ${time}
3203
+ 过期时间: ${ddl}
3204
+ `);
3205
+ }
3206
+ }
3207
+ const result = list.length > 0 ? list.join("\n") : null;
3208
+ return result ? wrapInHTML3(result) : null;
3209
+ }
3210
+ };
3211
+ var broadcastMessage = class {
3212
+ static {
3213
+ __name(this, "broadcastMessage");
3214
+ }
3215
+ ctx;
3216
+ config;
3217
+ message;
3218
+ alreadySentChannels = /* @__PURE__ */ new Set();
3219
+ setTime;
3220
+ img;
3221
+ constructor(ctx, cfg, message, img) {
3222
+ this.message = message;
3223
+ this.alreadySentChannels = /* @__PURE__ */ new Set();
3224
+ this.ctx = ctx;
3225
+ this.config = cfg;
3226
+ this.img = img;
3227
+ this.setTime = /* @__PURE__ */ new Date();
3228
+ for (const channelId of this.config.Broadcast_block_channel) {
3229
+ this.alreadySentChannels.add(channelId);
3230
+ }
3231
+ }
3232
+ async sendMessage(session) {
3233
+ const channelId = session.channelId;
3234
+ if (this.alreadySentChannels.has(channelId))
3235
+ return;
3236
+ try {
3237
+ if (this.img) {
3238
+ session.send(await wrapInHTML3(this.message));
3239
+ } else {
3240
+ session.send(this.message);
3241
+ }
3242
+ logger12.info(`向群 ${channelId} 广播: ${this.message}`);
3243
+ this.alreadySentChannels.add(channelId);
3244
+ } catch (error) {
3245
+ logger12.error(`广播失败 ${channelId}: ${error}`);
3246
+ }
3247
+ }
3248
+ getAlreadySentChannels() {
3249
+ const alreadySentChannels = Array.from(this.alreadySentChannels).filter((channelId) => !this.config.Broadcast_block_channel.includes(channelId));
3250
+ return alreadySentChannels.length > 0 ? alreadySentChannels.join("\n") : "无";
3251
+ }
3252
+ };
3253
+ async function wrapInHTML3(str) {
3254
+ if (!puppeteer) {
3255
+ logger12.warn("puppeteer未就绪");
3256
+ return str;
3257
+ }
3258
+ return puppeteer.render(`<html><body>${str.replaceAll(/\n/g, "<BR>")}</body>
3259
+ <style>
3260
+ body {
3261
+ padding: 10px;
3262
+ display: inline-block;
3263
+ }
3264
+ </style>
3265
+ </html>`);
3266
+ }
3267
+ __name(wrapInHTML3, "wrapInHTML");
3268
+
2920
3269
  // src/index.tsx
2921
- var logger12 = new import_koishi13.Logger("satori-ai");
3270
+ var logger13 = new import_koishi14.Logger("satori-ai");
2922
3271
  var puppeteer = null;
2923
3272
  function refreshPuppeteer(ctx) {
2924
3273
  if (ctx.puppeteer) {
2925
3274
  puppeteer = ctx.puppeteer;
2926
3275
  } else {
2927
- logger12.warn("puppeteer未就绪");
3276
+ logger13.warn("puppeteer未就绪");
2928
3277
  }
2929
3278
  }
2930
3279
  __name(refreshPuppeteer, "refreshPuppeteer");
@@ -2940,6 +3289,7 @@ var SAT = class extends Sat {
2940
3289
  this.memoryManager = new MemoryManager(ctx, this.getMemoryConfig());
2941
3290
  this.portraitManager = new UserPortraitManager(ctx, config);
2942
3291
  this.moodManager = new MoodManager(ctx, config);
3292
+ this.broadcastManager = new BroadcastManager(ctx, config);
2943
3293
  ensureCensorFileExists(this.config.dataDir);
2944
3294
  ctx.middleware(createMiddleware(ctx, this, this.getMiddlewareConfig()));
2945
3295
  this.registerCommands(ctx);
@@ -2955,6 +3305,7 @@ var SAT = class extends Sat {
2955
3305
  ChannelParallelCount = /* @__PURE__ */ new Map();
2956
3306
  onlineUsers = [];
2957
3307
  moodManager;
3308
+ broadcastManager;
2958
3309
  usersToWarn = /* @__PURE__ */ new Map();
2959
3310
  game;
2960
3311
  getAPIConfig() {
@@ -3110,9 +3461,9 @@ var SAT = class extends Sat {
3110
3461
  const messages = generateAuxiliaryPrompt(prompt, response.content, user, this.getFavorabilityConfig());
3111
3462
  const result = await this.apiClient.auxiliaryChat(messages);
3112
3463
  if (result.error) {
3113
- logger12.error(`辅助判断失败:${result.content}`);
3464
+ logger13.error(`辅助判断失败:${result.content}`);
3114
3465
  } else {
3115
- logger12.info(`辅助判断:${result.content}`);
3466
+ logger13.info(`辅助判断:${result.content}`);
3116
3467
  return handleAuxiliaryResult(this.ctx, session, this.getFavorabilityConfig(), result.content);
3117
3468
  }
3118
3469
  }
@@ -3199,13 +3550,13 @@ var SAT = class extends Sat {
3199
3550
  await inputContentCheck(this.ctx, censored, session.userId, this.getFavorabilityConfig(), session, this.moodManager);
3200
3551
  }
3201
3552
  if (this.config.log_ask_response)
3202
- logger12.info(`用户 ${session.username}:${processedPrompt}`);
3553
+ logger13.info(`用户 ${session.username}:${processedPrompt}`);
3203
3554
  return censored;
3204
3555
  }
3205
3556
  // 生成回复
3206
3557
  async generateResponse(session, prompt) {
3207
3558
  if (this.getChannelParallelCount(session) > this.config.max_parallel_count) {
3208
- logger12.info(`频道 ${session.channelId} 并发数过高(${this.getChannelParallelCount(session)}),${session.username}等待中...`);
3559
+ logger13.info(`频道 ${session.channelId} 并发数过高(${this.getChannelParallelCount(session)}),${session.username}等待中...`);
3209
3560
  }
3210
3561
  while (this.getChannelParallelCount(session) > this.config.max_parallel_count) {
3211
3562
  this.updateChannelParallelCount(session, -1);
@@ -3213,7 +3564,7 @@ var SAT = class extends Sat {
3213
3564
  this.updateChannelParallelCount(session, 1);
3214
3565
  }
3215
3566
  const messages = await this.buildMessages(session, prompt);
3216
- logger12.info(`频道 ${session.channelId} 处理:${session.userId},剩余${this.getChannelParallelCount(session)}并发`);
3567
+ logger13.info(`频道 ${session.channelId} 处理:${session.userId},剩余${this.getChannelParallelCount(session)}并发`);
3217
3568
  const user = await getUser(this.ctx, session.userId);
3218
3569
  let response = await this.getChatResponse(user, messages);
3219
3570
  if (response.error)
@@ -3227,11 +3578,11 @@ var SAT = class extends Sat {
3227
3578
  let response = await this.apiClient.chat(user, messages);
3228
3579
  if (this.config.log_ask_response) {
3229
3580
  if (this.config.enable_favorability && this.config.enable_mood)
3230
- logger12.info(`Satori AI:(心情值:${this.moodManager.getMoodValue(user.userid)})${response.content}`);
3581
+ logger13.info(`Satori AI:(心情值:${this.moodManager.getMoodValue(user.userid)})${response.content}`);
3231
3582
  else
3232
- logger12.info(`Satori AI:${response.content}`);
3583
+ logger13.info(`Satori AI:${response.content}`);
3233
3584
  }
3234
- if (this.config.reasoner_filter)
3585
+ if (this.config.reasoner_filter && !response.error)
3235
3586
  response = filterResponse(response.content, this.config.reasoner_filter_word.split("-"));
3236
3587
  return response;
3237
3588
  }
@@ -3253,7 +3604,7 @@ var SAT = class extends Sat {
3253
3604
  messages.push({ role: "user", content: prompt });
3254
3605
  let payload = messages.map((msg) => msg.role + ":" + msg.content).join("\n");
3255
3606
  if (this.config.log_system_prompt)
3256
- logger12.info(`系统提示:
3607
+ logger13.info(`系统提示:
3257
3608
  ${payload}`);
3258
3609
  return messages;
3259
3610
  }
@@ -3358,7 +3709,7 @@ ${useNoReasoner ? promptForNoReasoner : promptForReasoner}
3358
3709
  response = `@${session.username} ` + response;
3359
3710
  }
3360
3711
  if (this.config.sentences_divide && response.length > 10) {
3361
- const sentences = splitSentences(response, this.config.min_sentences_length, this.config.max_sentences_length).map((text) => import_koishi13.h.text(text));
3712
+ const sentences = splitSentences(response, this.config.min_sentences_length, this.config.max_sentences_length).map((text) => import_koishi14.h.text(text));
3362
3713
  for (const sentence of sentences) {
3363
3714
  await session.send(sentence);
3364
3715
  await new Promise((resolve3) => setTimeout(resolve3, this.config.time_interval));
@@ -3371,7 +3722,7 @@ ${useNoReasoner ? promptForNoReasoner : promptForReasoner}
3371
3722
  if (!this.config.enable_warning)
3372
3723
  return;
3373
3724
  if (auxiliaryResult.includes("好感↓")) {
3374
- logger12.info(`在群${session.channelId}中,${session.username}骚扰我!`);
3725
+ logger13.info(`在群${session.channelId}中,${session.username}骚扰我!`);
3375
3726
  this.usersToWarn.set(session.channelId, session.username);
3376
3727
  }
3377
3728
  }
package/lib/types.d.ts CHANGED
@@ -240,6 +240,8 @@ export declare namespace Sat {
240
240
  warning_admin_id: string;
241
241
  maxRetryTimes: number;
242
242
  retry_delay_time: number;
243
+ Broadcast_block_channel: string[];
244
+ waiting_time: number;
243
245
  enable_game: boolean;
244
246
  game_block_channel: string[];
245
247
  enable_gobang: boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-satori-ai",
3
3
  "description": "基于 Satori 适配器的智能对话插件,具备用户画像、长期记忆、常识管理和好感度系统",
4
- "version": "1.3.3",
4
+ "version": "1.3.5",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "types": "lib/index.d.ts",