/** @noSelfInFile */ import { bj_MAX_PLAYER_SLOTS } from "../globals/define"; import { Dialog, DialogButton } from "./dialog"; import { Handle } from "./handle"; import { MapPlayer } from "./player"; import { Region } from "./region"; import { Unit } from "./unit"; import { Widget } from "./widget"; export class Trigger extends Handle { /** @deprecated use `Trigger.create` instead. */ constructor() { if (Handle.initFromHandle()) { super(); return; } const handle = CreateTrigger(); if (handle === undefined) { Error("w3ts failed to create trigger handle."); } super(handle); } public static create(): Trigger { const handle = CreateTrigger(); const obj = this.getObject(handle) as Trigger; const values: Record = {}; values.handle = handle; return Object.assign(obj, values); } public set enabled(flag: boolean) { if (flag) { EnableTrigger(this.handle); } else { DisableTrigger(this.handle); } } public get enabled() { return IsTriggerEnabled(this.handle); } public get evalCount() { return GetTriggerEvalCount(this.handle); } public static get eventId() { return GetTriggerEventId(); } public get execCount() { return GetTriggerExecCount(this.handle); } /** * Marks the given trigger to wait/no longer wait for `TriggerSleepAction`s in sub trigger executions started via `TriggerExecuteWait`. * Since this is an attribute of the execution rather than the trigger object, this affects future runs of the given trigger, and not * those already started. */ public set waitOnSleeps(flag: boolean) { TriggerWaitOnSleeps(this.handle, flag); } public get waitOnSleeps() { return IsTriggerWaitOnSleeps(this.handle); } public addAction(actionFunc: () => void) { return TriggerAddAction(this.handle, actionFunc); } /** * Adds a new condition to the trigger. * * Adding more conditions later wil join them by AND (that means all conditions need to evaluate to `true`) * * @example * ```ts * const t = Trigger.create() * * // trigger fires if a unit is attacked * t.registerAnyUnitEvent(EVENT_PLAYER_UNIT_ATTACKED) * * // but only if the unit name matches * t.addCondition(Condition(() => Unit.fromHandle(GetAttacker()).name === 'Attacker Unit')) * * t.addAction(() => { * //do something... * }) * ``` * @param condition The condition which must evaluate to true in order to run the trigger's actions. */ public addCondition(condition: boolexpr | (() => boolean)) { if (typeof condition === "function") { const cf = Condition(condition); return cf != null ? TriggerAddCondition(this.handle, cf) : undefined; } return TriggerAddCondition(this.handle, condition); } /** * @bug Do not destroy the current running Trigger (when waits are involved) * as it can cause handle stack corruption as documented [here](http://www.wc3c.net/showthread.php?t=110519). */ public destroy() { DestroyTrigger(this.handle); } /** * Evaluates all functions that were added to the trigger via `addCondition`. * All return-values from all added condition-functions are `and`ed together as the final return-value. * Returns the boolean value of the return value from the condition-function. * So if 0/0.0/null would be returned in the condition-function, `eval` * would return false. Note that `""` would return `true`. * @note If a condition-function crashes the thread or does not return any value `eval` will return false. * @note If you want to return false for a condition-function that returns string (for whatever reason) return `null` instead of `""` * @note *All* functions added via `addCondition` are run. There is no short-circuting. If you want short-circuting use `And` or `Or`. * @note All functions added via `addCondition` are run in the order they were added. */ public eval() { return TriggerEvaluate(this.handle); } /** * Calls the actions of a trigger in a new execution context. * Control will return to the caller when the trigger has finished or has been suspended via TriggerSleepAction. */ public exec() { return TriggerExecute(this.handle); } /** * Does the same as `exec` but if the caller has been marked with `waitOnSleeps` before its * execution, it will additionally wait for `TriggerSleepAction`s of the callee, so this really ensures that * the callee has finished. If there was a `TriggerSleepAction`, there will be a short delay before returning. */ public execWait() { TriggerExecuteWait(this.handle); } public registerAnyUnitEvent(whichPlayerUnitEvent: playerunitevent) { for (let i = 0; i < bj_MAX_PLAYER_SLOTS; i++) { TriggerRegisterPlayerUnitEvent(this.handle, Player(i), whichPlayerUnitEvent, () => { return true; }); } } public registerDeathEvent(whichWidget: Widget) { return TriggerRegisterDeathEvent(this.handle, whichWidget.handle); } public registerDialogButtonEvent(whichButton: DialogButton) { return TriggerRegisterDialogButtonEvent(this.handle, whichButton.handle); } public registerDialogEvent(whichDialog: Dialog) { return TriggerRegisterDialogEvent(this.handle, whichDialog.handle); } public registerEnterRegion(whichRegion: Region, filter: boolexpr | (() => boolean) | undefined) { return TriggerRegisterEnterRegion( this.handle, whichRegion.handle, typeof filter === "function" ? Filter(filter) : filter || null ); } public registerFilterUnitEvent( whichUnit: Unit, whichEvent: unitevent, filter: boolexpr | (() => boolean) | undefined ) { return TriggerRegisterFilterUnitEvent( this.handle, whichUnit.handle, whichEvent, typeof filter === "function" ? Filter(filter) : filter || null ); } public registerGameEvent(whichGameEvent: gameevent) { return TriggerRegisterGameEvent(this.handle, whichGameEvent); } public registerGameStateEvent(whichState: gamestate, opcode: limitop, limitval: number) { return TriggerRegisterGameStateEvent(this.handle, whichState, opcode, limitval); } public registerLeaveRegion(whichRegion: Region, filter: boolexpr | (() => boolean) | undefined) { return TriggerRegisterLeaveRegion( this.handle, whichRegion.handle, typeof filter === "function" ? Filter(filter) : filter || null ); } public registerPlayerAllianceChange(whichPlayer: MapPlayer, whichAlliance: alliancetype) { return TriggerRegisterPlayerAllianceChange(this.handle, whichPlayer.handle, whichAlliance); } public registerPlayerChatEvent( whichPlayer: MapPlayer, chatMessageToDetect: string, exactMatchOnly: boolean ) { return TriggerRegisterPlayerChatEvent( this.handle, whichPlayer.handle, chatMessageToDetect, exactMatchOnly ); } public registerPlayerEvent(whichPlayer: MapPlayer, whichPlayerEvent: playerevent) { return TriggerRegisterPlayerEvent(this.handle, whichPlayer.handle, whichPlayerEvent); } public registerPlayerKeyEvent( trig: trigger, key: number, status: number, sync: boolean, funcHandle: () => void ) { return DzTriggerRegisterKeyEventByCode(trig, key, status, sync, funcHandle); } public registerPlayerMouseEvent( trig: trigger, btn: number, status: number, sync: boolean, funcHandle: () => void ) { return DzTriggerRegisterMouseEventByCode(trig, btn, status, sync, funcHandle); } public registerPlayerStateEvent( whichPlayer: MapPlayer, whichState: playerstate, opcode: limitop, limitval: number ) { return TriggerRegisterPlayerStateEvent( this.handle, whichPlayer.handle, whichState, opcode, limitval ); } public registerPlayerUnitEvent( whichPlayer: MapPlayer, whichPlayerUnitEvent: playerunitevent, filter: boolexpr | (() => boolean) | undefined ) { return TriggerRegisterPlayerUnitEvent( this.handle, whichPlayer.handle, whichPlayerUnitEvent, typeof filter === "function" ? Filter(filter) : filter || null ); } // Creates it's own timer and triggers when it expires public registerTimerEvent(timeout: number, periodic: boolean) { return TriggerRegisterTimerEvent(this.handle, timeout, periodic); } // Triggers when the timer you tell it about expires public registerTimerExpireEvent(t: timer) { return TriggerRegisterTimerExpireEvent(this.handle, t); } public registerTrackableHitEvent(whichTrackable: trackable) { return TriggerRegisterTrackableHitEvent(this.handle, whichTrackable); } public registerTrackableTrackEvent(whichTrackable: trackable) { return TriggerRegisterTrackableTrackEvent(this.handle, whichTrackable); } public registerUnitEvent(whichUnit: Unit, whichEvent: unitevent) { return TriggerRegisterUnitEvent(this.handle, whichUnit.handle, whichEvent); } public registerUnitInRange( whichUnit: Unit, range: number, filter: boolexpr | (() => boolean) | undefined ) { return TriggerRegisterUnitInRange( this.handle, whichUnit.handle, range, typeof filter === "function" ? Filter(filter) : filter || null ); } public registerUnitStateEvent( whichUnit: Unit, whichState: unitstate, opcode: limitop, limitval: number ) { return TriggerRegisterUnitStateEvent( this.handle, whichUnit.handle, whichState, opcode, limitval ); } public registerVariableEvent(varName: string, opcode: limitop, limitval: number) { return TriggerRegisterVariableEvent(this.handle, varName, opcode, limitval); } public removeAction(whichAction: triggeraction) { return TriggerRemoveAction(this.handle, whichAction); } public removeActions() { return TriggerClearActions(this.handle); } public removeCondition(whichCondition: triggercondition) { return TriggerRemoveCondition(this.handle, whichCondition); } public removeConditions() { return TriggerClearConditions(this.handle); } public reset() { ResetTrigger(this.handle); } public static fromEvent() { return this.fromHandle(GetTriggeringTrigger()); } public static fromHandle(handle: trigger | undefined): Trigger | undefined { return handle ? this.getObject(handle) : undefined; } }