labag 2.6.6 → 3.0.0

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.
@@ -1,88 +0,0 @@
1
- import { patterns } from "../pattern";
2
- import { Mode } from "../mode";
3
- import { randInt } from "../utils/randInt";
4
-
5
- export default new Mode({
6
- active: false,
7
- name: "superhhh",
8
- rates: {
9
- gss: -19, // 36 -> 17
10
- hhh: -19, // 24 -> 5
11
- hentai: 1, // 17 -> 18
12
- handson: 7, // 12 -> 19
13
- kachu: 12, // 8 -> 20
14
- rrr: 18, // 3 -> 21
15
- },
16
- eventListener: {
17
- gameStart: (_, mode) => {
18
- mode.active = false;
19
- mode.variable.times = 0;
20
- mode.variable.score = 0;
21
- },
22
- roundStart: (_, mode) => {
23
- if (!mode.active) return;
24
- mode.variable.score = 0;
25
- mode.variable.times -= 1;
26
- },
27
- rollSlots: (_, mode) => {
28
- mode.variable.randNum = randInt(1, 100);
29
- },
30
- calculateScore: (game, mode) => {
31
- if (mode.active) return;
32
- if (
33
- game.patterns.every(
34
- (p) => p?.name === mode.variable.bindPattern.name,
35
- ) &&
36
- mode.variable.randNum <= mode.variable.rate
37
- ) {
38
- mode.variable.score += Math.round(game.score / 2);
39
- game.marginScore += mode.variable.score;
40
- }
41
- },
42
- roundEnd: (game, mode) => {
43
- const { patterns } = game;
44
- const { variable } = mode;
45
-
46
- let hhhCount = 0;
47
- let allHHH = true;
48
- for (const p of patterns) {
49
- if (p?.name === mode.variable.bindPattern.name) hhhCount++;
50
- else allHHH = false;
51
- }
52
-
53
- if (mode.active) {
54
- if (allHHH) {
55
- variable.times += mode.variable.extendTimes;
56
- }
57
- if (variable.times <= 0) {
58
- mode.active = false;
59
- }
60
- }
61
- if (!mode.active) {
62
- if (variable.randNum <= variable.rate && hhhCount > 0) {
63
- mode.active = true;
64
- variable.times += variable.bonusTimes;
65
-
66
- for (let i = 0; i < patterns.length; i++) {
67
- if (patterns[i]?.name === mode.variable.bindPattern.name) {
68
- patterns[i] = variable.pattern;
69
- }
70
- }
71
- }
72
- }
73
- },
74
- },
75
- variable: {
76
- times: 0,
77
- rate: 15,
78
- score: 0,
79
- randNum: 0,
80
- bindPattern: patterns[1],
81
- bonusTimes: 6,
82
- pattern: {
83
- name: "superhhh",
84
- scores: [1500, 800, 300],
85
- },
86
- extendTimes: 2,
87
- },
88
- });
package/src/pattern.ts DELETED
@@ -1,31 +0,0 @@
1
- import { Pattern } from "./types";
2
-
3
- /**
4
- * 定義遊戲中可用的圖案及其分數。
5
- */
6
- export const patterns = [
7
- {
8
- name: "gss",
9
- scores: [800, 400, 180],
10
- },
11
- {
12
- name: "hhh",
13
- scores: [1500, 800, 300],
14
- },
15
- {
16
- name: "hentai",
17
- scores: [2500, 1200, 500],
18
- },
19
- {
20
- name: "handson",
21
- scores: [2900, 1450, 690],
22
- },
23
- {
24
- name: "kachu",
25
- scores: [12000, 8000, 1250],
26
- },
27
- {
28
- name: "rrr",
29
- scores: [20000, 12000, 2500],
30
- },
31
- ] as const satisfies readonly Pattern[];
@@ -1,74 +0,0 @@
1
- import { LaBaG } from "./labag";
2
- import { GameRecord } from "./recorder";
3
-
4
- export class RecordChecker {
5
- private game: LaBaG;
6
-
7
- constructor(game: LaBaG) {
8
- this.game = game;
9
- }
10
-
11
- /**
12
- * 驗證紀錄的分數是否正確。
13
- * @param record - 要驗證的遊戲紀錄。
14
- * @returns 如果計算出的分數與紀錄中的分數一致則返回 true,否則返回 false。
15
- */
16
- check(record: GameRecord): boolean {
17
- const calculatedScore = this.calculateScore(record);
18
- return calculatedScore === record.score;
19
- }
20
-
21
- /**
22
- * 根據紀錄還原並計算分數。
23
- * @param record - 遊戲紀錄。
24
- * @returns 計算出的分數。
25
- */
26
- calculateScore(record: GameRecord): number {
27
- // 警告:此操作會重置並覆蓋當前傳入的遊戲實例狀態
28
- this.game.init();
29
- this.game.times = record.times;
30
-
31
- for (const round of record.rounds) {
32
- if (!this.game.isRunning) {
33
- break;
34
- }
35
-
36
- // 1. 回合開始
37
- this.game.roundStart();
38
-
39
- // 2. 觸發 rollSlots 事件,這會執行所有模式的隨機邏輯
40
- this.game.rollSlots();
41
-
42
- // 3. 覆蓋隨機數字與圖案 (確保使用紀錄中的數值)
43
- const { ranges } = this.game.getCurrentConfig();
44
-
45
- for (let i = 0; i < 3; i++) {
46
- const num = round.randNums?.[i.toString()] ?? 0;
47
- this.game.randNums[i] = num;
48
-
49
- const match = ranges.find((r) => num <= r.threshold);
50
- this.game.patterns[i] = match ? match.pattern : null;
51
- }
52
-
53
- // 4. 覆蓋模式的隨機變數 (確保使用紀錄中的數值)
54
- this.game.modes.forEach((mode) => {
55
- const recordedNum = round.randNums?.[mode.name];
56
- if (recordedNum !== undefined && mode.variable) {
57
- mode.variable.randNum = recordedNum;
58
- }
59
- });
60
-
61
- // 5. 計算分數
62
- this.game.calculateScore();
63
-
64
- // 6. 回合結束
65
- this.game.roundEnd();
66
- }
67
-
68
- if (!this.game.isRunning) {
69
- this.game.gameOver();
70
- }
71
-
72
- return this.game.score;
73
- }
74
- }
package/src/recorder.ts DELETED
@@ -1,93 +0,0 @@
1
- import { LaBaG } from "./labag";
2
-
3
- export type RoundRecord = {
4
- readonly randNums: Readonly<Record<string, number>>;
5
- };
6
-
7
- export type GameRecord = {
8
- times: number;
9
- score: number;
10
- readonly rounds: ReadonlyArray<RoundRecord>;
11
- };
12
-
13
- export type RecorderOptions = {
14
- debug?: boolean; // 若為 true,會以 console.debug 輸出紀錄
15
- };
16
- export class Recorder {
17
- private game: LaBaG;
18
- #rounds: RoundRecord[] = [];
19
- private score: number = 0;
20
- private started = false;
21
- private debug = false;
22
-
23
- constructor(gameInstance: LaBaG, options?: RecorderOptions) {
24
- this.game = gameInstance;
25
- this.debug = options?.debug ?? false;
26
- }
27
-
28
- get rounds(): ReadonlyArray<RoundRecord> {
29
- return this.#rounds;
30
- }
31
-
32
- private onRoundEnd = (g: LaBaG) => {
33
- const randNums: Record<string, number> = {};
34
-
35
- g.randNums.forEach((value, key) => {
36
- if (typeof value === "number" && !Number.isNaN(value)) {
37
- randNums[key] = value;
38
- }
39
- });
40
-
41
- // 收集各模式的 randNum(若為數值且有效)
42
- g.modes.forEach((mode) => {
43
- const rn = mode?.variable?.randNum;
44
- if (typeof rn === "number" && !Number.isNaN(rn)) {
45
- randNums[mode.name] = rn;
46
- }
47
- });
48
-
49
- const round: RoundRecord = {
50
- randNums,
51
- };
52
-
53
- if (this.debug)
54
- console.debug("recorder:onRoundEnd", {
55
- round,
56
- score: g.score,
57
- });
58
-
59
- this.#rounds.push(round);
60
- this.score = g.score;
61
- };
62
-
63
- init(clearExisting = true) {
64
- if (this.started) return;
65
- if (clearExisting) {
66
- this.clear();
67
- }
68
- if (typeof this.game.addEventListener === "function") {
69
- this.game.addEventListener("roundEnd", this.onRoundEnd);
70
- this.started = true;
71
- }
72
- }
73
-
74
- dispose() {
75
- if (!this.started) return;
76
- this.game.removeEventListener("roundEnd", this.onRoundEnd);
77
- this.started = false;
78
- }
79
- clear() {
80
- if (this.debug) console.debug("clear");
81
- this.score = 0;
82
- this.#rounds = [];
83
- }
84
-
85
- getRecord(): GameRecord {
86
- if (this.debug) console.debug("get");
87
- return {
88
- times: this.game.times,
89
- score: this.score,
90
- rounds: this.#rounds,
91
- };
92
- }
93
- }