libram 0.8.14 → 0.8.15

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,3 +1,4 @@
1
+ import { Effect } from "kolmafia";
1
2
  import { Requirement } from "../../maximize";
2
3
  export default class CommunityService {
3
4
  private choice;
@@ -86,6 +87,11 @@ export default class CommunityService {
86
87
  * @returns The number of turns to complete this test according to council.php.
87
88
  */
88
89
  actualCost(): number;
90
+ /**
91
+ * @param effects A spread array of Effects to consider
92
+ * @returns The number of turns we expect to save if we start using those effects
93
+ */
94
+ turnsSavedBy(...effects: Effect[]): number;
89
95
  /**
90
96
  * A log of the predicted turns, actual turns, and duration of each CS test performed.
91
97
  */
@@ -1,11 +1,10 @@
1
1
  import { equippedItem, familiarWeight, getPower, haveEquipped, myAdventures, myBasestat, myBuffedstat, myFamiliar, myMaxhp, myThrall, myTurncount, numericModifier, print, runChoice, toSlot, visitUrl, weightAdjustment, } from "kolmafia";
2
2
  import { have } from "../../lib";
3
3
  import { Requirement } from "../../maximize";
4
- import { get as getModifier } from "../../modifier";
5
4
  import { get } from "../../property";
6
5
  import { MummingTrunk } from "../../resources";
7
6
  import { $effect, $familiar, $item, $items, $path, $slot, $stat, $thrall, } from "../../template-string";
8
- import { sum } from "../../utils";
7
+ import { clamp, sum } from "../../utils";
9
8
  const thralls = new Map([
10
9
  [$stat `muscle`, $thrall `Elbow Macaroni`],
11
10
  [$stat `moxie`, $thrall `Penne Dreadful`],
@@ -20,6 +19,11 @@ const statCommunityServicePredictor = (stat) => {
20
19
  };
21
20
  const visitCouncil = () => visitUrl("council.php");
22
21
  const baseWeight = () => have($effect `Fidoxene`) ? 20 : familiarWeight(myFamiliar());
22
+ function hypotheticalModifier(modifier, ...effects) {
23
+ const newEffects = effects.filter((e) => !have(e));
24
+ return (numericModifier(modifier) +
25
+ sum(newEffects, (effect) => numericModifier(effect, modifier)));
26
+ }
23
27
  export default class CommunityService {
24
28
  choice;
25
29
  stat;
@@ -194,6 +198,15 @@ export default class CommunityService {
194
198
  actualCost() {
195
199
  return this._actualCost(visitCouncil());
196
200
  }
201
+ /**
202
+ * @param effects A spread array of Effects to consider
203
+ * @returns The number of turns we expect to save if we start using those effects
204
+ */
205
+ turnsSavedBy(...effects) {
206
+ const currentTurns = clamp(this.prediction, 1, 60);
207
+ const newTurns = clamp(this.predictor(...effects), 1, 60);
208
+ return currentTurns - newTurns;
209
+ }
197
210
  /**
198
211
  * A log of the predicted turns, actual turns, and duration of each CS test performed.
199
212
  */
@@ -226,8 +239,9 @@ export default class CommunityService {
226
239
  static Muscle = new CommunityService(2, "Muscle", "Feed The Children", statCommunityServicePredictor($stat `Muscle`), new Requirement(["Muscle"], {}));
227
240
  static Mysticality = new CommunityService(3, "Mysticality", "Build Playground Mazes", statCommunityServicePredictor($stat `Mysticality`), new Requirement(["Mysticality"], {}));
228
241
  static Moxie = new CommunityService(4, "Moxie", "Feed Conspirators", statCommunityServicePredictor($stat `Moxie`), new Requirement(["Moxie"], {}));
229
- static FamiliarWeight = new CommunityService(5, "Familiar Weight", "Breed More Collies", () => 60 - Math.floor((baseWeight() + weightAdjustment()) / 5), new Requirement(["Familiar Weight"], {}));
230
- static WeaponDamage = new CommunityService(6, "Weapon Damage", "Reduce Gazelle Population", () => {
242
+ static FamiliarWeight = new CommunityService(5, "Familiar Weight", "Breed More Collies", (...effects) => 60 -
243
+ Math.floor((baseWeight() + hypotheticalModifier("Familiar Weight", ...effects)) / 5), new Requirement(["Familiar Weight"], {}));
244
+ static WeaponDamage = new CommunityService(6, "Weapon Damage", "Reduce Gazelle Population", (...effects) => {
231
245
  const weaponPower = getPower(equippedItem($slot `weapon`));
232
246
  const offhandPower = toSlot(equippedItem($slot `off-hand`)) === $slot `weapon`
233
247
  ? getPower(equippedItem($slot `off-hand`))
@@ -240,27 +254,39 @@ export default class CommunityService {
240
254
  // We add 0.001 because the floor function sometimes introduces weird rounding errors
241
255
  return (60 -
242
256
  Math.floor((multiplier *
243
- (getModifier("Weapon Damage") -
257
+ (hypotheticalModifier("Weapon Damage", ...effects) -
244
258
  0.15 * (weaponPower + offhandPower + familiarPower))) /
245
259
  50 +
246
260
  0.001) -
247
- Math.floor((multiplier * getModifier("Weapon Damage Percent")) / 50 + 0.001));
261
+ Math.floor((multiplier *
262
+ hypotheticalModifier("Weapon Damage Percent", ...effects)) /
263
+ 50 +
264
+ 0.001));
248
265
  }, new Requirement(["Weapon Damage", "Weapon Damage Percent"], {}));
249
- static SpellDamage = new CommunityService(7, "Spell Damage", "Make Sausage", () => {
266
+ static SpellDamage = new CommunityService(7, "Spell Damage", "Make Sausage", (...effects) => {
250
267
  const dragonfishDamage = myFamiliar() === $familiar `Magic Dragonfish`
251
268
  ? numericModifier($familiar `Magic Dragonfish`, "Spell Damage Percent", baseWeight() + weightAdjustment(), $item.none)
252
269
  : 0;
253
270
  // We add 0.001 because the floor function sometimes introduces weird rounding errors
254
271
  return (60 -
255
- Math.floor(getModifier("Spell Damage") / 50 + 0.001) -
256
- Math.floor((getModifier("Spell Damage Percent") - dragonfishDamage) / 50 + 0.001));
272
+ Math.floor(hypotheticalModifier("Spell Damage", ...effects) / 50 + 0.001) -
273
+ Math.floor((hypotheticalModifier("Spell Damage Percent", ...effects) -
274
+ dragonfishDamage) /
275
+ 50 +
276
+ 0.001));
257
277
  }, new Requirement(["Spell Damage", "Spell Damage Percent"], {}));
258
- static Noncombat = new CommunityService(8, "Non-Combat", "Be a Living Statue", () => {
259
- const noncombatRate = -1 * getModifier("Combat Rate");
278
+ static Noncombat = new CommunityService(8, "Non-Combat", "Be a Living Statue", (...effects) => {
279
+ const noncombatRate = -1 * hypotheticalModifier("Combat Rate", ...effects);
260
280
  const unsoftcappedRate = noncombatRate > 25 ? 25 + (noncombatRate - 25) * 5 : noncombatRate;
261
- return 60 - 3 * Math.floor(unsoftcappedRate / 5);
281
+ const currentFamiliarModifier = -1 *
282
+ numericModifier(myFamiliar(), "Combat Rate", familiarWeight(myFamiliar()) + numericModifier("Familiar Weight"), equippedItem($slot `familiar`));
283
+ const newFamiliarModifier = -1 *
284
+ numericModifier(myFamiliar(), "Combat Rate", familiarWeight(myFamiliar()) +
285
+ hypotheticalModifier("Combat Rate", ...effects), equippedItem($slot `familiar`));
286
+ const adjustedRate = unsoftcappedRate - currentFamiliarModifier + newFamiliarModifier;
287
+ return 60 - 3 * Math.floor(adjustedRate / 5);
262
288
  }, new Requirement(["-combat"], {}));
263
- static BoozeDrop = new CommunityService(9, "Item Drop", "Make Margaritas", () => {
289
+ static BoozeDrop = new CommunityService(9, "Item Drop", "Make Margaritas", (...effects) => {
264
290
  const mummingCostume = MummingTrunk.currentCostumes().get(myFamiliar());
265
291
  const mummingBuff = mummingCostume && mummingCostume[0] === "Item Drop"
266
292
  ? mummingCostume[1]
@@ -277,16 +303,26 @@ export default class CommunityService {
277
303
  // We add 0.001 because the floor function sometimes introduces weird rounding errors
278
304
  return (60 -
279
305
  Math.floor((multiplier *
280
- (getModifier("Item Drop") -
306
+ (hypotheticalModifier("Item Drop", ...effects) -
281
307
  familiarItemDrop -
282
308
  numericModifier(myThrall(), "Item Drop"))) /
283
309
  30 +
284
310
  0.001) -
285
- Math.floor((getModifier("Booze Drop") - familiarBoozeDrop) / 15 + 0.001));
311
+ Math.floor((hypotheticalModifier("Booze Drop", ...effects) - familiarBoozeDrop) /
312
+ 15 +
313
+ 0.001));
286
314
  }, new Requirement(["Item Drop", "2 Booze Drop"], {
287
315
  preventEquip: $items `broken champagne bottle`,
288
316
  }));
289
- static HotRes = new CommunityService(10, "Hot Resistance", "Clean Steam Tunnels", () => 60 - getModifier("Hot Resistance"), new Requirement(["Hot Resistance"], {}));
317
+ static HotRes = new CommunityService(10, "Hot Resistance", "Clean Steam Tunnels", (...effects) => {
318
+ const currentFamiliarModifier = numericModifier(myFamiliar(), "Hot Resistance", familiarWeight(myFamiliar()) + numericModifier("Familiar Weight"), equippedItem($slot `familiar`));
319
+ const newFamiliarModifier = numericModifier(myFamiliar(), "Hot Resistance", familiarWeight(myFamiliar()) +
320
+ hypotheticalModifier("Familiar Weight", ...effects), equippedItem($slot `familiar`));
321
+ return (60 -
322
+ (hypotheticalModifier("Hot Resistance", ...effects) -
323
+ currentFamiliarModifier +
324
+ newFamiliarModifier));
325
+ }, new Requirement(["Hot Resistance"], {}));
290
326
  static CoilWire = new CommunityService(11, "Coil Wire", "Coil Wire", () => 60, new Requirement([], {}));
291
327
  static donate = () => {
292
328
  visitCouncil();
@@ -0,0 +1,32 @@
1
+ import { Location } from "kolmafia";
2
+ /**
3
+ * @returns Whether or not you can currently access Gingerbread City
4
+ */
5
+ export declare function available(): boolean;
6
+ /**
7
+ * @returns The number of Gingerbread encounters until it's Midnight in the city; this may be negative if the time has passed
8
+ */
9
+ export declare function minutesToMidnight(): number;
10
+ /**
11
+ * @returns The number of Gingerbread encounters until it's Noon in the city; this may be negative if the time has passed
12
+ */
13
+ export declare function minutesToNoon(): number;
14
+ export declare const LOCATIONS: readonly Location[];
15
+ /**
16
+ * @returns A list of all Gingerbread locations at which you can currently adventure
17
+ */
18
+ export declare function availableLocations(): Location[];
19
+ /**
20
+ * @param location The location in question
21
+ * @returns The id of the Noon choice adventure at that location; 0 if inapplicable
22
+ */
23
+ export declare function getNoonChoiceId(location: Location): number;
24
+ /**
25
+ * @param location The location in question
26
+ * @returns The id of the Midnight choice adventure at that location; 0 if inapplicable
27
+ */
28
+ export declare function getMidnightChoiceId(location: Location): number;
29
+ /**
30
+ * @returns Whether or not it is possible for you to fight Judge Fudge today
31
+ */
32
+ export declare function canJudgeFudge(): boolean;
@@ -0,0 +1,73 @@
1
+ import { canAdventure } from "kolmafia";
2
+ import { get } from "../../property";
3
+ import { $location, $locations } from "../../template-string";
4
+ /**
5
+ * @returns Whether or not you can currently access Gingerbread City
6
+ */
7
+ export function available() {
8
+ return ((get("gingerbreadCityAvailable") || get("_gingerbreadCityToday")) &&
9
+ turns() < availableTurns());
10
+ }
11
+ function turns() {
12
+ return (get("_gingerbreadCityTurns") + (get("_gingerbreadClockAdvanced") ? 5 : 0));
13
+ }
14
+ function availableTurns() {
15
+ return 20 + (get("gingerExtraAdventures") ? 10 : 0);
16
+ }
17
+ /**
18
+ * @returns The number of Gingerbread encounters until it's Midnight in the city; this may be negative if the time has passed
19
+ */
20
+ export function minutesToMidnight() {
21
+ return 19 - turns();
22
+ }
23
+ /**
24
+ * @returns The number of Gingerbread encounters until it's Noon in the city; this may be negative if the time has passed
25
+ */
26
+ export function minutesToNoon() {
27
+ return 9 - turns();
28
+ }
29
+ export const LOCATIONS = Object.freeze($locations `Gingerbread Civic Center, Gingerbread Train Station, Gingerbread Industrial Zone, Gingerbread Upscale Retail District, Gingerbread Sewers`);
30
+ /**
31
+ * @returns A list of all Gingerbread locations at which you can currently adventure
32
+ */
33
+ export function availableLocations() {
34
+ return LOCATIONS.filter((l) => canAdventure(l));
35
+ }
36
+ const NOONS = new Map([
37
+ [$location `Gingerbread Train Station`, 1204],
38
+ [$location `Gingerbread Civic Center`, 1202],
39
+ [$location `Gingerbread Industrial Zone`, 1206],
40
+ [$location `Gingerbread Upscale Retail District`, 1208],
41
+ ]);
42
+ const MIDNIGHTS = new Map([
43
+ [$location `Gingerbread Train Station`, 1205],
44
+ [$location `Gingerbread Civic Center`, 1203],
45
+ [$location `Gingerbread Industrial Zone`, 1207],
46
+ [$location `Gingerbread Upscale Retail District`, 1209],
47
+ ]);
48
+ /**
49
+ * @param location The location in question
50
+ * @returns The id of the Noon choice adventure at that location; 0 if inapplicable
51
+ */
52
+ export function getNoonChoiceId(location) {
53
+ return NOONS.get(location) ?? 0;
54
+ }
55
+ /**
56
+ * @param location The location in question
57
+ * @returns The id of the Midnight choice adventure at that location; 0 if inapplicable
58
+ */
59
+ export function getMidnightChoiceId(location) {
60
+ return MIDNIGHTS.get(location) ?? 0;
61
+ }
62
+ /**
63
+ * @returns Whether or not it is possible for you to fight Judge Fudge today
64
+ */
65
+ export function canJudgeFudge() {
66
+ if (minutesToNoon() >= 0) {
67
+ return true;
68
+ }
69
+ if (minutesToMidnight() >= 0 && get("_gingerbreadColumnDestroyed")) {
70
+ return true;
71
+ }
72
+ return false;
73
+ }
@@ -1,4 +1,4 @@
1
- import { toLocation, toMonster, availableAmount, Item, visitUrl, } from "kolmafia";
1
+ import { toLocation, toMonster, availableAmount, Item, visitUrl, myTotalTurnsSpent, totalTurnsPlayed, } from "kolmafia";
2
2
  import { logger } from "../..";
3
3
  import { canVisitUrl } from "../../lib";
4
4
  import { get } from "../../property";
@@ -15,6 +15,8 @@ const parsedProp = () => get("crystalBallPredictions")
15
15
  .split("|")
16
16
  .map((element) => element.split(":"))
17
17
  .map(([, location, monster]) => [toLocation(location), toMonster(monster)]);
18
+ const getLastPondered = () => `${myTotalTurnsSpent()};${totalTurnsPlayed()};${get("lastAdventure")}`;
19
+ let lastPondered = "";
18
20
  /**
19
21
  * Ponders your orb (if it is able to do so safely) and then returns a Map keyed by location consisting of extant predictions
20
22
  *
@@ -23,12 +25,15 @@ const parsedProp = () => get("crystalBallPredictions")
23
25
  export function ponder() {
24
26
  if (!have())
25
27
  return new Map();
26
- if (canVisitUrl()) {
27
- logger.debug("Now pondering Crystal Ball.");
28
- visitUrl("inventory.php?ponder=1", false);
29
- }
30
- else {
31
- logger.debug("Failed to ponder Crystall Ball.");
28
+ if (lastPondered !== getLastPondered()) {
29
+ if (canVisitUrl()) {
30
+ logger.debug("Now pondering Crystal Ball.");
31
+ visitUrl("inventory.php?ponder=1", false);
32
+ lastPondered = getLastPondered();
33
+ }
34
+ else {
35
+ logger.debug("Failed to ponder Crystall Ball.");
36
+ }
32
37
  }
33
38
  return new Map(parsedProp());
34
39
  }
@@ -17,6 +17,7 @@ import * as ChateauMantegna from "./2015/ChateauMantegna";
17
17
  import * as DeckOfEveryCard from "./2015/DeckOfEveryCard";
18
18
  import * as Dinseylandfill from "./2015/Dinseylandfill";
19
19
  import * as MayoClinic from "./2015/MayoClinic";
20
+ import * as GingerBread from "./2016/GingerBread";
20
21
  import * as SourceTerminal from "./2016/SourceTerminal";
21
22
  import * as Witchess from "./2016/Witchess";
22
23
  import * as AsdonMartin from "./2017/AsdonMartin";
@@ -45,6 +46,6 @@ import * as AugustScepter from "./2023/AugustScepter";
45
46
  import * as CinchoDeMayo from "./2023/CinchoDeMayo";
46
47
  import * as ClosedCircuitPayphone from "./2023/ClosedCircuitPayphone";
47
48
  import * as CursedMonkeyPaw from "./2023/CursedMonkeyPaw";
48
- export { AugustScepter, AutumnAton, AsdonMartin, Bandersnatch, BarrelShrine, BeachComb, CampAway, Cartography, ChateauMantegna, CinchoDeMayo, ClosedCircuitPayphone, CombatLoversLocket, CrimboShrub, CrownOfThrones, CrystalBall, CursedMonkeyPaw, DaylightShavings, DeckOfEveryCard, Dinseylandfill, DNALab, FloristFriar, GreyGoose, Guzzlr, Horsery, JuneCleaver, JungMan, Latte, LookingGlass, MayoClinic, MummingTrunk, ObtuseAngel, Pantogram, RainDoh, ReagnimatedGnome, RetroCape, Robortender, Snapper, SongBoom, SourceTerminal, Spacegate, SpookyPutty, Stickers, StompingBoots, TrainSet, TunnelOfLove, WinterGarden, Witchess, };
49
+ export { AugustScepter, AutumnAton, AsdonMartin, Bandersnatch, BarrelShrine, BeachComb, CampAway, Cartography, ChateauMantegna, CinchoDeMayo, ClosedCircuitPayphone, CombatLoversLocket, CrimboShrub, CrownOfThrones, CrystalBall, CursedMonkeyPaw, DaylightShavings, DeckOfEveryCard, Dinseylandfill, DNALab, FloristFriar, GingerBread, GreyGoose, Guzzlr, Horsery, JuneCleaver, JungMan, Latte, LookingGlass, MayoClinic, MummingTrunk, ObtuseAngel, Pantogram, RainDoh, ReagnimatedGnome, RetroCape, Robortender, Snapper, SongBoom, SourceTerminal, Spacegate, SpookyPutty, Stickers, StompingBoots, TrainSet, TunnelOfLove, WinterGarden, Witchess, };
49
50
  export * from "./putty-likes";
50
51
  export * from "./LibramSummon";
@@ -17,6 +17,7 @@ import * as ChateauMantegna from "./2015/ChateauMantegna";
17
17
  import * as DeckOfEveryCard from "./2015/DeckOfEveryCard";
18
18
  import * as Dinseylandfill from "./2015/Dinseylandfill";
19
19
  import * as MayoClinic from "./2015/MayoClinic";
20
+ import * as GingerBread from "./2016/GingerBread";
20
21
  import * as SourceTerminal from "./2016/SourceTerminal";
21
22
  import * as Witchess from "./2016/Witchess";
22
23
  import * as AsdonMartin from "./2017/AsdonMartin";
@@ -45,6 +46,6 @@ import * as AugustScepter from "./2023/AugustScepter";
45
46
  import * as CinchoDeMayo from "./2023/CinchoDeMayo";
46
47
  import * as ClosedCircuitPayphone from "./2023/ClosedCircuitPayphone";
47
48
  import * as CursedMonkeyPaw from "./2023/CursedMonkeyPaw";
48
- export { AugustScepter, AutumnAton, AsdonMartin, Bandersnatch, BarrelShrine, BeachComb, CampAway, Cartography, ChateauMantegna, CinchoDeMayo, ClosedCircuitPayphone, CombatLoversLocket, CrimboShrub, CrownOfThrones, CrystalBall, CursedMonkeyPaw, DaylightShavings, DeckOfEveryCard, Dinseylandfill, DNALab, FloristFriar, GreyGoose, Guzzlr, Horsery, JuneCleaver, JungMan, Latte, LookingGlass, MayoClinic, MummingTrunk, ObtuseAngel, Pantogram, RainDoh, ReagnimatedGnome, RetroCape, Robortender, Snapper, SongBoom, SourceTerminal, Spacegate, SpookyPutty, Stickers, StompingBoots, TrainSet, TunnelOfLove, WinterGarden, Witchess, };
49
+ export { AugustScepter, AutumnAton, AsdonMartin, Bandersnatch, BarrelShrine, BeachComb, CampAway, Cartography, ChateauMantegna, CinchoDeMayo, ClosedCircuitPayphone, CombatLoversLocket, CrimboShrub, CrownOfThrones, CrystalBall, CursedMonkeyPaw, DaylightShavings, DeckOfEveryCard, Dinseylandfill, DNALab, FloristFriar, GingerBread, GreyGoose, Guzzlr, Horsery, JuneCleaver, JungMan, Latte, LookingGlass, MayoClinic, MummingTrunk, ObtuseAngel, Pantogram, RainDoh, ReagnimatedGnome, RetroCape, Robortender, Snapper, SongBoom, SourceTerminal, Spacegate, SpookyPutty, Stickers, StompingBoots, TrainSet, TunnelOfLove, WinterGarden, Witchess, };
49
50
  export * from "./putty-likes";
50
51
  export * from "./LibramSummon";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libram",
3
- "version": "0.8.14",
3
+ "version": "0.8.15",
4
4
  "description": "JavaScript helper library for KoLmafia",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -53,7 +53,7 @@
53
53
  "husky": "^4.3.6",
54
54
  "java-parser": "^1.4.0",
55
55
  "jest": "^27.1.0",
56
- "kolmafia": "^5.27457.0",
56
+ "kolmafia": "^5.27647.0",
57
57
  "lint-staged": ">=10",
58
58
  "node-fetch": "^2.6.1",
59
59
  "prettier": "^2.1.2",