/****
* 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();