From d59b8cf2bf02c66f15f423900017b0df0c377b4f Mon Sep 17 00:00:00 2001 From: spaghetti-squash Date: Sat, 5 Sep 2026 08:53:35 -0400 Subject: [PATCH 1/7] else for macros --- src/combat.ts | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/combat.ts b/src/combat.ts index bb7b1564eb..60962488e3 100644 --- a/src/combat.ts +++ b/src/combat.ts @@ -469,12 +469,23 @@ export class Macro { * * @param condition The BALLS condition for the if statement. * @param ifTrue Continuation if the condition is true. + * @param ifFalse Optional else-branch for the macro * @returns {Macro} This object itself. */ - if_(condition: PreBALLSPredicate, ifTrue: string | Macro): this { - return this.step(`if ${Macro.makeBALLSPredicate(condition)}`) - .step(ifTrue) - .step("endif"); + if_( + condition: PreBALLSPredicate, + ifTrue: string | Macro, + ifFalse?: string | Macro, + ): this { + this.step(`if ${Macro.makeBALLSPredicate(condition)}`).step(ifTrue); + + if (ifFalse) { + this.step("else").step(ifFalse).step("endelf"); + } else { + this.step("endif"); + } + + return this; } /** @@ -482,39 +493,48 @@ export class Macro { * * @param condition The BALLS condition for the if statement. * @param ifTrue Continuation if the condition is true. + * @param ifFalse Optional else-branch for the macro * @returns {Macro} This object itself. */ static if_( this: Constructor, condition: PreBALLSPredicate, ifTrue: string | Macro, + ifFalse?: string | Macro, ): T { - return new this().if_(condition, ifTrue); + return new this().if_(condition, ifTrue, ifFalse); } /** * Add an "if" statement to this macro, inverting the condition. * * @param condition The BALLS condition for the if statement. - * @param ifTrue Continuation if the condition is true. + * @param ifTrue Continuation if the negated condition is true. + * @param ifFalse Optional else-branch for the macro * @returns {Macro} This object itself. */ - ifNot(condition: PreBALLSPredicate, ifTrue: string | Macro): this { - return this.if_(`!${Macro.makeBALLSPredicate(condition)}`, ifTrue); + ifNot( + condition: PreBALLSPredicate, + ifTrue: string | Macro, + ifFalse?: string | Macro, + ): this { + return this.if_(`!${Macro.makeBALLSPredicate(condition)}`, ifTrue, ifFalse); } /** * Create a new macro with an "if" statement, inverting the condition. * * @param condition The BALLS condition for the if statement. - * @param ifTrue Continuation if the condition is true. + * @param ifTrue Continuation if the negated condition is true. + * @param ifFalse Optional else-branch for the macro * @returns {Macro} This object itself. */ static ifNot( this: Constructor, condition: PreBALLSPredicate, ifTrue: string | Macro, + ifFalse?: string | Macro, ): T { - return new this().ifNot(condition, ifTrue); + return new this().ifNot(condition, ifTrue, ifFalse); } /** From 29780a1b1539c0f67a3ab3a37bd3c1764c5a6d42 Mon Sep 17 00:00:00 2001 From: spaghetti-squash Date: Sat, 5 Sep 2026 09:23:48 -0400 Subject: [PATCH 2/7] support long else...if blocks? --- src/combat.ts | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/combat.ts b/src/combat.ts index 60962488e3..7f3797e5ad 100644 --- a/src/combat.ts +++ b/src/combat.ts @@ -185,6 +185,14 @@ type Constructor = { new (): T }; export class InvalidMacroError extends Error {} +export type ElseIfComponent = { + predicate: PreBALLSPredicate; + macro: string | Macro; +}; +export type ElseTrain = + | ElseIfComponent[] + | [...ElseIfComponent[], string | Macro]; + /** * BALLS macro builder for direct submission to KoL. * Create a new macro with `new Macro()` and add steps using the instance methods. @@ -469,22 +477,25 @@ export class Macro { * * @param condition The BALLS condition for the if statement. * @param ifTrue Continuation if the condition is true. - * @param ifFalse Optional else-branch for the macro + * @param elseTrain Spread array of { predicate, macro } elif entries, followed by an final bare string/Macro for the final Else * @returns {Macro} This object itself. */ if_( condition: PreBALLSPredicate, ifTrue: string | Macro, - ifFalse?: string | Macro, + ...elseTrain: ElseTrain ): this { this.step(`if ${Macro.makeBALLSPredicate(condition)}`).step(ifTrue); - if (ifFalse) { - this.step("else").step(ifFalse).step("endelf"); - } else { - this.step("endif"); + for (const elseEntry of elseTrain) { + if (typeof elseEntry === "object" && !(elseEntry instanceof Macro)) { + const { predicate, macro } = elseEntry; + this.step(`elif ${Macro.makeBALLSPredicate(predicate)}`).step(macro); + } else { + this.step("else").step(elseEntry); + } } - + if (elseTrain.length) this.step("endelse"); return this; } @@ -493,16 +504,16 @@ export class Macro { * * @param condition The BALLS condition for the if statement. * @param ifTrue Continuation if the condition is true. - * @param ifFalse Optional else-branch for the macro + * @param elseTrain Spread array of { predicate, macro } elif entries, followed by an final bare string/Macro for the final Else * @returns {Macro} This object itself. */ static if_( this: Constructor, condition: PreBALLSPredicate, ifTrue: string | Macro, - ifFalse?: string | Macro, + ...elseTrain: ElseTrain ): T { - return new this().if_(condition, ifTrue, ifFalse); + return new this().if_(condition, ifTrue, ...elseTrain); } /** From 2f1ad15f1d87e70e4eee609a7199de305464df3b Mon Sep 17 00:00:00 2001 From: spaghetti-squash Date: Sat, 5 Sep 2026 12:29:33 -0400 Subject: [PATCH 3/7] actually append the endif --- src/combat.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/combat.ts b/src/combat.ts index 4a83238033..4ccca7ed29 100644 --- a/src/combat.ts +++ b/src/combat.ts @@ -563,7 +563,7 @@ export class Macro { if_( condition: PreBALLSPredicate, ifTrue: string | Macro, - ...elseTrain: ElseTrain + ...elseTrain: ElseTrain ): this { this.step(`if ${Macro.makeBALLSPredicate(condition)}`).step(ifTrue); @@ -575,7 +575,7 @@ export class Macro { this.step("else").step(elseEntry); } } - if (elseTrain.length) this.step("endelse"); + this.step(elseTrain.length ? "endelse" : "endif"); return this; } From 99dbff0965ae283f9ec54889b30185fa647cbde0 Mon Sep 17 00:00:00 2001 From: spaghetti-squash Date: Sat, 5 Sep 2026 12:35:14 -0400 Subject: [PATCH 4/7] allow explicit undefined passing in if_ --- src/combat.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/combat.ts b/src/combat.ts index 4ccca7ed29..268da410f9 100644 --- a/src/combat.ts +++ b/src/combat.ts @@ -191,7 +191,7 @@ export type ElseIfComponent = { }; export type ElseTrain = | ElseIfComponent[] - | [...ElseIfComponent[], string | Macro]; + | [...ElseIfComponent[], undefined | string | Macro]; /** * BALLS macro builder for direct submission to KoL. @@ -567,7 +567,7 @@ export class Macro { ): this { this.step(`if ${Macro.makeBALLSPredicate(condition)}`).step(ifTrue); - for (const elseEntry of elseTrain) { + for (const elseEntry of elseTrain.filter(Boolean)) { if (typeof elseEntry === "object" && !(elseEntry instanceof Macro)) { const { predicate, macro } = elseEntry; this.step(`elif ${Macro.makeBALLSPredicate(predicate)}`).step(macro); @@ -575,7 +575,7 @@ export class Macro { this.step("else").step(elseEntry); } } - this.step(elseTrain.length ? "endelse" : "endif"); + this.step(elseTrain.filter(Boolean).length ? "endelse" : "endif"); return this; } From 61cb93c689e88dab76dabfdfe1f3faebb370bdf2 Mon Sep 17 00:00:00 2001 From: spaghetti-squash Date: Sat, 5 Sep 2026 12:53:24 -0400 Subject: [PATCH 5/7] horrible Type bullshit --- src/combat.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/combat.ts b/src/combat.ts index 268da410f9..83f6a2772c 100644 --- a/src/combat.ts +++ b/src/combat.ts @@ -25,6 +25,7 @@ import { overlappingSkillNames, } from "./overlappingNames.js"; import { get, set } from "./property.js"; +import { notNullish } from "./utils.js"; const MACRO_NAME = "Script Autoattack Macro"; /** @@ -567,7 +568,9 @@ export class Macro { ): this { this.step(`if ${Macro.makeBALLSPredicate(condition)}`).step(ifTrue); - for (const elseEntry of elseTrain.filter(Boolean)) { + for (const elseEntry of (elseTrain as ElseTrain[number][]).filter( + notNullish, + )) { if (typeof elseEntry === "object" && !(elseEntry instanceof Macro)) { const { predicate, macro } = elseEntry; this.step(`elif ${Macro.makeBALLSPredicate(predicate)}`).step(macro); From 5e066877e134b0f0a0f9739109ceda33bfcc6b11 Mon Sep 17 00:00:00 2001 From: spaghetti-squash Date: Sat, 5 Sep 2026 12:56:01 -0400 Subject: [PATCH 6/7] it DOES always end in endif --- src/combat.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/combat.ts b/src/combat.ts index 83f6a2772c..54545b0cbd 100644 --- a/src/combat.ts +++ b/src/combat.ts @@ -578,7 +578,9 @@ export class Macro { this.step("else").step(elseEntry); } } - this.step(elseTrain.filter(Boolean).length ? "endelse" : "endif"); + + this.step("endif"); + return this; } From 9d4f3db37f5b5a6ee4b8f05f3df4334883bd3db6 Mon Sep 17 00:00:00 2001 From: spaghetti-squash Date: Mon, 7 Sep 2026 11:17:39 -0400 Subject: [PATCH 7/7] use an overload signature for if_ --- src/combat.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/combat.ts b/src/combat.ts index 54545b0cbd..eeb971c846 100644 --- a/src/combat.ts +++ b/src/combat.ts @@ -565,12 +565,27 @@ export class Macro { condition: PreBALLSPredicate, ifTrue: string | Macro, ...elseTrain: ElseTrain + ): this; + /** + * Add an "if" statement to this macro, followed by a long chain of "elif"s, followed optionally by an "else" + * + * @param elseTrain Spread array of { predicate, macro } elif entries, followed by an final bare string/Macro for the final Else. + * @returns {Macro} This object itself. + */ + if_(...elseTrain: ElseTrain): this; + if_( + leadingArg: PreBALLSPredicate | ElseIfComponent, + secondArg: typeof leadingArg extends ElseIfComponent + ? ElseIfComponent | undefined + : string | Macro, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...rest: any[] ): this { - this.step(`if ${Macro.makeBALLSPredicate(condition)}`).step(ifTrue); + if (!(typeof leadingArg === "object" && "predicate" in leadingArg)) { + this.step(`if ${Macro.makeBALLSPredicate(leadingArg)}`, secondArg); + } - for (const elseEntry of (elseTrain as ElseTrain[number][]).filter( - notNullish, - )) { + for (const elseEntry of (rest as ElseTrain[number][]).filter(notNullish)) { if (typeof elseEntry === "object" && !(elseEntry instanceof Macro)) { const { predicate, macro } = elseEntry; this.step(`elif ${Macro.makeBALLSPredicate(predicate)}`).step(macro);