/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.vx = 0;
self.vy = 0;
self.lifetime = 120;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.lifetime--;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.shootCooldown = 0;
self.moveTimer = 0;
// Enemy magazine/reload system
self.magazine = 7;
self.maxMagazine = 7;
self.reloading = false;
self.reloadTime = 1500; // ms
self.reloadTimeout = null;
// Start at a random non-captured zone position
var availableZones = [];
for (var k = 0; k < zones.length; k++) {
if (!zones[k].captured) {
availableZones.push(k);
}
}
if (availableZones.length > 0) {
var randomIndex = Math.floor(Math.random() * availableZones.length);
var zoneIndex = availableZones[randomIndex];
self.targetX = [512, 1536, 512, 1536][zoneIndex];
self.targetY = [683, 683, 2049, 2049][zoneIndex];
} else {
// If all zones are captured, default to center
self.targetX = 1024;
self.targetY = 1366;
}
self.update = function () {
// Move towards target position
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 20) {
// Store previous position
var prevX = self.x;
var prevY = self.y;
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
// Prevent enemy from moving through walls
for (var w = 0; w < walls.length; w++) {
if (self.intersects(walls[w])) {
self.x = prevX;
self.y = prevY;
break;
}
}
}
// Change target position periodically
self.moveTimer++;
// Find available (uncaptured) zones
var availableZones = [];
for (var k = 0; k < zones.length; k++) {
if (!zones[k].captured) {
availableZones.push(k);
}
}
// If only one zone left, allow random movement within that zone's area
if (availableZones.length === 1) {
if (self.moveTimer > 120 || dist < 20) {
self.moveTimer = 0;
var zoneIndex = availableZones[0];
// Pick a random point within the zone's bounds (±180px from center)
var zx = [512, 1536, 512, 1536][zoneIndex];
var zy = [683, 683, 2049, 2049][zoneIndex];
var offsetX = (Math.random() - 0.5) * 360;
var offsetY = (Math.random() - 0.5) * 360;
self.targetX = zx + offsetX;
self.targetY = zy + offsetY;
}
} else if (self.moveTimer > 180 || dist < 20) {
self.moveTimer = 0;
// Move to a random non-captured zone position
if (availableZones.length > 0) {
var randomIndex = Math.floor(Math.random() * availableZones.length);
var zoneIndex = availableZones[randomIndex];
self.targetX = [512, 1536, 512, 1536][zoneIndex];
self.targetY = [683, 683, 2049, 2049][zoneIndex];
}
}
// Shoot at player
if (self.shootCooldown > 0) {
self.shootCooldown--;
} else if (player) {
var pdx = player.x - self.x;
var pdy = player.y - self.y;
var pdist = Math.sqrt(pdx * pdx + pdy * pdy);
// Only attack when within 500 pixel proximity
if (pdist < 500) {
self.shoot(player.x, player.y);
}
}
};
self.shoot = function (targetX, targetY) {
// Only shoot if not reloading and magazine > 0
if (self.reloading) {
return;
}
if (self.magazine <= 0) {
// Start reload if not already
if (!self.reloading) {
self.reloading = true;
self.reloadTimeout = LK.setTimeout(function () {
self.magazine = self.maxMagazine;
self.reloading = false;
}, self.reloadTime);
}
return;
}
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
var dx = targetX - self.x;
var dy = targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
bullet.vx = dx / dist * bullet.speed;
bullet.vy = dy / dist * bullet.speed;
bullet.isEnemyBullet = true;
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 60;
self.magazine--;
if (self.magazine <= 0) {
// Start reload
self.reloading = true;
self.reloadTimeout = LK.setTimeout(function () {
self.magazine = self.maxMagazine;
self.reloading = false;
}, self.reloadTime);
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Stylized, highly visible gun: colored barrel, body, handle, and sight
// Gun barrel (long, thick, bright yellow)
var gunBarrelLength = 70;
var gunBarrel = self.attachAsset('zone', {
width: gunBarrelLength,
height: 18,
tint: 0xffe066,
anchorX: 0.1,
anchorY: 0.5,
alpha: 1
});
gunBarrel.x = 54;
gunBarrel.y = 0;
// Gun body (short, thick, black)
var gunBody = self.attachAsset('zone', {
width: 38,
height: 28,
tint: 0x222222,
anchorX: 0.1,
anchorY: 0.5,
alpha: 1
});
gunBody.x = 38;
gunBody.y = 0;
// Gun handle (angled, orange)
var gunHandle = self.attachAsset('zone', {
width: 18,
height: 36,
tint: 0xff9900,
anchorX: 0.1,
anchorY: 0.1,
alpha: 1
});
gunHandle.x = 38 + 20;
gunHandle.y = 16;
gunHandle.rotation = Math.PI / 2.1;
// Gun trigger guard (small, black ellipse)
var gunTrigger = self.attachAsset('bullet', {
width: 12,
height: 10,
color: 0x111111,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9
});
gunTrigger.x = 38 + 10;
gunTrigger.y = 13;
// Gun sight (small, red dot at barrel tip)
var gunSight = self.attachAsset('bullet', {
width: 10,
height: 10,
color: 0xff2222,
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
gunSight.x = gunBarrel.x + gunBarrelLength - 10;
gunSight.y = 0;
// Group all as the gun
self.gun = gunBarrel;
self.gunBody = gunBody;
self.gunHandle = gunHandle;
self.gunTrigger = gunTrigger;
self.gunSight = gunSight;
self.gunAngle = 0; // Track current gun angle
self.speed = 8;
self.shootCooldown = 0;
self.health = 12;
self.maxHealth = 12;
// Create health bar container
var healthBar = self.addChild(new Container());
healthBar.y = -playerGraphics.height / 2 - 50;
healthBar.visible = false;
// Health bar background
var healthBg = healthBar.attachAsset('zone', {
width: 260,
height: 28,
tint: 0x333333,
anchorX: 0.5,
anchorY: 0.5
});
// Health bar fill
var healthFill = healthBar.attachAsset('zone', {
width: 252,
height: 22,
tint: 0x00ff00,
anchorX: 0,
anchorY: 0.5
});
healthFill.x = -130;
self.healthBar = healthBar;
self.healthFill = healthFill;
self.healthBg = healthBg;
self.takeDamage = function () {
self.health--;
self.healthBar.visible = true;
// Update health bar width
self.healthFill.width = self.health / self.maxHealth * 252;
// Update health bar color based on health
if (self.health <= 3) {
self.healthFill.tint = 0xff0000;
} else if (self.health <= 6) {
self.healthFill.tint = 0xffaa00;
} else {
self.healthFill.tint = 0x00ff00;
}
// Flash effect
LK.effects.flashObject(self, 0xff0000, 300);
if (self.health <= 0) {
LK.showGameOver({
text: "Wasted"
});
}
};
// Override intersects to use a smaller hitbox for the player
self.intersects = function (other) {
// Use a smaller hitbox (60% of width/height, centered)
var scale = 0.6;
var px = self.x,
py = self.y;
var pw = playerGraphics.width * scale,
ph = playerGraphics.height * scale;
var pLeft = px - pw / 2,
pRight = px + pw / 2,
pTop = py - ph / 2,
pBottom = py + ph / 2;
// Get other's bounds (assume center anchor)
var ox = other.x,
oy = other.y;
var ow = typeof other.width !== "undefined" ? other.width : other.graphics && other.graphics.width ? other.graphics.width : 0;
var oh = typeof other.height !== "undefined" ? other.height : other.graphics && other.graphics.height ? other.graphics.height : 0;
if (ow === 0 && typeof other.children !== "undefined" && other.children.length > 0 && typeof other.children[0].width !== "undefined") {
ow = other.children[0].width;
oh = other.children[0].height;
}
var oLeft = ox - ow / 2,
oRight = ox + ow / 2,
oTop = oy - oh / 2,
oBottom = oy + oh / 2;
// AABB collision
return !(pRight < oLeft || pLeft > oRight || pBottom < oTop || pTop > oBottom);
};
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.shoot = function (targetX, targetY) {
// Magazine/cooldown logic
if (typeof playerMagazine !== "undefined" && typeof playerReloading !== "undefined") {
if (playerReloading) {
// Can't shoot while reloading
return;
}
if (playerMagazine <= 0) {
// Start reload if not already
if (!playerReloading) {
playerReloading = true;
// Track reload bar timing
if (typeof Date !== "undefined") {
reloadStartTime = Date.now();
reloadEndTime = reloadStartTime + playerReloadTime;
}
// Animate reload bar fill and bullet icons
if (typeof reloadBar !== "undefined") {
reloadBar.visible = true;
reloadFill.width = 0;
// Clear all bullet icons (hide them) when magazine is empty
for (var i = 0; i < reloadBar.bulletIcons.length; i++) {
reloadBar.bulletIcons[i].alpha = 0;
reloadBar.bulletIcons[i].color = 0x888888;
}
}
playerReloadTimeout = LK.setTimeout(function () {
playerMagazine = playerMaxMagazine;
playerReloading = false;
reloadStartTime = 0;
reloadEndTime = 0;
// On reload complete, show all bullets as filled
if (typeof reloadBar !== "undefined") {
for (var i = 0; i < reloadBar.bulletIcons.length; i++) {
reloadBar.bulletIcons[i].alpha = 1;
reloadBar.bulletIcons[i].color = 0xffff00;
}
reloadFill.width = 200;
}
}, playerReloadTime);
}
return;
}
}
if (self.shootCooldown <= 0) {
// Use selected bullet type
var type = bulletTypes[currentBulletType];
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
// Change bullet asset and speed
if (type.id !== 'bullet') {
bullet.removeChild(bullet.children[0]);
var bulletGraphics = bullet.attachAsset(type.id, {
anchorX: 0.5,
anchorY: 0.5
});
bullet.speed = type.speed;
} else {
bullet.speed = type.speed;
}
var dx = targetX - self.x;
var dy = targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
bullet.vx = dx / dist * bullet.speed;
bullet.vy = dy / dist * bullet.speed;
// Rotate the gun towards the shooting direction
if (self.gun) {
var angle = Math.atan2(dy, dx);
// If shooting to the left (targetX < self.x), teleport gun to left side
if (targetX < self.x) {
// Place gun on left side
self.gun.x = -54;
self.gunBody.x = -38;
self.gunHandle.x = -38 - 20;
self.gunTrigger.x = -38 - 10;
// Sight will be repositioned below
} else {
// Place gun on right side (default)
self.gun.x = 54;
self.gunBody.x = 38;
self.gunHandle.x = 38 + 20;
self.gunTrigger.x = 38 + 10;
// Sight will be repositioned below
}
self.gun.rotation = angle;
self.gunAngle = angle;
// Rotate the body and trigger to match the barrel
if (self.gunBody) {
self.gunBody.rotation = angle;
}
if (self.gunTrigger) {
self.gunTrigger.rotation = angle;
}
// Rotate the handle with a slight offset to look like a real gun
if (self.gunHandle) {
self.gunHandle.rotation = angle + Math.PI / 2.1;
}
// Rotate and reposition the sight to always be at the barrel tip
if (self.gunSight) {
self.gunSight.rotation = angle;
// Place at the tip of the barrel
var barrelLen = 70;
self.gunSight.x = self.gun.x + Math.cos(angle) * (barrelLen - 10);
self.gunSight.y = self.gun.y + Math.sin(angle) * (barrelLen - 10);
}
}
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 15;
// Decrement magazine
if (typeof playerMagazine !== "undefined") {
playerMagazine--;
// Immediately update reload bar bullet icons after shooting
if (typeof reloadBar !== "undefined" && reloadBar.bulletIcons) {
for (var i = 0; i < reloadBar.bulletIcons.length; i++) {
if (i < playerMagazine && !playerReloading) {
reloadBar.bulletIcons[i].alpha = 1;
reloadBar.bulletIcons[i].color = 0xffff00;
} else {
reloadBar.bulletIcons[i].alpha = 0.3;
reloadBar.bulletIcons[i].color = 0x888888;
}
}
}
if (playerMagazine <= 0) {
playerReloading = true;
playerReloadTimeout = LK.setTimeout(function () {
playerMagazine = playerMaxMagazine;
playerReloading = false;
}, playerReloadTime);
}
}
}
};
return self;
});
// Wall class for bullet-blocking cover
var Wall = Container.expand(function () {
var self = Container.call(this);
self.wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
self.isCapturedVisual = false;
// Method to change wall visual to captured
self.setCapturedVisual = function () {
if (!self.isCapturedVisual) {
self.removeChild(self.wallGraphics);
self.wallGraphics = self.attachAsset('wall_captured', {
anchorX: 0.5,
anchorY: 0.5
});
self.isCapturedVisual = true;
}
};
// No update needed, static
return self;
});
var Zone = Container.expand(function (zoneIndex) {
var self = Container.call(this);
var zoneAssetId = 'zone' + (zoneIndex + 1);
var zoneGraphics = self.attachAsset(zoneAssetId, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
// Increase brightness using tween color effect (tint to a lighter color)
tween(zoneGraphics, {
tint: 0xffffff
}, {
duration: 500
});
self.graphics = zoneGraphics;
self.captureProgress = 0;
self.captureTime = 180;
self.captured = false;
self.playerInside = false;
var progressBar = self.addChild(new Container());
progressBar.y = -zoneGraphics.height / 2 - 30;
var progressBg = progressBar.attachAsset('zone', {
width: 300,
height: 20,
tint: 0x333333,
anchorX: 0.5,
anchorY: 0.5
});
var progressFill = progressBar.attachAsset('zone', {
width: 0,
height: 16,
tint: 0x00ff00,
anchorX: 0,
anchorY: 0.5
});
progressFill.x = -148;
self.progressFill = progressFill;
progressBar.visible = false;
self.progressBar = progressBar;
self.update = function () {
if (self.playerInside && !self.captured) {
self.captureProgress++;
self.progressBar.visible = true;
self.progressFill.width = self.captureProgress / self.captureTime * 296;
if (self.captureProgress >= self.captureTime) {
self.captured = true;
self.graphics.tint = 0x00ff00;
self.graphics.alpha = 0.5;
self.progressBar.visible = false;
LK.getSound('capture').play();
capturedZones++;
updateScore();
// Change visual of only the nearest wall to this zone
var nearestWall = null;
var minDist = Infinity;
for (var w = 0; w < walls.length; w++) {
var wx = walls[w].x;
var wy = walls[w].y;
var dx = wx - self.x;
var dy = wy - self.y;
var distSq = dx * dx + dy * dy;
if (distSq < minDist) {
minDist = distSq;
nearestWall = walls[w];
}
}
if (nearestWall && typeof nearestWall.setCapturedVisual === "function") {
nearestWall.setCapturedVisual();
}
// If all zones are captured, set all walls to captured visual
if (capturedZones >= totalZones) {
for (var w = 0; w < walls.length; w++) {
if (typeof walls[w].setCapturedVisual === "function") {
walls[w].setCapturedVisual();
}
}
}
// No reload bar or magazine logic here; zone capture does not affect reload bar
}
} else if (!self.playerInside && self.captureProgress > 0 && !self.captured) {
self.captureProgress = Math.max(0, self.captureProgress - 2);
if (self.captureProgress === 0) {
self.progressBar.visible = false;
} else {
self.progressFill.width = self.captureProgress / self.captureTime * 296;
}
}
};
self.checkPlayerInside = function (player) {
var halfWidth = self.graphics.width / 2;
var halfHeight = self.graphics.height / 2;
var dx = Math.abs(player.x - self.x);
var dy = Math.abs(player.y - self.y);
self.playerInside = dx < halfWidth && dy < halfHeight;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Wall asset for small cover/wall (cephé)
var player;
var bullets = [];
var enemyBullets = [];
var enemies = [];
var zones = [];
var walls = []; // Array for wall objects
var capturedZones = 0;
var totalZones = 4;
var isDragging = false;
var lastShootX = 0;
var lastShootY = 0;
// Bullet type switching
var bulletTypes = [{
id: 'bullet',
color: 0xffff00,
speed: 15,
size: 20
},
// normal
{
id: 'bullet_red',
color: 0xff4444,
speed: 10,
size: 32
},
// slow, big
{
id: 'bullet_blue',
color: 0x44aaff,
speed: 22,
size: 14
} // fast, small
];
var currentBulletType = 0;
if (typeof storage.getItem === "function" && storage.getItem("currentBulletType") !== undefined && storage.getItem("currentBulletType") !== null) {
currentBulletType = parseInt(storage.getItem("currentBulletType"));
}
// Dynamically create bullet assets if not already present
for (var i = 1; i < bulletTypes.length; i++) {
if (!LK.getAsset(bulletTypes[i].id, {})) {}
}
// UI for bullet type
var bulletTypeTxt = new Text2('Bullet: Normal', {
size: 60,
fill: 0xffffff
});
bulletTypeTxt.anchor.set(0.5, 0);
bulletTypeTxt.y = 120;
bulletTypeTxt.x = 1024;
LK.gui.top.addChild(bulletTypeTxt);
// Button to switch bullet type (simple text button)
var switchBtn = new Text2('Change Bullet', {
size: 60,
fill: 0x00ffcc
});
switchBtn.anchor.set(0.5, 0);
switchBtn.y = 210;
switchBtn.x = 1024;
LK.gui.top.addChild(switchBtn);
// Touch/click event for switchBtn
switchBtn.down = function (x, y, obj) {
currentBulletType = (currentBulletType + 1) % bulletTypes.length;
storage.set("currentBulletType", currentBulletType);
var names = ['Normal', 'Big', 'Fast'];
bulletTypeTxt.setText('Bullet: ' + names[currentBulletType]);
};
var scoreTxt = new Text2('Zones: 0/4', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
function updateScore() {
scoreTxt.setText('Zones: ' + capturedZones + '/' + totalZones);
if (capturedZones >= totalZones) {
LK.showYouWin({
text: "Mission Passed"
});
}
;
// Update reload bar position and progress, animate fill and update bullet icons
if (typeof reloadBar !== "undefined" && typeof player !== "undefined") {
reloadBar.x = player.x;
reloadBar.y = player.y - 170;
// Update bullet icons: show filled for each bullet in magazine, empty for spent
if (typeof playerMagazine !== "undefined" && reloadBar.bulletIcons) {
for (var i = 0; i < reloadBar.bulletIcons.length; i++) {
if (i < playerMagazine && !playerReloading) {
reloadBar.bulletIcons[i].alpha = 1;
reloadBar.bulletIcons[i].color = 0xffff00;
} else {
reloadBar.bulletIcons[i].alpha = 0.3;
reloadBar.bulletIcons[i].color = 0x888888;
}
}
}
if (typeof playerReloading !== "undefined" && playerReloading) {
reloadBar.visible = true;
// Animate fill: (playerReloadTime - remaining) / playerReloadTime
if (typeof reloadStartTime !== "undefined" && typeof reloadEndTime !== "undefined" && reloadStartTime > 0 && reloadEndTime > reloadStartTime) {
var now = Date.now();
var progress = Math.min(1, Math.max(0, (now - reloadStartTime) / (reloadEndTime - reloadStartTime)));
reloadFill.width = 200 * progress;
// Animate bullet icons: fill them as reload progresses
var bulletsToShow = Math.floor(progress * 10);
for (var i = 0; i < reloadBar.bulletIcons.length; i++) {
if (i < bulletsToShow) {
reloadBar.bulletIcons[i].alpha = 1;
reloadBar.bulletIcons[i].color = 0xffff00;
} else {
reloadBar.bulletIcons[i].alpha = 0.3;
reloadBar.bulletIcons[i].color = 0x888888;
}
}
} else {
reloadFill.width = 0;
}
} else if (typeof playerReloading !== "undefined" && !playerReloading) {
reloadBar.visible = true;
reloadFill.width = 200;
}
}
}
;
// Magazine and cooldown for player shooting
var playerMagazine = 10;
var playerMaxMagazine = 10;
var playerReloading = false;
var playerReloadTimeout = null;
var playerReloadTime = 1500; // ms
// Track reload progress for bar
var reloadStartTime = 0;
var reloadEndTime = 0;
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Reload progress bar UI above player, with 7 bullet icons and animated fill
var reloadBar = new Container();
// Background bar
var reloadBg = reloadBar.attachAsset('reloadBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
// Animated fill (for reload progress)
var reloadFill = reloadBar.attachAsset('reloadBarFill', {
width: 0,
anchorX: 0,
anchorY: 0.5
});
reloadFill.x = -100;
reloadFill.y = 0;
// 10 bullet icons (small ellipses) spaced evenly on the bar
reloadBar.bulletIcons = [];
var bulletIconSpacing = 20;
var bulletIconStartX = -90;
for (var i = 0; i < 10; i++) {
var icon = reloadBar.attachAsset('bullet', {
width: 18,
height: 18,
anchorX: 0.5,
anchorY: 0.5,
color: 0xffff00
});
icon.x = bulletIconStartX + i * bulletIconSpacing;
icon.y = 0;
reloadBar.bulletIcons.push(icon);
}
reloadBar.visible = true;
game.addChild(reloadBar);
var zonePositions = [{
x: 512,
y: 683
}, {
x: 1536,
y: 683
}, {
x: 512,
y: 2049
}, {
x: 1536,
y: 2049
}];
for (var i = 0; i < zonePositions.length; i++) {
var zone = new Zone(i);
zone.x = zonePositions[i].x;
zone.y = zonePositions[i].y;
zones.push(zone);
game.addChild(zone);
}
// Place walls (cephé) at strategic locations
var wallPositions = [{
x: 1024,
y: 500,
rotation: 0
},
// Top center
{
x: 1024,
y: 2200,
rotation: 0
},
// Bottom center
{
x: 400,
y: 1366,
rotation: Math.PI / 2
},
// Left center
{
x: 1648,
y: 1366,
rotation: Math.PI / 2
},
// Right center
{
x: 800,
y: 900,
rotation: 0.3
},
// Near zone 1
{
x: 1300,
y: 1800,
rotation: -0.3
} // Near zone 4
];
for (var i = 0; i < wallPositions.length; i++) {
var wall = new Wall();
wall.x = wallPositions[i].x;
wall.y = wallPositions[i].y;
if (wallPositions[i].rotation) wall.rotation = wallPositions[i].rotation;
walls.push(wall);
game.addChild(wall);
}
// Spawn enemies at zone positions
for (var i = 0; i < 3; i++) {
var enemy = new Enemy();
var zoneIndex = i % zonePositions.length;
enemy.x = zonePositions[zoneIndex].x;
enemy.y = zonePositions[zoneIndex].y;
enemies.push(enemy);
game.addChild(enemy);
}
game.down = function (x, y, obj) {
isDragging = true;
lastShootX = x;
lastShootY = y;
};
game.move = function (x, y, obj) {
if (isDragging) {
// Calculate drag direction from last position
var dx = x - lastShootX;
var dy = y - lastShootY;
var dist = Math.sqrt(dx * dx + dy * dy);
// Move player in the direction of the drag, but slower
if (dist > 0) {
var moveScale = 0.5; // Increase speed to 50% of drag
// Store previous position
var prevX = player.x;
var prevY = player.y;
player.x += dx * moveScale;
player.y += dy * moveScale;
player.x = Math.max(30, Math.min(2018, player.x));
player.y = Math.max(30, Math.min(2702, player.y));
// Check collision with walls, revert if colliding
for (var w = 0; w < walls.length; w++) {
if (player.intersects(walls[w])) {
player.x = prevX;
player.y = prevY;
break;
}
}
}
lastShootX = x;
lastShootY = y;
}
};
game.up = function (x, y, obj) {
if (isDragging) {
player.shoot(x, y);
isDragging = false;
}
};
game.update = function () {
// Update reload bar position to follow player
if (typeof reloadBar !== "undefined" && typeof player !== "undefined") {
reloadBar.x = player.x;
reloadBar.y = player.y - 170;
}
// Update zones
for (var i = zones.length - 1; i >= 0; i--) {
zones[i].checkPlayerInside(player);
}
// Update player bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Block by wall
var hitWall = false;
for (var w = 0; w < walls.length; w++) {
if (bullet.intersects(walls[w])) {
hitWall = true;
break;
}
}
if (hitWall || bullet.lifetime <= 0 || bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
if (bullet.intersects(enemies[j])) {
bullet.destroy();
bullets.splice(i, 1);
// Mini explosion effect at enemy position
var explosion = new Container();
var expAsset = explosion.attachAsset('bullet', {
width: 80,
height: 80,
color: 0xffaa00,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
explosion.x = enemies[j].x;
explosion.y = enemies[j].y;
game.addChild(explosion);
// Animate explosion: scale up and fade out, then destroy
tween(expAsset, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 350,
onComplete: function onComplete() {
explosion.destroy();
}
});
enemies[j].destroy();
enemies.splice(j, 1);
// Spawn new enemy at a random non-captured zone position
// Recalculate availableZones at spawn time to ensure accuracy
if (zones.filter(function (z) {
return !z.captured;
}).length > 0) {
// Delay enemy spawn by 2 seconds (2000ms)
LK.setTimeout(function () {
var availableZones = [];
for (var k = 0; k < zones.length; k++) {
if (!zones[k].captured) {
availableZones.push(k);
}
}
if (availableZones.length > 0) {
var newEnemy = new Enemy();
var randomIndex = Math.floor(Math.random() * availableZones.length);
var zoneIndex = availableZones[randomIndex];
newEnemy.x = zonePositions[zoneIndex].x;
newEnemy.y = zonePositions[zoneIndex].y;
enemies.push(newEnemy);
game.addChild(newEnemy);
}
}, 2000);
}
break;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Block by wall
var hitWall = false;
for (var w = 0; w < walls.length; w++) {
if (bullet.intersects(walls[w])) {
hitWall = true;
break;
}
}
if (hitWall || bullet.lifetime <= 0 || bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
bullet.destroy();
enemyBullets.splice(i, 1);
player.takeDamage();
}
// Example: Restart the game if you want to force a full reset (uncomment to use)
// LK.showGameOver();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.vx = 0;
self.vy = 0;
self.lifetime = 120;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.lifetime--;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.shootCooldown = 0;
self.moveTimer = 0;
// Enemy magazine/reload system
self.magazine = 7;
self.maxMagazine = 7;
self.reloading = false;
self.reloadTime = 1500; // ms
self.reloadTimeout = null;
// Start at a random non-captured zone position
var availableZones = [];
for (var k = 0; k < zones.length; k++) {
if (!zones[k].captured) {
availableZones.push(k);
}
}
if (availableZones.length > 0) {
var randomIndex = Math.floor(Math.random() * availableZones.length);
var zoneIndex = availableZones[randomIndex];
self.targetX = [512, 1536, 512, 1536][zoneIndex];
self.targetY = [683, 683, 2049, 2049][zoneIndex];
} else {
// If all zones are captured, default to center
self.targetX = 1024;
self.targetY = 1366;
}
self.update = function () {
// Move towards target position
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 20) {
// Store previous position
var prevX = self.x;
var prevY = self.y;
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
// Prevent enemy from moving through walls
for (var w = 0; w < walls.length; w++) {
if (self.intersects(walls[w])) {
self.x = prevX;
self.y = prevY;
break;
}
}
}
// Change target position periodically
self.moveTimer++;
// Find available (uncaptured) zones
var availableZones = [];
for (var k = 0; k < zones.length; k++) {
if (!zones[k].captured) {
availableZones.push(k);
}
}
// If only one zone left, allow random movement within that zone's area
if (availableZones.length === 1) {
if (self.moveTimer > 120 || dist < 20) {
self.moveTimer = 0;
var zoneIndex = availableZones[0];
// Pick a random point within the zone's bounds (±180px from center)
var zx = [512, 1536, 512, 1536][zoneIndex];
var zy = [683, 683, 2049, 2049][zoneIndex];
var offsetX = (Math.random() - 0.5) * 360;
var offsetY = (Math.random() - 0.5) * 360;
self.targetX = zx + offsetX;
self.targetY = zy + offsetY;
}
} else if (self.moveTimer > 180 || dist < 20) {
self.moveTimer = 0;
// Move to a random non-captured zone position
if (availableZones.length > 0) {
var randomIndex = Math.floor(Math.random() * availableZones.length);
var zoneIndex = availableZones[randomIndex];
self.targetX = [512, 1536, 512, 1536][zoneIndex];
self.targetY = [683, 683, 2049, 2049][zoneIndex];
}
}
// Shoot at player
if (self.shootCooldown > 0) {
self.shootCooldown--;
} else if (player) {
var pdx = player.x - self.x;
var pdy = player.y - self.y;
var pdist = Math.sqrt(pdx * pdx + pdy * pdy);
// Only attack when within 500 pixel proximity
if (pdist < 500) {
self.shoot(player.x, player.y);
}
}
};
self.shoot = function (targetX, targetY) {
// Only shoot if not reloading and magazine > 0
if (self.reloading) {
return;
}
if (self.magazine <= 0) {
// Start reload if not already
if (!self.reloading) {
self.reloading = true;
self.reloadTimeout = LK.setTimeout(function () {
self.magazine = self.maxMagazine;
self.reloading = false;
}, self.reloadTime);
}
return;
}
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
var dx = targetX - self.x;
var dy = targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
bullet.vx = dx / dist * bullet.speed;
bullet.vy = dy / dist * bullet.speed;
bullet.isEnemyBullet = true;
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 60;
self.magazine--;
if (self.magazine <= 0) {
// Start reload
self.reloading = true;
self.reloadTimeout = LK.setTimeout(function () {
self.magazine = self.maxMagazine;
self.reloading = false;
}, self.reloadTime);
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Stylized, highly visible gun: colored barrel, body, handle, and sight
// Gun barrel (long, thick, bright yellow)
var gunBarrelLength = 70;
var gunBarrel = self.attachAsset('zone', {
width: gunBarrelLength,
height: 18,
tint: 0xffe066,
anchorX: 0.1,
anchorY: 0.5,
alpha: 1
});
gunBarrel.x = 54;
gunBarrel.y = 0;
// Gun body (short, thick, black)
var gunBody = self.attachAsset('zone', {
width: 38,
height: 28,
tint: 0x222222,
anchorX: 0.1,
anchorY: 0.5,
alpha: 1
});
gunBody.x = 38;
gunBody.y = 0;
// Gun handle (angled, orange)
var gunHandle = self.attachAsset('zone', {
width: 18,
height: 36,
tint: 0xff9900,
anchorX: 0.1,
anchorY: 0.1,
alpha: 1
});
gunHandle.x = 38 + 20;
gunHandle.y = 16;
gunHandle.rotation = Math.PI / 2.1;
// Gun trigger guard (small, black ellipse)
var gunTrigger = self.attachAsset('bullet', {
width: 12,
height: 10,
color: 0x111111,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9
});
gunTrigger.x = 38 + 10;
gunTrigger.y = 13;
// Gun sight (small, red dot at barrel tip)
var gunSight = self.attachAsset('bullet', {
width: 10,
height: 10,
color: 0xff2222,
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
gunSight.x = gunBarrel.x + gunBarrelLength - 10;
gunSight.y = 0;
// Group all as the gun
self.gun = gunBarrel;
self.gunBody = gunBody;
self.gunHandle = gunHandle;
self.gunTrigger = gunTrigger;
self.gunSight = gunSight;
self.gunAngle = 0; // Track current gun angle
self.speed = 8;
self.shootCooldown = 0;
self.health = 12;
self.maxHealth = 12;
// Create health bar container
var healthBar = self.addChild(new Container());
healthBar.y = -playerGraphics.height / 2 - 50;
healthBar.visible = false;
// Health bar background
var healthBg = healthBar.attachAsset('zone', {
width: 260,
height: 28,
tint: 0x333333,
anchorX: 0.5,
anchorY: 0.5
});
// Health bar fill
var healthFill = healthBar.attachAsset('zone', {
width: 252,
height: 22,
tint: 0x00ff00,
anchorX: 0,
anchorY: 0.5
});
healthFill.x = -130;
self.healthBar = healthBar;
self.healthFill = healthFill;
self.healthBg = healthBg;
self.takeDamage = function () {
self.health--;
self.healthBar.visible = true;
// Update health bar width
self.healthFill.width = self.health / self.maxHealth * 252;
// Update health bar color based on health
if (self.health <= 3) {
self.healthFill.tint = 0xff0000;
} else if (self.health <= 6) {
self.healthFill.tint = 0xffaa00;
} else {
self.healthFill.tint = 0x00ff00;
}
// Flash effect
LK.effects.flashObject(self, 0xff0000, 300);
if (self.health <= 0) {
LK.showGameOver({
text: "Wasted"
});
}
};
// Override intersects to use a smaller hitbox for the player
self.intersects = function (other) {
// Use a smaller hitbox (60% of width/height, centered)
var scale = 0.6;
var px = self.x,
py = self.y;
var pw = playerGraphics.width * scale,
ph = playerGraphics.height * scale;
var pLeft = px - pw / 2,
pRight = px + pw / 2,
pTop = py - ph / 2,
pBottom = py + ph / 2;
// Get other's bounds (assume center anchor)
var ox = other.x,
oy = other.y;
var ow = typeof other.width !== "undefined" ? other.width : other.graphics && other.graphics.width ? other.graphics.width : 0;
var oh = typeof other.height !== "undefined" ? other.height : other.graphics && other.graphics.height ? other.graphics.height : 0;
if (ow === 0 && typeof other.children !== "undefined" && other.children.length > 0 && typeof other.children[0].width !== "undefined") {
ow = other.children[0].width;
oh = other.children[0].height;
}
var oLeft = ox - ow / 2,
oRight = ox + ow / 2,
oTop = oy - oh / 2,
oBottom = oy + oh / 2;
// AABB collision
return !(pRight < oLeft || pLeft > oRight || pBottom < oTop || pTop > oBottom);
};
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.shoot = function (targetX, targetY) {
// Magazine/cooldown logic
if (typeof playerMagazine !== "undefined" && typeof playerReloading !== "undefined") {
if (playerReloading) {
// Can't shoot while reloading
return;
}
if (playerMagazine <= 0) {
// Start reload if not already
if (!playerReloading) {
playerReloading = true;
// Track reload bar timing
if (typeof Date !== "undefined") {
reloadStartTime = Date.now();
reloadEndTime = reloadStartTime + playerReloadTime;
}
// Animate reload bar fill and bullet icons
if (typeof reloadBar !== "undefined") {
reloadBar.visible = true;
reloadFill.width = 0;
// Clear all bullet icons (hide them) when magazine is empty
for (var i = 0; i < reloadBar.bulletIcons.length; i++) {
reloadBar.bulletIcons[i].alpha = 0;
reloadBar.bulletIcons[i].color = 0x888888;
}
}
playerReloadTimeout = LK.setTimeout(function () {
playerMagazine = playerMaxMagazine;
playerReloading = false;
reloadStartTime = 0;
reloadEndTime = 0;
// On reload complete, show all bullets as filled
if (typeof reloadBar !== "undefined") {
for (var i = 0; i < reloadBar.bulletIcons.length; i++) {
reloadBar.bulletIcons[i].alpha = 1;
reloadBar.bulletIcons[i].color = 0xffff00;
}
reloadFill.width = 200;
}
}, playerReloadTime);
}
return;
}
}
if (self.shootCooldown <= 0) {
// Use selected bullet type
var type = bulletTypes[currentBulletType];
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
// Change bullet asset and speed
if (type.id !== 'bullet') {
bullet.removeChild(bullet.children[0]);
var bulletGraphics = bullet.attachAsset(type.id, {
anchorX: 0.5,
anchorY: 0.5
});
bullet.speed = type.speed;
} else {
bullet.speed = type.speed;
}
var dx = targetX - self.x;
var dy = targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
bullet.vx = dx / dist * bullet.speed;
bullet.vy = dy / dist * bullet.speed;
// Rotate the gun towards the shooting direction
if (self.gun) {
var angle = Math.atan2(dy, dx);
// If shooting to the left (targetX < self.x), teleport gun to left side
if (targetX < self.x) {
// Place gun on left side
self.gun.x = -54;
self.gunBody.x = -38;
self.gunHandle.x = -38 - 20;
self.gunTrigger.x = -38 - 10;
// Sight will be repositioned below
} else {
// Place gun on right side (default)
self.gun.x = 54;
self.gunBody.x = 38;
self.gunHandle.x = 38 + 20;
self.gunTrigger.x = 38 + 10;
// Sight will be repositioned below
}
self.gun.rotation = angle;
self.gunAngle = angle;
// Rotate the body and trigger to match the barrel
if (self.gunBody) {
self.gunBody.rotation = angle;
}
if (self.gunTrigger) {
self.gunTrigger.rotation = angle;
}
// Rotate the handle with a slight offset to look like a real gun
if (self.gunHandle) {
self.gunHandle.rotation = angle + Math.PI / 2.1;
}
// Rotate and reposition the sight to always be at the barrel tip
if (self.gunSight) {
self.gunSight.rotation = angle;
// Place at the tip of the barrel
var barrelLen = 70;
self.gunSight.x = self.gun.x + Math.cos(angle) * (barrelLen - 10);
self.gunSight.y = self.gun.y + Math.sin(angle) * (barrelLen - 10);
}
}
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 15;
// Decrement magazine
if (typeof playerMagazine !== "undefined") {
playerMagazine--;
// Immediately update reload bar bullet icons after shooting
if (typeof reloadBar !== "undefined" && reloadBar.bulletIcons) {
for (var i = 0; i < reloadBar.bulletIcons.length; i++) {
if (i < playerMagazine && !playerReloading) {
reloadBar.bulletIcons[i].alpha = 1;
reloadBar.bulletIcons[i].color = 0xffff00;
} else {
reloadBar.bulletIcons[i].alpha = 0.3;
reloadBar.bulletIcons[i].color = 0x888888;
}
}
}
if (playerMagazine <= 0) {
playerReloading = true;
playerReloadTimeout = LK.setTimeout(function () {
playerMagazine = playerMaxMagazine;
playerReloading = false;
}, playerReloadTime);
}
}
}
};
return self;
});
// Wall class for bullet-blocking cover
var Wall = Container.expand(function () {
var self = Container.call(this);
self.wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
self.isCapturedVisual = false;
// Method to change wall visual to captured
self.setCapturedVisual = function () {
if (!self.isCapturedVisual) {
self.removeChild(self.wallGraphics);
self.wallGraphics = self.attachAsset('wall_captured', {
anchorX: 0.5,
anchorY: 0.5
});
self.isCapturedVisual = true;
}
};
// No update needed, static
return self;
});
var Zone = Container.expand(function (zoneIndex) {
var self = Container.call(this);
var zoneAssetId = 'zone' + (zoneIndex + 1);
var zoneGraphics = self.attachAsset(zoneAssetId, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
// Increase brightness using tween color effect (tint to a lighter color)
tween(zoneGraphics, {
tint: 0xffffff
}, {
duration: 500
});
self.graphics = zoneGraphics;
self.captureProgress = 0;
self.captureTime = 180;
self.captured = false;
self.playerInside = false;
var progressBar = self.addChild(new Container());
progressBar.y = -zoneGraphics.height / 2 - 30;
var progressBg = progressBar.attachAsset('zone', {
width: 300,
height: 20,
tint: 0x333333,
anchorX: 0.5,
anchorY: 0.5
});
var progressFill = progressBar.attachAsset('zone', {
width: 0,
height: 16,
tint: 0x00ff00,
anchorX: 0,
anchorY: 0.5
});
progressFill.x = -148;
self.progressFill = progressFill;
progressBar.visible = false;
self.progressBar = progressBar;
self.update = function () {
if (self.playerInside && !self.captured) {
self.captureProgress++;
self.progressBar.visible = true;
self.progressFill.width = self.captureProgress / self.captureTime * 296;
if (self.captureProgress >= self.captureTime) {
self.captured = true;
self.graphics.tint = 0x00ff00;
self.graphics.alpha = 0.5;
self.progressBar.visible = false;
LK.getSound('capture').play();
capturedZones++;
updateScore();
// Change visual of only the nearest wall to this zone
var nearestWall = null;
var minDist = Infinity;
for (var w = 0; w < walls.length; w++) {
var wx = walls[w].x;
var wy = walls[w].y;
var dx = wx - self.x;
var dy = wy - self.y;
var distSq = dx * dx + dy * dy;
if (distSq < minDist) {
minDist = distSq;
nearestWall = walls[w];
}
}
if (nearestWall && typeof nearestWall.setCapturedVisual === "function") {
nearestWall.setCapturedVisual();
}
// If all zones are captured, set all walls to captured visual
if (capturedZones >= totalZones) {
for (var w = 0; w < walls.length; w++) {
if (typeof walls[w].setCapturedVisual === "function") {
walls[w].setCapturedVisual();
}
}
}
// No reload bar or magazine logic here; zone capture does not affect reload bar
}
} else if (!self.playerInside && self.captureProgress > 0 && !self.captured) {
self.captureProgress = Math.max(0, self.captureProgress - 2);
if (self.captureProgress === 0) {
self.progressBar.visible = false;
} else {
self.progressFill.width = self.captureProgress / self.captureTime * 296;
}
}
};
self.checkPlayerInside = function (player) {
var halfWidth = self.graphics.width / 2;
var halfHeight = self.graphics.height / 2;
var dx = Math.abs(player.x - self.x);
var dy = Math.abs(player.y - self.y);
self.playerInside = dx < halfWidth && dy < halfHeight;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Wall asset for small cover/wall (cephé)
var player;
var bullets = [];
var enemyBullets = [];
var enemies = [];
var zones = [];
var walls = []; // Array for wall objects
var capturedZones = 0;
var totalZones = 4;
var isDragging = false;
var lastShootX = 0;
var lastShootY = 0;
// Bullet type switching
var bulletTypes = [{
id: 'bullet',
color: 0xffff00,
speed: 15,
size: 20
},
// normal
{
id: 'bullet_red',
color: 0xff4444,
speed: 10,
size: 32
},
// slow, big
{
id: 'bullet_blue',
color: 0x44aaff,
speed: 22,
size: 14
} // fast, small
];
var currentBulletType = 0;
if (typeof storage.getItem === "function" && storage.getItem("currentBulletType") !== undefined && storage.getItem("currentBulletType") !== null) {
currentBulletType = parseInt(storage.getItem("currentBulletType"));
}
// Dynamically create bullet assets if not already present
for (var i = 1; i < bulletTypes.length; i++) {
if (!LK.getAsset(bulletTypes[i].id, {})) {}
}
// UI for bullet type
var bulletTypeTxt = new Text2('Bullet: Normal', {
size: 60,
fill: 0xffffff
});
bulletTypeTxt.anchor.set(0.5, 0);
bulletTypeTxt.y = 120;
bulletTypeTxt.x = 1024;
LK.gui.top.addChild(bulletTypeTxt);
// Button to switch bullet type (simple text button)
var switchBtn = new Text2('Change Bullet', {
size: 60,
fill: 0x00ffcc
});
switchBtn.anchor.set(0.5, 0);
switchBtn.y = 210;
switchBtn.x = 1024;
LK.gui.top.addChild(switchBtn);
// Touch/click event for switchBtn
switchBtn.down = function (x, y, obj) {
currentBulletType = (currentBulletType + 1) % bulletTypes.length;
storage.set("currentBulletType", currentBulletType);
var names = ['Normal', 'Big', 'Fast'];
bulletTypeTxt.setText('Bullet: ' + names[currentBulletType]);
};
var scoreTxt = new Text2('Zones: 0/4', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
function updateScore() {
scoreTxt.setText('Zones: ' + capturedZones + '/' + totalZones);
if (capturedZones >= totalZones) {
LK.showYouWin({
text: "Mission Passed"
});
}
;
// Update reload bar position and progress, animate fill and update bullet icons
if (typeof reloadBar !== "undefined" && typeof player !== "undefined") {
reloadBar.x = player.x;
reloadBar.y = player.y - 170;
// Update bullet icons: show filled for each bullet in magazine, empty for spent
if (typeof playerMagazine !== "undefined" && reloadBar.bulletIcons) {
for (var i = 0; i < reloadBar.bulletIcons.length; i++) {
if (i < playerMagazine && !playerReloading) {
reloadBar.bulletIcons[i].alpha = 1;
reloadBar.bulletIcons[i].color = 0xffff00;
} else {
reloadBar.bulletIcons[i].alpha = 0.3;
reloadBar.bulletIcons[i].color = 0x888888;
}
}
}
if (typeof playerReloading !== "undefined" && playerReloading) {
reloadBar.visible = true;
// Animate fill: (playerReloadTime - remaining) / playerReloadTime
if (typeof reloadStartTime !== "undefined" && typeof reloadEndTime !== "undefined" && reloadStartTime > 0 && reloadEndTime > reloadStartTime) {
var now = Date.now();
var progress = Math.min(1, Math.max(0, (now - reloadStartTime) / (reloadEndTime - reloadStartTime)));
reloadFill.width = 200 * progress;
// Animate bullet icons: fill them as reload progresses
var bulletsToShow = Math.floor(progress * 10);
for (var i = 0; i < reloadBar.bulletIcons.length; i++) {
if (i < bulletsToShow) {
reloadBar.bulletIcons[i].alpha = 1;
reloadBar.bulletIcons[i].color = 0xffff00;
} else {
reloadBar.bulletIcons[i].alpha = 0.3;
reloadBar.bulletIcons[i].color = 0x888888;
}
}
} else {
reloadFill.width = 0;
}
} else if (typeof playerReloading !== "undefined" && !playerReloading) {
reloadBar.visible = true;
reloadFill.width = 200;
}
}
}
;
// Magazine and cooldown for player shooting
var playerMagazine = 10;
var playerMaxMagazine = 10;
var playerReloading = false;
var playerReloadTimeout = null;
var playerReloadTime = 1500; // ms
// Track reload progress for bar
var reloadStartTime = 0;
var reloadEndTime = 0;
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Reload progress bar UI above player, with 7 bullet icons and animated fill
var reloadBar = new Container();
// Background bar
var reloadBg = reloadBar.attachAsset('reloadBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
// Animated fill (for reload progress)
var reloadFill = reloadBar.attachAsset('reloadBarFill', {
width: 0,
anchorX: 0,
anchorY: 0.5
});
reloadFill.x = -100;
reloadFill.y = 0;
// 10 bullet icons (small ellipses) spaced evenly on the bar
reloadBar.bulletIcons = [];
var bulletIconSpacing = 20;
var bulletIconStartX = -90;
for (var i = 0; i < 10; i++) {
var icon = reloadBar.attachAsset('bullet', {
width: 18,
height: 18,
anchorX: 0.5,
anchorY: 0.5,
color: 0xffff00
});
icon.x = bulletIconStartX + i * bulletIconSpacing;
icon.y = 0;
reloadBar.bulletIcons.push(icon);
}
reloadBar.visible = true;
game.addChild(reloadBar);
var zonePositions = [{
x: 512,
y: 683
}, {
x: 1536,
y: 683
}, {
x: 512,
y: 2049
}, {
x: 1536,
y: 2049
}];
for (var i = 0; i < zonePositions.length; i++) {
var zone = new Zone(i);
zone.x = zonePositions[i].x;
zone.y = zonePositions[i].y;
zones.push(zone);
game.addChild(zone);
}
// Place walls (cephé) at strategic locations
var wallPositions = [{
x: 1024,
y: 500,
rotation: 0
},
// Top center
{
x: 1024,
y: 2200,
rotation: 0
},
// Bottom center
{
x: 400,
y: 1366,
rotation: Math.PI / 2
},
// Left center
{
x: 1648,
y: 1366,
rotation: Math.PI / 2
},
// Right center
{
x: 800,
y: 900,
rotation: 0.3
},
// Near zone 1
{
x: 1300,
y: 1800,
rotation: -0.3
} // Near zone 4
];
for (var i = 0; i < wallPositions.length; i++) {
var wall = new Wall();
wall.x = wallPositions[i].x;
wall.y = wallPositions[i].y;
if (wallPositions[i].rotation) wall.rotation = wallPositions[i].rotation;
walls.push(wall);
game.addChild(wall);
}
// Spawn enemies at zone positions
for (var i = 0; i < 3; i++) {
var enemy = new Enemy();
var zoneIndex = i % zonePositions.length;
enemy.x = zonePositions[zoneIndex].x;
enemy.y = zonePositions[zoneIndex].y;
enemies.push(enemy);
game.addChild(enemy);
}
game.down = function (x, y, obj) {
isDragging = true;
lastShootX = x;
lastShootY = y;
};
game.move = function (x, y, obj) {
if (isDragging) {
// Calculate drag direction from last position
var dx = x - lastShootX;
var dy = y - lastShootY;
var dist = Math.sqrt(dx * dx + dy * dy);
// Move player in the direction of the drag, but slower
if (dist > 0) {
var moveScale = 0.5; // Increase speed to 50% of drag
// Store previous position
var prevX = player.x;
var prevY = player.y;
player.x += dx * moveScale;
player.y += dy * moveScale;
player.x = Math.max(30, Math.min(2018, player.x));
player.y = Math.max(30, Math.min(2702, player.y));
// Check collision with walls, revert if colliding
for (var w = 0; w < walls.length; w++) {
if (player.intersects(walls[w])) {
player.x = prevX;
player.y = prevY;
break;
}
}
}
lastShootX = x;
lastShootY = y;
}
};
game.up = function (x, y, obj) {
if (isDragging) {
player.shoot(x, y);
isDragging = false;
}
};
game.update = function () {
// Update reload bar position to follow player
if (typeof reloadBar !== "undefined" && typeof player !== "undefined") {
reloadBar.x = player.x;
reloadBar.y = player.y - 170;
}
// Update zones
for (var i = zones.length - 1; i >= 0; i--) {
zones[i].checkPlayerInside(player);
}
// Update player bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Block by wall
var hitWall = false;
for (var w = 0; w < walls.length; w++) {
if (bullet.intersects(walls[w])) {
hitWall = true;
break;
}
}
if (hitWall || bullet.lifetime <= 0 || bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
if (bullet.intersects(enemies[j])) {
bullet.destroy();
bullets.splice(i, 1);
// Mini explosion effect at enemy position
var explosion = new Container();
var expAsset = explosion.attachAsset('bullet', {
width: 80,
height: 80,
color: 0xffaa00,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
explosion.x = enemies[j].x;
explosion.y = enemies[j].y;
game.addChild(explosion);
// Animate explosion: scale up and fade out, then destroy
tween(expAsset, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 350,
onComplete: function onComplete() {
explosion.destroy();
}
});
enemies[j].destroy();
enemies.splice(j, 1);
// Spawn new enemy at a random non-captured zone position
// Recalculate availableZones at spawn time to ensure accuracy
if (zones.filter(function (z) {
return !z.captured;
}).length > 0) {
// Delay enemy spawn by 2 seconds (2000ms)
LK.setTimeout(function () {
var availableZones = [];
for (var k = 0; k < zones.length; k++) {
if (!zones[k].captured) {
availableZones.push(k);
}
}
if (availableZones.length > 0) {
var newEnemy = new Enemy();
var randomIndex = Math.floor(Math.random() * availableZones.length);
var zoneIndex = availableZones[randomIndex];
newEnemy.x = zonePositions[zoneIndex].x;
newEnemy.y = zonePositions[zoneIndex].y;
enemies.push(newEnemy);
game.addChild(newEnemy);
}
}, 2000);
}
break;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Block by wall
var hitWall = false;
for (var w = 0; w < walls.length; w++) {
if (bullet.intersects(walls[w])) {
hitWall = true;
break;
}
}
if (hitWall || bullet.lifetime <= 0 || bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
bullet.destroy();
enemyBullets.splice(i, 1);
player.takeDamage();
}
// Example: Restart the game if you want to force a full reset (uncomment to use)
// LK.showGameOver();
}
};
Gangside ghetto Neighbourhood. In-Game asset. 2d. High contrast. No shadows
Tuğladan duvar üzerinde ballas grafitisi var,west side tarzında. In-Game asset. 2d. High contrast. No shadows
Tuğla duvar duvarda CJ's Gang yazacak. In-Game asset. 2d. High contrast. No shadows
CJ. In-Game asset. 2d. High contrast. No shadows
Ballas üyesi gang. In-Game asset. 2d. High contrast. No shadows