From c8090b52793cbdec4c28ceea3acddf19fc167cfd Mon Sep 17 00:00:00 2001 From: Florrie Date: Thu, 16 Aug 2018 09:17:16 -0300 Subject: Stun --- index.js | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 122 insertions(+), 26 deletions(-) (limited to 'index.js') diff --git a/index.js b/index.js index 2c4ed29..2762bf6 100644 --- a/index.js +++ b/index.js @@ -126,40 +126,57 @@ class ATBBar { } update(dt) { - // ATB gauge progressing - if (!this.battleCharacter.isExecutingChain) { - this.progress = Math.min(1, this.progress + dt * 1.5 / this.segmentCount) - } - - // ATB gauge visual update - this.visualProgress += 8 * dt * (this.progress - this.visualProgress) - if (this.battle.playerCharacter !== this.battleCharacter) { this.aiUpdate(dt) } - // Start action chain once user has confirmed (willExecuteChain = true) and IP gauge is full enough to execute all actions - if (this.battleCharacter.willExecuteChain && this.progress >= 1 - this.getRemainingSpace() / this.segmentCount) { - this.battleCharacter.willExecuteChain = false - this.battleCharacter.isExecutingChain = true // Will be acknowledged by next if statement + // Decrease stun time + if (this.battleCharacter.stunTime) { + this.battleCharacter.stunTime -= dt + if (this.battleCharacter.stunTime <= 0) { + this.battleCharacter.stunTime = 0 + this.battleCharacter.stunIsSignificant = null + } } - // Action chaining - if (this.battleCharacter.isExecutingChain && !this.battleCharacter.isExecutingAction) { - if (this.queuedActions.length) { - const action = this.queuedActions.shift() - this.battleCharacter.executeAction(action) - this.progress -= 1 / this.segmentCount * action.size - } else { - this.battleCharacter.isExecutingChain = false - if (this.battle.playerCharacter === this.battleCharacter && !this.battle.currentMenu) { - this.battle.showActionMenu({direction: 1}) + if (this.battleCharacter.stunIsSignificant) { + // Do nothing. Ow! + } else { + // ATB gauge progressing + if (!this.battleCharacter.isExecutingChain) { + this.progress = Math.min(1, this.progress + dt * 1.5 / this.segmentCount) + } + + // Start action chain once user has confirmed (willExecuteChain = true) and IP gauge is full enough to execute all actions + if (this.battleCharacter.willExecuteChain && this.progress >= 1 - this.getRemainingSpace() / this.segmentCount) { + this.battleCharacter.willExecuteChain = false + this.battleCharacter.isExecutingChain = true // Will be acknowledged by next if statement + } + + // Action chaining + if (this.battleCharacter.isExecutingChain && !this.battleCharacter.isExecutingAction) { + if (this.queuedActions.length) { + const action = this.queuedActions.shift() + this.battleCharacter.executeAction(action) + this.progress -= 1 / this.segmentCount * action.size + } else { + this.battleCharacter.isExecutingChain = false + if (this.battle.playerCharacter === this.battleCharacter && !this.battle.currentMenu) { + this.battle.showActionMenu({direction: 1}) + } } } } + + // ATB gauge visual update + this.visualProgress += 8 * dt * (this.progress - this.visualProgress) } aiUpdate(dt) { + if (this.battleCharacter.stunIsSignificant) { + return + } + // Determine a target if (!this.battleCharacter.targetCharacter) { this.battleCharacter.determineNewTarget('enemy') @@ -169,7 +186,7 @@ class ATBBar { if (!this.battleCharacter.isExecutingChain && this.battleCharacter.targetCharacter && this.getRemainingSpace() >= 0) { // TODO: More moves while (this.getRemainingSpace()) { - this.queuedActions.push(actionDatabase.fire[0]) + this.enqueue(actionDatabase.fire[0]) } } @@ -187,6 +204,15 @@ class ATBBar { this.queuedActions.pop() } + enqueue(action) { + if (this.stunIsSignificant) { + return false + } else { + this.queuedActions.push(action) + return true + } + } + activate() { if (this.queuedActions.length) { // Cut off the actions that we don't have enough ATB charge for @@ -433,7 +459,7 @@ class ActionMenu extends BaseBattleMenu { const remainingSpace = atbBar.getRemainingSpace() if (effectiveLevel <= remainingSpace) { - atbBar.queuedActions.push(option.chain[effectiveLevel - 1]) + atbBar.enqueue(option.chain[effectiveLevel - 1]) if (effectiveLevel === remainingSpace) { this.battle.showTargetMenu() @@ -486,6 +512,37 @@ class BattleCharacter extends Sprite { this.dead = false this.hpBar = new HPBar(this) + + // "Stun" is not an actual status effect of paralysis, but refers to how + // long a character is prevented from acting after having been hit. + // Effects during when stunTime is non-zero: + // * The character cannot execute any actions. Enqueued actions are delayed + // until stun time ends. + // * The character's ATB gauge continues progressing UNLESS the stun is + // "significant" (see stunIsSignificant). + // * The character's actionExecuteTime continues decreasing. Therefore, + // if a character is hit by a strong attack and stunned for some time, + // they will immediately activate the next queued action once the stun + // time has passed (assuming actionExecuteTime has reached zero). + // * The character can still input (enqueue) actions UNLESS the stun is + // "significant". Newly-queued actions will not be executed until the + // stun time has passed. + // The property stunTime usually should not be set directly. Instead, use + // the addStunTime function, which contains additional accounting for + // stunIsSignificant. + this.stunTime = 0 + + // This property refers to whether a character's stun time is considered + // "significant". If a stun is significant, the character's ATB gauge will + // not progress during the stun. Furthermore, the character's queued + // actions will all be removed (but the ATB gauge itself will not be + // reset), and the character will be prevented from enqueueing any actions + // until the stun has passed. Significant stuns create an effect of the + // character "losing focus", and give incentive to avoid heavy blows. + // A stun will automatically be considered significant if the stun time + // passes a certain threshold. For example, while a single light attack may + // not significantly stun a character, a persistent barrage may be able to. + this.stunIsSignificant = null } update(dt) { @@ -502,7 +559,11 @@ class BattleCharacter extends Sprite { this.atbBar.update(dt) if (this.isExecutingChain && !this.isExecutingAction) { - throw new Error('Executing chain but not action for more than one update.. ATB Bar should have queued an action or ended the chain') + // It's okay to be executing a chain but not any particular action if we + // are significantly stunned. + if (!this.stunIsSignificant) { + throw new Error('Executing chain but not action for more than one update.. ATB Bar should have queued an action or ended the chain') + } } if (this.actionExecuteTime) { @@ -520,9 +581,19 @@ class BattleCharacter extends Sprite { } applySpriteEffects(ctx) { + let filter = '' if (this.dead) { - ctx.filter = 'grayscale(100%)' + filter += 'grayscale(100%) ' + } + // TODO: Just for debugging, change this later + if (false && this.stunTime) { + if (this.stunIsSignificant) { + filter += 'blur(2px) ' + } else { + filter += 'blur(1px) ' + } } + ctx.filter = filter.trim() } executeAction(action) { @@ -575,12 +646,36 @@ class BattleCharacter extends Sprite { this.hp = 0 this.dead = true + this.stunTime = 0 + this.stunIsSignificant = null + for (const battleCharacter of this.battle.getAllBattleCharacters()) { if (battleCharacter.targetCharacter === this) { battleCharacter.determineNewTarget('enemy') // TODO: battleCharacter.targetType } } } + + addStunTime(time, forceSignificant = false) { + // Takes the optional argument forceSignificant. This should almost always + // be left as false, even if a move seems like it should be considered + // "heavy" (e.g. getting whacked by the swipe of a huge claw). Instead, let + // the logic in this function decide whether a stun is significant. Doing + // so lets the character's own stun threshold be taken into account. + + this.stunTime += time + + // TODO: Generate this somehow. + const significantStunThreshold = 1 + + if (forceSignificant || this.stunTime >= significantStunThreshold) { + // Only set stunIsSignificant here. ATBBar will control the resulting + // effects of having been significantly stunned. + this.stunIsSignificant = true + } else { + this.stunIsSignificant = false + } + } } class Battle { @@ -885,6 +980,7 @@ class MagicProjectile { if (Math.abs(this.target.x - this.x) <= 5 && Math.abs(this.target.y - this.y) <= 5) { if (['fire', 'blizz'].includes(this.action.chain)) { this.target.takeDamage(this.action.size * 20) + this.target.addStunTime([0.1, 0.25, 0.4][this.action.size - 1]) } else if (this.action.id === 'cure') { this.target.recoverHP(200) } -- cgit 1.3.0-6-gf8a5