diff options
-rw-r--r-- | img/warmech.kra | bin | 489626 -> 489625 bytes | |||
-rw-r--r-- | img/warmech.kra~ | bin | 0 -> 489626 bytes | |||
-rw-r--r-- | index.js | 69 |
3 files changed, 65 insertions, 4 deletions
diff --git a/img/warmech.kra b/img/warmech.kra index fa58950..76a6862 100644 --- a/img/warmech.kra +++ b/img/warmech.kra Binary files differdiff --git a/img/warmech.kra~ b/img/warmech.kra~ new file mode 100644 index 0000000..fa58950 --- /dev/null +++ b/img/warmech.kra~ Binary files differdiff --git a/index.js b/index.js index b0e57b9..c927385 100644 --- a/index.js +++ b/index.js @@ -27,8 +27,8 @@ const actionDatabase = { aquara: {id: 'aquara', chain: 'aqua', label: 'Aquara', size: 2, target: 'enemy', color: '#33F', damage: 60, stun: 0}, aquaga: {id: 'aquaga', chain: 'aqua', label: 'Aquaga', size: 3, target: 'enemy', color: '#33F', damage: 90, stun: 0}, - manasvinSwipe: {id: 'manasvinSwipe', label: 'Swipe', size: 3, target: 'enemy', color: '#CCC', damage: 125, stun: 1.25}, - manasvinRecover: {id: 'manasvinRecover', label: 'Recover', size: 6, target: 'ally', color: '#AFA', heal: 300} + manasvinSwipe: {id: 'manasvinSwipe', label: 'Swipe', size: 3, target: 'enemy', color: '#CCC', damage: 125, stun: 1.25, cooldown: 1}, + manasvinRecover: {id: 'manasvinRecover', label: 'Recover', size: 6, target: 'ally', color: '#AFA', heal: 175, cooldown: 2} } class Sprite { @@ -193,7 +193,7 @@ class ATBBar { // TODO: AI logic for when to change target // TODO: Maybe need to bump the Math.random() chances. if (!this.battleCharacter.isExecutingChain && !this.battleCharacter.willExecuteChain && (this.battleCharacter.aiID === 'manasvinWarmech' || !this.battleCharacter.targetCharacter)) { - if (this.battleCharacter.aiID === 'manasvinWarmech' && Math.random() < 1 - this.battleCharacter.hp / this.battleCharacter.maxHP) { + if (this.battleCharacter.aiID === 'manasvinWarmech' && Math.random() < (1 - this.battleCharacter.hp / this.battleCharacter.maxHP) / 2) { this.battleCharacter.determineNewTarget('ally') } else { this.battleCharacter.determineNewTarget('enemy') @@ -227,7 +227,13 @@ class ATBBar { } dequeue() { - this.queuedActions.pop() + if (this.queuedActions.length) { + const action = this.queuedActions.pop() + + if (action.cooldown) { + delete this.battleCharacter.cooldownTimers[action.id] // Or set to 0, either works + } + } } enqueue(action) { @@ -235,7 +241,14 @@ class ATBBar { return false } else if (this.getRemainingSpace() < action.size) { return false + } else if (action.cooldown && this.battleCharacter.cooldownTimers[action.id]) { + return false } else { + if (action.cooldown) { + this.battleCharacter.cooldownTimers[action.id] = action.cooldown * this.segmentCount + this.battleCharacter.cooldownTimers[action.id] -= this.segmentCount - this.getRemainingSpace() + console.log('Enqueued cooldown:', action.label, action.cooldown * this.segmentCount, this.battleCharacter.cooldownTimers[action.id]) + } this.queuedActions.push(action) return true } @@ -612,6 +625,44 @@ class BattleCharacter extends Sprite { // Array of action IDs. this.knownActions = [] + + // Cooldown timers for each action with a cooldown. If an action's cooldown + // is greater than zero, it can't be queued. A unit of "1" on the cooldown + // means one full ATB gauge of executed actions. So if the ATB gauge length + // is 3 and a size-2 action is executed, all timers go down by 2/3. + // Note, however, that on this particular object a unit of 1 refers to one + // individual ATB segment; however, the cooldown property on an action + // object behaves as described above. The reason 1 refers to a single ATB + // segment here is to avoid decimal rounding issues; 3 - 2 is 1 but that's + // not so certain with math similar to 1 - 2/3. + // If an action can successfully be enqueued (i.e. its cooldown timer is + // already zero), its cooldown timer is immediately set according to its + // cooldown property. This is so that it cannot be enqueued twice within + // a single queue. If it is dequeued, its cooldown timer is reset to 0. + // A cooldown timer of 1 means "can't be executed more than once in a + // single queue", but will be able to be enqueued and executed during the + // next queue (there is no number of queues that must be waited). + // Technically this behavior is surprising since, if a move with cooldown + // is executed at the end of a queue, it can enqueued immediately again + // in the next queue - after less than its cooldown timer has passed. + // To implement this, the cooldown timer is initially offset by the number + // of ATB segments before where its action is enqueued to. So in our 2-size + // action with a cooldown of 1 being enqueued to the end of a 3-ATB gauge + // queue, its cooldown timer is initially offset by -1/3, so that only 2/3, + // or 2 ATB segments, must pass before it can be enqueued again. Since the + // move with cooldown itself is a 2-size move, it will account for the + // remaining ATB space. + // As of this writing there is no designed special behavior for what should + // happen if an action with cooldown is executed as part of a queue that is + // not entirely filled. My understanding is that its cooldown timer would + // not be fully cleared since, even taking into account the initial offset, + // not enough actions would have been executed after the cooldown action. + // Thus another ATB gauge would have to be waited until the timer reaches + // zero. This is not an issue for AI-controlled characters (i.e. enemies), + // though, since they usually fill their queue as full as possible; and AI- + // controlled characters are the ones which the cooldown mechanic was + // designed for in the first place. + this.cooldownTimers = {} } update(dt) { @@ -680,6 +731,14 @@ class BattleCharacter extends Sprite { this.actionExecuteTime = 0.4 + 0.2 * action.size this.actionBeingExecuted = action + for (const [ actionID, remainingSegments ] of Object.entries(this.cooldownTimers)) { + if (remainingSegments > action.size) { + this.cooldownTimers[actionID] -= action.size + } else { + delete this.cooldownTimers[actionID] // Or set to 0, either works + } + } + if (this.targetCharacter) { this.battle.animationEntities.push(new MagicProjectile(this, this.targetCharacter, action)) } @@ -1195,6 +1254,8 @@ const battle = new Battle({ ] }) +battle.teams[0].characters[0].atbBar.segmentCount = 4 +battle.teams[0].characters[1].atbBar.segmentCount = 4 battle.teams[1].characters[0].image.src = 'img/warmech.png' const camera = battle.camera |