@schedule1-tools/mixer 0.0.1 → 0.1.2
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.
- package/README.md +18 -8
- package/dist/core/effectSet.d.ts +1 -28
- package/dist/core/effectSet.js +9 -109
- package/dist/core/mixer.js +40 -46
- package/dist/data/products.js +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,12 +10,11 @@ npm install @schedule1-tools/mixer
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
import { decodeMixState, encodeMixState, mixSubstances } from '@schedule1-tools/mixer';
|
|
13
|
+
### Calculate a mix
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
```typescript
|
|
17
16
|
const result = mixSubstances('OG Kush', ['Cuke', 'Flu Medicine', 'Gasoline']);
|
|
18
|
-
|
|
17
|
+
|
|
19
18
|
/*
|
|
20
19
|
{
|
|
21
20
|
effects: [ 'Be', 'Eu', 'Se', 'To' ],
|
|
@@ -25,17 +24,24 @@ console.log(result);
|
|
|
25
24
|
profitMargin: 0.27
|
|
26
25
|
}
|
|
27
26
|
*/
|
|
27
|
+
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
### Encode a mix state for sharing
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
30
32
|
const encoded = encodeMixState({
|
|
31
33
|
product: 'OG Kush',
|
|
32
34
|
substances: ['Cuke', 'Flu Medicine', 'Gasoline'],
|
|
33
35
|
});
|
|
34
|
-
console.log(encoded); // "T0cgS3VzaDpBQkM"
|
|
35
36
|
|
|
36
|
-
//
|
|
37
|
+
// "T0cgS3VzaDpBQkM"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Decode a mix state
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
37
43
|
const decoded = decodeMixState('T0cgS3VzaDpBQkM');
|
|
38
|
-
|
|
44
|
+
|
|
39
45
|
/*
|
|
40
46
|
{
|
|
41
47
|
product: 'OG Kush',
|
|
@@ -52,3 +58,7 @@ The package also exports the following data objects:
|
|
|
52
58
|
- `products`: Information about all products
|
|
53
59
|
- `substances`: Information about all substances
|
|
54
60
|
- `effectRulesBySubstance`: Rules for how substances transform effects
|
|
61
|
+
|
|
62
|
+
## Notice
|
|
63
|
+
|
|
64
|
+
This is a fan-made project and is not affiliated with, authorized, maintained, sponsored, or endorsed by the developers of Schedule I the game. All game-related content, including but not limited to names, trademarks, and copyrights, belong to their respective owners.
|
package/dist/core/effectSet.d.ts
CHANGED
|
@@ -1,38 +1,11 @@
|
|
|
1
1
|
import type { EffectCode } from '../types';
|
|
2
2
|
export declare class EffectSet {
|
|
3
|
-
private
|
|
4
|
-
private static readonly indexToEffect;
|
|
5
|
-
private static initialized;
|
|
6
|
-
private bits;
|
|
7
|
-
/**
|
|
8
|
-
* Initialize the static mapping between effect codes and bit positions
|
|
9
|
-
*/
|
|
10
|
-
private static initialize;
|
|
3
|
+
private effects;
|
|
11
4
|
constructor(initialEffects?: EffectCode[]);
|
|
12
|
-
/**
|
|
13
|
-
* Add an effect to the set
|
|
14
|
-
* @returns true if the effect was added, false if it was already present
|
|
15
|
-
*/
|
|
16
5
|
add(effect: EffectCode): boolean;
|
|
17
|
-
/**
|
|
18
|
-
* Remove an effect from the set
|
|
19
|
-
* @returns true if the effect was removed, false if it wasn't present
|
|
20
|
-
*/
|
|
21
6
|
remove(effect: EffectCode): boolean;
|
|
22
|
-
/**
|
|
23
|
-
* Check if an effect is in the set
|
|
24
|
-
*/
|
|
25
7
|
has(effect: EffectCode): boolean;
|
|
26
|
-
/**
|
|
27
|
-
* Convert the set to an array of effect codes
|
|
28
|
-
*/
|
|
29
8
|
toArray(): EffectCode[];
|
|
30
|
-
/**
|
|
31
|
-
* Get the number of effects in the set
|
|
32
|
-
*/
|
|
33
9
|
size(): number;
|
|
34
|
-
/**
|
|
35
|
-
* Create a copy of this effect set
|
|
36
|
-
*/
|
|
37
10
|
clone(): EffectSet;
|
|
38
11
|
}
|
package/dist/core/effectSet.js
CHANGED
|
@@ -2,132 +2,32 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EffectSet = void 0;
|
|
4
4
|
class EffectSet {
|
|
5
|
-
/**
|
|
6
|
-
* Initialize the static mapping between effect codes and bit positions
|
|
7
|
-
*/
|
|
8
|
-
static initialize() {
|
|
9
|
-
if (this.initialized)
|
|
10
|
-
return;
|
|
11
|
-
const effects = [
|
|
12
|
-
'Ag',
|
|
13
|
-
'At',
|
|
14
|
-
'Ba',
|
|
15
|
-
'Be',
|
|
16
|
-
'Ca',
|
|
17
|
-
'Cd',
|
|
18
|
-
'Cy',
|
|
19
|
-
'Di',
|
|
20
|
-
'El',
|
|
21
|
-
'En',
|
|
22
|
-
'Eu',
|
|
23
|
-
'Ex',
|
|
24
|
-
'Fc',
|
|
25
|
-
'Fo',
|
|
26
|
-
'Gi',
|
|
27
|
-
'Gl',
|
|
28
|
-
'Je',
|
|
29
|
-
'La',
|
|
30
|
-
'Lf',
|
|
31
|
-
'Mu',
|
|
32
|
-
'Pa',
|
|
33
|
-
'Re',
|
|
34
|
-
'Sc',
|
|
35
|
-
'Se',
|
|
36
|
-
'Sh',
|
|
37
|
-
'Si',
|
|
38
|
-
'Sl',
|
|
39
|
-
'Sm',
|
|
40
|
-
'Sn',
|
|
41
|
-
'Sp',
|
|
42
|
-
'To',
|
|
43
|
-
'Tp',
|
|
44
|
-
'Tt',
|
|
45
|
-
'Zo',
|
|
46
|
-
];
|
|
47
|
-
effects.forEach((effect, index) => {
|
|
48
|
-
this.effectToIndex.set(effect, index);
|
|
49
|
-
this.indexToEffect.set(index, effect);
|
|
50
|
-
});
|
|
51
|
-
this.initialized = true;
|
|
52
|
-
}
|
|
53
5
|
constructor(initialEffects = []) {
|
|
54
|
-
this.
|
|
55
|
-
EffectSet.initialize();
|
|
56
|
-
for (const effect of initialEffects) {
|
|
57
|
-
this.add(effect);
|
|
58
|
-
}
|
|
6
|
+
this.effects = new Set(initialEffects);
|
|
59
7
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Add an effect to the set
|
|
62
|
-
* @returns true if the effect was added, false if it was already present
|
|
63
|
-
*/
|
|
64
8
|
add(effect) {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
return false;
|
|
68
|
-
const mask = 1 << index;
|
|
69
|
-
const alreadyExists = (this.bits & mask) !== 0;
|
|
70
|
-
this.bits |= mask;
|
|
9
|
+
const alreadyExists = this.effects.has(effect);
|
|
10
|
+
this.effects.add(effect);
|
|
71
11
|
return !alreadyExists;
|
|
72
12
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Remove an effect from the set
|
|
75
|
-
* @returns true if the effect was removed, false if it wasn't present
|
|
76
|
-
*/
|
|
77
13
|
remove(effect) {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
return false;
|
|
81
|
-
const mask = 1 << index;
|
|
82
|
-
const existed = (this.bits & mask) !== 0;
|
|
83
|
-
this.bits &= ~mask;
|
|
14
|
+
const existed = this.effects.has(effect);
|
|
15
|
+
this.effects.delete(effect);
|
|
84
16
|
return existed;
|
|
85
17
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Check if an effect is in the set
|
|
88
|
-
*/
|
|
89
18
|
has(effect) {
|
|
90
|
-
|
|
91
|
-
if (index === undefined)
|
|
92
|
-
return false;
|
|
93
|
-
return (this.bits & (1 << index)) !== 0;
|
|
19
|
+
return this.effects.has(effect);
|
|
94
20
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Convert the set to an array of effect codes
|
|
97
|
-
*/
|
|
98
21
|
toArray() {
|
|
99
|
-
|
|
100
|
-
for (let i = 0; i < 34; i++) {
|
|
101
|
-
if ((this.bits & (1 << i)) !== 0) {
|
|
102
|
-
const effect = EffectSet.indexToEffect.get(i);
|
|
103
|
-
if (effect)
|
|
104
|
-
result.push(effect);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return result;
|
|
22
|
+
return Array.from(this.effects);
|
|
108
23
|
}
|
|
109
|
-
/**
|
|
110
|
-
* Get the number of effects in the set
|
|
111
|
-
*/
|
|
112
24
|
size() {
|
|
113
|
-
|
|
114
|
-
let n = this.bits;
|
|
115
|
-
while (n) {
|
|
116
|
-
n &= n - 1;
|
|
117
|
-
count++;
|
|
118
|
-
}
|
|
119
|
-
return count;
|
|
25
|
+
return this.effects.size;
|
|
120
26
|
}
|
|
121
|
-
/**
|
|
122
|
-
* Create a copy of this effect set
|
|
123
|
-
*/
|
|
124
27
|
clone() {
|
|
125
28
|
const clone = new EffectSet();
|
|
126
|
-
clone.
|
|
29
|
+
clone.effects = new Set(this.effects);
|
|
127
30
|
return clone;
|
|
128
31
|
}
|
|
129
32
|
}
|
|
130
33
|
exports.EffectSet = EffectSet;
|
|
131
|
-
EffectSet.effectToIndex = new Map();
|
|
132
|
-
EffectSet.indexToEffect = new Map();
|
|
133
|
-
EffectSet.initialized = false;
|
package/dist/core/mixer.js
CHANGED
|
@@ -16,16 +16,40 @@ function mixSubstances(product, substanceCodes) {
|
|
|
16
16
|
}
|
|
17
17
|
const productInfo = products_1.products[product];
|
|
18
18
|
const effectsSet = new effectSet_1.EffectSet(productInfo.effects);
|
|
19
|
-
const processedEffects = new effectSet_1.EffectSet();
|
|
20
|
-
const removedEffects = new effectSet_1.EffectSet();
|
|
21
19
|
let totalCost = 0;
|
|
22
20
|
for (const code of substanceCodes) {
|
|
23
21
|
const substance = substances_1.substances[code];
|
|
24
22
|
if (!substance)
|
|
25
23
|
continue;
|
|
26
24
|
totalCost += substance.price;
|
|
27
|
-
|
|
28
|
-
if (
|
|
25
|
+
const rules = rules_1.effectRulesBySubstance[code];
|
|
26
|
+
if (rules && rules.length > 0) {
|
|
27
|
+
const initialEffects = effectsSet.clone();
|
|
28
|
+
const processedEffects = new effectSet_1.EffectSet();
|
|
29
|
+
const removedEffects = new effectSet_1.EffectSet();
|
|
30
|
+
const appliedRules = new Set();
|
|
31
|
+
// Phase 1: Apply rules where conditions are met in the initial state
|
|
32
|
+
for (let i = 0; i < rules.length; i++) {
|
|
33
|
+
const rule = rules[i];
|
|
34
|
+
if (checkRulePreconditions(rule, initialEffects)) {
|
|
35
|
+
applyReplaceEffects(rule.replace, initialEffects, effectsSet, processedEffects, removedEffects);
|
|
36
|
+
appliedRules.add(i);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Phase 2: Apply rules where conditions are met after phase 1
|
|
40
|
+
for (let i = 0; i < rules.length; i++) {
|
|
41
|
+
if (appliedRules.has(i))
|
|
42
|
+
continue;
|
|
43
|
+
const rule = rules[i];
|
|
44
|
+
if (meetsPhaseTwo(rule, initialEffects, effectsSet, removedEffects)) {
|
|
45
|
+
if (canApplyTransformation(rule.replace, effectsSet)) {
|
|
46
|
+
applyTransformations(rule.replace, effectsSet, processedEffects);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Add substance effects AFTER applying rules
|
|
52
|
+
if (effectsSet.size() < MAX_EFFECTS && substance.effect) {
|
|
29
53
|
for (const effect of substance.effect) {
|
|
30
54
|
if (!effectsSet.has(effect)) {
|
|
31
55
|
effectsSet.add(effect);
|
|
@@ -37,9 +61,8 @@ function mixSubstances(product, substanceCodes) {
|
|
|
37
61
|
}
|
|
38
62
|
const finalEffects = effectsSet.toArray().slice(0, MAX_EFFECTS);
|
|
39
63
|
const effectValue = calculateEffectValue(finalEffects);
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const profit = sellPrice - totalCost - productPrice;
|
|
64
|
+
const sellPrice = Math.round(productInfo.price * (1 + effectValue));
|
|
65
|
+
const profit = sellPrice - totalCost;
|
|
43
66
|
const profitMargin = Math.round((profit / sellPrice) * 100) / 100;
|
|
44
67
|
return {
|
|
45
68
|
effects: finalEffects,
|
|
@@ -49,35 +72,6 @@ function mixSubstances(product, substanceCodes) {
|
|
|
49
72
|
profitMargin,
|
|
50
73
|
};
|
|
51
74
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Apply the rules for a substance to the current effect set
|
|
54
|
-
*/
|
|
55
|
-
function applySubstanceRules(substanceCode, effectsSet, processedEffects, removedEffects) {
|
|
56
|
-
const rules = rules_1.effectRulesBySubstance[substanceCode];
|
|
57
|
-
if (!rules || rules.length === 0)
|
|
58
|
-
return;
|
|
59
|
-
const initialEffects = effectsSet.clone();
|
|
60
|
-
const appliedRules = new Set();
|
|
61
|
-
// Phase 1: Apply rules where conditions are met in the initial state
|
|
62
|
-
for (let i = 0; i < rules.length; i++) {
|
|
63
|
-
const rule = rules[i];
|
|
64
|
-
if (checkRulePreconditions(rule, initialEffects)) {
|
|
65
|
-
applyReplaceEffects(rule.replace, initialEffects, effectsSet, processedEffects, removedEffects);
|
|
66
|
-
appliedRules.add(i);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// Phase 2: Apply rules where conditions are met after phase 1
|
|
70
|
-
for (let i = 0; i < rules.length; i++) {
|
|
71
|
-
if (appliedRules.has(i))
|
|
72
|
-
continue;
|
|
73
|
-
const rule = rules[i];
|
|
74
|
-
if (meetsPhaseTwo(rule, initialEffects, effectsSet, removedEffects)) {
|
|
75
|
-
if (canApplyTransformation(rule.replace, effectsSet)) {
|
|
76
|
-
applyTransformations(rule.replace, effectsSet, processedEffects);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
75
|
/**
|
|
82
76
|
* Check if a rule's preconditions are met
|
|
83
77
|
*/
|
|
@@ -125,16 +119,6 @@ function meetsPhaseTwo(rule, initialEffects, currentEffects, removedEffects) {
|
|
|
125
119
|
}
|
|
126
120
|
return true;
|
|
127
121
|
}
|
|
128
|
-
/**
|
|
129
|
-
* Check if a transformation can be applied
|
|
130
|
-
*/
|
|
131
|
-
function canApplyTransformation(replace, effectsSet) {
|
|
132
|
-
for (const oldEffect of Object.keys(replace)) {
|
|
133
|
-
if (effectsSet.has(oldEffect))
|
|
134
|
-
return true;
|
|
135
|
-
}
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
122
|
/**
|
|
139
123
|
* Apply effect replacements
|
|
140
124
|
*/
|
|
@@ -148,6 +132,16 @@ function applyReplaceEffects(replace, initialEffects, effectsSet, processedEffec
|
|
|
148
132
|
}
|
|
149
133
|
}
|
|
150
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if a transformation can be applied
|
|
137
|
+
*/
|
|
138
|
+
function canApplyTransformation(replace, effectsSet) {
|
|
139
|
+
for (const oldEffect of Object.keys(replace)) {
|
|
140
|
+
if (effectsSet.has(oldEffect))
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
151
145
|
/**
|
|
152
146
|
* Apply transformations to effects
|
|
153
147
|
*/
|
package/dist/data/products.js
CHANGED
|
@@ -22,16 +22,16 @@ exports.products = {
|
|
|
22
22
|
effects: ['Se'],
|
|
23
23
|
abbreviation: 'GE',
|
|
24
24
|
},
|
|
25
|
-
Cocaine: {
|
|
26
|
-
price: 150,
|
|
27
|
-
effects: [],
|
|
28
|
-
abbreviation: 'CE',
|
|
29
|
-
},
|
|
30
25
|
Meth: {
|
|
31
26
|
price: 70,
|
|
32
27
|
effects: [],
|
|
33
28
|
abbreviation: 'MH',
|
|
34
29
|
},
|
|
30
|
+
Cocaine: {
|
|
31
|
+
price: 150,
|
|
32
|
+
effects: [],
|
|
33
|
+
abbreviation: 'CE',
|
|
34
|
+
},
|
|
35
35
|
};
|
|
36
36
|
exports.productAbbreviations = Object.entries(exports.products).reduce((acc, [product, data]) => {
|
|
37
37
|
acc[data.abbreviation] = product;
|