Fix CPU fight round, KO and match end flow
Browse files- js/port.js +60 -11
- js/runtime.js +14 -0
js/port.js
CHANGED
|
@@ -99,10 +99,15 @@ function parseCommands(text) {
|
|
| 99 |
}
|
| 100 |
|
| 101 |
function startMatch() {
|
|
|
|
|
|
|
|
|
|
| 102 |
game = {
|
| 103 |
-
p1
|
| 104 |
-
tick: 0, time: 99 * 60, round: 'ROUND 1',
|
|
|
|
| 105 |
};
|
|
|
|
| 106 |
window.game = game;
|
| 107 |
}
|
| 108 |
|
|
@@ -124,16 +129,57 @@ document.querySelector('#restart').onclick = () => startMatch();
|
|
| 124 |
function step() {
|
| 125 |
if (!game) return;
|
| 126 |
game.tick++;
|
| 127 |
-
if (game.
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
|
|
|
| 132 |
game.p1.update(null, game.p2, game);
|
| 133 |
game.p2.update(null, game.p1, game);
|
| 134 |
resolveCollisions(game.p1, game.p2, game);
|
| 135 |
-
if (game.p1.life <= 0 || game.p2.life <= 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
}
|
| 138 |
|
| 139 |
function worldBoxes(f, boxes) {
|
|
@@ -239,8 +285,12 @@ function drawHud(w, h) {
|
|
| 239 |
const fill = 156 * Math.max(0, f.life / f.maxLife); ctx.fillStyle = col; ctx.fillRect(mirror ? x + 158 - fill : x + 2, 10, fill, 7);
|
| 240 |
ctx.fillStyle = '#f6f1df'; ctx.font = 'bold 9px system-ui'; ctx.fillText(f.name, x, 31);
|
| 241 |
}
|
| 242 |
-
ctx.textAlign = 'center'; ctx.fillStyle = '#fff6d8'; ctx.font = 'bold 21px ui-monospace'; ctx.fillText(String(
|
| 243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
ctx.restore();
|
| 245 |
}
|
| 246 |
|
|
@@ -249,7 +299,6 @@ function loop(now) {
|
|
| 249 |
if (!paused && game && intro.hidden) {
|
| 250 |
accum += dt;
|
| 251 |
for (let n = 0; accum >= 1000 / 60 && n < 5; n++) { step(); accum -= 1000 / 60; }
|
| 252 |
-
if (game.roundLeft <= 20) game.time = Math.max(0, game.time - dt * .06);
|
| 253 |
}
|
| 254 |
draw(); requestAnimationFrame(loop);
|
| 255 |
}
|
|
|
|
| 99 |
}
|
| 100 |
|
| 101 |
function startMatch() {
|
| 102 |
+
const p1 = new Fighter(assets, -72, 1, '#c84b39', true);
|
| 103 |
+
const p2 = new Fighter(assets, 72, -1, '#3159a4');
|
| 104 |
+
p1.name = 'CPU 1 路 KFM'; p2.name = 'CPU 2 路 KFM';
|
| 105 |
game = {
|
| 106 |
+
p1, p2,
|
| 107 |
+
tick: 0, time: 99, roundFrames: 99 * 60, round: 'ROUND 1', phase: 'intro', phaseTicks: 75,
|
| 108 |
+
roundNo: 1, roundsToWin: 2, shake: 0,
|
| 109 |
};
|
| 110 |
+
game.p1.resetMatch(); game.p2.resetMatch();
|
| 111 |
window.game = game;
|
| 112 |
}
|
| 113 |
|
|
|
|
| 129 |
function step() {
|
| 130 |
if (!game) return;
|
| 131 |
game.tick++;
|
| 132 |
+
if (game.phase === 'intro') {
|
| 133 |
+
if (--game.phaseTicks === 25) game.round = 'FIGHT!';
|
| 134 |
+
if (game.phaseTicks <= 0) { game.phase = 'fight'; game.round = ''; }
|
| 135 |
+
return;
|
| 136 |
+
}
|
| 137 |
+
if (game.phase === 'fight') {
|
| 138 |
game.p1.update(null, game.p2, game);
|
| 139 |
game.p2.update(null, game.p1, game);
|
| 140 |
resolveCollisions(game.p1, game.p2, game);
|
| 141 |
+
if (game.p1.life <= 0 || game.p2.life <= 0) {
|
| 142 |
+
const winner = game.p1.life === game.p2.life ? null : game.p1.life > game.p2.life ? game.p1 : game.p2;
|
| 143 |
+
finishRound(winner, 'K.O.');
|
| 144 |
+
} else if (--game.roundFrames <= 0) {
|
| 145 |
+
const winner = game.p1.life === game.p2.life ? null : game.p1.life > game.p2.life ? game.p1 : game.p2;
|
| 146 |
+
finishRound(winner, 'TIME OVER');
|
| 147 |
+
}
|
| 148 |
+
game.time = Math.ceil(Math.max(0, game.roundFrames) / 60);
|
| 149 |
+
return;
|
| 150 |
+
}
|
| 151 |
+
if (game.phaseTicks > 0) { game.phaseTicks--; return; }
|
| 152 |
+
if (game.phase === 'roundEnd') {
|
| 153 |
+
const winner = [game.p1, game.p2].find(f => f.roundsWon >= game.roundsToWin);
|
| 154 |
+
if (winner) {
|
| 155 |
+
game.phase = 'matchEnd'; game.phaseTicks = 240;
|
| 156 |
+
game.round = `${winner.name} WINS THE MATCH`;
|
| 157 |
+
} else {
|
| 158 |
+
game.roundNo++;
|
| 159 |
+
game.p1.resetRound(); game.p2.resetRound();
|
| 160 |
+
game.round = `ROUND ${game.roundNo}`; game.roundFrames = 99 * 60; game.time = 99;
|
| 161 |
+
game.phase = 'intro'; game.phaseTicks = 75;
|
| 162 |
+
}
|
| 163 |
+
return;
|
| 164 |
}
|
| 165 |
+
if (game.phase === 'matchEnd') {
|
| 166 |
+
game.p1.resetMatch(); game.p2.resetMatch();
|
| 167 |
+
game.p1.x = -72; game.p2.x = 72; game.p1.facing = 1; game.p2.facing = -1;
|
| 168 |
+
game.p1.name = 'CPU 1 路 KFM'; game.p2.name = 'CPU 2 路 KFM';
|
| 169 |
+
game.roundNo = 1; game.roundFrames = 99 * 60; game.time = 99;
|
| 170 |
+
game.round = 'ROUND 1'; game.phase = 'intro'; game.phaseTicks = 75;
|
| 171 |
+
}
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
function finishRound(winner, result) {
|
| 175 |
+
if (game.phase !== 'fight') return;
|
| 176 |
+
if (winner) winner.roundsWon++;
|
| 177 |
+
const loser = winner ? (winner === game.p1 ? game.p2 : game.p1) : null;
|
| 178 |
+
if (winner) winner.setAction(181, 181);
|
| 179 |
+
if (loser) loser.setAction(5151, 5151);
|
| 180 |
+
if (!winner) { game.p1.setAction(5151, 5151); game.p2.setAction(5151, 5151); }
|
| 181 |
+
game.phase = 'roundEnd'; game.phaseTicks = 135;
|
| 182 |
+
game.round = winner ? `${winner === game.p1 ? 'KUNG FU MAN' : 'CPU'} WINS` : `DRAW 路 ${result}`;
|
| 183 |
}
|
| 184 |
|
| 185 |
function worldBoxes(f, boxes) {
|
|
|
|
| 285 |
const fill = 156 * Math.max(0, f.life / f.maxLife); ctx.fillStyle = col; ctx.fillRect(mirror ? x + 158 - fill : x + 2, 10, fill, 7);
|
| 286 |
ctx.fillStyle = '#f6f1df'; ctx.font = 'bold 9px system-ui'; ctx.fillText(f.name, x, 31);
|
| 287 |
}
|
| 288 |
+
ctx.textAlign = 'center'; ctx.fillStyle = '#fff6d8'; ctx.font = 'bold 21px ui-monospace'; ctx.fillText(String(game.time).padStart(2, '0'), unitW / 2, 25);
|
| 289 |
+
for (let n = 0; n < game.roundsToWin; n++) {
|
| 290 |
+
ctx.fillStyle = n < game.p1.roundsWon ? '#ffd36a' : '#59636d'; ctx.beginPath(); ctx.arc(10 + n * 14, 39, 4, 0, Math.PI * 2); ctx.fill();
|
| 291 |
+
ctx.fillStyle = n < game.p2.roundsWon ? '#ffd36a' : '#59636d'; ctx.beginPath(); ctx.arc(unitW - 10 - n * 14, 39, 4, 0, Math.PI * 2); ctx.fill();
|
| 292 |
+
}
|
| 293 |
+
if (game.round) { ctx.fillStyle = '#fff6d8'; ctx.font = 'bold 21px system-ui'; ctx.fillText(game.round, unitW / 2, 82); }
|
| 294 |
ctx.restore();
|
| 295 |
}
|
| 296 |
|
|
|
|
| 299 |
if (!paused && game && intro.hidden) {
|
| 300 |
accum += dt;
|
| 301 |
for (let n = 0; accum >= 1000 / 60 && n < 5; n++) { step(); accum -= 1000 / 60; }
|
|
|
|
| 302 |
}
|
| 303 |
draw(); requestAnimationFrame(loop);
|
| 304 |
}
|
js/runtime.js
CHANGED
|
@@ -98,6 +98,7 @@ export class Fighter {
|
|
| 98 |
this.grounded = true; this.crouch = false; this.airJumps = 0; this.dirHistory = [];
|
| 99 |
this.aiWait = 0; this.lastAI = 0;
|
| 100 |
this.prevKeys = new Set();
|
|
|
|
| 101 |
this.frame = this.currentFrame();
|
| 102 |
}
|
| 103 |
setAction(action, state = this.state) {
|
|
@@ -118,6 +119,19 @@ export class Fighter {
|
|
| 118 |
}
|
| 119 |
this.frame = this.currentFrame();
|
| 120 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
currentFrame() {
|
| 122 |
const a = this.assets.actions.get(this.action);
|
| 123 |
if (!a?.frames.length) return null;
|
|
|
|
| 98 |
this.grounded = true; this.crouch = false; this.airJumps = 0; this.dirHistory = [];
|
| 99 |
this.aiWait = 0; this.lastAI = 0;
|
| 100 |
this.prevKeys = new Set();
|
| 101 |
+
this.roundsWon = 0;
|
| 102 |
this.frame = this.currentFrame();
|
| 103 |
}
|
| 104 |
setAction(action, state = this.state) {
|
|
|
|
| 119 |
}
|
| 120 |
this.frame = this.currentFrame();
|
| 121 |
}
|
| 122 |
+
resetRound() {
|
| 123 |
+
this.life = this.maxLife;
|
| 124 |
+
this.x = this.player ? -68 : 68;
|
| 125 |
+
this.y = this.vx = this.vy = 0;
|
| 126 |
+
this.facing = this.player ? 1 : -1;
|
| 127 |
+
this.state = 0; this.stateFrame = this.actionFrame = this.elemTick = 0;
|
| 128 |
+
this.hitstun = this.hitPause = this.hurtFlash = 0;
|
| 129 |
+
this.ctrl = true; this.grounded = true; this.crouch = false;
|
| 130 |
+
this.hit = null; this.hitDefs = []; this.prevKeys.clear(); this.dirHistory.length = 0;
|
| 131 |
+
this._power = 0;
|
| 132 |
+
this.setAction(0, 0);
|
| 133 |
+
}
|
| 134 |
+
resetMatch() { this.roundsWon = 0; this.resetRound(); }
|
| 135 |
currentFrame() {
|
| 136 |
const a = this.assets.actions.get(this.action);
|
| 137 |
if (!a?.frames.length) return null;
|