libram 0.4.1 → 0.4.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/dist/Clan.js +268 -485
- package/dist/Copier.js +11 -48
- package/dist/Dungeon.js +77 -157
- package/dist/Kmail.d.ts +3 -1
- package/dist/Kmail.js +92 -243
- package/dist/Path.js +68 -120
- package/dist/ascend.js +153 -172
- package/dist/combat.d.ts +84 -0
- package/dist/combat.js +294 -387
- package/dist/console.js +13 -36
- package/dist/diet/index.d.ts +23 -8
- package/dist/diet/index.js +320 -426
- package/dist/diet/knapsack.d.ts +1 -1
- package/dist/diet/knapsack.js +90 -100
- package/dist/dungeons/Dreadsylvania.d.ts +4 -0
- package/dist/dungeons/Dreadsylvania.js +14 -0
- package/dist/dungeons/Dungeon.d.ts +28 -0
- package/dist/dungeons/Dungeon.js +99 -0
- package/dist/dungeons/Hobopolis.d.ts +4 -0
- package/dist/dungeons/Hobopolis.js +14 -0
- package/dist/dungeons/SlimeTube.d.ts +4 -0
- package/dist/dungeons/SlimeTube.js +14 -0
- package/dist/freerun.d.ts +16 -4
- package/dist/freerun.js +88 -99
- package/dist/index.d.ts +3 -1
- package/dist/index.js +21 -300
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +264 -515
- package/dist/logger.js +23 -63
- package/dist/maximize.js +289 -562
- package/dist/modifier.js +35 -46
- package/dist/modifierTypes.js +8 -22
- package/dist/mood.js +220 -531
- package/dist/property.d.ts +2 -0
- package/dist/property.js +96 -242
- package/dist/propertyTypes.d.ts +2 -2
- package/dist/propertyTypes.js +1 -0
- package/dist/propertyTyping.js +42 -53
- package/dist/resources/2007/CandyHearts.d.ts +9 -0
- package/dist/resources/2007/CandyHearts.js +24 -0
- package/dist/resources/2008/DivineFavors.d.ts +9 -0
- package/dist/resources/2008/DivineFavors.js +27 -0
- package/dist/resources/2009/Bandersnatch.js +37 -112
- package/dist/resources/2009/LoveSongs.d.ts +9 -0
- package/dist/resources/2009/LoveSongs.js +24 -0
- package/dist/resources/2009/SpookyPutty.js +20 -46
- package/dist/resources/2010/Brickos.d.ts +9 -0
- package/dist/resources/2010/Brickos.js +21 -0
- package/dist/resources/2010/CrownOfThrones.d.ts +9 -0
- package/dist/resources/2010/CrownOfThrones.js +550 -374
- package/dist/resources/2011/Gygaxian.d.ts +9 -0
- package/dist/resources/2011/Gygaxian.js +24 -0
- package/dist/resources/2011/ObtuseAngel.js +21 -63
- package/dist/resources/2012/RainDoh.js +14 -40
- package/dist/resources/2012/Resolutions.d.ts +9 -0
- package/dist/resources/2012/Resolutions.js +28 -0
- package/dist/resources/2013/Florist.d.ts +1 -0
- package/dist/resources/2013/Florist.js +137 -207
- package/dist/resources/2013/PulledTaffy.d.ts +9 -0
- package/dist/resources/2013/PulledTaffy.js +33 -0
- package/dist/resources/2014/WinterGarden.js +15 -43
- package/dist/resources/2015/ChateauMantegna.js +52 -86
- package/dist/resources/2015/MayoClinic.d.ts +1 -0
- package/dist/resources/2015/MayoClinic.js +30 -65
- package/dist/resources/2016/SourceTerminal.d.ts +1 -0
- package/dist/resources/2016/SourceTerminal.js +114 -237
- package/dist/resources/2016/Witchess.js +33 -59
- package/dist/resources/2017/TunnelOfLove.js +62 -111
- package/dist/resources/2018/SongBoom.js +32 -68
- package/dist/resources/2019/BeachComb.js +26 -44
- package/dist/resources/2019/Snapper.d.ts +28 -0
- package/dist/resources/2019/Snapper.js +70 -0
- package/dist/resources/2020/Guzzlr.js +79 -163
- package/dist/resources/LibramSummon.d.ts +12 -0
- package/dist/resources/LibramSummon.js +66 -0
- package/dist/resources/index.d.ts +3 -1
- package/dist/resources/index.js +19 -105
- package/dist/resources/putty-likes.js +15 -30
- package/dist/since.d.ts +1 -0
- package/dist/since.js +56 -112
- package/dist/template-string.js +40 -132
- package/dist/utils.js +36 -134
- package/package.json +2 -2
package/dist/diet/index.js
CHANGED
|
@@ -1,439 +1,333 @@
|
|
|
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
|
-
require("core-js/modules/es.array.includes.js");
|
|
28
|
-
|
|
29
|
-
require("core-js/modules/es.array.sort.js");
|
|
30
|
-
|
|
31
|
-
require("core-js/modules/es.string.includes.js");
|
|
32
|
-
|
|
33
|
-
require("core-js/modules/es.array.concat.js");
|
|
34
|
-
|
|
35
|
-
require("core-js/modules/es.array.slice.js");
|
|
36
|
-
|
|
37
|
-
require("core-js/modules/es.array.find.js");
|
|
38
|
-
|
|
39
|
-
var _kolmafia = require("kolmafia");
|
|
40
|
-
|
|
41
|
-
var _knapsack3 = require("./knapsack");
|
|
42
|
-
|
|
43
|
-
var _lib = require("../lib");
|
|
44
|
-
|
|
45
|
-
var _templateString = require("../template-string");
|
|
46
|
-
|
|
47
|
-
var _utils = require("../utils");
|
|
48
|
-
|
|
49
|
-
var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6, _templateObject7, _templateObject8, _templateObject9, _templateObject10, _templateObject11, _templateObject12, _templateObject13, _templateObject14, _templateObject15, _templateObject16, _templateObject17, _templateObject18, _templateObject19, _templateObject20;
|
|
50
|
-
|
|
51
|
-
function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
|
|
52
|
-
|
|
53
|
-
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
54
|
-
|
|
55
|
-
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
|
56
|
-
|
|
57
|
-
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
|
58
|
-
|
|
59
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
60
|
-
|
|
61
|
-
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
62
|
-
|
|
63
|
-
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
64
|
-
|
|
65
|
-
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
66
|
-
|
|
67
|
-
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
68
|
-
|
|
69
|
-
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
70
|
-
|
|
71
|
-
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
72
|
-
|
|
73
|
-
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
74
|
-
|
|
75
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
76
|
-
|
|
77
|
-
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
78
|
-
|
|
79
|
-
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
|
80
|
-
|
|
81
|
-
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
82
|
-
|
|
83
|
-
// TODO: Include other consumption modifiers.
|
|
84
|
-
function expectedAdventures(item) {
|
|
85
|
-
var forkMug = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
86
|
-
if (item.adventures === "") return 0;
|
|
87
|
-
|
|
88
|
-
var _item$adventures$spli = item.adventures.split(/[-–—]/).map(s => parseInt(s)),
|
|
89
|
-
_item$adventures$spli2 = _slicedToArray(_item$adventures$spli, 2),
|
|
90
|
-
min = _item$adventures$spli2[0],
|
|
91
|
-
recordedMax = _item$adventures$spli2[1];
|
|
92
|
-
|
|
93
|
-
var max = recordedMax !== null && recordedMax !== void 0 ? recordedMax : min;
|
|
94
|
-
|
|
95
|
-
var interpolated = _toConsumableArray(new Array(max - min + 1).keys()).map(n => n + min);
|
|
96
|
-
|
|
97
|
-
return (0, _utils.sum)(interpolated, n => Math.floor(n * (forkMug ? 1.3 : 1))) / interpolated.length;
|
|
98
|
-
} // Assuming list is already sorted, count adjacent items.
|
|
99
|
-
// Effectively run-length encoding.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
function aggregate(list, isEqual) {
|
|
103
|
-
var aggregatedList = [];
|
|
104
|
-
|
|
105
|
-
var _iterator = _createForOfIteratorHelper(list),
|
|
106
|
-
_step;
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
110
|
-
var item = _step.value;
|
|
111
|
-
|
|
112
|
-
if (aggregatedList.length === 0) {
|
|
113
|
-
aggregatedList.push([item, 1]);
|
|
114
|
-
} else {
|
|
115
|
-
var last = aggregatedList[aggregatedList.length - 1];
|
|
116
|
-
|
|
117
|
-
var _last = _slicedToArray(last, 1),
|
|
118
|
-
lastItem = _last[0];
|
|
119
|
-
|
|
120
|
-
if (isEqual(item, lastItem)) {
|
|
121
|
-
last[1]++;
|
|
122
|
-
} else {
|
|
123
|
-
aggregatedList.push([item, 1]);
|
|
1
|
+
import { fullnessLimit, getWorkshed, inebrietyLimit, itemType, mallPrice, mallPrices, myFullness, myPrimestat, mySpleenUse, npcPrice, spleenLimit, } from "kolmafia";
|
|
2
|
+
import { knapsack } from "./knapsack";
|
|
3
|
+
import { have } from "../lib";
|
|
4
|
+
import { $effect, $item, $items, $skill, $stat } from "../template-string";
|
|
5
|
+
import { sum } from "../utils";
|
|
6
|
+
// TODO: Include other consumption modifiers - Salty Mouth?
|
|
7
|
+
// TODO: Include Gar-ish etc.
|
|
8
|
+
function expectedAdventures(item, modifiers) {
|
|
9
|
+
if (item.adventures === "")
|
|
10
|
+
return 0;
|
|
11
|
+
const [min, recordedMax] = item.adventures
|
|
12
|
+
.split(/[-–—]/)
|
|
13
|
+
.map((s) => parseInt(s));
|
|
14
|
+
const max = recordedMax ?? min;
|
|
15
|
+
const interpolated = [...new Array(max - min + 1).keys()].map((n) => n + min);
|
|
16
|
+
const forkMugMultiplier = (itemType(item) === "food" && item.notes?.includes("SALAD")) ||
|
|
17
|
+
(itemType(item) === "booze" && item.notes?.includes("BEER"))
|
|
18
|
+
? 1.5
|
|
19
|
+
: 1.3;
|
|
20
|
+
const refinedPalate = modifiers.refinedPalate && item.notes?.includes("WINE");
|
|
21
|
+
const pinkyRing = modifiers.pinkyRing && item.notes?.includes("WINE");
|
|
22
|
+
return (sum(interpolated, (baseAdventures) => {
|
|
23
|
+
let adventures = baseAdventures;
|
|
24
|
+
if (modifiers.forkMug) {
|
|
25
|
+
adventures = Math.floor(adventures * forkMugMultiplier);
|
|
124
26
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
27
|
+
if (refinedPalate)
|
|
28
|
+
adventures = Math.floor(adventures * 1.25);
|
|
29
|
+
if (pinkyRing)
|
|
30
|
+
adventures = Math.round(adventures * 1.125);
|
|
31
|
+
if (item.notes?.includes("MARTINI") && modifiers.tuxedoShirt) {
|
|
32
|
+
adventures += 2;
|
|
33
|
+
}
|
|
34
|
+
if (have($skill `Saucemaven`) && item.notes?.includes("SAUCY")) {
|
|
35
|
+
adventures += myPrimestat() === $stat `Mysticality` ? 5 : 3;
|
|
36
|
+
}
|
|
37
|
+
if (itemType(item) === "food" && modifiers.seasoning)
|
|
38
|
+
adventures++;
|
|
39
|
+
if (itemType(item) === "food" && modifiers.mayoflex)
|
|
40
|
+
adventures++;
|
|
41
|
+
return adventures;
|
|
42
|
+
}) / interpolated.length);
|
|
134
43
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
44
|
+
export class MenuItem {
|
|
45
|
+
constructor(item, options = {}) {
|
|
46
|
+
const { size, organ, maximum, additionalValue, wishEffect } = {
|
|
47
|
+
...options,
|
|
48
|
+
...(MenuItem.defaultOptions.get(item) ?? {}),
|
|
49
|
+
};
|
|
50
|
+
this.item = item;
|
|
51
|
+
this.maximum = maximum === "auto" ? item.dailyusesleft : maximum;
|
|
52
|
+
this.additionalValue = additionalValue;
|
|
53
|
+
this.wishEffect = wishEffect;
|
|
54
|
+
const typ = itemType(this.item);
|
|
55
|
+
this.organ = organ ?? (isOrgan(typ) ? typ : undefined);
|
|
56
|
+
this.size =
|
|
57
|
+
size ??
|
|
58
|
+
(this.organ === "food"
|
|
59
|
+
? this.item.fullness
|
|
60
|
+
: this.organ === "booze"
|
|
61
|
+
? this.item.inebriety
|
|
62
|
+
: this.organ === "spleen item"
|
|
63
|
+
? this.item.spleen
|
|
64
|
+
: 0);
|
|
65
|
+
}
|
|
66
|
+
equals(other) {
|
|
67
|
+
return this.item === other.item && this.wishEffect === other.wishEffect;
|
|
159
68
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
value: function price() {
|
|
163
|
-
return (0, _kolmafia.npcPrice)(this.item) > 0 ? (0, _kolmafia.npcPrice)(this.item) : (0, _kolmafia.mallPrice)(this.item);
|
|
69
|
+
toString() {
|
|
70
|
+
return this.item.toString();
|
|
164
71
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return MenuItem;
|
|
168
|
-
}();
|
|
169
|
-
|
|
170
|
-
exports.MenuItem = MenuItem;
|
|
171
|
-
|
|
172
|
-
_defineProperty(MenuItem, "seasoning", new MenuItem((0, _templateString.$item)(_templateObject17 || (_templateObject17 = _taggedTemplateLiteral(["Special Seasoning"])))));
|
|
173
|
-
|
|
174
|
-
_defineProperty(MenuItem, "mayoflex", new MenuItem((0, _templateString.$item)(_templateObject18 || (_templateObject18 = _taggedTemplateLiteral(["Mayoflex"])))));
|
|
175
|
-
|
|
176
|
-
_defineProperty(MenuItem, "fork", new MenuItem((0, _templateString.$item)(_templateObject19 || (_templateObject19 = _taggedTemplateLiteral(["Ol' Scratch's salad fork"])))));
|
|
177
|
-
|
|
178
|
-
_defineProperty(MenuItem, "mug", new MenuItem((0, _templateString.$item)(_templateObject20 || (_templateObject20 = _taggedTemplateLiteral(["Frosty's frosty mug"])))));
|
|
179
|
-
|
|
180
|
-
var DietPlanner = /*#__PURE__*/function () {
|
|
181
|
-
function DietPlanner(mpa, menu) {
|
|
182
|
-
_classCallCheck(this, DietPlanner);
|
|
183
|
-
|
|
184
|
-
_defineProperty(this, "mpa", void 0);
|
|
185
|
-
|
|
186
|
-
_defineProperty(this, "menu", void 0);
|
|
187
|
-
|
|
188
|
-
_defineProperty(this, "checkFork", void 0);
|
|
189
|
-
|
|
190
|
-
_defineProperty(this, "checkMug", void 0);
|
|
191
|
-
|
|
192
|
-
_defineProperty(this, "useSeasoning", void 0);
|
|
193
|
-
|
|
194
|
-
_defineProperty(this, "useMayoflex", void 0);
|
|
195
|
-
|
|
196
|
-
_defineProperty(this, "spleenValue", 0);
|
|
197
|
-
|
|
198
|
-
this.mpa = mpa;
|
|
199
|
-
this.checkFork = menu.some(item => item.item === MenuItem.fork.item);
|
|
200
|
-
this.checkMug = menu.some(item => item.item === MenuItem.mug.item);
|
|
201
|
-
this.useSeasoning = menu.some(item => item.item === (0, _templateString.$item)(_templateObject || (_templateObject = _taggedTemplateLiteral(["Special Seasoning"]))));
|
|
202
|
-
this.useMayoflex = (0, _kolmafia.getWorkshed)() === (0, _templateString.$item)(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["portable Mayo Clinic"]))) && menu.some(item => item.item === (0, _templateString.$item)(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["Mayoflex"]))));
|
|
203
|
-
this.menu = menu.filter(item => ["food", "booze", "spleen item"].includes((0, _kolmafia.itemType)(item.item)));
|
|
204
|
-
|
|
205
|
-
if (menu.length > 100) {
|
|
206
|
-
(0, _kolmafia.mallPrices)("food");
|
|
207
|
-
(0, _kolmafia.mallPrices)("booze");
|
|
72
|
+
price() {
|
|
73
|
+
return npcPrice(this.item) > 0 ? npcPrice(this.item) : mallPrice(this.item);
|
|
208
74
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
75
|
+
}
|
|
76
|
+
MenuItem.defaultOptions = new Map([
|
|
77
|
+
[$item `Mr. Burnsger`, { maximum: "auto" }],
|
|
78
|
+
[
|
|
79
|
+
$item `distention pill`,
|
|
80
|
+
{
|
|
81
|
+
organ: "food",
|
|
82
|
+
maximum: "auto",
|
|
83
|
+
size: -1,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
$item `synthetic dog hair pill`,
|
|
88
|
+
{ organ: "booze", maximum: "auto", size: -1 },
|
|
89
|
+
],
|
|
90
|
+
[$item `cuppa Voraci tea`, { organ: "food", maximum: "auto", size: -1 }],
|
|
91
|
+
[$item `cuppa Sobrie tea`, { organ: "booze", maximum: "auto", size: -1 }],
|
|
92
|
+
[$item `mojo filter`, { organ: "spleen item", maximum: "auto", size: -1 }],
|
|
93
|
+
]);
|
|
94
|
+
const organs = ["food", "booze", "spleen item"];
|
|
95
|
+
function isOrgan(x) {
|
|
96
|
+
return organs.includes(x);
|
|
97
|
+
}
|
|
98
|
+
class DietPlanner {
|
|
99
|
+
constructor(mpa, menu) {
|
|
100
|
+
this.spleenValue = 0;
|
|
101
|
+
this.mpa = mpa;
|
|
102
|
+
this.fork = menu.find((item) => item.item === $item `Ol' Scratch's salad fork`);
|
|
103
|
+
this.mug = menu.find((item) => item.item === $item `Frosty's frosty mug`);
|
|
104
|
+
this.seasoning = menu.find((item) => item.item === $item `Special Seasoning`);
|
|
105
|
+
this.mayoflex =
|
|
106
|
+
getWorkshed() === $item `portable Mayo Clinic`
|
|
107
|
+
? menu.find((item) => item.item === $item `Mayoflex`)
|
|
108
|
+
: undefined;
|
|
109
|
+
this.pinkyRing = have($item `mafia pinky ring`);
|
|
110
|
+
this.tuxedoShirt = have($item `tuxedo shirt`);
|
|
111
|
+
this.menu = menu.filter((item) => item.organ);
|
|
112
|
+
if (menu.length > 100) {
|
|
113
|
+
mallPrices("food");
|
|
114
|
+
mallPrices("booze");
|
|
115
|
+
}
|
|
116
|
+
const spleenItems = menu.filter((item) => itemType(item.item) === "spleen item");
|
|
117
|
+
spleenItems.sort((x, y) => -(this.consumptionValue(x) / x.item.spleen -
|
|
118
|
+
this.consumptionValue(y) / y.item.spleen));
|
|
119
|
+
if (spleenItems.length > 0) {
|
|
120
|
+
this.spleenValue =
|
|
121
|
+
this.consumptionValue(spleenItems[0]) / spleenItems[0].item.spleen;
|
|
122
|
+
}
|
|
215
123
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
_createClass(DietPlanner, [{
|
|
219
|
-
key: "consumptionValue",
|
|
220
|
-
value: function consumptionValue(menuItem) {
|
|
221
|
-
return this.consumptionHelpersAndValue(menuItem)[1];
|
|
124
|
+
consumptionValue(menuItem) {
|
|
125
|
+
return this.consumptionHelpersAndValue(menuItem, {})[1];
|
|
222
126
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
127
|
+
consumptionHelpersAndValue(menuItem, overrideModifiers) {
|
|
128
|
+
const helpers = [];
|
|
129
|
+
if (this.seasoning &&
|
|
130
|
+
itemType(menuItem.item) === "food" &&
|
|
131
|
+
this.mpa > mallPrice($item `Special Seasoning`)) {
|
|
132
|
+
helpers.push(this.seasoning);
|
|
133
|
+
}
|
|
134
|
+
if (this.mayoflex &&
|
|
135
|
+
itemType(menuItem.item) === "food" &&
|
|
136
|
+
this.mpa > npcPrice($item `Mayoflex`)) {
|
|
137
|
+
helpers.push(this.mayoflex);
|
|
138
|
+
}
|
|
139
|
+
const defaultModifiers = {
|
|
140
|
+
forkMug: false,
|
|
141
|
+
seasoning: this.seasoning ? helpers.includes(this.seasoning) : false,
|
|
142
|
+
mayoflex: this.mayoflex ? helpers.includes(this.mayoflex) : false,
|
|
143
|
+
refinedPalate: false,
|
|
144
|
+
pinkyRing: this.pinkyRing,
|
|
145
|
+
tuxedoShirt: this.tuxedoShirt,
|
|
146
|
+
...overrideModifiers,
|
|
147
|
+
};
|
|
148
|
+
const forkMug = itemType(menuItem.item) === "food"
|
|
149
|
+
? this.fork
|
|
150
|
+
: itemType(menuItem.item) === "booze"
|
|
151
|
+
? this.mug
|
|
152
|
+
: null;
|
|
153
|
+
const forkMugPrice = forkMug ? forkMug.price() : Infinity;
|
|
154
|
+
const baseCost = menuItem.price() + sum(helpers, (item) => item.price());
|
|
155
|
+
const valueRaw = expectedAdventures(menuItem.item, defaultModifiers) * this.mpa -
|
|
156
|
+
baseCost +
|
|
157
|
+
(menuItem.additionalValue ?? 0);
|
|
158
|
+
const valueForkMug = expectedAdventures(menuItem.item, {
|
|
159
|
+
...defaultModifiers,
|
|
160
|
+
forkMug: true,
|
|
161
|
+
}) *
|
|
162
|
+
this.mpa -
|
|
163
|
+
baseCost -
|
|
164
|
+
forkMugPrice +
|
|
165
|
+
(menuItem.additionalValue ?? 0);
|
|
166
|
+
const valueSpleen = $items `jar of fermented pickle juice, extra-greasy slider`.includes(menuItem.item)
|
|
167
|
+
? 5 * this.spleenValue
|
|
168
|
+
: 0;
|
|
169
|
+
return forkMug && valueForkMug > valueRaw
|
|
170
|
+
? [[...helpers, forkMug, menuItem], valueForkMug + valueSpleen]
|
|
171
|
+
: [[...helpers, menuItem], valueRaw + valueSpleen];
|
|
247
172
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// .map((item) => item.name)
|
|
264
|
-
// .join(", ")}`
|
|
265
|
-
// );
|
|
266
|
-
|
|
267
|
-
return [value, aggregate(itemList, (x, y) => x.every((elem, index) => elem === y[index]))];
|
|
173
|
+
planOrgan(organ, capacity, overrideModifiers = {}) {
|
|
174
|
+
// print(`Plan ${organ} < ${capacity}`);
|
|
175
|
+
const submenu = this.menu.filter((item) => item.organ === organ);
|
|
176
|
+
const knapsackValues = submenu.map((menuItem) => [
|
|
177
|
+
...this.consumptionHelpersAndValue(menuItem, overrideModifiers),
|
|
178
|
+
menuItem.size,
|
|
179
|
+
menuItem.maximum,
|
|
180
|
+
]);
|
|
181
|
+
return knapsack(knapsackValues, capacity);
|
|
182
|
+
// print(
|
|
183
|
+
// `Items: ${itemList.length} ${([] as Item[])
|
|
184
|
+
// .concat(...itemList)
|
|
185
|
+
// .map((item) => item.name)
|
|
186
|
+
// .join(", ")}`
|
|
187
|
+
// );
|
|
268
188
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
var _ref2 = _slicedToArray(_ref, 2),
|
|
276
|
-
organ = _ref2[0],
|
|
277
|
-
capacity = _ref2[1];
|
|
278
|
-
|
|
279
|
-
return this.planOrgan(organ, capacity);
|
|
280
|
-
});
|
|
281
|
-
return [(0, _utils.sum)(valuePlans, _ref3 => {
|
|
282
|
-
var _ref4 = _slicedToArray(_ref3, 1),
|
|
283
|
-
value = _ref4[0];
|
|
284
|
-
|
|
285
|
-
return value;
|
|
286
|
-
}), (_ref5 = []).concat.apply(_ref5, _toConsumableArray(valuePlans.map(_ref6 => {
|
|
287
|
-
var _ref7 = _slicedToArray(_ref6, 2),
|
|
288
|
-
plan = _ref7[1];
|
|
289
|
-
|
|
290
|
-
return plan;
|
|
291
|
-
})))];
|
|
189
|
+
planOrgans(organCapacities, overrideModifiers = {}) {
|
|
190
|
+
const valuePlans = organCapacities.map(([organ, capacity]) => this.planOrgan(organ, capacity, overrideModifiers));
|
|
191
|
+
return [
|
|
192
|
+
sum(valuePlans, ([value]) => value),
|
|
193
|
+
[].concat(...valuePlans.map(([, plan]) => plan)),
|
|
194
|
+
];
|
|
292
195
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
var _iterator2 = _createForOfIteratorHelper(organSizes),
|
|
308
|
-
_step2;
|
|
309
|
-
|
|
310
|
-
try {
|
|
311
|
-
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
312
|
-
var _step2$value = _slicedToArray(_step2.value, 2),
|
|
313
|
-
organ = _step2$value[0],
|
|
314
|
-
size = _step2$value[1];
|
|
315
|
-
|
|
316
|
-
var current = organCapacitiesWithMap.get(organ);
|
|
317
|
-
|
|
318
|
-
if (current !== undefined) {
|
|
319
|
-
organCapacitiesWithMap.set(organ, current - size);
|
|
320
|
-
}
|
|
196
|
+
planOrgansWithTrials(organCapacities, trialItems, overrideModifiers = {}) {
|
|
197
|
+
if (trialItems.length === 0) {
|
|
198
|
+
return this.planOrgans(organCapacities, overrideModifiers);
|
|
199
|
+
}
|
|
200
|
+
const organCapacitiesWithMap = new Map(organCapacities);
|
|
201
|
+
const [trialItem, organSizes] = trialItems[0];
|
|
202
|
+
// print(`TRYING ${trialItem.item.name}`);
|
|
203
|
+
for (const [organ, size] of organSizes) {
|
|
204
|
+
const current = organCapacitiesWithMap.get(organ);
|
|
205
|
+
if (current !== undefined) {
|
|
206
|
+
organCapacitiesWithMap.set(organ, current - size);
|
|
207
|
+
}
|
|
321
208
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
var _this$consumptionHelp = this.consumptionHelpersAndValue(trialItem),
|
|
341
|
-
_this$consumptionHelp2 = _slicedToArray(_this$consumptionHelp, 2),
|
|
342
|
-
helpers = _this$consumptionHelp2[0],
|
|
343
|
-
value = _this$consumptionHelp2[1];
|
|
344
|
-
|
|
345
|
-
(0, _kolmafia.print)("".concat(new Array(5 - trialItems.length).join(">"), " ").concat(valueWithout > valueWith + value ? "WITHOUT" : "WITH", " ").concat(trialItem.item, " ").concat(value.toFixed(0), ": ").concat(valueWithout.toFixed(0), " vs. ").concat((valueWith + value).toFixed(0)));
|
|
346
|
-
return valueWithout > valueWith + value ? [valueWithout, planWithout] : [valueWith, [].concat(_toConsumableArray(planWith), [[helpers.map(menuItem => menuItem.item), 1]])];
|
|
209
|
+
const organCapacitiesWith = [...organCapacitiesWithMap];
|
|
210
|
+
const isRefinedPalate = trialItem.item === $item `pocket wish` &&
|
|
211
|
+
trialItem.wishEffect === $effect `Refined Palate`;
|
|
212
|
+
const [valueWithout, planWithout] = this.planOrgansWithTrials(organCapacities, trialItems.slice(1), overrideModifiers);
|
|
213
|
+
const [valueWith, planWith] = this.planOrgansWithTrials(organCapacitiesWith, trialItems.slice(1), isRefinedPalate
|
|
214
|
+
? { ...overrideModifiers, refinedPalate: true }
|
|
215
|
+
: overrideModifiers);
|
|
216
|
+
const [helpersAndItem, value] = this.consumptionHelpersAndValue(trialItem, {});
|
|
217
|
+
// print(
|
|
218
|
+
// `${new Array(trialItems.length).join(">")} ${
|
|
219
|
+
// valueWithout > valueWith + value ? "WITHOUT" : "WITH"
|
|
220
|
+
// } ${trialItem.item} ${trialItem.wishEffect ?? ""} ${value.toFixed(
|
|
221
|
+
// 0
|
|
222
|
+
// )}: ${valueWithout.toFixed(0)} vs. ${(valueWith + value).toFixed(0)}`
|
|
223
|
+
// );
|
|
224
|
+
return valueWithout > valueWith + value
|
|
225
|
+
? [valueWithout, planWithout]
|
|
226
|
+
: [valueWith, [...planWith, [helpersAndItem, 1]]];
|
|
347
227
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Because the knapsack solver is one-dimensional only, any items that touch
|
|
231
|
+
* multiple organs have to be treated specially. What we do is run the knapsack
|
|
232
|
+
* solver multiple times, trying with + without each interacting item.
|
|
233
|
+
*/
|
|
234
|
+
const interactingItems = [
|
|
235
|
+
[
|
|
236
|
+
$item `spice melange`,
|
|
237
|
+
[
|
|
238
|
+
["food", -3],
|
|
239
|
+
["booze", -3],
|
|
240
|
+
],
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
$item `Ultra Mega Sour Ball`,
|
|
244
|
+
[
|
|
245
|
+
["food", -3],
|
|
246
|
+
["booze", -3],
|
|
247
|
+
],
|
|
248
|
+
],
|
|
249
|
+
[
|
|
250
|
+
$item `The Plumber's mushroom stew`,
|
|
251
|
+
[
|
|
252
|
+
["food", 3],
|
|
253
|
+
["booze", -1],
|
|
254
|
+
],
|
|
255
|
+
],
|
|
256
|
+
[
|
|
257
|
+
$item `The Mad Liquor`,
|
|
258
|
+
[
|
|
259
|
+
["food", -1],
|
|
260
|
+
["booze", 3],
|
|
261
|
+
],
|
|
262
|
+
],
|
|
263
|
+
[
|
|
264
|
+
$item `Doc Clock's thyme cocktail`,
|
|
265
|
+
[
|
|
266
|
+
["food", -2],
|
|
267
|
+
["booze", 4],
|
|
268
|
+
],
|
|
269
|
+
],
|
|
270
|
+
[
|
|
271
|
+
$item `Mr. Burnsger`,
|
|
272
|
+
[
|
|
273
|
+
["food", 4],
|
|
274
|
+
["booze", -2],
|
|
275
|
+
],
|
|
276
|
+
],
|
|
277
|
+
];
|
|
278
|
+
/**
|
|
279
|
+
* Plan out an optimal diet using a knapsack algorithm.
|
|
280
|
+
* @param mpa Meat per adventure value.
|
|
281
|
+
* @param menu Array of MenuItems to consider for diet purposes.
|
|
282
|
+
* @param organCapacities Optional override of each organ's capacity.
|
|
283
|
+
* @returns Array of [menu item and helpers, count].
|
|
284
|
+
*/
|
|
285
|
+
export function planDiet(mpa, menu, organCapacities = [
|
|
286
|
+
["food", null],
|
|
287
|
+
["booze", null],
|
|
288
|
+
["spleen item", null],
|
|
289
|
+
]) {
|
|
290
|
+
const dietPlanner = new DietPlanner(mpa, menu);
|
|
291
|
+
// print("MENU:");
|
|
292
|
+
for (const menuItem of menu) {
|
|
293
|
+
const [helpers, value] = dietPlanner.consumptionHelpersAndValue(menuItem, {});
|
|
294
|
+
// print(`${menuItem.item.name}: ${helpers.join(", ")} ${value}`);
|
|
295
|
+
}
|
|
296
|
+
const resolvedOrganCapacities = organCapacities.map(([organ, size]) => [
|
|
297
|
+
organ,
|
|
298
|
+
size ??
|
|
299
|
+
(organ === "food"
|
|
300
|
+
? fullnessLimit() -
|
|
301
|
+
myFullness() +
|
|
302
|
+
(have($item `distention pill`) ? 1 : 0)
|
|
303
|
+
: organ === "booze"
|
|
304
|
+
? inebrietyLimit() + (have($item `synthetic dog hair pill`) ? 1 : 0)
|
|
305
|
+
: organ === "spleen item"
|
|
306
|
+
? spleenLimit() - mySpleenUse()
|
|
307
|
+
: 0),
|
|
308
|
+
]);
|
|
309
|
+
const allItems = new Map(menu.map((menuItem) => [menuItem.item, menuItem]));
|
|
310
|
+
const includedInteractingItems = interactingItems
|
|
311
|
+
.map(([item, sizes]) => [allItems.get(item), sizes])
|
|
312
|
+
.filter(([menuItem]) => menuItem);
|
|
313
|
+
// print(
|
|
314
|
+
// `included interacting: ${includedInteractingItems
|
|
315
|
+
// .map(([menuItem]) => menuItem.item.name)
|
|
316
|
+
// .join(", ")}`
|
|
317
|
+
// );
|
|
318
|
+
// TODO: support toasted brie.
|
|
319
|
+
// Refined Palate must also be treated as an interacting item, as it's a one-time cost.
|
|
320
|
+
const palateWish = menu.find((menuItem) => menuItem.item === $item `pocket wish` &&
|
|
321
|
+
menuItem.wishEffect === $effect `Refined Palate`);
|
|
322
|
+
if (palateWish) {
|
|
323
|
+
includedInteractingItems.push([palateWish, []]);
|
|
376
324
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
size = _ref9[1];
|
|
387
|
-
|
|
388
|
-
return [organ, size !== null && size !== void 0 ? size : organ === "food" ? (0, _kolmafia.fullnessLimit)() - (0, _kolmafia.myFullness)() + ((0, _lib.have)((0, _templateString.$item)(_templateObject13 || (_templateObject13 = _taggedTemplateLiteral(["distention pill"])))) ? 1 : 0) : organ === "booze" ? (0, _kolmafia.inebrietyLimit)() + ((0, _lib.have)((0, _templateString.$item)(_templateObject14 || (_templateObject14 = _taggedTemplateLiteral(["synthetic dog hair pill"])))) ? 1 : 0) : organ === "spleen item" ? (0, _kolmafia.spleenLimit)() - (0, _kolmafia.mySpleenUse)() : 0];
|
|
389
|
-
});
|
|
390
|
-
var allItems = new Map(menu.map(menuItem => [menuItem.item, menuItem]));
|
|
391
|
-
var includedInteractingItems = interactingItems.map(_ref10 => {
|
|
392
|
-
var _ref11 = _slicedToArray(_ref10, 2),
|
|
393
|
-
item = _ref11[0],
|
|
394
|
-
sizes = _ref11[1];
|
|
395
|
-
|
|
396
|
-
return [allItems.get(item), sizes];
|
|
397
|
-
}).filter(_ref12 => {
|
|
398
|
-
var _ref13 = _slicedToArray(_ref12, 1),
|
|
399
|
-
menuItem = _ref13[0];
|
|
400
|
-
|
|
401
|
-
return menuItem;
|
|
402
|
-
}); // print(
|
|
403
|
-
// `included interacting: ${includedInteractingItems
|
|
404
|
-
// .map(([menuItem]) => menuItem.item.name)
|
|
405
|
-
// .join(", ")}`
|
|
406
|
-
// );
|
|
407
|
-
|
|
408
|
-
var _dietPlanner$planOrga = dietPlanner.planOrgansWithTrials(resolvedOrganCapacities.filter(_ref14 => {
|
|
409
|
-
var _ref15 = _slicedToArray(_ref14, 1),
|
|
410
|
-
organ = _ref15[0];
|
|
411
|
-
|
|
412
|
-
return ["food", "booze"].includes(organ);
|
|
413
|
-
}), includedInteractingItems),
|
|
414
|
-
_dietPlanner$planOrga2 = _slicedToArray(_dietPlanner$planOrga, 2),
|
|
415
|
-
planFoodBooze = _dietPlanner$planOrga2[1];
|
|
416
|
-
|
|
417
|
-
var additionalSpleen = (0, _utils.sum)(planFoodBooze, _ref16 => {
|
|
418
|
-
var _ref17 = _slicedToArray(_ref16, 2),
|
|
419
|
-
items = _ref17[0],
|
|
420
|
-
number = _ref17[1];
|
|
421
|
-
|
|
422
|
-
return items.includes((0, _templateString.$item)(_templateObject15 || (_templateObject15 = _taggedTemplateLiteral(["jar of fermented pickle juice"])))) || items.includes((0, _templateString.$item)(_templateObject16 || (_templateObject16 = _taggedTemplateLiteral(["extra-greasy slider"])))) ? 5 * number : 0;
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
var _ref18 = (_resolvedOrganCapacit = resolvedOrganCapacities.find(_ref20 => {
|
|
426
|
-
var _ref21 = _slicedToArray(_ref20, 1),
|
|
427
|
-
organ = _ref21[0];
|
|
428
|
-
|
|
429
|
-
return organ === "spleen item";
|
|
430
|
-
})) !== null && _resolvedOrganCapacit !== void 0 ? _resolvedOrganCapacit : ["spleen item", 0],
|
|
431
|
-
_ref19 = _slicedToArray(_ref18, 2),
|
|
432
|
-
availableSpleen = _ref19[1];
|
|
433
|
-
|
|
434
|
-
var _dietPlanner$planOrga3 = dietPlanner.planOrgan("spleen item", availableSpleen + additionalSpleen),
|
|
435
|
-
_dietPlanner$planOrga4 = _slicedToArray(_dietPlanner$planOrga3, 2),
|
|
436
|
-
planSpleen = _dietPlanner$planOrga4[1];
|
|
437
|
-
|
|
438
|
-
return [].concat(_toConsumableArray(planFoodBooze), _toConsumableArray(planSpleen));
|
|
439
|
-
}
|
|
325
|
+
const [, planFoodBooze] = dietPlanner.planOrgansWithTrials(resolvedOrganCapacities.filter(([organ]) => ["food", "booze"].includes(organ)), includedInteractingItems);
|
|
326
|
+
// Count sliders and pickle juice, figure out how much extra spleen we got.
|
|
327
|
+
const additionalSpleen = sum(planFoodBooze, ([items, number]) => items.some((menuItem) => $items `jar of fermented pickle juice, extra-greasy slider`.includes(menuItem.item))
|
|
328
|
+
? 5 * number
|
|
329
|
+
: 0);
|
|
330
|
+
const [, availableSpleen] = resolvedOrganCapacities.find(([organ]) => organ === "spleen item") ?? ["spleen item", 0];
|
|
331
|
+
const [, planSpleen] = dietPlanner.planOrgan("spleen item", availableSpleen + additionalSpleen);
|
|
332
|
+
return [...planFoodBooze, ...planSpleen];
|
|
333
|
+
}
|