libram 0.5.7 → 0.6.1

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.
@@ -26,6 +26,14 @@ export declare type FindActionSourceConstraints = {
26
26
  * only actions that cost nothing.
27
27
  */
28
28
  maximumCost?: () => number;
29
+ /**
30
+ * Function allowing for custom logic if an action should be allowed.
31
+ * If undefined, allow all actions to be considered by other constraints.
32
+ *
33
+ * @param action The action that is being considered.
34
+ * @returns True if the action should be allowed.
35
+ */
36
+ allowedAction?: (action: ActionSource) => boolean;
29
37
  };
30
38
  export declare type ActionConstraints = {
31
39
  /**
@@ -124,6 +124,8 @@ export class ActionSource {
124
124
  }
125
125
  function filterAction(action, constraints) {
126
126
  return (action.available() &&
127
+ (constraints.allowedAction === undefined ||
128
+ constraints.allowedAction(action)) &&
127
129
  !(constraints.requireFamiliar?.() && !action.constraints.familiar) &&
128
130
  !(constraints.requireUnlimited?.() && !action.isUnlimited()) &&
129
131
  !(constraints.noFamiliar?.() && action.constraints.familiar) &&
package/dist/ascend.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import { Path } from "./Path";
2
+ import { Ceiling, Desk, Nightstand } from "./resources/2015/ChateauMantegna";
2
3
  export declare enum Lifestyle {
3
4
  casual = 1,
4
5
  softcore = 2,
5
6
  normal = 2,
6
7
  hardcore = 3
7
8
  }
9
+ 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";
8
10
  /**
9
11
  * Hops the gash, perming no skills
10
12
  * @param path path of choice, as a Path object--these exist as properties of Paths
@@ -14,19 +16,28 @@ export declare enum Lifestyle {
14
16
  * @param consumable From the astral deli. Pick the container item, not the product.
15
17
  * @param pet From the astral pet store.
16
18
  */
17
- export declare function ascend(path: Path, playerClass: Class, lifestyle: Lifestyle, moon: string | number, consumable?: Item | undefined, pet?: Item | undefined): void;
19
+ export declare function ascend(path: Path, playerClass: Class, lifestyle: Lifestyle, moon: MoonSign, consumable?: Item | undefined, pet?: Item | undefined): void;
20
+ 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"];
21
+ declare type Workshed = typeof worksheds[number];
22
+ 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"];
23
+ declare type Garden = typeof gardens[number];
24
+ declare const eudorae: readonly ["My Own Pen Pal kit", "GameInformPowerDailyPro subscription card", "Xi Receiver Unit", "New-You Club Membership Form", "Our Daily Candles™ order form"];
25
+ declare type Eudora = typeof eudorae[number];
18
26
  /**
19
27
  * Sets up various iotms you may want to use in the coming ascension
28
+ * @param ascensionItems.workshed Workshed to switch to.
29
+ * @param ascensionItems.garden Garden to switch to.
20
30
  * @param ascensionItems An object potentially containing your workshed, garden, and eudora, all as items
21
- * @param chateauItems An object potentially containing your chateau desk, ceiling, and nightstand, all as items
22
31
  * @param throwOnFail If true, this will throw an error when it fails to switch something
23
32
  */
24
- export declare function prepareAscension(ascensionItems?: {
25
- workshed?: Item;
26
- garden?: Item;
27
- eudora?: Item;
28
- }, chateauItems?: {
29
- desk?: Item;
30
- ceiling?: Item;
31
- nightstand?: Item;
32
- }, throwOnFail?: boolean): void;
33
+ export declare function prepareAscension({ workshed, garden, eudora, chateau, }?: {
34
+ workshed?: Workshed;
35
+ garden?: Garden;
36
+ eudora?: Eudora;
37
+ chateau?: {
38
+ desk?: Desk;
39
+ ceiling?: Ceiling;
40
+ nightstand?: Nightstand;
41
+ };
42
+ }): void;
43
+ export {};
package/dist/ascend.js CHANGED
@@ -1,8 +1,6 @@
1
- import { containsText, eudoraItem, getCampground, getWorkshed, toInt, use, visitUrl, xpath, } from "kolmafia";
1
+ import { containsText, eudoraItem, getCampground, getWorkshed, toInt, toItem, use, visitUrl, xpath, } from "kolmafia";
2
2
  import { $item, $items, $stat } from "./template-string";
3
- import { get } from "./property";
4
3
  import { ChateauMantegna } from "./resources";
5
- import { have } from "./lib";
6
4
  export var Lifestyle;
7
5
  (function (Lifestyle) {
8
6
  Lifestyle[Lifestyle["casual"] = 1] = "casual";
@@ -22,7 +20,7 @@ function toMoonId(moon, playerClass) {
22
20
  case $stat `Moxie`:
23
21
  return 2;
24
22
  default:
25
- throw `unknown prime stat for ${playerClass}`;
23
+ throw new Error(`unknown prime stat for ${playerClass}`);
26
24
  }
27
25
  };
28
26
  switch (moon.toLowerCase()) {
@@ -74,102 +72,110 @@ export function ascend(path, playerClass, lifestyle, moon, consumable = $item `a
74
72
  if (!containsText(visitUrl("charpane.php"), "Astral Spirit")) {
75
73
  visitUrl("ascend.php?action=ascend&confirm=on&confirm2=on");
76
74
  }
77
- if (!containsText(visitUrl("charpane.php"), "Astral Spirit"))
78
- throw "Failed to ascend.";
79
- if (!path.classes.includes(playerClass))
80
- throw `Invalid class ${playerClass} for this path`;
75
+ if (!containsText(visitUrl("charpane.php"), "Astral Spirit")) {
76
+ throw new Error("Failed to ascend.");
77
+ }
78
+ if (!path.classes.includes(playerClass)) {
79
+ throw new Error(`Invalid class ${playerClass} for this path`);
80
+ }
81
81
  if (path.id < 0)
82
- throw `Invalid path ID ${path.id}`;
82
+ throw new Error(`Invalid path ID ${path.id}`);
83
83
  const moonId = toMoonId(moon, playerClass);
84
84
  if (moonId < 1 || moonId > 9)
85
- throw `Invalid moon ${moon}`;
85
+ throw new Error(`Invalid moon ${moon}`);
86
86
  if (consumable &&
87
- !$items `astral six-pack, astral hot dog dinner, carton of astral energy drinks`.includes(consumable))
88
- throw `Invalid consumable ${consumable}`;
87
+ !$items `astral six-pack, astral hot dog dinner, [10882]carton of astral energy drinks`.includes(consumable)) {
88
+ throw new Error(`Invalid consumable ${consumable}`);
89
+ }
89
90
  if (pet &&
90
- !$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))
91
- throw `Invalid astral item ${pet}`;
91
+ !$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}`);
93
+ }
92
94
  visitUrl("afterlife.php?action=pearlygates");
93
- if (consumable)
95
+ if (consumable) {
94
96
  visitUrl(`afterlife.php?action=buydeli&whichitem=${toInt(consumable)}`);
97
+ }
95
98
  if (pet)
96
99
  visitUrl(`afterlife.php?action=buyarmory&whichitem=${toInt(pet)}`);
97
- 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&pwd`, true);
100
+ 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);
98
101
  }
99
- const worksheds = $items `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`;
100
- const gardens = $items `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`;
101
- // eslint-disable-next-line libram/verify-constants
102
- const eudorae = $items `My Own Pen Pal kit, GameInformPowerDailyPro subscription card, Xi Receiver Unit, New-You Club Membership Form, Our Daily Candles™ order form`;
103
- const desks = $items `fancy stationery set, Swiss piggy bank, continental juice bar`;
104
- const ceilings = $items `antler chandelier, ceiling fan, artificial skylight`;
105
- const nightstands = $items `foreign language tapes, bowl of potpourri, electric muscle stimulator`;
102
+ const worksheds = [
103
+ "warbear LP-ROM burner",
104
+ "warbear jackhammer drill press",
105
+ "warbear induction oven",
106
+ "warbear high-efficiency still",
107
+ "warbear chemistry lab",
108
+ "warbear auto-anvil",
109
+ "spinning wheel",
110
+ "snow machine",
111
+ "Little Geneticist DNA-Splicing Lab",
112
+ "portable Mayo Clinic",
113
+ "Asdon Martin keyfob",
114
+ "diabolic pizza cube",
115
+ "cold medicine cabinet",
116
+ ];
117
+ const gardens = [
118
+ "packet of pumpkin seeds",
119
+ "Peppermint Pip Packet",
120
+ "packet of dragon's teeth",
121
+ "packet of beer seeds",
122
+ "packet of winter seeds",
123
+ "packet of thanksgarden seeds",
124
+ "packet of tall grass seeds",
125
+ "packet of mushroom spores",
126
+ ];
127
+ const eudorae = [
128
+ "My Own Pen Pal kit",
129
+ "GameInformPowerDailyPro subscription card",
130
+ "Xi Receiver Unit",
131
+ "New-You Club Membership Form",
132
+ "Our Daily Candles™ order form",
133
+ ];
106
134
  /**
107
135
  * Sets up various iotms you may want to use in the coming ascension
136
+ * @param ascensionItems.workshed Workshed to switch to.
137
+ * @param ascensionItems.garden Garden to switch to.
108
138
  * @param ascensionItems An object potentially containing your workshed, garden, and eudora, all as items
109
- * @param chateauItems An object potentially containing your chateau desk, ceiling, and nightstand, all as items
110
139
  * @param throwOnFail If true, this will throw an error when it fails to switch something
111
140
  */
112
- export function prepareAscension(ascensionItems, chateauItems, throwOnFail = true) {
113
- if (ascensionItems) {
114
- if (ascensionItems.workshed && getWorkshed() !== ascensionItems.workshed) {
115
- if (!worksheds.includes(ascensionItems.workshed) && throwOnFail)
116
- throw `Invalid workshed: ${ascensionItems.workshed}!`;
117
- else if (!have(ascensionItems.workshed) && throwOnFail)
118
- throw `I'm sorry buddy, but you don't seem to own a ${ascensionItems.workshed}. Which makes it REALLY hard for us to plop one into your workshed.`;
119
- else if (get("_workshedItemUsed") && throwOnFail)
120
- throw `Seems like you've already swapped your workshed, buddy.`;
121
- else
122
- use(ascensionItems.workshed);
123
- if (getWorkshed() !== ascensionItems.workshed && throwOnFail)
124
- throw `We really thought we changed your workshed to a ${ascensionItems.workshed}, but Mafia is saying otherwise.`;
141
+ export function prepareAscension({ workshed, garden, eudora, chateau, } = {}) {
142
+ if (workshed && getWorkshed() !== toItem(workshed)) {
143
+ use(Item.get(workshed));
144
+ }
145
+ if (garden && !Object.getOwnPropertyNames(getCampground()).includes(garden)) {
146
+ use(Item.get(garden));
147
+ if (!Object.getOwnPropertyNames(getCampground()).includes(garden)) {
148
+ throw new Error(`We really thought we changed your garden to a ${garden}, but Mafia is saying otherwise.`);
149
+ }
150
+ }
151
+ if (eudora && eudoraItem().name !== eudora) {
152
+ const eudoraNumber = 1 + eudorae.indexOf(eudora);
153
+ if (!xpath(visitUrl("account.php?tab=correspondence"), `//select[@name="whichpenpal"]/option/@value`).includes(eudoraNumber.toString())) {
154
+ throw new Error(`I'm sorry buddy, but you don't seem to be subscribed to ${eudora}. Which makes it REALLY hard to correspond with them.`);
125
155
  }
126
- if (ascensionItems.garden &&
127
- !Object.getOwnPropertyNames(getCampground()).includes(ascensionItems.garden.name)) {
128
- if (!gardens.includes(ascensionItems.garden) && throwOnFail)
129
- throw `Invalid garden: ${ascensionItems.garden}!`;
130
- else if (!have(ascensionItems.garden) && throwOnFail)
131
- throw `I'm sorry buddy, but you don't seem to own a ${ascensionItems.garden}. Which makes it REALLY hard for us to plant one into your garden.`;
132
- else
133
- use(ascensionItems.garden);
134
- if (!Object.getOwnPropertyNames(getCampground()).includes(ascensionItems.garden.name) &&
135
- throwOnFail)
136
- throw `We really thought we changed your garden to a ${ascensionItems.garden}, but Mafia is saying otherwise.`;
156
+ else {
157
+ visitUrl(`account.php?actions[]=whichpenpal&whichpenpal=${eudoraNumber}&action=Update`, true);
137
158
  }
138
- if (ascensionItems.eudora && eudoraItem() !== ascensionItems.eudora) {
139
- if (!eudorae.includes(ascensionItems.eudora) && throwOnFail)
140
- throw `Invalid eudora: ${ascensionItems.eudora}!`;
141
- const eudoraNumber = 1 + eudorae.indexOf(ascensionItems.eudora);
142
- if (!xpath(visitUrl("account.php?tab=correspondence"), `//select[@name="whichpenpal"]/option/@value`).includes(eudoraNumber.toString()) &&
143
- throwOnFail)
144
- throw `I'm sorry buddy, but you don't seem to be subscribed to ${ascensionItems.eudora}. Which makes it REALLY hard to correspond with them.`;
145
- else
146
- visitUrl(`account.php?actions[]=whichpenpal&whichpenpal=${eudoraNumber}&action=Update`, true);
147
- if (eudoraItem() !== ascensionItems.eudora && throwOnFail)
148
- throw `We really thought we chaned your eudora to a ${ascensionItems.eudora}, but Mafia is saying otherwise.`;
159
+ if (eudoraItem() !== toItem(eudora)) {
160
+ throw new Error(`We really thought we changed your eudora to a ${eudora}, but Mafia is saying otherwise.`);
149
161
  }
150
162
  }
151
- if (chateauItems && ChateauMantegna.have()) {
152
- if (chateauItems.ceiling &&
153
- ChateauMantegna.getCeiling() !== chateauItems.ceiling) {
154
- if (!ceilings.includes(chateauItems.ceiling) && throwOnFail)
155
- throw `Invalid chateau ceiling: ${chateauItems.ceiling}!`;
156
- else if (!ChateauMantegna.changeCeiling(chateauItems.ceiling) &&
157
- throwOnFail)
158
- throw `We tried, but were unable to change your chateau ceiling to ${chateauItems.ceiling}. Probably.`;
163
+ if (chateau && ChateauMantegna.have()) {
164
+ const { desk, ceiling, nightstand } = chateau;
165
+ if (ceiling && ChateauMantegna.getCeiling() !== ceiling) {
166
+ if (!ChateauMantegna.changeCeiling(ceiling)) {
167
+ throw new Error(`We tried, but were unable to change your chateau ceiling to ${ceiling}. Probably.`);
168
+ }
159
169
  }
160
- if (chateauItems.desk && ChateauMantegna.getDesk() !== chateauItems.desk) {
161
- if (!desks.includes(chateauItems.desk) && throwOnFail)
162
- throw `Invalid chateau desk: ${chateauItems.desk}!`;
163
- else if (!ChateauMantegna.changeDesk(chateauItems.desk) && throwOnFail)
164
- throw `We tried, but were unable to change your chateau desk to ${chateauItems.desk}. Probably.`;
170
+ if (desk && ChateauMantegna.getDesk() !== desk) {
171
+ if (!ChateauMantegna.changeDesk(desk)) {
172
+ throw new Error(`We tried, but were unable to change your chateau desk to ${desk}. Probably.`);
173
+ }
165
174
  }
166
- if (chateauItems.nightstand &&
167
- ChateauMantegna.getNightstand() !== chateauItems.nightstand) {
168
- if (!nightstands.includes(chateauItems.nightstand) && throwOnFail)
169
- throw `Invalid chateau nightstand: ${chateauItems.nightstand}!`;
170
- else if (!ChateauMantegna.changeNightstand(chateauItems.nightstand) &&
171
- throwOnFail)
172
- throw `We tried, but were unable to change your chateau nightstand to ${chateauItems.nightstand}. Probably.`;
175
+ if (nightstand && ChateauMantegna.getNightstand() !== nightstand) {
176
+ if (!ChateauMantegna.changeNightstand(nightstand)) {
177
+ throw new Error(`We tried, but were unable to change your chateau nightstand to ${nightstand}. Probably.`);
178
+ }
173
179
  }
174
180
  }
175
181
  }
@@ -0,0 +1,84 @@
1
+ import { Requirement } from "../../maximize";
2
+ /**
3
+ * A log of the predicted turns, actual turns, and duration of each CS test performed.
4
+ */
5
+ export declare const log: {
6
+ [index: string]: {
7
+ predictedTurns: number;
8
+ turnCost: number;
9
+ seconds: number;
10
+ };
11
+ };
12
+ declare class Test {
13
+ private choice;
14
+ private property;
15
+ private predictor;
16
+ private maximizeRequirements;
17
+ /**
18
+ * Class to store properties of various CS tests.
19
+ * @param id The id the game HTML uses to identify the test; this is used primarily in runChoice.
20
+ * @param property The name of the test as a string, often used as part of the string property "csServicesPerformed".
21
+ * @param predictor A function that returns an estimate for the number of turns that the test will take given your character's current state.
22
+ * @param maximizeRequirements A Requirement object, if applicable, that aligns with the things needed to maximize for this particular test.
23
+ */
24
+ constructor(id: number, property: string, predictor: () => number, maximizeRequirements?: Requirement | null);
25
+ /**
26
+ * @returns The id number of the test, used primarily in runChoice.
27
+ */
28
+ get id(): number;
29
+ /**
30
+ * @returns The name of the test, used primarily as part of the string property "csServicesPerformed"
31
+ */
32
+ get name(): string;
33
+ /**
34
+ * @returns The predicted number of turns this test will take given your character's current state.
35
+ */
36
+ get prediction(): number;
37
+ /**
38
+ * @returns A Requirement object, if applicable, that aligns with the things needed to maximize for this particular test.
39
+ */
40
+ get requirement(): Requirement | null;
41
+ /**
42
+ * Checks the "csServicesPerformed" property to see whether mafia currently believes this test is complete.
43
+ * @returns Whether mafia currently believes this test is complete.
44
+ */
45
+ isDone(): boolean;
46
+ /**
47
+ * Maximizes based on the Requirement associated with this particular test.
48
+ */
49
+ maximize(): void;
50
+ /**
51
+ * Attempts to turn in the test to the Council of Loathing.
52
+ * @returns Whether mafia believes the test is complete at the end of this function.
53
+ */
54
+ do(): boolean;
55
+ /**
56
+ * Wrapper function that prepares for a test and then completes it, adding time and turn details to the log.
57
+ * @param prepare A function that does all necessary preparations for this CS test, including choosing your outfit.
58
+ * @param beCertain Whether we should check council.php instead of mafia to determine whether the test is complete.
59
+ * @returns The output of the prepare function given, or null if the test is already complete.
60
+ */
61
+ run<T>(prepare: () => T, beCertain?: boolean): T | null;
62
+ /**
63
+ * Checks council.php to verify that a test is complete; more reliable than isDone, but requires an additional pagehit.
64
+ * @returns Whether council.php suggests that the test is complete.
65
+ */
66
+ verifyIsDone(): boolean;
67
+ }
68
+ export declare const HP: Test;
69
+ export declare const Muscle: Test;
70
+ export declare const Mysticality: Test;
71
+ export declare const Moxie: Test;
72
+ export declare const FamiliarWeight: Test;
73
+ export declare const WeaponDamage: Test;
74
+ export declare const SpellDamage: Test;
75
+ export declare const Noncombat: Test;
76
+ export declare const BoozeDrop: Test;
77
+ export declare const HotRes: Test;
78
+ export declare const CoilWire: Test;
79
+ /**
80
+ * Prints turncount and time details of the test in question.
81
+ * @param colour The colour (or color) you'd like the log to be printed in.
82
+ */
83
+ export declare function printLog(colour?: string): void;
84
+ export {};
@@ -0,0 +1,193 @@
1
+ import { equippedItem, familiarWeight, getPower, haveEquipped, myBasestat, myBuffedstat, myClass, myFamiliar, myLevel, myMaxhp, myThrall, myTurncount, numericModifier, print, runChoice, toSlot, visitUrl, weightAdjustment, } from "kolmafia";
2
+ import { get } from "../../property";
3
+ import { Requirement } from "../../maximize";
4
+ import { $class, $effect, $familiar, $item, $items, $slot, $stat, $thrall, } from "../../template-string";
5
+ import { get as getModifier } from "../../modifier";
6
+ import { have } from "../../lib";
7
+ import { SongBoom } from "../../resources";
8
+ import { sum } from "../../utils";
9
+ /**
10
+ * A log of the predicted turns, actual turns, and duration of each CS test performed.
11
+ */
12
+ export const log = {};
13
+ class Test {
14
+ choice;
15
+ property;
16
+ predictor;
17
+ maximizeRequirements;
18
+ /**
19
+ * Class to store properties of various CS tests.
20
+ * @param id The id the game HTML uses to identify the test; this is used primarily in runChoice.
21
+ * @param property The name of the test as a string, often used as part of the string property "csServicesPerformed".
22
+ * @param predictor A function that returns an estimate for the number of turns that the test will take given your character's current state.
23
+ * @param maximizeRequirements A Requirement object, if applicable, that aligns with the things needed to maximize for this particular test.
24
+ */
25
+ constructor(id, property, predictor, maximizeRequirements = null) {
26
+ this.choice = id;
27
+ this.property = property;
28
+ this.predictor = predictor;
29
+ this.maximizeRequirements = maximizeRequirements;
30
+ }
31
+ /**
32
+ * @returns The id number of the test, used primarily in runChoice.
33
+ */
34
+ get id() {
35
+ return this.choice;
36
+ }
37
+ /**
38
+ * @returns The name of the test, used primarily as part of the string property "csServicesPerformed"
39
+ */
40
+ get name() {
41
+ return this.property;
42
+ }
43
+ /**
44
+ * @returns The predicted number of turns this test will take given your character's current state.
45
+ */
46
+ get prediction() {
47
+ return this.predictor();
48
+ }
49
+ /**
50
+ * @returns A Requirement object, if applicable, that aligns with the things needed to maximize for this particular test.
51
+ */
52
+ get requirement() {
53
+ return this.maximizeRequirements;
54
+ }
55
+ /**
56
+ * Checks the "csServicesPerformed" property to see whether mafia currently believes this test is complete.
57
+ * @returns Whether mafia currently believes this test is complete.
58
+ */
59
+ isDone() {
60
+ return get("csServicesPerformed").includes(this.property);
61
+ }
62
+ /**
63
+ * Maximizes based on the Requirement associated with this particular test.
64
+ */
65
+ maximize() {
66
+ if (this.maximizeRequirements)
67
+ this.maximizeRequirements.maximize();
68
+ }
69
+ /**
70
+ * Attempts to turn in the test to the Council of Loathing.
71
+ * @returns Whether mafia believes the test is complete at the end of this function.
72
+ */
73
+ do() {
74
+ visitUrl("council.php");
75
+ runChoice(this.choice);
76
+ return this.isDone();
77
+ }
78
+ /**
79
+ * Wrapper function that prepares for a test and then completes it, adding time and turn details to the log.
80
+ * @param prepare A function that does all necessary preparations for this CS test, including choosing your outfit.
81
+ * @param beCertain Whether we should check council.php instead of mafia to determine whether the test is complete.
82
+ * @returns The output of the prepare function given, or null if the test is already complete.
83
+ */
84
+ run(prepare, beCertain = false) {
85
+ const finishedFunction = beCertain ? this.verifyIsDone : this.isDone;
86
+ if (finishedFunction())
87
+ return null;
88
+ const startTime = Date.now();
89
+ const startTurns = myTurncount();
90
+ try {
91
+ return prepare();
92
+ }
93
+ finally {
94
+ const prediction = this.predictor();
95
+ this.do();
96
+ const loggedTest = {
97
+ predictedTurns: prediction,
98
+ turnCost: myTurncount() - startTurns,
99
+ seconds: (Date.now() - startTime) / 1000,
100
+ };
101
+ if (finishedFunction()) {
102
+ log[this.property] = loggedTest;
103
+ }
104
+ }
105
+ }
106
+ /**
107
+ * Checks council.php to verify that a test is complete; more reliable than isDone, but requires an additional pagehit.
108
+ * @returns Whether council.php suggests that the test is complete.
109
+ */
110
+ verifyIsDone() {
111
+ return !visitUrl("council.php").includes(`<input type=hidden name=option value=${this.choice}>`);
112
+ }
113
+ }
114
+ const thralls = new Map([
115
+ [$stat `muscle`, $thrall `Elbow Macaroni`],
116
+ [$stat `moxie`, $thrall `Bind Penne Dreadful`],
117
+ ]);
118
+ const statTestPredictor = (stat) => {
119
+ let baseStat = stat;
120
+ if (myClass() === $class `Pastamancer`) {
121
+ const thrall = thralls.get(stat);
122
+ if (thrall && myThrall() === thrall)
123
+ baseStat = $stat `mysticality`;
124
+ }
125
+ return () => 60 - Math.floor((1 / 30) * (myBuffedstat(stat) - myBasestat(baseStat)));
126
+ };
127
+ export const HP = new Test(1, "Donate Blood", () => 60 - Math.floor((myMaxhp() - myBuffedstat($stat `muscle`) - 3) / 30), new Requirement(["HP"], {}));
128
+ export const Muscle = new Test(2, "Feed The Children", statTestPredictor($stat `Muscle`), new Requirement(["Muscle"], {}));
129
+ export const Mysticality = new Test(3, "Build Playground Mazes", statTestPredictor($stat `Mysticality`), new Requirement(["Mysticality"], {}));
130
+ export const Moxie = new Test(4, "Feed Conspirators", statTestPredictor($stat `Moxie`), new Requirement(["Moxie"], {}));
131
+ export const FamiliarWeight = new Test(5, "Breed More Collies", () => 60 - Math.floor((familiarWeight(myFamiliar()) + weightAdjustment()) / 5), new Requirement(["Familiar Weight"], {}));
132
+ export const WeaponDamage = new Test(6, "Reduce Gazelle Population", () => {
133
+ const weaponPower = getPower(equippedItem($slot `weapon`));
134
+ const offhandPower = toSlot(equippedItem($slot `off-hand`)) === $slot `weapon`
135
+ ? getPower(equippedItem($slot `off-hand`))
136
+ : 0;
137
+ const familiarPower = toSlot(equippedItem($slot `familiar`)) === $slot `weapon`
138
+ ? getPower(equippedItem($slot `familiar`))
139
+ : 0;
140
+ const songDamage = SongBoom.song() === "These Fists Were Made for Punchin'" ? myLevel() : 0;
141
+ // mafia does not currently count swagger
142
+ const multiplier = have($effect `Bow-Legged Swagger`) ? 2 : 1;
143
+ return (60 -
144
+ Math.floor((multiplier *
145
+ (getModifier("Weapon Damage") -
146
+ 0.15 * (weaponPower + offhandPower + familiarPower) -
147
+ songDamage)) /
148
+ 50 +
149
+ 0.001) -
150
+ Math.floor((multiplier * getModifier("Weapon Damage Percent")) / 50 + 0.001));
151
+ }, new Requirement(["Weapon Damage", "Weapon Damage Percent"], {}));
152
+ export const SpellDamage = new Test(7, "Make Sausage", () => {
153
+ const dragonfishDamage = myFamiliar() === $familiar `Magic Dragonfish`
154
+ ? numericModifier($familiar `Magic Dragonfish`, "Spell Damage Percent", familiarWeight($familiar `Magic Dragonfish`) + weightAdjustment(), $item `none`)
155
+ : 0;
156
+ return (60 -
157
+ Math.floor(getModifier("Spell Damage") / 50 + 0.001) -
158
+ Math.floor((getModifier("Spell Damage Percent") - dragonfishDamage) / 50 + 0.001));
159
+ }, new Requirement(["Spell Damage", "Spell Damage Percent"], {}));
160
+ export const Noncombat = new Test(8, "Be a Living Statue", () => {
161
+ const noncombatRate = -1 * getModifier("Combat Rate");
162
+ const unsoftcappedRate = noncombatRate > 25 ? 25 + (noncombatRate - 25) * 5 : noncombatRate;
163
+ return 60 - 3 * Math.floor(unsoftcappedRate / 5);
164
+ }, new Requirement(["-combat"], {}));
165
+ export const BoozeDrop = new Test(9, "Make Margaritas", () => {
166
+ const familiarItemDrop = numericModifier(myFamiliar(), "Item Drop", familiarWeight(myFamiliar()) + weightAdjustment(), equippedItem($slot `familiar`));
167
+ //Champagne doubling does NOT count for CS, so we undouble
168
+ const multiplier = haveEquipped($item `broken champagne bottle`) &&
169
+ get("garbageChampagneCharge") > 0
170
+ ? 0.5
171
+ : 1;
172
+ return (60 -
173
+ multiplier *
174
+ Math.floor((getModifier("Item Drop") - familiarItemDrop) / 30 + 0.001) -
175
+ Math.floor(getModifier("Booze Drop") / 15 + 0.001));
176
+ }, new Requirement(["Item Drop", "2 Booze Drop"], {
177
+ preventEquip: $items `broken champagne bottle`,
178
+ }));
179
+ export const HotRes = new Test(10, "Clean Steam Tunnels", () => 60 - getModifier("Hot Resistance"), new Requirement(["Hot Resistance"], {}));
180
+ export const CoilWire = new Test(11, "Coil Wire", () => 60, null);
181
+ /**
182
+ * Prints turncount and time details of the test in question.
183
+ * @param colour The colour (or color) you'd like the log to be printed in.
184
+ */
185
+ export function printLog(colour = "blue") {
186
+ const logEntries = Object.entries(log);
187
+ for (const [testName, testEntry] of logEntries) {
188
+ const { predictedTurns, turnCost, seconds } = testEntry;
189
+ print(`We predicted the ${testName} test would take ${predictedTurns} turns, ${predictedTurns === turnCost ? "and" : "but"} it took ${turnCost} turns`, colour);
190
+ print(`${testName} took ${seconds} seconds`, colour);
191
+ }
192
+ print(`All together, you have spent ${sum(logEntries, ([, testEntry]) => testEntry.seconds)} seconds during this Community Service run`, colour);
193
+ }
@@ -0,0 +1,2 @@
1
+ import * as CommunityService from "./2015/CommunityService";
2
+ export { CommunityService };
@@ -0,0 +1,2 @@
1
+ import * as CommunityService from "./2015/CommunityService";
2
+ export { CommunityService };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./actions";
2
2
  export * from "./ascend";
3
3
  export * from "./Clan";
4
+ export * from "./challengePaths";
4
5
  export * from "./combat";
5
6
  export * as Counter from "./counter";
6
7
  export * from "./diet";
@@ -19,6 +20,7 @@ export * from "./utils";
19
20
  export { get, PropertiesManager, set, setProperties, withProperties, withProperty, withChoices, withChoice, } from "./property";
20
21
  export { get as getModifier } from "./modifier";
21
22
  export { Modifiers } from "./modifier";
23
+ export { Session } from "./session";
22
24
  export * as Dreadsylvania from "./dungeons/Dreadsylvania";
23
25
  export * as Hobopolis from "./dungeons/Hobopolis";
24
26
  export * as SlimeTube from "./dungeons/SlimeTube";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./actions";
2
2
  export * from "./ascend";
3
3
  export * from "./Clan";
4
+ export * from "./challengePaths";
4
5
  export * from "./combat";
5
6
  export * as Counter from "./counter";
6
7
  export * from "./diet";
@@ -18,6 +19,7 @@ export * as property from "./property";
18
19
  export * from "./utils";
19
20
  export { get, PropertiesManager, set, setProperties, withProperties, withProperty, withChoices, withChoice, } from "./property";
20
21
  export { get as getModifier } from "./modifier";
22
+ export { Session } from "./session";
21
23
  export * as Dreadsylvania from "./dungeons/Dreadsylvania";
22
24
  export * as Hobopolis from "./dungeons/Hobopolis";
23
25
  export * as SlimeTube from "./dungeons/SlimeTube";