libram 0.6.8 → 0.6.11

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/ascend.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Class, Item } from "kolmafia";
1
+ import { Skill, Class, Item } from "kolmafia";
2
2
  import { Path } from "./Path";
3
3
  import { Ceiling, Desk, Nightstand } from "./resources/2015/ChateauMantegna";
4
4
  export declare enum Lifestyle {
@@ -7,6 +7,10 @@ export declare enum Lifestyle {
7
7
  normal = 2,
8
8
  hardcore = 3
9
9
  }
10
+ export declare class AscendError extends Error {
11
+ cause?: Skill | Item | Class | Path;
12
+ constructor(cause?: Skill | Item | Class | Path);
13
+ }
10
14
  declare type MoonSign = number | "mongoose" | "wallaby" | "vole" | "platypus" | "opossum" | "marmot" | "wombat" | "blender" | "packrat" | "degrassi" | "degrassi knoll" | "friendly degrassi knoll" | "knoll" | "canada" | "canadia" | "little canadia" | "gnomads" | "gnomish" | "gnomish gnomads camp";
11
15
  /**
12
16
  * Hops the gash, perming no skills
@@ -17,7 +21,7 @@ declare type MoonSign = number | "mongoose" | "wallaby" | "vole" | "platypus" |
17
21
  * @param consumable From the astral deli. Pick the container item, not the product.
18
22
  * @param pet From the astral pet store.
19
23
  */
20
- export declare function ascend(path: Path, playerClass: Class, lifestyle: Lifestyle, moon: MoonSign, consumable?: Item | undefined, pet?: Item | undefined): void;
24
+ export declare function ascend(path: Path, playerClass: Class, lifestyle: Lifestyle, moon: MoonSign, consumable?: Item | undefined, pet?: Item | undefined, permSkills?: Map<Skill, Lifestyle> | undefined): void;
21
25
  declare const worksheds: readonly ["warbear LP-ROM burner", "warbear jackhammer drill press", "warbear induction oven", "warbear high-efficiency still", "warbear chemistry lab", "warbear auto-anvil", "spinning wheel", "snow machine", "Little Geneticist DNA-Splicing Lab", "portable Mayo Clinic", "Asdon Martin keyfob", "diabolic pizza cube", "cold medicine cabinet"];
22
26
  declare type Workshed = typeof worksheds[number];
23
27
  declare const gardens: readonly ["packet of pumpkin seeds", "Peppermint Pip Packet", "packet of dragon's teeth", "packet of beer seeds", "packet of winter seeds", "packet of thanksgarden seeds", "packet of tall grass seeds", "packet of mushroom spores"];
package/dist/ascend.js CHANGED
@@ -1,4 +1,4 @@
1
- import { containsText, eudoraItem, getCampground, getWorkshed, Item, toInt, use, visitUrl, xpath, } from "kolmafia";
1
+ import { Skill, Class, containsText, eudoraItem, getCampground, getWorkshed, Item, toInt, use, visitUrl, xpath, haveSkill, } from "kolmafia";
2
2
  import { ChateauMantegna } from "./resources";
3
3
  import { $item, $items, $stat } from "./template-string";
4
4
  export var Lifestyle;
@@ -8,6 +8,32 @@ export var Lifestyle;
8
8
  Lifestyle[Lifestyle["normal"] = 2] = "normal";
9
9
  Lifestyle[Lifestyle["hardcore"] = 3] = "hardcore";
10
10
  })(Lifestyle || (Lifestyle = {}));
11
+ export class AscendError extends Error {
12
+ cause;
13
+ constructor(cause) {
14
+ if (!cause) {
15
+ super("Failed to ascend--do you have a pending trade offer?");
16
+ }
17
+ else if (cause instanceof Skill) {
18
+ const reason = cause.permable
19
+ ? haveSkill(cause)
20
+ ? "invalid for mysterious reasons"
21
+ : "not a skill you currently know"
22
+ : "unpermable";
23
+ super(`Skill ${cause} is ${reason}!`);
24
+ }
25
+ else if (cause instanceof Item) {
26
+ super(`Invalid astral item: ${cause}!`);
27
+ }
28
+ else if (cause instanceof Class) {
29
+ super(`Invalid class ${cause} for this path!`);
30
+ }
31
+ else {
32
+ super(`Invalid path ${cause}!`);
33
+ }
34
+ this.cause = cause;
35
+ }
36
+ }
11
37
  function toMoonId(moon, playerClass) {
12
38
  if (typeof moon === "number")
13
39
  return moon;
@@ -68,28 +94,34 @@ function toMoonId(moon, playerClass) {
68
94
  * @param consumable From the astral deli. Pick the container item, not the product.
69
95
  * @param pet From the astral pet store.
70
96
  */
71
- export function ascend(path, playerClass, lifestyle, moon, consumable = $item `astral six-pack`, pet = undefined) {
72
- if (!containsText(visitUrl("charpane.php"), "Astral Spirit")) {
73
- visitUrl("ascend.php?action=ascend&confirm=on&confirm2=on");
74
- }
75
- if (!containsText(visitUrl("charpane.php"), "Astral Spirit")) {
76
- throw new Error("Failed to ascend.");
77
- }
97
+ export function ascend(path, playerClass, lifestyle, moon, consumable = $item `astral six-pack`, pet = undefined, permSkills = undefined) {
78
98
  if (!path.classes.includes(playerClass)) {
79
- throw new Error(`Invalid class ${playerClass} for this path`);
99
+ throw new AscendError(playerClass);
80
100
  }
81
101
  if (path.id < 0)
82
- throw new Error(`Invalid path ID ${path.id}`);
102
+ throw new AscendError(path);
83
103
  const moonId = toMoonId(moon, playerClass);
84
104
  if (moonId < 1 || moonId > 9)
85
105
  throw new Error(`Invalid moon ${moon}`);
86
106
  if (consumable &&
87
107
  !$items `astral six-pack, astral hot dog dinner, [10882]carton of astral energy drinks`.includes(consumable)) {
88
- throw new Error(`Invalid consumable ${consumable}`);
108
+ throw new AscendError(consumable);
89
109
  }
90
110
  if (pet &&
91
111
  !$items `astral bludgeon, astral shield, astral chapeau, astral bracer, astral longbow, astral shorts, astral mace, astral ring, astral statuette, astral pistol, astral mask, astral pet sweater, astral shirt, astral belt`.includes(pet)) {
92
- throw new Error(`Invalid astral item ${pet}`);
112
+ throw new AscendError(pet);
113
+ }
114
+ const illegalSkill = permSkills
115
+ ? Array.from(permSkills.keys()).find((skill) => !skill.permable || !haveSkill(skill))
116
+ : undefined;
117
+ if (illegalSkill) {
118
+ throw new AscendError(illegalSkill);
119
+ }
120
+ if (!containsText(visitUrl("charpane.php"), "Astral Spirit")) {
121
+ visitUrl("ascend.php?action=ascend&confirm=on&confirm2=on");
122
+ }
123
+ if (!containsText(visitUrl("charpane.php"), "Astral Spirit")) {
124
+ throw new AscendError();
93
125
  }
94
126
  visitUrl("afterlife.php?action=pearlygates");
95
127
  if (consumable) {
@@ -97,6 +129,14 @@ export function ascend(path, playerClass, lifestyle, moon, consumable = $item `a
97
129
  }
98
130
  if (pet)
99
131
  visitUrl(`afterlife.php?action=buyarmory&whichitem=${toInt(pet)}`);
132
+ if (permSkills) {
133
+ for (const [skill, permLevel] of permSkills.entries()) {
134
+ if (permLevel !== Lifestyle.casual) {
135
+ const permText = permLevel === Lifestyle.hardcore ? "hcperm" : "scperm";
136
+ visitUrl(`afterlife.php?action=${permText}&whichskill=${toInt(skill)}`);
137
+ }
138
+ }
139
+ }
100
140
  visitUrl(`afterlife.php?action=ascend&confirmascend=1&whichsign=${moonId}&gender=2&whichclass=${toInt(playerClass)}&whichpath=${path.id}&asctype=${lifestyle}&nopetok=1&noskillsok=1&lamepathok=1&lamesignok=1&pwd`, true);
101
141
  }
102
142
  const worksheds = [
@@ -1,12 +1,14 @@
1
1
  import { Requirement } from "../../maximize";
2
2
  export default class CommunityService {
3
3
  private choice;
4
+ private stat;
4
5
  private property;
5
6
  private predictor;
6
7
  private maximizeRequirements;
7
8
  /**
8
9
  * Class to store properties of various CS tests.
9
10
  * @param id The id the game HTML uses to identify the test; this is used primarily in runChoice.
11
+ * @param stat The principle stat the test measures, often used as more easily memorable shorthand for the specific tests
10
12
  * @param property The name of the test as a string, often used as part of the string property "csServicesPerformed".
11
13
  * @param predictor A function that returns an estimate for the number of turns that the test will take given your character's current state.
12
14
  * @param maximizeRequirements A Requirement object, if applicable, that aligns with the things needed to maximize for this particular test.
@@ -16,6 +18,10 @@ export default class CommunityService {
16
18
  * @returns The id number of the test, used primarily in runChoice.
17
19
  */
18
20
  get id(): number;
21
+ /**
22
+ * @returns The primary stat the test measures, used primarily as memorable shorthand in place of test names.
23
+ */
24
+ get statName(): string;
19
25
  /**
20
26
  * @returns The name of the test, used primarily as part of the string property "csServicesPerformed"
21
27
  */
@@ -28,6 +34,7 @@ export default class CommunityService {
28
34
  * @returns A Requirement object, if applicable, that aligns with the things needed to maximize for this particular test.
29
35
  */
30
36
  get requirement(): Requirement | null;
37
+ static logTask(name: string, action: () => number | void): void;
31
38
  /**
32
39
  * Checks the "csServicesPerformed" property to see whether mafia currently believes this test is complete.
33
40
  * @returns Whether mafia currently believes this test is complete.
@@ -45,16 +52,22 @@ export default class CommunityService {
45
52
  /**
46
53
  * Wrapper function that prepares for a test and then completes it, adding time and turn details to the log.
47
54
  * @param prepare A function that does all necessary preparations for this CS test, including choosing your outfit. Optionally returns the number of turns you expect to spend preparing for the test.
48
- * @param beCertain Whether we should check council.php instead of mafia to determine whether the test is complete.
49
- * @param maxTurns We will run the test iff the predicted turns is less than or equal to this parameter.
55
+ * @param maxTurns We will run the test iff the predicted/actual turns is less than or equal to this parameter.
50
56
  * @returns "completed", "failed", or "already completed".
51
57
  */
52
- run(prepare: () => void | number, beCertain?: boolean, maxTurns?: number): "completed" | "failed" | "already completed";
58
+ run(prepare: () => void | number, maxTurns?: number): "completed" | "failed" | "already completed";
59
+ private _verifyIsDone;
53
60
  /**
54
61
  * Checks council.php to verify that a test is complete; more reliable than isDone, but requires an additional pagehit.
55
62
  * @returns Whether council.php suggests that the test is complete.
56
63
  */
57
64
  verifyIsDone(): boolean;
65
+ private _actualCost;
66
+ /**
67
+ * Checks council.php for the number of turns this test will take; more reliable than prediction, but requires an additional pagehit.
68
+ * @returns The number of turns to complete this test according to council.php.
69
+ */
70
+ actualCost(): number;
58
71
  /**
59
72
  * A log of the predicted turns, actual turns, and duration of each CS test performed.
60
73
  */
@@ -63,6 +76,7 @@ export default class CommunityService {
63
76
  predictedTurns: number;
64
77
  turnCost: number;
65
78
  seconds: number;
79
+ type: "test" | "task";
66
80
  };
67
81
  };
68
82
  /**
@@ -1,4 +1,4 @@
1
- import { equippedItem, familiarWeight, getPower, haveEquipped, myBasestat, myBuffedstat, myFamiliar, myMaxhp, myThrall, myTurncount, numericModifier, print, runChoice, toSlot, visitUrl, weightAdjustment, } from "kolmafia";
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
4
  import { get as getModifier } from "../../modifier";
@@ -16,20 +16,24 @@ const statCommunityServicePredictor = (stat) => {
16
16
  (myBuffedstat(stat) -
17
17
  myBasestat(thralls.get(stat) === myThrall() ? $stat `mysticality` : stat)));
18
18
  };
19
+ const visitCouncil = () => visitUrl("council.php");
19
20
  export default class CommunityService {
20
21
  choice;
22
+ stat;
21
23
  property;
22
24
  predictor;
23
25
  maximizeRequirements;
24
26
  /**
25
27
  * Class to store properties of various CS tests.
26
28
  * @param id The id the game HTML uses to identify the test; this is used primarily in runChoice.
29
+ * @param stat The principle stat the test measures, often used as more easily memorable shorthand for the specific tests
27
30
  * @param property The name of the test as a string, often used as part of the string property "csServicesPerformed".
28
31
  * @param predictor A function that returns an estimate for the number of turns that the test will take given your character's current state.
29
32
  * @param maximizeRequirements A Requirement object, if applicable, that aligns with the things needed to maximize for this particular test.
30
33
  */
31
- constructor(id, property, predictor, maximizeRequirements) {
34
+ constructor(id, stat, property, predictor, maximizeRequirements) {
32
35
  this.choice = id;
36
+ this.stat = stat;
33
37
  this.property = property;
34
38
  this.predictor = predictor;
35
39
  this.maximizeRequirements = maximizeRequirements;
@@ -40,6 +44,12 @@ export default class CommunityService {
40
44
  get id() {
41
45
  return this.choice;
42
46
  }
47
+ /**
48
+ * @returns The primary stat the test measures, used primarily as memorable shorthand in place of test names.
49
+ */
50
+ get statName() {
51
+ return this.stat;
52
+ }
43
53
  /**
44
54
  * @returns The name of the test, used primarily as part of the string property "csServicesPerformed"
45
55
  */
@@ -58,6 +68,17 @@ export default class CommunityService {
58
68
  get requirement() {
59
69
  return this.maximizeRequirements;
60
70
  }
71
+ static logTask(name, action) {
72
+ const startTime = Date.now();
73
+ const startTurns = myTurncount();
74
+ const estimatedTurns = action() ?? 0;
75
+ CommunityService.log[name] = {
76
+ type: "task",
77
+ turnCost: myTurncount() - startTurns,
78
+ predictedTurns: estimatedTurns,
79
+ seconds: (Date.now() - startTime) / 1000,
80
+ };
81
+ }
61
82
  /**
62
83
  * Checks the "csServicesPerformed" property to see whether mafia currently believes this test is complete.
63
84
  * @returns Whether mafia currently believes this test is complete.
@@ -78,21 +99,19 @@ export default class CommunityService {
78
99
  */
79
100
  do() {
80
101
  if (get("csServicesPerformed").trim().length === 0)
81
- visitUrl("council.php");
82
- visitUrl("council.php");
83
- runChoice(this.choice);
84
- return this.isDone();
102
+ visitCouncil();
103
+ visitCouncil();
104
+ const councilText = runChoice(this.choice);
105
+ return this._verifyIsDone(councilText);
85
106
  }
86
107
  /**
87
108
  * Wrapper function that prepares for a test and then completes it, adding time and turn details to the log.
88
109
  * @param prepare A function that does all necessary preparations for this CS test, including choosing your outfit. Optionally returns the number of turns you expect to spend preparing for the test.
89
- * @param beCertain Whether we should check council.php instead of mafia to determine whether the test is complete.
90
- * @param maxTurns We will run the test iff the predicted turns is less than or equal to this parameter.
110
+ * @param maxTurns We will run the test iff the predicted/actual turns is less than or equal to this parameter.
91
111
  * @returns "completed", "failed", or "already completed".
92
112
  */
93
- run(prepare, beCertain = false, maxTurns = Infinity) {
94
- const finishedFunction = () => beCertain ? this.verifyIsDone() : this.isDone();
95
- if (finishedFunction())
113
+ run(prepare, maxTurns = Infinity) {
114
+ if (this.isDone())
96
115
  return "already completed";
97
116
  const startTime = Date.now();
98
117
  const startTurns = myTurncount();
@@ -104,25 +123,43 @@ export default class CommunityService {
104
123
  return "failed";
105
124
  }
106
125
  const prediction = this.predictor();
107
- if (prediction <= maxTurns) {
108
- this.do();
109
- }
110
- if (finishedFunction()) {
111
- CommunityService.log[this.property] = {
112
- predictedTurns: prediction + additionalTurns,
113
- turnCost: myTurncount() - startTurns,
114
- seconds: (Date.now() - startTime) / 1000,
115
- };
116
- return "completed";
126
+ const council = visitCouncil();
127
+ const turns = this._actualCost(council);
128
+ if (!turns)
129
+ return "already completed";
130
+ if (turns > Math.min(maxTurns, myAdventures())) {
131
+ return "failed";
117
132
  }
118
- return "failed";
133
+ if (!this.do())
134
+ return "failed";
135
+ CommunityService.log[this.property] = {
136
+ predictedTurns: prediction + additionalTurns,
137
+ turnCost: myTurncount() - startTurns,
138
+ seconds: (Date.now() - startTime) / 1000,
139
+ type: "test",
140
+ };
141
+ return "completed";
142
+ }
143
+ _verifyIsDone(councilText) {
144
+ return !councilText.includes(`<input type=hidden name=option value=${this.choice}>`);
119
145
  }
120
146
  /**
121
147
  * Checks council.php to verify that a test is complete; more reliable than isDone, but requires an additional pagehit.
122
148
  * @returns Whether council.php suggests that the test is complete.
123
149
  */
124
150
  verifyIsDone() {
125
- return !visitUrl("council.php").includes(`<input type=hidden name=option value=${this.choice}>`);
151
+ return this._verifyIsDone(visitCouncil());
152
+ }
153
+ _actualCost(councilText) {
154
+ const match = councilText.match(`<input type=hidden name=option value=${this.id}>.*?Perform Service \\((\\d+) Adventures\\)`);
155
+ return match ? parseInt(match[1]) : 0;
156
+ }
157
+ /**
158
+ * Checks council.php for the number of turns this test will take; more reliable than prediction, but requires an additional pagehit.
159
+ * @returns The number of turns to complete this test according to council.php.
160
+ */
161
+ actualCost() {
162
+ return this._actualCost(visitCouncil());
126
163
  }
127
164
  /**
128
165
  * A log of the predicted turns, actual turns, and duration of each CS test performed.
@@ -135,19 +172,28 @@ export default class CommunityService {
135
172
  static printLog(colour = "blue") {
136
173
  const logEntries = Object.entries(CommunityService.log);
137
174
  for (const [testName, testEntry] of logEntries) {
138
- const { predictedTurns, turnCost, seconds } = testEntry;
139
- print(`We predicted the ${testName} test would take ${predictedTurns} turns, ${predictedTurns === turnCost ? "and" : "but"} it took ${turnCost} turns`, colour);
140
- print(`${testName} took ${seconds} seconds`, colour);
175
+ const { type, predictedTurns, turnCost, seconds } = testEntry;
176
+ if (type === "test") {
177
+ print(`We predicted the ${testName} test would take ${predictedTurns} turns, ${predictedTurns === turnCost ? "and" : "but"} it took ${turnCost} turns.`, colour);
178
+ print(`${testName} took ${seconds.toFixed(1)} seconds.`, colour);
179
+ }
180
+ else {
181
+ if (!(predictedTurns === 0 && turnCost === 0)) {
182
+ print(`We predicted the task ${testName} would take ${predictedTurns} turns, ${predictedTurns === turnCost ? "and" : "but"} it took ${turnCost} turns.`, colour);
183
+ }
184
+ print(`The task ${testName} took ${seconds.toFixed(1)} seconds.`, colour);
185
+ }
141
186
  }
142
- print(`All together, you have spent ${sum(logEntries, ([, testEntry]) => testEntry.seconds)} seconds during this Community Service run`, colour);
187
+ const totalTime = sum(logEntries, ([, testEntry]) => testEntry.seconds);
188
+ print(`All together, you have spent ${totalTime.toFixed(1)} seconds during this Community Service run`, colour);
143
189
  }
144
190
  // Below, we have the tests themselves.
145
- static HP = new CommunityService(1, "Donate Blood", () => 60 - Math.floor((myMaxhp() - myBuffedstat($stat `muscle`) - 3) / 30), new Requirement(["HP"], {}));
146
- static Muscle = new CommunityService(2, "Feed The Children", statCommunityServicePredictor($stat `Muscle`), new Requirement(["Muscle"], {}));
147
- static Mysticality = new CommunityService(3, "Build Playground Mazes", statCommunityServicePredictor($stat `Mysticality`), new Requirement(["Mysticality"], {}));
148
- static Moxie = new CommunityService(4, "Feed Conspirators", statCommunityServicePredictor($stat `Moxie`), new Requirement(["Moxie"], {}));
149
- static FamiliarWeight = new CommunityService(5, "Breed More Collies", () => 60 - Math.floor((familiarWeight(myFamiliar()) + weightAdjustment()) / 5), new Requirement(["Familiar Weight"], {}));
150
- static WeaponDamage = new CommunityService(6, "Reduce Gazelle Population", () => {
191
+ static HP = new CommunityService(1, "HP", "Donate Blood", () => 60 - Math.floor((myMaxhp() - myBuffedstat($stat `muscle`) - 3) / 30), new Requirement(["HP"], {}));
192
+ static Muscle = new CommunityService(2, "Muscle", "Feed The Children", statCommunityServicePredictor($stat `Muscle`), new Requirement(["Muscle"], {}));
193
+ static Mysticality = new CommunityService(3, "Mysticality", "Build Playground Mazes", statCommunityServicePredictor($stat `Mysticality`), new Requirement(["Mysticality"], {}));
194
+ static Moxie = new CommunityService(4, "Moxie", "Feed Conspirators", statCommunityServicePredictor($stat `Moxie`), new Requirement(["Moxie"], {}));
195
+ static FamiliarWeight = new CommunityService(5, "Familiar Weight", "Breed More Collies", () => 60 - Math.floor((familiarWeight(myFamiliar()) + weightAdjustment()) / 5), new Requirement(["Familiar Weight"], {}));
196
+ static WeaponDamage = new CommunityService(6, "Weapon Damage", "Reduce Gazelle Population", () => {
151
197
  const weaponPower = getPower(equippedItem($slot `weapon`));
152
198
  const offhandPower = toSlot(equippedItem($slot `off-hand`)) === $slot `weapon`
153
199
  ? getPower(equippedItem($slot `off-hand`))
@@ -166,7 +212,7 @@ export default class CommunityService {
166
212
  0.001) -
167
213
  Math.floor((multiplier * getModifier("Weapon Damage Percent")) / 50 + 0.001));
168
214
  }, new Requirement(["Weapon Damage", "Weapon Damage Percent"], {}));
169
- static SpellDamage = new CommunityService(7, "Make Sausage", () => {
215
+ static SpellDamage = new CommunityService(7, "Spell Damage", "Make Sausage", () => {
170
216
  const dragonfishDamage = myFamiliar() === $familiar `Magic Dragonfish`
171
217
  ? numericModifier($familiar `Magic Dragonfish`, "Spell Damage Percent", familiarWeight($familiar `Magic Dragonfish`) + weightAdjustment(), $item `none`)
172
218
  : 0;
@@ -175,12 +221,12 @@ export default class CommunityService {
175
221
  Math.floor(getModifier("Spell Damage") / 50 + 0.001) -
176
222
  Math.floor((getModifier("Spell Damage Percent") - dragonfishDamage) / 50 + 0.001));
177
223
  }, new Requirement(["Spell Damage", "Spell Damage Percent"], {}));
178
- static Noncombat = new CommunityService(8, "Be a Living Statue", () => {
224
+ static Noncombat = new CommunityService(8, "Non-Combat", "Be a Living Statue", () => {
179
225
  const noncombatRate = -1 * getModifier("Combat Rate");
180
226
  const unsoftcappedRate = noncombatRate > 25 ? 25 + (noncombatRate - 25) * 5 : noncombatRate;
181
227
  return 60 - 3 * Math.floor(unsoftcappedRate / 5);
182
228
  }, new Requirement(["-combat"], {}));
183
- static BoozeDrop = new CommunityService(9, "Make Margaritas", () => {
229
+ static BoozeDrop = new CommunityService(9, "Item Drop", "Make Margaritas", () => {
184
230
  const mummingCostume = MummingTrunk.currentCostumes().get(myFamiliar());
185
231
  const mummingBuff = mummingCostume && mummingCostume[0] === "Item Drop"
186
232
  ? mummingCostume[1]
@@ -202,10 +248,10 @@ export default class CommunityService {
202
248
  }, new Requirement(["Item Drop", "2 Booze Drop"], {
203
249
  preventEquip: $items `broken champagne bottle`,
204
250
  }));
205
- static HotRes = new CommunityService(10, "Clean Steam Tunnels", () => 60 - getModifier("Hot Resistance"), new Requirement(["Hot Resistance"], {}));
206
- static CoilWire = new CommunityService(11, "Coil Wire", () => 60, new Requirement([], {}));
251
+ static HotRes = new CommunityService(10, "Hot Resistance", "Clean Steam Tunnels", () => 60 - getModifier("Hot Resistance"), new Requirement(["Hot Resistance"], {}));
252
+ static CoilWire = new CommunityService(11, "Coil Wire", "Coil Wire", () => 60, new Requirement([], {}));
207
253
  static donate = () => {
208
- visitUrl("council.php");
254
+ visitCouncil();
209
255
  visitUrl("choice.php?whichchoice=1089&option=30");
210
256
  };
211
257
  }
package/dist/combat.d.ts CHANGED
@@ -103,7 +103,7 @@ export declare class Macro {
103
103
  * @param ifTrue Continuation if the condition is true.
104
104
  * @returns {Macro} This object itself.
105
105
  */
106
- if_(condition: string | Monster | Effect | Skill | Item | Location | Class | Stat, ifTrue: string | Macro): this;
106
+ if_(condition: string | Monster | Monster[] | Effect | Skill | Item | Location | Class | Stat, ifTrue: string | Macro): this;
107
107
  /**
108
108
  * Create a new macro with an "if" statement.
109
109
  * @param condition The BALLS condition for the if statement.
package/dist/combat.js CHANGED
@@ -211,6 +211,12 @@ export class Macro {
211
211
  if (condition instanceof Monster) {
212
212
  ballsCondition = `monsterid ${condition.id}`;
213
213
  }
214
+ else if (condition instanceof Array) {
215
+ ballsCondition = condition
216
+ .map((mon) => `monsterid ${mon.id}`)
217
+ .join(" || ");
218
+ ballsCondition = `(${ballsCondition})`;
219
+ }
214
220
  else if (condition instanceof Effect) {
215
221
  ballsCondition = `haseffect ${toInt(condition)}`;
216
222
  }
package/dist/counter.d.ts CHANGED
@@ -1,9 +1,15 @@
1
1
  /**
2
- * Returns null for counters that do not exist, and otherwise returns the duration of the counter
2
+ * Returns Infinity for counters that do not exist, and otherwise returns the duration of the counter
3
3
  * @param counter The name of the counter in question
4
- * @returns null if the counter does not exist; otherwise returns the duration of the counter
4
+ * @returns Infinity if the counter does not exist; otherwise returns the duration of the counter
5
5
  */
6
- export declare function get(counter: string): number | null;
6
+ export declare function get(counter: string): number;
7
+ /**
8
+ * The world is everything that is the case. This determines which counters are the case.
9
+ * @param counter The name of the counter in question
10
+ * @returns True for counters which currently exist; false for those which do not
11
+ */
12
+ export declare function exists(counter: string): boolean;
7
13
  /**
8
14
  * Creates a manual counter with specified name and duration
9
15
  * @param counter Name of the counter to manually create
package/dist/counter.js CHANGED
@@ -1,19 +1,27 @@
1
1
  import { cliExecute, getCounter, getCounters } from "kolmafia";
2
2
  /**
3
- * Returns null for counters that do not exist, and otherwise returns the duration of the counter
3
+ * Returns Infinity for counters that do not exist, and otherwise returns the duration of the counter
4
4
  * @param counter The name of the counter in question
5
- * @returns null if the counter does not exist; otherwise returns the duration of the counter
5
+ * @returns Infinity if the counter does not exist; otherwise returns the duration of the counter
6
6
  */
7
7
  export function get(counter) {
8
8
  const value = getCounter(counter);
9
- //getCounter returns -1 for counters that don't exist, but it also returns -1 for counters whose value is -1
9
+ // getCounter returns -1 for counters that don't exist, but it also returns -1 for counters whose value is -1
10
10
  if (value === -1) {
11
- //if we have a counter with value -1, we check to see if that counter exists via getCounters()
12
- //We return null if it doesn't exist
13
- return getCounters(counter, -1, -1).trim() === "" ? null : -1;
11
+ // if we have a counter with value -1, we check to see if that counter exists via getCounters()
12
+ // We return null if it doesn't exist
13
+ return getCounters(counter, -1, -1).trim() === "" ? Infinity : -1;
14
14
  }
15
15
  return value;
16
16
  }
17
+ /**
18
+ * The world is everything that is the case. This determines which counters are the case.
19
+ * @param counter The name of the counter in question
20
+ * @returns True for counters which currently exist; false for those which do not
21
+ */
22
+ export function exists(counter) {
23
+ return (getCounter(counter) !== -1 || getCounters(counter, -1, -1).trim() !== "");
24
+ }
17
25
  /**
18
26
  * Creates a manual counter with specified name and duration
19
27
  * @param counter Name of the counter to manually create
@@ -10,6 +10,7 @@ declare type MenuItemOptions<T> = {
10
10
  priceOverride?: number;
11
11
  mayo?: Item;
12
12
  data?: T;
13
+ useRetrievePrice?: boolean;
13
14
  };
14
15
  export declare class MenuItem<T> {
15
16
  item: Item;
@@ -21,6 +22,7 @@ export declare class MenuItem<T> {
21
22
  priceOverride?: number;
22
23
  mayo?: Item;
23
24
  data?: T;
25
+ static defaultPriceFunction: (item: Item) => number;
24
26
  static defaultOptions<T>(): Map<Item, MenuItemOptions<T>>;
25
27
  /**
26
28
  * Construct a new menu item, possibly with extra properties. Items in MenuItem.defaultOptions have intelligent defaults.
@@ -60,6 +60,7 @@ export class MenuItem {
60
60
  priceOverride;
61
61
  mayo;
62
62
  data;
63
+ static defaultPriceFunction = (item) => npcPrice(item) > 0 ? npcPrice(item) : mallPrice(item);
63
64
  static defaultOptions() {
64
65
  return new Map([
65
66
  [
@@ -159,8 +160,7 @@ export class MenuItem {
159
160
  return this.item.toString();
160
161
  }
161
162
  price() {
162
- return (this.priceOverride ??
163
- (npcPrice(this.item) > 0 ? npcPrice(this.item) : mallPrice(this.item)));
163
+ return this.priceOverride ?? MenuItem.defaultPriceFunction?.(this.item);
164
164
  }
165
165
  }
166
166
  const organs = ["food", "booze", "spleen item"];
package/dist/lib.js CHANGED
@@ -3,7 +3,7 @@ import "core-js/modules/es.object.entries";
3
3
  import "core-js/features/array/flat";
4
4
  import { appearanceRates, autosellPrice, availableAmount, booleanModifier, cliExecute, Effect, Familiar, fullnessLimit, getCampground, getCounters, getPlayerId, getPlayerName, getRelated, haveEffect, haveFamiliar, haveServant, haveSkill, holiday, inebrietyLimit, Item, Location, mallPrice, Monster, myEffects, myFamiliar, myFullness, myInebriety, myPath, mySpleenUse, myThrall, myTurncount, numericModifier, Servant, Skill, spleenLimit, Thrall, toItem, toSkill, totalTurnsPlayed, } from "kolmafia";
5
5
  import { get } from "./property";
6
- import { $class, $familiar, $item, $items, $monsters } from "./template-string";
6
+ import { $class, $familiar, $item, $items, $monsters, $skill, } from "./template-string";
7
7
  import { chunk } from "./utils";
8
8
  /**
9
9
  * Returns the current maximum Accordion Thief songs the player can have in their head
@@ -323,10 +323,25 @@ export function getBanishedMonsters() {
323
323
  break;
324
324
  // toItem doesn"t error if the item doesn"t exist, so we have to use that.
325
325
  const banisherItem = toItem(banisher);
326
- const banisherObject = [Item.get("none"), null].includes(banisherItem)
327
- ? Skill.get(banisher)
328
- : banisherItem;
329
- result.set(banisherObject, Monster.get(foe));
326
+ if (banisher.toLowerCase() === "saber force") {
327
+ result.set($skill `Use the Force`, Monster.get(foe));
328
+ }
329
+ else if ([
330
+ Item.get("none"),
331
+ Item.get(`training scroll: Snokebomb`),
332
+ Item.get(`tomayohawk-style reflex hammer`),
333
+ null,
334
+ ].includes(banisherItem)) {
335
+ if (Skill.get(banisher) === $skill `none`) {
336
+ break;
337
+ }
338
+ else {
339
+ result.set(Skill.get(banisher), Monster.get(foe));
340
+ }
341
+ }
342
+ else {
343
+ result.set(banisherItem, Monster.get(foe));
344
+ }
330
345
  }
331
346
  return result;
332
347
  }