/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Monster class: handles monster sprite and health bar var Monster = Container.expand(function () { var self = Container.call(this); // Monster sprite var monsterSprite = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5 }); monsterSprite.y = 0; // Health bar background var hpbarBg = self.attachAsset('hpbar_bg', { anchorX: 0.5, anchorY: 0.5, y: 260 }); // Health bar foreground var hpbarFg = self.attachAsset('hpbar_fg', { anchorX: 0.5, anchorY: 0.5, y: 260 }); // Health bar value (0-1) self.hp = 1; self.maxHp = 1; // Set health (0-1) self.setHp = function (value) { self.hp = Math.max(0, Math.min(1, value)); // Scale health bar foreground hpbarFg.scaleX = self.hp; // Anchor stays at left edge hpbarFg.x = hpbarBg.x - 1600 * (1 - self.hp) / 2; }; // Flash monster on smite self.flash = function (color, duration) { tween(monsterSprite, { tint: color }, { duration: duration / 2, easing: tween.linear, onFinish: function onFinish() { tween(monsterSprite, { tint: 0x3cb371 }, { duration: duration / 2, easing: tween.linear }); } }); }; // Reset health bar self.reset = function () { self.setHp(1); }; // Initialize self.setHp(1); return self; }); // Smite effect (player or enemy) var SmiteFX = Container.expand(function () { var self = Container.call(this); var fx = self.attachAsset('smite_fx', { anchorX: 0.5, anchorY: 0.5 }); self.play = function (x, y, isEnemy) { self.x = x; self.y = y; fx.alpha = 1; fx.scaleX = fx.scaleY = 1; if (isEnemy) { fx.tint = 0xff3333; } else { fx.tint = 0x3399ff; } tween(fx, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { self.visible = false; } }); self.visible = true; }; self.visible = false; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181c24 }); /**** * Game Code ****/ // Sound for smite // Enemy smite: red ellipse // Smite effect: blue ellipse // Health bar foreground: yellow-green // Health bar background: gray // Monster: Big ellipse, green // Game constants var MONSTER_CENTER_X = 2048 / 2; var MONSTER_CENTER_Y = 1100; var HPBAR_Y_OFFSET = 250; var SMITE_WINDOW = 0.12; // Smite is successful if HP <= this fraction var ENEMY_SMITE_WINDOW = 0.10; // Enemy can smite if HP <= this var ROUNDS_TOTAL = 10; // Game state var round = 1; var score = 0; var smiteUsed = false; var enemySmiteUsed = false; var monsterHp = 1; var monsterHpSpeed = 0.003; // per tick var monsterHpMinSpeed = 0.002; var monsterHpMaxSpeed = 0.006; var smiteFx, enemySmiteFx; var monster; var resultTxt, roundTxt, scoreTxt, infoTxt; var lastTickTime = 0; var smiteTime = 0; var enemySmiteTime = 0; var smiteAccuracy = []; var smiteReaction = []; var smiteSuccess = []; var smiteAllowed = true; var enemySmiteScheduled = false; var enemySmiteTick = 0; var enemySmiteActive = false; // UI: Score, round, info scoreTxt = new Text2('Score: 0', { size: 90, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); roundTxt = new Text2('Round 1/10', { size: 70, fill: "#fff" }); roundTxt.anchor.set(0.5, 0); LK.gui.top.addChild(roundTxt); roundTxt.y = 110; infoTxt = new Text2('Tap the monster to Smite!', { size: 70, fill: 0xB8E62E }); infoTxt.anchor.set(0.5, 0); LK.gui.top.addChild(infoTxt); infoTxt.y = 220; // Result text (centered, hidden by default) resultTxt = new Text2('', { size: 120, fill: "#fff" }); resultTxt.anchor.set(0.5, 0.5); resultTxt.visible = false; LK.gui.center.addChild(resultTxt); // Single monster support var NUM_MONSTERS = 1; var monsters = []; var monsterHps = []; var monsterHpSpeeds = []; var monsterHpMinSpeeds = []; var monsterHpMaxSpeeds = []; var monsterSmitesUsed = []; var monsterEnemySmitesUsed = []; var monsterSmiteMarkers = []; var monsterSmiteFxs = []; var monsterEnemySmiteFxs = []; var monsterCenters = []; var SMITE_DAMAGE = 0.12; // Create the single monster, centered var m = new Monster(); game.addChild(m); m.x = MONSTER_CENTER_X; m.y = MONSTER_CENTER_Y; monsters.push(m); m.maxHp = 100; // Default, will be randomized in startRound monsterCenters.push({ x: m.x, y: m.y }); monsterHps.push(1); monsterHpSpeeds.push(monsterHpMinSpeed + Math.random() * (monsterHpMaxSpeed - monsterHpMinSpeed)); monsterHpMinSpeeds.push(monsterHpMinSpeed); monsterHpMaxSpeeds.push(monsterHpMaxSpeed); monsterSmitesUsed.push(false); monsterEnemySmitesUsed.push(false); // Smite marker for this monster var smiteMarker = LK.getAsset('hpbar_fg', { anchorX: 0.5, anchorY: 0.5, width: 10, height: 70, tint: 0x3399ff }); game.addChild(smiteMarker); monsterSmiteMarkers.push(smiteMarker); // Smite FX for this monster var smiteFx1 = new SmiteFX(); game.addChild(smiteFx1); smiteFx1.x = m.x; smiteFx1.y = m.y; monsterSmiteFxs.push(smiteFx1); // Enemy Smite FX for this monster var enemySmiteFx1 = new SmiteFX(); game.addChild(enemySmiteFx1); enemySmiteFx1.x = m.x; enemySmiteFx1.y = m.y; monsterEnemySmiteFxs.push(enemySmiteFx1); // Smite damage text (shows the value) var smiteDmgTxt = new Text2('Smite: ' + Math.round(SMITE_DAMAGE * 100) + '%', { size: 70, fill: 0x3399FF }); smiteDmgTxt.anchor.set(0.5, 0); LK.gui.top.addChild(smiteDmgTxt); smiteDmgTxt.y = 320; // Monster HP number text var monsterHpTxt = new Text2('HP: 100', { size: 90, fill: "#fff" }); monsterHpTxt.anchor.set(0.5, 0.5); monsterHpTxt.x = m.x; monsterHpTxt.y = m.y - 180; game.addChild(monsterHpTxt); // Helper to update smite marker position for the monster function updateSmiteMarkers() { var m = monsters[0]; var smiteMarker = monsterSmiteMarkers[0]; // HP bar is at m.x, m.y + HPBAR_Y_OFFSET // HP bar width is 1600, anchorX = 0.5 (centered) var barWidth = 1600; var barY = m.y + HPBAR_Y_OFFSET; var barX = m.x; // Smite marker should be at the position where HP = SMITE_DAMAGE var markerFrac = 1 - SMITE_DAMAGE; var markerX = barX - barWidth / 2 + barWidth * markerFrac; smiteMarker.x = markerX; smiteMarker.y = barY; smiteMarker.width = 24; smiteMarker.height = 80; smiteMarker.visible = true; } updateSmiteMarkers(); // Smite FX smiteFx = new SmiteFX(); game.addChild(smiteFx); smiteFx.x = MONSTER_CENTER_X; smiteFx.y = MONSTER_CENTER_Y; enemySmiteFx = new SmiteFX(); game.addChild(enemySmiteFx); enemySmiteFx.x = MONSTER_CENTER_X; enemySmiteFx.y = MONSTER_CENTER_Y; // Helper: Start a new round function startRound() { // Randomize monster HP and Smite damage for this round var minHp = 80; var maxHp = 300; var minSmite = 0.10; var maxSmite = 0.25; var thisHp = minHp + Math.floor(Math.random() * (maxHp - minHp + 1)); var thisSmite = minSmite + Math.random() * (maxSmite - minSmite); SMITE_DAMAGE = thisSmite; // Reset state for all monsters for (var i = 0; i < NUM_MONSTERS; ++i) { monsterHps[i] = 1; monsters[i].reset(); monsterSmitesUsed[i] = false; monsterEnemySmitesUsed[i] = false; monsterSmiteFxs[i].visible = false; monsterEnemySmiteFxs[i].visible = false; // Randomize HP speed for this round monsterHpSpeeds[i] = monsterHpMinSpeeds[i] + Math.random() * (monsterHpMaxSpeeds[i] - monsterHpMinSpeeds[i]); // Store the randomized max HP for this round monsters[i].maxHp = thisHp; } smiteAllowed = true; enemySmiteActive = false; enemySmiteScheduled = false; resultTxt.visible = false; // (Optional: You can add per-monster enemy smite logic here if you want enemy smite for each monster. For now, keep global enemy smite for simplicity.) // Update UI roundTxt.setText('Round ' + round + '/' + ROUNDS_TOTAL); scoreTxt.setText('Score: ' + score); infoTxt.setText('Tap the monster to Smite!'); lastTickTime = LK.ticks; updateSmiteMarkers(); // Reset monster HP number and Smite damage text monsterHpTxt.setText('HP: ' + thisHp); smiteDmgTxt.setText('Smite: ' + Math.round(SMITE_DAMAGE * 100) + '% (' + Math.round(SMITE_DAMAGE * thisHp) + ' HP)'); } // Helper: End round, show result, schedule next function endRound(success, isEnemy) { smiteAllowed = false; smiteSuccess.push(success ? 1 : 0); // Show result if (isEnemy) { resultTxt.setText('Enemy Stole It!'); resultTxt.setStyle({ fill: 0xFF3333 }); LK.getSound('enemy').play(); } else if (success) { resultTxt.setText('Smite Success!'); resultTxt.setStyle({ fill: 0x3399FF }); LK.getSound('smite').play(); } else { resultTxt.setText('Missed!'); resultTxt.setStyle({ fill: 0xFFCC00 }); LK.getSound('fail').play(); } resultTxt.visible = true; // Animate monster // Find which monster to flash: if called from player smite, flash the tapped monster; if enemy, flash the first unsmitten monster var flashIndex = 0; for (var i = 0; i < NUM_MONSTERS; ++i) { if (!monsterSmitesUsed[i] && !monsterEnemySmitesUsed[i]) { flashIndex = i; break; } } if (isEnemy) { monsters[flashIndex].flash(0xff3333, 600); } else if (success) { monsters[flashIndex].flash(0x3399ff, 600); } else { monsters[flashIndex].flash(0xffcc00, 600); } // Next round or finish LK.setTimeout(function () { resultTxt.visible = false; if (round < ROUNDS_TOTAL) { round++; startRound(); } else { showSummary(); } }, 1100); } // Helper: Show summary at end function showSummary() { // Calculate stats var acc = 0, react = 0, hits = 0; for (var i = 0; i < smiteAccuracy.length; ++i) { acc += smiteAccuracy[i]; react += smiteReaction[i]; if (smiteSuccess[i]) hits++; } var avgAcc = smiteAccuracy.length ? acc / smiteAccuracy.length : 0; var avgReact = smiteReaction.length ? react / smiteReaction.length : 0; var summary = 'Game Over\n\n' + 'Score: ' + score + '/' + ROUNDS_TOTAL + '\n' + 'Avg. Accuracy: ' + (avgAcc * 100).toFixed(1) + '%\n' + 'Avg. Reaction: ' + (avgReact * 1000).toFixed(0) + 'ms'; resultTxt.setText(summary); resultTxt.setStyle({ fill: 0xB8E62E }); resultTxt.visible = true; infoTxt.setText('Tap to play again!'); // Reset on tap game.down = function (x, y, obj) { // Reset state round = 1; score = 0; smiteAccuracy = []; smiteReaction = []; smiteSuccess = []; startRound(); game.down = handleDown; }; } // Handle player smite function handleDown(x, y, obj) { // Only allow if round is active if (!smiteAllowed) return; // Check which monster was tapped var tappedMonster = -1; for (var i = 0; i < NUM_MONSTERS; ++i) { var m = monsters[i]; var dx = x - m.x; var dy = y - m.y; var rx = m.width / 2; var ry = m.height / 2; if (dx * dx / (rx * rx) + dy * dy / (ry * ry) <= 1) { tappedMonster = i; break; } } if (tappedMonster === -1) return; // No monster tapped // Only allow if not already smited for this monster if (monsterSmitesUsed[tappedMonster]) return; monsterSmitesUsed[tappedMonster] = true; smiteTime = LK.ticks - lastTickTime; // Smite effect monsterSmiteFxs[tappedMonster].play(monsters[tappedMonster].x, monsters[tappedMonster].y, false); // Check if successful var maxHp = monsters[tappedMonster].maxHp || 100; var hpValue = monsterHps[tappedMonster] * maxHp; var smiteThreshold = SMITE_DAMAGE * maxHp; var success = hpValue <= smiteThreshold && hpValue >= smiteThreshold - 5 && !enemySmiteActive; if (success) { score++; } // Record accuracy (how close to 0) var acc = hpValue <= maxHp ? 1 - hpValue / smiteThreshold : 0; acc = Math.max(0, Math.min(1, acc)); smiteAccuracy.push(1 - hpValue / maxHp); smiteReaction.push(smiteTime / 60); endRound(success, false); } // Attach event game.down = handleDown; // Main update loop game.update = function () { // If round is active if (smiteAllowed) { var allSmitten = true; for (var i = 0; i < NUM_MONSTERS; ++i) { // Decrease HP for each monster if (!monsterSmitesUsed[i]) { monsterHps[i] -= monsterHpSpeeds[i]; if (monsterHps[i] < 0) monsterHps[i] = 0; monsters[i].setHp(monsterHps[i]); allSmitten = false; } // If player missed (HP reaches 0) if (monsterHps[i] <= 0 && !monsterEnemySmitesUsed[i] && !monsterSmitesUsed[i]) { monsterSmitesUsed[i] = true; smiteAccuracy.push(0); smiteReaction.push(1); endRound(false, false); return; } // If player missed (HP drops below Smite threshold minus 5) var maxHp = monsters[i].maxHp || 100; var smiteThreshold = SMITE_DAMAGE * maxHp; if (monsterHps[i] * maxHp < smiteThreshold - 5 && !monsterEnemySmitesUsed[i] && !monsterSmitesUsed[i]) { monsterSmitesUsed[i] = true; smiteAccuracy.push(0); smiteReaction.push(1); endRound(false, false); return; } } // Update monster HP number and Smite damage value var maxHp = monsters[0].maxHp || 100; var hpValue = Math.round(monsterHps[0] * maxHp); monsterHpTxt.setText('HP: ' + hpValue); smiteDmgTxt.setText('Smite: ' + Math.round(SMITE_DAMAGE * 100) + '% (' + Math.round(SMITE_DAMAGE * maxHp) + ' HP)'); // If all monsters have been smitten, end round if (allSmitten) { // For now, treat as success if all monsters smitten endRound(true, false); return; } } }; // Start first round startRound();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Monster class: handles monster sprite and health bar
var Monster = Container.expand(function () {
var self = Container.call(this);
// Monster sprite
var monsterSprite = self.attachAsset('monster', {
anchorX: 0.5,
anchorY: 0.5
});
monsterSprite.y = 0;
// Health bar background
var hpbarBg = self.attachAsset('hpbar_bg', {
anchorX: 0.5,
anchorY: 0.5,
y: 260
});
// Health bar foreground
var hpbarFg = self.attachAsset('hpbar_fg', {
anchorX: 0.5,
anchorY: 0.5,
y: 260
});
// Health bar value (0-1)
self.hp = 1;
self.maxHp = 1;
// Set health (0-1)
self.setHp = function (value) {
self.hp = Math.max(0, Math.min(1, value));
// Scale health bar foreground
hpbarFg.scaleX = self.hp;
// Anchor stays at left edge
hpbarFg.x = hpbarBg.x - 1600 * (1 - self.hp) / 2;
};
// Flash monster on smite
self.flash = function (color, duration) {
tween(monsterSprite, {
tint: color
}, {
duration: duration / 2,
easing: tween.linear,
onFinish: function onFinish() {
tween(monsterSprite, {
tint: 0x3cb371
}, {
duration: duration / 2,
easing: tween.linear
});
}
});
};
// Reset health bar
self.reset = function () {
self.setHp(1);
};
// Initialize
self.setHp(1);
return self;
});
// Smite effect (player or enemy)
var SmiteFX = Container.expand(function () {
var self = Container.call(this);
var fx = self.attachAsset('smite_fx', {
anchorX: 0.5,
anchorY: 0.5
});
self.play = function (x, y, isEnemy) {
self.x = x;
self.y = y;
fx.alpha = 1;
fx.scaleX = fx.scaleY = 1;
if (isEnemy) {
fx.tint = 0xff3333;
} else {
fx.tint = 0x3399ff;
}
tween(fx, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
self.visible = false;
}
});
self.visible = true;
};
self.visible = false;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c24
});
/****
* Game Code
****/
// Sound for smite
// Enemy smite: red ellipse
// Smite effect: blue ellipse
// Health bar foreground: yellow-green
// Health bar background: gray
// Monster: Big ellipse, green
// Game constants
var MONSTER_CENTER_X = 2048 / 2;
var MONSTER_CENTER_Y = 1100;
var HPBAR_Y_OFFSET = 250;
var SMITE_WINDOW = 0.12; // Smite is successful if HP <= this fraction
var ENEMY_SMITE_WINDOW = 0.10; // Enemy can smite if HP <= this
var ROUNDS_TOTAL = 10;
// Game state
var round = 1;
var score = 0;
var smiteUsed = false;
var enemySmiteUsed = false;
var monsterHp = 1;
var monsterHpSpeed = 0.003; // per tick
var monsterHpMinSpeed = 0.002;
var monsterHpMaxSpeed = 0.006;
var smiteFx, enemySmiteFx;
var monster;
var resultTxt, roundTxt, scoreTxt, infoTxt;
var lastTickTime = 0;
var smiteTime = 0;
var enemySmiteTime = 0;
var smiteAccuracy = [];
var smiteReaction = [];
var smiteSuccess = [];
var smiteAllowed = true;
var enemySmiteScheduled = false;
var enemySmiteTick = 0;
var enemySmiteActive = false;
// UI: Score, round, info
scoreTxt = new Text2('Score: 0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
roundTxt = new Text2('Round 1/10', {
size: 70,
fill: "#fff"
});
roundTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(roundTxt);
roundTxt.y = 110;
infoTxt = new Text2('Tap the monster to Smite!', {
size: 70,
fill: 0xB8E62E
});
infoTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(infoTxt);
infoTxt.y = 220;
// Result text (centered, hidden by default)
resultTxt = new Text2('', {
size: 120,
fill: "#fff"
});
resultTxt.anchor.set(0.5, 0.5);
resultTxt.visible = false;
LK.gui.center.addChild(resultTxt);
// Single monster support
var NUM_MONSTERS = 1;
var monsters = [];
var monsterHps = [];
var monsterHpSpeeds = [];
var monsterHpMinSpeeds = [];
var monsterHpMaxSpeeds = [];
var monsterSmitesUsed = [];
var monsterEnemySmitesUsed = [];
var monsterSmiteMarkers = [];
var monsterSmiteFxs = [];
var monsterEnemySmiteFxs = [];
var monsterCenters = [];
var SMITE_DAMAGE = 0.12;
// Create the single monster, centered
var m = new Monster();
game.addChild(m);
m.x = MONSTER_CENTER_X;
m.y = MONSTER_CENTER_Y;
monsters.push(m);
m.maxHp = 100; // Default, will be randomized in startRound
monsterCenters.push({
x: m.x,
y: m.y
});
monsterHps.push(1);
monsterHpSpeeds.push(monsterHpMinSpeed + Math.random() * (monsterHpMaxSpeed - monsterHpMinSpeed));
monsterHpMinSpeeds.push(monsterHpMinSpeed);
monsterHpMaxSpeeds.push(monsterHpMaxSpeed);
monsterSmitesUsed.push(false);
monsterEnemySmitesUsed.push(false);
// Smite marker for this monster
var smiteMarker = LK.getAsset('hpbar_fg', {
anchorX: 0.5,
anchorY: 0.5,
width: 10,
height: 70,
tint: 0x3399ff
});
game.addChild(smiteMarker);
monsterSmiteMarkers.push(smiteMarker);
// Smite FX for this monster
var smiteFx1 = new SmiteFX();
game.addChild(smiteFx1);
smiteFx1.x = m.x;
smiteFx1.y = m.y;
monsterSmiteFxs.push(smiteFx1);
// Enemy Smite FX for this monster
var enemySmiteFx1 = new SmiteFX();
game.addChild(enemySmiteFx1);
enemySmiteFx1.x = m.x;
enemySmiteFx1.y = m.y;
monsterEnemySmiteFxs.push(enemySmiteFx1);
// Smite damage text (shows the value)
var smiteDmgTxt = new Text2('Smite: ' + Math.round(SMITE_DAMAGE * 100) + '%', {
size: 70,
fill: 0x3399FF
});
smiteDmgTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(smiteDmgTxt);
smiteDmgTxt.y = 320;
// Monster HP number text
var monsterHpTxt = new Text2('HP: 100', {
size: 90,
fill: "#fff"
});
monsterHpTxt.anchor.set(0.5, 0.5);
monsterHpTxt.x = m.x;
monsterHpTxt.y = m.y - 180;
game.addChild(monsterHpTxt);
// Helper to update smite marker position for the monster
function updateSmiteMarkers() {
var m = monsters[0];
var smiteMarker = monsterSmiteMarkers[0];
// HP bar is at m.x, m.y + HPBAR_Y_OFFSET
// HP bar width is 1600, anchorX = 0.5 (centered)
var barWidth = 1600;
var barY = m.y + HPBAR_Y_OFFSET;
var barX = m.x;
// Smite marker should be at the position where HP = SMITE_DAMAGE
var markerFrac = 1 - SMITE_DAMAGE;
var markerX = barX - barWidth / 2 + barWidth * markerFrac;
smiteMarker.x = markerX;
smiteMarker.y = barY;
smiteMarker.width = 24;
smiteMarker.height = 80;
smiteMarker.visible = true;
}
updateSmiteMarkers();
// Smite FX
smiteFx = new SmiteFX();
game.addChild(smiteFx);
smiteFx.x = MONSTER_CENTER_X;
smiteFx.y = MONSTER_CENTER_Y;
enemySmiteFx = new SmiteFX();
game.addChild(enemySmiteFx);
enemySmiteFx.x = MONSTER_CENTER_X;
enemySmiteFx.y = MONSTER_CENTER_Y;
// Helper: Start a new round
function startRound() {
// Randomize monster HP and Smite damage for this round
var minHp = 80;
var maxHp = 300;
var minSmite = 0.10;
var maxSmite = 0.25;
var thisHp = minHp + Math.floor(Math.random() * (maxHp - minHp + 1));
var thisSmite = minSmite + Math.random() * (maxSmite - minSmite);
SMITE_DAMAGE = thisSmite;
// Reset state for all monsters
for (var i = 0; i < NUM_MONSTERS; ++i) {
monsterHps[i] = 1;
monsters[i].reset();
monsterSmitesUsed[i] = false;
monsterEnemySmitesUsed[i] = false;
monsterSmiteFxs[i].visible = false;
monsterEnemySmiteFxs[i].visible = false;
// Randomize HP speed for this round
monsterHpSpeeds[i] = monsterHpMinSpeeds[i] + Math.random() * (monsterHpMaxSpeeds[i] - monsterHpMinSpeeds[i]);
// Store the randomized max HP for this round
monsters[i].maxHp = thisHp;
}
smiteAllowed = true;
enemySmiteActive = false;
enemySmiteScheduled = false;
resultTxt.visible = false;
// (Optional: You can add per-monster enemy smite logic here if you want enemy smite for each monster. For now, keep global enemy smite for simplicity.)
// Update UI
roundTxt.setText('Round ' + round + '/' + ROUNDS_TOTAL);
scoreTxt.setText('Score: ' + score);
infoTxt.setText('Tap the monster to Smite!');
lastTickTime = LK.ticks;
updateSmiteMarkers();
// Reset monster HP number and Smite damage text
monsterHpTxt.setText('HP: ' + thisHp);
smiteDmgTxt.setText('Smite: ' + Math.round(SMITE_DAMAGE * 100) + '% (' + Math.round(SMITE_DAMAGE * thisHp) + ' HP)');
}
// Helper: End round, show result, schedule next
function endRound(success, isEnemy) {
smiteAllowed = false;
smiteSuccess.push(success ? 1 : 0);
// Show result
if (isEnemy) {
resultTxt.setText('Enemy Stole It!');
resultTxt.setStyle({
fill: 0xFF3333
});
LK.getSound('enemy').play();
} else if (success) {
resultTxt.setText('Smite Success!');
resultTxt.setStyle({
fill: 0x3399FF
});
LK.getSound('smite').play();
} else {
resultTxt.setText('Missed!');
resultTxt.setStyle({
fill: 0xFFCC00
});
LK.getSound('fail').play();
}
resultTxt.visible = true;
// Animate monster
// Find which monster to flash: if called from player smite, flash the tapped monster; if enemy, flash the first unsmitten monster
var flashIndex = 0;
for (var i = 0; i < NUM_MONSTERS; ++i) {
if (!monsterSmitesUsed[i] && !monsterEnemySmitesUsed[i]) {
flashIndex = i;
break;
}
}
if (isEnemy) {
monsters[flashIndex].flash(0xff3333, 600);
} else if (success) {
monsters[flashIndex].flash(0x3399ff, 600);
} else {
monsters[flashIndex].flash(0xffcc00, 600);
}
// Next round or finish
LK.setTimeout(function () {
resultTxt.visible = false;
if (round < ROUNDS_TOTAL) {
round++;
startRound();
} else {
showSummary();
}
}, 1100);
}
// Helper: Show summary at end
function showSummary() {
// Calculate stats
var acc = 0,
react = 0,
hits = 0;
for (var i = 0; i < smiteAccuracy.length; ++i) {
acc += smiteAccuracy[i];
react += smiteReaction[i];
if (smiteSuccess[i]) hits++;
}
var avgAcc = smiteAccuracy.length ? acc / smiteAccuracy.length : 0;
var avgReact = smiteReaction.length ? react / smiteReaction.length : 0;
var summary = 'Game Over\n\n' + 'Score: ' + score + '/' + ROUNDS_TOTAL + '\n' + 'Avg. Accuracy: ' + (avgAcc * 100).toFixed(1) + '%\n' + 'Avg. Reaction: ' + (avgReact * 1000).toFixed(0) + 'ms';
resultTxt.setText(summary);
resultTxt.setStyle({
fill: 0xB8E62E
});
resultTxt.visible = true;
infoTxt.setText('Tap to play again!');
// Reset on tap
game.down = function (x, y, obj) {
// Reset state
round = 1;
score = 0;
smiteAccuracy = [];
smiteReaction = [];
smiteSuccess = [];
startRound();
game.down = handleDown;
};
}
// Handle player smite
function handleDown(x, y, obj) {
// Only allow if round is active
if (!smiteAllowed) return;
// Check which monster was tapped
var tappedMonster = -1;
for (var i = 0; i < NUM_MONSTERS; ++i) {
var m = monsters[i];
var dx = x - m.x;
var dy = y - m.y;
var rx = m.width / 2;
var ry = m.height / 2;
if (dx * dx / (rx * rx) + dy * dy / (ry * ry) <= 1) {
tappedMonster = i;
break;
}
}
if (tappedMonster === -1) return; // No monster tapped
// Only allow if not already smited for this monster
if (monsterSmitesUsed[tappedMonster]) return;
monsterSmitesUsed[tappedMonster] = true;
smiteTime = LK.ticks - lastTickTime;
// Smite effect
monsterSmiteFxs[tappedMonster].play(monsters[tappedMonster].x, monsters[tappedMonster].y, false);
// Check if successful
var maxHp = monsters[tappedMonster].maxHp || 100;
var hpValue = monsterHps[tappedMonster] * maxHp;
var smiteThreshold = SMITE_DAMAGE * maxHp;
var success = hpValue <= smiteThreshold && hpValue >= smiteThreshold - 5 && !enemySmiteActive;
if (success) {
score++;
}
// Record accuracy (how close to 0)
var acc = hpValue <= maxHp ? 1 - hpValue / smiteThreshold : 0;
acc = Math.max(0, Math.min(1, acc));
smiteAccuracy.push(1 - hpValue / maxHp);
smiteReaction.push(smiteTime / 60);
endRound(success, false);
}
// Attach event
game.down = handleDown;
// Main update loop
game.update = function () {
// If round is active
if (smiteAllowed) {
var allSmitten = true;
for (var i = 0; i < NUM_MONSTERS; ++i) {
// Decrease HP for each monster
if (!monsterSmitesUsed[i]) {
monsterHps[i] -= monsterHpSpeeds[i];
if (monsterHps[i] < 0) monsterHps[i] = 0;
monsters[i].setHp(monsterHps[i]);
allSmitten = false;
}
// If player missed (HP reaches 0)
if (monsterHps[i] <= 0 && !monsterEnemySmitesUsed[i] && !monsterSmitesUsed[i]) {
monsterSmitesUsed[i] = true;
smiteAccuracy.push(0);
smiteReaction.push(1);
endRound(false, false);
return;
}
// If player missed (HP drops below Smite threshold minus 5)
var maxHp = monsters[i].maxHp || 100;
var smiteThreshold = SMITE_DAMAGE * maxHp;
if (monsterHps[i] * maxHp < smiteThreshold - 5 && !monsterEnemySmitesUsed[i] && !monsterSmitesUsed[i]) {
monsterSmitesUsed[i] = true;
smiteAccuracy.push(0);
smiteReaction.push(1);
endRound(false, false);
return;
}
}
// Update monster HP number and Smite damage value
var maxHp = monsters[0].maxHp || 100;
var hpValue = Math.round(monsterHps[0] * maxHp);
monsterHpTxt.setText('HP: ' + hpValue);
smiteDmgTxt.setText('Smite: ' + Math.round(SMITE_DAMAGE * 100) + '% (' + Math.round(SMITE_DAMAGE * maxHp) + ' HP)');
// If all monsters have been smitten, end round
if (allSmitten) {
// For now, treat as success if all monsters smitten
endRound(true, false);
return;
}
}
};
// Start first round
startRound();