<commit_before>const BaseStep = require('./basestep.js');
const Event = require('../event');
const GamePipeline = require('../gamepipeline.js');
const SimpleStep = require('./simplestep.js');

class AbilityResolver extends BaseStep {
    constructor(game, ability, context) {
        super(game);

        this.ability = ability;
        this.context = context;
        this.pipeline = new GamePipeline();
        this.pipeline.initialise([
            new SimpleStep(game, () => this.createSnapshot()),
            new SimpleStep(game, () => this.markActionAsTaken()),
            new SimpleStep(game, () => this.recordShadowStatus()),
            new SimpleStep(game, () => this.game.pushAbilityContext(this.context)),
            new SimpleStep(game, () => this.context.resolutionStage = 'cost'),
            new SimpleStep(game, () => this.resolveCosts()),
            new SimpleStep(game, () => this.waitForCostResolution()),
            new SimpleStep(game, () => this.payCosts()),
            new SimpleStep(game, () => this.context.resolutionStage = 'effect'),
            new SimpleStep(game, () => this.choosePlayer()),
            new SimpleStep(game, () => this.waitForChoosePlayerResolution()),
            new SimpleStep(game, () => this.resolveTargets()),
            new SimpleStep(game, () => this.waitForTargetResolution()),
            new SimpleStep(game, () => this.incrementAbilityLimit()),
            new SimpleStep(game, () => this.executeHandler()),
            new SimpleStep(game, () => this.raiseCardPlayedIfEvent()),
            new SimpleStep(game, () => this.game.popAbilityContext())
        ]);
    }

    queueStep(step) {
        this.pipeline.queueStep(step);
    }

    isComplete() {
        return this.pipeline.length === 0;
    }

    onCardClicked(player, card) {
        return this.pipeline.handleCardClicked(player, card);
    }

    onMenuCommand(player, arg, method, promptId) {
        return this.pipeline.handleMenuCommand(player, arg, method, promptId);
    }

    cancelStep() {
        this.cancelled = true;
        this.pipeline.cancelStep();
    }

    continue() {
        try {
            return this.pipeline.continue();
        } catch(e) {
            this.game.reportError(e);

            let currentAbilityContext = this.game.currentAbilityContext;
            if(currentAbilityContext === this.context) {
                this.game.popAbilityContext();
            }

            return true;
        }
    }

    createSnapshot() {
        if(['attachment', 'character', 'event', 'location'].includes(this.context.source.getType())) {
            this.context.cardStateWhenInitiated = this.context.source.createSnapshot();
        }
    }

    markActionAsTaken() {
        if(this.ability.isAction()) {
            this.game.markActionAsTaken(this.context);
        }
    }

    recordShadowStatus() {
        this.needsOutOfShadowEvent = this.ability.isPlayableEventAbility() && this.context.source.location === 'shadows';
    }

    resolveCosts() {
        this.canPayResults = this.ability.resolveCosts(this.context);
    }

    waitForCostResolution() {
        this.cancelled = this.canPayResults.some(result => result.resolved && !result.value);

        if(!this.canPayResults.every(result => result.resolved)) {
            return false;
        }
    }

    payCosts() {
        if(this.cancelled) {
            return;
        }

        this.ability.payCosts(this.context);
    }

    choosePlayer() {
        if(this.cancelled || !this.ability.needsChoosePlayer()) {
            return;
        }

        this.playerResult = this.ability.resolvePlayer(this.context);
    }

    waitForChoosePlayerResolution() {
        if(this.cancelled || !this.playerResult) {
            return;
        }

        if(!this.playerResult.resolved) {<commit_msg>Fully cancel ability during cost resolution (#2868)<commit_after>const BaseStep = require('./basestep.js');
const Event = require('../event');
const GamePipeline = require('../gamepipeline.js');
const SimpleStep = require('./simplestep.js');

class AbilityResolver extends BaseStep {
    constructor(game, ability, context) {
        super(game);

        this.ability = ability;
        this.context = context;
        this.pipeline = new GamePipeline();
        this.pipeline.initialise([
            new SimpleStep(game, () => this.createSnapshot()),
            new SimpleStep(game, () => this.markActionAsTaken()),
            new SimpleStep(game, () => this.recordShadowStatus()),
            new SimpleStep(game, () => this.game.pushAbilityContext(this.context)),
            new SimpleStep(game, () => this.context.resolutionStage = 'cost'),
            new SimpleStep(game, () => this.resolveCosts()),
            new SimpleStep(game, () => this.waitForCostResolution()),
            new SimpleStep(game, () => this.payCosts()),
            new SimpleStep(game, () => this.context.resolutionStage = 'effect'),
            new SimpleStep(game, () => this.choosePlayer()),
            new SimpleStep(game, () => this.waitForChoosePlayerResolution()),
            new SimpleStep(game, () => this.resolveTargets()),
            new SimpleStep(game, () => this.waitForTargetResolution()),
            new SimpleStep(game, () => this.incrementAbilityLimit()),
            new SimpleStep(game, () => this.executeHandler()),
            new SimpleStep(game, () => this.raiseCardPlayedIfEvent()),
            new SimpleStep(game, () => this.game.popAbilityContext())
        ]);
    }

    queueStep(step) {
        this.pipeline.queueStep(step);
    }

    isComplete() {
        return this.pipeline.length === 0;
    }

    onCardClicked(player, card) {
        return this.pipeline.handleCardClicked(player, card);
    }

    onMenuCommand(player, arg, method, promptId) {
        return this.pipeline.handleMenuCommand(player, arg, method, promptId);
    }

    cancelStep() {
        this.cancelled = true;
        this.pipeline.cancelStep();
    }

    continue() {
        try {
            return this.pipeline.continue();
        } catch(e) {
            this.game.reportError(e);

            let currentAbilityContext = this.game.currentAbilityContext;
            if(currentAbilityContext === this.context) {
                this.game.popAbilityContext();
            }

            return true;
        }
    }

    createSnapshot() {
        if(['attachment', 'character', 'event', 'location'].includes(this.context.source.getType())) {
            this.context.cardStateWhenInitiated = this.context.source.createSnapshot();
        }
    }

    markActionAsTaken() {
        if(this.ability.isAction()) {
            this.game.markActionAsTaken(this.context);
        }
    }

    recordShadowStatus() {
        this.needsOutOfShadowEvent = this.ability.isPlayableEventAbility() && this.context.source.location === 'shadows';
    }

    resolveCosts() {
        if(this.cancelled) {
            return;
        }

        this.canPayResults = this.ability.resolveCosts(this.context);
    }

    waitForCostResolution() {
        if(this.cancelled) {
            return;
        }

        this.cancelled = this.canPayResults.some(result => result.resolved && !result.value);

        if(!this.canPayResults.every(result => result.resolved)) {
            return false;
        }
    }

    payCosts() {
        if(this.cancelled) {
            return;
        }

        this.ability.payCosts(this.context);
    }

    choosePlayer() {
        if(this.cancelled || !this.ability.needsChoosePlayer()) {
            return;
        }

        this.playerResult = this.ability.resolvePlayer(this.context);
    }

    waitForChoosePlayerResolution() {
        if(this.cancelled || !this.playerResult) {
            return;
        }

        if(!this.playerResult.resolved) {
{'fraction_non_alphanumeric': 0.1390736949846469, 'fraction_numerical': 0.0007676560900716479, 'mean_word_length': 2.3136922424756254, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4427774', 'n_tokens_mistral': 2198, 'n_tokens_neox': 2147, 'n_words': 332}