-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mod.ts
170 lines (164 loc) · 4.96 KB
/
mod.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
export interface RawGachaData {
tier: number;
featured?: boolean;
result: any;
chance: number;
}
export interface GachaData {
tier: number;
result: any;
chance: number;
}
export interface GachaChoice {
result: any;
chance: number;
}
export interface GachaTier {
items: number;
chance: number;
tier: number;
}
/**
* Gacha system for Deno
* @class GachaMachine
*/
export class GachaMachine {
items: GachaData[];
tiers: GachaTier[];
pool: number[];
rawItems: GachaData[];
/**
* A gacha system
* @param {GachaData[]} items - Array of items featured in the gacha
* @param {number[]} pool - Array of tiers featured in the gacha
*/
constructor(items: Array<RawGachaData>, pool: Array<number> = []) {
this.items = [];
this.tiers = [];
this.pool = pool;
if(!this.pool || this.pool.length === 0) {
this.pool = items.filter((x, i) => items.indexOf(items.find(y => y.tier === x.tier) || x) !== i).map(x => x.tier) || [1]
}
this.pool.forEach(x => {
if(!items.map(y => y.tier).includes(x)) throw new Error(`None of the items in the machine match one or more tiers from the provided pool (${x}).`)
})
// console.log(this.pool)
this.rawItems = items;
this.configItems(items);
this.configTiers(items);
}
/**
* Configure gacha items (meant for internal use)
* @param {RawGachaData[]} items - Array of gacha items
* @returns {GachaData[]} newItems
*/
configItems(items: RawGachaData[]): GachaData[] {
let newItems: GachaData[] = items = items.sort((a, b) => a.tier - b.tier)
.map((x) => ({
chance: x.featured ? (x.chance + 1) : x.chance,
result: x.result,
tier: x.tier,
}));
this.items = newItems;
return newItems;
}
/**
* Configure gacha tiers (meant for internal use).
* @param {RawGachaData[]} items - Array of gacha items.
* @returns {GachaTier[]} tierList - Array of gacha tiers.
*/
configTiers(items: Array<RawGachaData>): GachaTier[] {
let tiers: GachaTier[] = [];
for (let i = 0; i < this.pool.length; ++i) {
tiers[this.pool[i]] = { items: 0, chance: 0, tier: this.pool[i] };
}
for (let i = items.length; i > 0; --i) {
if (!this.pool.includes(items[i - 1].tier)) continue;
tiers[items[i - 1].tier].items += 1;
tiers[items[i - 1].tier].chance += items[i - 1].chance;
}
let tierList = [];
for (let i in tiers) {
tierList.push(tiers[i]);
}
this.tiers = tierList;
return tierList;
}
/**
* @param {number} num - Number of items to roll.
* @param {boolean} detailed - Whether to return the entire roll object instead of just the result.
* @param {number[]} pool - Custom pool to use for this roll.
* @returns {any[]} array of results.
*/
get(num = 1, detailed = false, pool: number[] = []): GachaChoice[] | any[] {
if(pool.length > 0) {
const newMachine = new GachaMachine(this.rawItems, pool)
return newMachine.get(num, detailed)
}
let result = [];
for (let i = num; i > 0; --i) {
result.push(this._get(detailed));
}
return result;
}
_get(detailed = false) {
let tier = GachaMachine._roll(
this.tiers.map((x) => ({ chance: x.chance, result: x.tier })),
);
const result = GachaMachine._roll(
this.items.filter((x) => x.tier == tier.result),
);
return detailed ? result : result.result;
}
/**
* Roll one from an array of gacha choices.
* @param {GachaChoice[]} choices - Choices to roll from.
* @returns {GachaChoice} rolled.
*/
static _roll(choices: GachaChoice[]): GachaChoice {
let filteredChoices = [];
let total = 0.0;
for (let i = 0; i < choices.length; ++i) {
if (choices[i].chance > 0.0) {
filteredChoices.push(choices[i]);
total += choices[i].chance;
}
}
let result = Math.random() * total;
let going = 0.0;
for (let i = 0; i < filteredChoices.length; ++i) {
going += filteredChoices[i].chance;
if (result < going) {
return filteredChoices[i];
}
}
return filteredChoices[Math.floor(Math.random() * filteredChoices.length)];
}
/**
* Create a gacha item
* @param {any} result - Data of the item.
* @param {number} chance - Weight of the item. More weight = more common.
* @param {number} tier - Tier of the item (optional and defaults to 1).
* @param {boolean} featured - Whether the item should be featured in its pool.
* @returns {RawGachaData} item - Item to be passed to the constructor
*/
static createItem(
result: any,
chance = 1,
tier = 1,
featured = false,
): RawGachaData {
return { result, chance, tier, featured };
}
/**
* Create a gacha choice
* @param {any} result - Any item to be rolled
* @param {number} chance - Weight of the item
* @returns {GachaChoice}
*/
static createRollChoice(result: any, chance = 1): GachaChoice {
return { result, chance };
}
}
export { GachaMachine as Fortuna };
export { GachaMachine as default };