User prompt
chasing bulletlara karşı oyunu oynamayı biraz daha imkanlı hale getir şuan ekranda bir anda birsürü chasing bullet oluşuyor chasing bullet modu denk geldiğinde ve hepsi bizi takip ettiği için oyunda elenmemek imkansız oluyor
User prompt
chasing bulletslar lütfen her saniye 1 tane olacak şekilde oluşsunlar çok fazla aniden birikince oyuncu deneyimini kötü etkiliyor
User prompt
endles run modundaki mermilerin bütün hepsi en başta yavaş atılsın hangi mod olursa olsun ve modların hızları skor ile bağlantılı olsun mesela skorumuz 30 olduysa gelen her mermi %3 daha hızlı olsun 36 ise %3.6 daha hızlı olsun gelen mermiler falan ama en başta gerçekten yavaş olsun bütün mermiler
User prompt
vertical bullets modunda daha 15 puan geçmişti yine etraftan chasing bulletslar gelmeye başladı
User prompt
ekranda side bullets yazdı ama 14-15 skor sonra bir anda chasing bullet geldi oyuna böyle şeyler yaşanmasın karışık gelmesin mermiler ekranda ne yazdıysa o gelsin 25 mermi boyunca
User prompt
endless run modu için bir mermi türü listesi oluştur örneğin yukarıdan gelen mermiler, sağlı sollu gelen mermiler, takip eden mermiler, konuma özel mermiler gibi ve başlangıçta bunlardan birinin rastgele seçilmesiyle oyunun başlamasını sağla ve oyun modunun ekranda adının yazmasını sağla 3 saniye ekranda yazsın ve ardından her mermi oluşturulduğunda oyunda oyun skoru 1 artsın ve skor her 25 arttığında oyun modu değişsin rastgele başka bir oyun oyun moduna denk gelsin ve böyle böyle her 25 skorda oyun modu değişe değişe oyuncuya sonsuza kadar giden bir deneyim sunulsun.
User prompt
endles run modundaki seviyelerin hepsini kaldır seviye mantığını kaldır
User prompt
sonsuz döngüde olmasını istemiştim fakat 3. döngüde oyunda ekranda herhangi bir şey çıkmamaya ama puan yinede artmaya başladı ekranda faster bullets yazmıştı
User prompt
hala her 25 puanda bir oyun modu değişmiyor ekranda herhangi bir şekilde değişen oyun modunun adı yazmıyor veya o oyuna moduna ait bir mermi ateşlenmiyor ama sadece skor artıyor fakat skor ateşlenen mermiyle aynı sayıda olmalı oyunda yani ya mermi bir yerde ateşleniyor ama biz göremiyoruz ya da bu skor sistemi bozuk
User prompt
şuanda endles run modunda 25. puandan sonra rastgele bir oyun modu seçilmiyor ve mermiler devam etmiyor lütfen oyunda her 25 mermide modun değişmesini ve o moda ait mermilerin ateşlenmesini sağla
User prompt
endless run modunda her 25 puanda rastgele oyun modu seçilmesini sağla ve o mermilerin varolmasını ve skorun oluşturulan mermi sayısına bağlı olarak artmasını sağla
User prompt
şuanda endless run modunda sürekli oyun modu kendi kendine değişiyor
User prompt
hala oyun modu değiştiğinde o oyun modunun mermileri spawn olmuyor
User prompt
endless run modunda ilk rastgele seçimde mermiler her modta düzgün geliyor fakat ikinci rastgele seçimde ekrana herhangi bir mermi gelmeden puan falan artıyor hangi mod seçilirse seçilsin ekrana mermi gelmiyor bunu düzeltebilir misin
User prompt
oyunda endless runner modunda genel olarak ikinci moda geçildiğinde mermiler gelmeden puan artışı durumu var bunu düzeltebilir misin
User prompt
edge to player bullets modundayken mermiler biraz daha yavaş ilerlesin
User prompt
endless runner modunda şuan top down moduna geçildiğinde ekrana herhangi bir mermi gelmiyor ve skor artmaya devam ediyor bunu düzelt
User prompt
endless run modunda top down modunda gelen mermilerin ucu yanlış yöne bakıyor lütfen aşağı bakacak şekilde düzelt
User prompt
ekranda hala eski oyun modundan kalma difficulty level vb. şeylerin yazısı çıkıyor sana kaldır demiştim
User prompt
endless runner modunda sadece her 25 skorda bir rastgele oyun modu seçilsin ve seçilen oyun modu ekranda 3 saniyeliğine yazsın ve skor da atılan mermi olarak hesaplansın başka bir endless runner kodu varsa onları kaldırabilirsin
User prompt
top down bullets oyun modu da çalışmıyor
User prompt
edge to player bullets modu çalışmıyor
User prompt
endless run modundayken rastgele hangi oyun modu geldiyse o oyun modunun 2-3 saniye ekranda yazmasını sağla ve her oyun modu değiştiğinde bu yaşansın
User prompt
endless runner modundayken rastgele oyun modları sonsuz döngüde gelsin ve skor puanı sadece oyunda bir mermi yaratıldığında artsın
User prompt
endless run modundaki chasing bulletları ivmeli yap karakterimiz ani haraketler yaparsa mermi ivmeyi hemen alamasın.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
chapter: 1,
endlessHighScore: 0
});
/****
* Classes
****/
// Takipçi ışık mermisi (ChasingLightBullet) sınıfı
var ChasingLightBullet = Container.expand(function () {
var self = Container.call(this);
// Mermi grafiği
var bulletAsset = self.attachAsset('lightBullet', {
anchorX: 0.5,
anchorY: 0.5
});
// Hız vektörü ve ivmeli takip için velocity/acceleration
self.speed = 10 + Math.random() * 3;
self.velX = 0;
self.velY = 0;
self.maxSpeed = self.speed;
self.accel = 0.45 + Math.random() * 0.12; // ivme (düşük olursa daha yavaş döner)
// Çarpışma kontrolü için son intersect durumu
self.lastIntersecting = false;
// Mermi boyutu
self.width = bulletAsset.width;
self.height = bulletAsset.height;
// Parlama animasyonu için tween
var pulseTween = null;
// Parlama animasyonu başlat
function startPulse() {
if (pulseTween) {
tween.stop(bulletAsset, {
alpha: true
});
pulseTween = null;
}
bulletAsset.alpha = 0.5 + Math.random() * 0.2;
// Tween ile alpha'yı 1'e çıkar, sonra tekrar 0.5-0.7 arası değere geri döndür, sonsuz döngü
function pulseUp() {
pulseTween = tween(bulletAsset, {
alpha: 1
}, {
duration: 350,
easing: tween.easeInOut,
onFinish: function onFinish() {
var min = 0.5,
max = 0.7;
var target = min + Math.random() * (max - min);
pulseTween = tween(bulletAsset, {
alpha: target
}, {
duration: 350,
easing: tween.easeInOut,
onFinish: pulseUp
});
}
});
}
pulseUp();
}
startPulse();
// Güncelleme fonksiyonu
self.update = function () {
// Son pozisyonları güncelle
if (self.lastX === undefined) {
self.lastX = self.x;
}
if (self.lastY === undefined) {
self.lastY = self.y;
}
// --- Bullet trail effect (effectwhite) ---
if (self._destroyed !== true) {
var trail = LK.getAsset('effectwhite', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: 0.25,
scaleY: 0.25,
alpha: 0.18
});
if (self.parent && typeof self.parent.addChild === "function") {
self.parent.addChild(trail);
} else if (typeof game !== "undefined" && typeof game.addChild === "function") {
game.addChild(trail);
}
tween(trail, {
alpha: 0
}, {
duration: 320,
onFinish: function onFinish() {
if (trail && typeof trail.destroy === "function") {
trail.destroy();
}
}
});
}
// Hedefe doğru ivmeli yönel
if (typeof shadow !== "undefined") {
// Hedef vektörü
var dx = shadow.x - self.x;
var dy = shadow.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0.01) {
// Normalleştir
var ndx = dx / dist;
var ndy = dy / dist;
// Hedef hız vektörü
var targetVX = ndx * self.maxSpeed;
var targetVY = ndy * self.maxSpeed;
// İvme ile mevcut velocity'yi hedef velocity'ye yaklaştır
self.velX += (targetVX - self.velX) * self.accel * 0.12;
self.velY += (targetVY - self.velY) * self.accel * 0.12;
// Toplam hız limiti (maxSpeed)
var vlen = Math.sqrt(self.velX * self.velX + self.velY * self.velY);
if (vlen > self.maxSpeed) {
self.velX = self.velX / vlen * self.maxSpeed;
self.velY = self.velY / vlen * self.maxSpeed;
}
// Pozisyonu güncelle
self.x += self.velX;
self.y += self.velY;
// Merminin ucunu velocity yönüne döndür
self.rotation = Math.atan2(self.velY, self.velX) + Math.PI / 2;
}
}
self.lastX = self.x;
self.lastY = self.y;
};
// Ekran dışına çıktı mı?
self.isOutOfBounds = function () {
return self.x < -self.width || self.x > 2048 + self.width || self.y < -self.height || self.y > 2732 + self.height;
};
// Yok edilirken animasyonu durdur
var _destroy = self.destroy;
self.destroy = function () {
if (pulseTween) {
pulseTween.stop();
}
_destroy.call(self);
};
return self;
});
// Işık mermisi (LightBullet) sınıfı
var LightBullet = Container.expand(function () {
var self = Container.call(this);
// Mermi grafiği
var bulletAsset = self.attachAsset('lightBullet', {
anchorX: 0.5,
anchorY: 0.5
});
// Hız vektörü
self.speedX = 0;
self.speedY = 0;
// Çarpışma kontrolü için son intersect durumu
self.lastIntersecting = false;
// Mermi boyutu
self.width = bulletAsset.width;
self.height = bulletAsset.height;
// Mermi güncelleme fonksiyonu
self.update = function () {
// Son pozisyonları güncelle
if (self.lastX === undefined) {
self.lastX = self.x;
}
if (self.lastY === undefined) {
self.lastY = self.y;
}
// --- Bullet trail effect (effectwhite) ---
if (self._destroyed !== true) {
// Her frame bir iz bırak (isteğe göre seyrekleştirilebilir)
var trail = LK.getAsset('effectwhite', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: 0.25,
scaleY: 0.25,
alpha: 0.18
});
// Trail'i ana sahneye ekle (self.parent varsa oraya ekle, yoksa game'e ekle)
if (self.parent && typeof self.parent.addChild === "function") {
self.parent.addChild(trail);
} else if (typeof game !== "undefined" && typeof game.addChild === "function") {
game.addChild(trail);
}
// Trail'i fade out ile yok et
tween(trail, {
alpha: 0
}, {
duration: 320,
onFinish: function onFinish() {
if (trail && typeof trail.destroy === "function") {
trail.destroy();
}
}
});
}
self.x += self.speedX;
self.y += self.speedY;
self.lastX = self.x;
self.lastY = self.y;
};
// Ekran dışına çıktı mı?
self.isOutOfBounds = function () {
return self.x < -self.width || self.x > 2048 + self.width || self.y < -self.height || self.y > 2732 + self.height;
};
return self;
});
// Gölge karakter (Shadow) sınıfı - Gerçekçi gölge efekti için yumuşak, bulanık elipsler ve dinamik hareket
var Shadow = Container.expand(function () {
var self = Container.call(this);
// Gölge için 3 büyük eliptik, yarı saydam, üst üste bindirilmiş layer
var ellipseLayers = [];
var ellipseParams = [
// scaleX, scaleY, alpha, blur
{
scaleX: 1.0,
scaleY: 0.7,
alpha: 0.22
}, {
scaleX: 0.8,
scaleY: 0.5,
alpha: 0.16
}, {
scaleX: 0.6,
scaleY: 0.35,
alpha: 0.10
}];
var baseSizeW = 160;
var baseSizeH = 220;
var baseColor = 0x222222;
for (var i = 0; i < ellipseParams.length; i++) {
var p = ellipseParams[i];
var ell = LK.getAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: p.scaleX,
scaleY: p.scaleY,
alpha: p.alpha
});
ell.x = 0;
ell.y = 0;
self.addChild(ell);
ellipseLayers.push(ell);
}
// Hafif yanal ve dikey dalgalanma için parametreler
var animTick = Math.floor(Math.random() * 1000);
var waveSpeed = 0.018;
var waveRadiusX = 18;
var waveRadiusY = 10;
// Gölgeye yumuşak bir "blur" efekti için üst üste bindirilmiş küçük gölge kutuları (soft edge)
var softEdgeBoxes = [];
var softBoxCount = 8;
var softBoxRadius = 70;
for (var i = 0; i < softBoxCount; i++) {
var angle = Math.PI * 2 / softBoxCount * i;
var box = LK.getAsset('shadowbox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.45 + Math.random() * 0.12,
scaleY: 0.45 + Math.random() * 0.12,
alpha: 0.08 + Math.random() * 0.06
});
// Başlangıçta merkezde
box.x = Math.cos(angle) * softBoxRadius;
box.y = Math.sin(angle) * softBoxRadius * 0.7;
self.addChild(box);
softEdgeBoxes.push({
box: box,
baseAngle: angle
});
}
// Gözler: shadowredeye assetini ekle (en üstte)
var eyeAsset = LK.getAsset('shadowredeye', {
anchorX: 0.5,
anchorY: 0.5
});
eyeAsset.x = 0;
eyeAsset.y = -baseSizeH * 0.18;
self.addChild(eyeAsset);
// Yarıçapı döndür (kenar çarpışma için)
self.getRadius = function () {
// En büyük elipsin genişliğinin yarısı
return baseSizeW * 0.5;
};
// --- Realistic shadow trail effect ---
self._shadowTrailTick = 0;
self._shadowTrailInterval = 2 + Math.floor(Math.random() * 2); // every 2-3 frames
self._lastTrailX = undefined;
self._lastTrailY = undefined;
self.update = function () {
animTick++;
// Elips katmanlarını hafifçe dalgalandır
for (var i = 0; i < ellipseLayers.length; i++) {
var ell = ellipseLayers[i];
var phase = animTick * waveSpeed + i * 0.7;
ell.x = Math.cos(phase) * (waveRadiusX * (1 - i * 0.3));
ell.y = Math.sin(phase * 0.8) * (waveRadiusY * (1 - i * 0.3));
// Hafifçe scale animasyonu
var scalePulse = 1 + Math.sin(phase * 1.2) * 0.04;
ell.scaleX = ellipseParams[i].scaleX * scalePulse;
ell.scaleY = ellipseParams[i].scaleY * scalePulse;
// Hafif alpha dalgalanması
ell.alpha = ellipseParams[i].alpha + Math.abs(Math.sin(phase * 0.7)) * 0.04;
}
// Soft edge kutuları yumuşakça döndür ve titreştir
for (var i = 0; i < softEdgeBoxes.length; i++) {
var obj = softEdgeBoxes[i];
var box = obj.box;
var baseAngle = obj.baseAngle;
var phase = animTick * 0.012 + baseAngle * 1.2;
var r = softBoxRadius + Math.sin(phase * 1.1 + i) * 8;
box.x = Math.cos(baseAngle + Math.sin(phase) * 0.18) * r;
box.y = Math.sin(baseAngle + Math.cos(phase) * 0.18) * r * 0.7;
// Hafifçe scale ve alpha animasyonu
var scalePulse = 1 + Math.sin(phase * 1.5) * 0.09;
box.scaleX = (0.45 + i * 0.01) * scalePulse;
box.scaleY = (0.45 + i * 0.01) * scalePulse;
box.alpha = 0.08 + Math.abs(Math.sin(phase * 0.7 + i)) * 0.07;
}
// Gözler kutunun merkezine sabitlenir
eyeAsset.x = 0;
eyeAsset.y = -baseSizeH * 0.18;
// --- Realistic shadow trail effect ---
self._shadowTrailTick++;
// Only create trail if moved enough (avoid stacking on same spot)
var minTrailDist = 12;
if (self._lastTrailX === undefined) {
self._lastTrailX = self.x;
}
if (self._lastTrailY === undefined) {
self._lastTrailY = self.y;
}
var dx = self.x - self._lastTrailX;
var dy = self.y - self._lastTrailY;
var dist = Math.sqrt(dx * dx + dy * dy);
if (self._shadowTrailTick % self._shadowTrailInterval === 0 && dist > minTrailDist * 0.5) {
// Trail asset: use shadow asset, blurred and faded
var tScale = 0.85 + Math.random() * 0.18;
var tAlpha = 0.13 + Math.random() * 0.07;
var tSkew = (Math.random() - 0.5) * 0.18;
var tY = self.y + (Math.random() - 0.5) * 12;
var tX = self.x + (Math.random() - 0.5) * 12;
var trail = LK.getAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5,
x: tX,
y: tY,
scaleX: tScale * (1 + tSkew),
scaleY: tScale * (0.7 + tSkew * 0.5),
alpha: tAlpha
});
// Trail should be behind the shadow, so add to parent if possible
if (self.parent && typeof self.parent.addChild === "function") {
self.parent.addChild(trail);
// Move trail behind shadow
if (typeof self.parent.children !== "undefined" && self.parent.children.length > 1) {
// Move trail just before shadow in display list
var idx = self.parent.children.indexOf(self);
if (idx > 0) {
// Remove and insert at idx-1
self.parent.children.splice(self.parent.children.length - 1, 1); // remove last (trail)
self.parent.children.splice(idx - 1, 0, trail);
}
}
} else if (typeof game !== "undefined" && typeof game.addChild === "function") {
game.addChild(trail);
}
// Fade out and destroy
tween(trail, {
alpha: 0
}, {
duration: 420 + Math.random() * 180,
onFinish: function onFinish() {
if (trail && typeof trail.destroy === "function") {
trail.destroy();
}
}
});
self._lastTrailX = self.x;
self._lastTrailY = self.y;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181a1b
});
/****
* Game Code
****/
// Oyun alanı boyutları
var GAME_W = 2048;
var GAME_H = 2732;
// Storage eklentisini başlat
// Menü ekranı için değişkenler
var menuContainer = null;
var menuButtons = [];
var menuActive = true;
var currentMode = null; // "chapter" veya "endless"
// Oyun içi değişkenler
var shadow = null;
var bullets = [];
var chasingBullets = [];
var score = 0;
var scoreTxt = null;
var dragNode = null;
var isGameActive = false;
var controlPanel = null;
var joystick = null;
var joystickRadius = 180 * 2;
var joystickKnobRadius = 60 * 1.2;
var isPanelActive = false;
var panelStartX = 0;
var panelStartY = 0;
var knobStartX = 0;
var knobStartY = 0;
var bulletInterval = 700;
var bulletTimer = null;
var chasingBulletInterval = 1200;
var chasingBulletTimer = null;
// Menü ekranı oluşturma fonksiyonu
function showMenu() {
menuActive = true;
isGameActive = false;
// Menü zaten varsa kaldır
if (menuContainer) {
menuContainer.destroy();
menuContainer = null;
menuButtons = [];
}
menuContainer = new Container();
// --- Main menu background (fills screen) ---
var bg = LK.getAsset('mainmenubackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: GAME_W,
height: GAME_H
});
menuContainer.addChild(bg);
// --- Animated white bar (mainmenuwhite) ---
var whiteBar = LK.getAsset('mainmenuwhite', {
anchorX: 0.5,
anchorY: 0,
x: GAME_W / 2,
y: 0,
width: GAME_W * 0.9,
height: 220
});
menuContainer.addChild(whiteBar);
// Animate whiteBar up and down forever
function animateWhiteBarDown() {
tween(whiteBar, {
y: GAME_H - whiteBar.height
}, {
duration: 2200,
easing: tween.easeInOut,
onFinish: animateWhiteBarUp
});
}
function animateWhiteBarUp() {
tween(whiteBar, {
y: 0
}, {
duration: 2200,
easing: tween.easeInOut,
onFinish: animateWhiteBarDown
});
}
animateWhiteBarDown();
// --- Logo (original scale, moved 400px down) ---
var logo = LK.getAsset('agentshadowlogo', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_W / 2,
y: 800,
scaleX: 1,
scaleY: 1
});
menuContainer.addChild(logo);
// Butonlar arası dikey boşluk
var btnSpacing = 80;
var btnW = 600;
var btnH = 180;
var startY = 1300; // 900 + 400
// Buton isimleri ve fonksiyonları
var btnDefs = [{
label: "Story Mode",
onClick: function onClick() {
currentMode = "chapter";
menuActive = false;
menuContainer.visible = false;
startGameFromChapter();
}
}, {
label: "Endless Run",
onClick: function onClick() {
currentMode = "endless";
menuActive = false;
menuContainer.visible = false;
startEndlessRun();
}
}, {
label: "Reset Story",
onClick: function onClick() {
storage.chapter = 1;
storage.section = 1;
// Geri bildirim için kısa bir animasyon veya renk değişimi yapılabilir
if (menuButtons[2] && menuButtons[2].bg) {
LK.effects.flashObject(menuButtons[2].bg, 0xff0000, 300);
}
}
}];
// Butonları oluştur
for (var i = 0; i < btnDefs.length; i++) {
var btnY = startY + i * (btnH + btnSpacing);
// Buton arka planı
var btnBg = LK.getAsset('blackbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_W / 2,
y: btnY,
width: btnW,
height: btnH
});
// Buton metni
var btnText = new Text2(btnDefs[i].label, {
size: 80,
fill: "#fff"
});
btnText.anchor.set(0.5, 0.5);
btnText.x = GAME_W / 2;
btnText.y = btnY;
// Buton Container'ı
var btnContainer = new Container();
btnContainer.bg = btnBg;
btnContainer.addChild(btnBg);
btnContainer.addChild(btnText);
// Story Mode artık kilitli değil, lock icon eklenmiyor
// Buton tıklama alanı
btnContainer.interactive = true;
btnContainer.buttonMode = true;
// Basit dokunma olayı
(function (def, btn) {
btnContainer.down = function (x, y, obj) {
if (!menuActive) {
return;
}
def.onClick();
};
})(btnDefs[i], btnContainer);
menuContainer.addChild(btnContainer);
menuButtons.push(btnContainer);
}
// Menü sahneye ekle
game.addChild(menuContainer);
}
// Menüden chapter modunda oyunu başlat
function startGameFromChapter() {
// Temizle
clearGameObjects();
// Son kaydedilen chapter ve bölüm bilgisini storage'dan al
var chapter = storage.chapter || 1;
var section = storage.section || 1;
// Karakteri oluştur ve ortala
shadow = new Shadow();
shadow.x = GAME_W / 2;
shadow.y = GAME_H * 0.8;
game.addChild(shadow);
// Skor (chapter modunda skor kullanılmaz, sadece kalan mermi sayısı gösterilir)
score = 0;
if (scoreTxt) {
scoreTxt.destroy();
}
// Chapter 1 bullet sayacı (skor yerine) -- bu skor metni yeni sistemde de güncelleniyor
var chapter1BulletCount = 50;
scoreTxt = new Text2(chapter1BulletCount + "", {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.visible = false; // Geri sayım bitene kadar görünmez
LK.gui.top.addChild(scoreTxt);
isGameActive = false; // Oyun başlatılmadan önce false
// --- YENİ CHAPTER 1: 5 aşamalı sistem ile başlat ---
// Bölüm ilerlemesine göre başlat
if (chapter === 1 && section === 2) {
startChapter1_2Bullets();
} else {
// Varsayılan: chapter 1 bölüm 1'den başlat
startChapter1Bullets();
}
}
// Diyalog ekranı fonksiyonu (geri getirildi)
function showDialogue(text, onClose) {
// Önce eski diyalog varsa kaldır
if (game._dialogueContainer) {
game._dialogueContainer.destroy();
game._dialogueContainer = null;
}
var dialogueContainer = new Container();
// Arka plan
var bg = LK.getAsset('dialoguebackground', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_W / 2,
y: GAME_H / 2,
width: 1500,
height: 750
});
dialogueContainer.addChild(bg);
// Sol üstte ??? etiketi
var whoTxt = new Text2("???", {
size: 110,
fill: "#222"
});
whoTxt.anchor.set(0, 0);
// 50 piksel sağa kaydır
whoTxt.x = Math.max((GAME_W - 1500) / 2 + 60 + 50, 40);
whoTxt.y = Math.max((GAME_H - 750) / 2 + 40, 40);
dialogueContainer.addChild(whoTxt);
// Sağda walkie talkie (daha büyük ve 300px sağa kaydır)
var walkie = LK.getAsset('walkietalkie', {
anchorX: 1,
anchorY: 1,
x: (GAME_W + 1500) / 2 - 60 + 300,
y: (GAME_H + 750) / 2 - 60,
scaleX: 1.15,
scaleY: 1.15
});
dialogueContainer.addChild(walkie);
// Kullanıcı manuel olarak satırları ayırabilsin: Eğer text bir dizi ise her eleman bir satır olarak alınır.
// Eğer string ise, tek satır olarak gösterilir.
var lines = [];
var dialogueFontSize = 54;
if (typeof text === "string") {
lines = [text];
} else if (Array.isArray(text)) {
lines = text.slice(0);
}
var textNodes = [];
// Diyalog kutusunun üst kısmından biraz aşağıda başlasın, satır aralığı daha rahat
var baseY = GAME_H / 2 - 750 / 2 + 170;
var lineSpacing = 70;
// Tüm satırları aynı anda göster, her biri kendi satırında
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
var txt = new Text2(line, {
size: dialogueFontSize,
fill: "#222"
});
// Sola yaslı: anchorX = 0, anchorY = 0
txt.anchor.set(0, 0);
// Diyalog kutusunun sol kenarına hizala (arka planın solundan biraz içeride başlasın)
txt.x = (GAME_W - 1500) / 2 + 80;
txt.y = baseY + i * lineSpacing;
dialogueContainer.addChild(txt);
textNodes.push(txt);
}
// Altta click to continue...
var ctc = new Text2("click to continue...", {
size: 70,
fill: "#222"
});
ctc.anchor.set(0.5, 1);
ctc.x = GAME_W / 2;
ctc.y = (GAME_H + 750) / 2 - 60;
ctc.alpha = 0.3;
dialogueContainer.addChild(ctc);
// Alpha animasyonu (0.3 <-> 1.0)
function pulseUp() {
tween(ctc, {
alpha: 1.0
}, {
duration: 600,
onFinish: pulseDown
});
}
function pulseDown() {
tween(ctc, {
alpha: 0.3
}, {
duration: 600,
onFinish: pulseUp
});
}
pulseUp();
// Kapatma için dokunma
dialogueContainer.interactive = true;
dialogueContainer.down = function (x, y, obj) {
if (game._dialogueContainer) {
game._dialogueContainer.destroy();
game._dialogueContainer = null;
}
if (typeof onClose === "function") {
onClose();
}
};
game.addChild(dialogueContainer);
game._dialogueContainer = dialogueContainer;
}
// Chapter 1'in ilk aşamasını başlatan fonksiyon (50 mermilik, her 5 mermide bir hızlanan bölüm)
function startChapter1Bullets() {
// Diyalog ekranı göster
showDialogue(["Agent Shadow, survive the incoming", "barrage. Move to dodge the light bullets."], function () {
// Diyalog kapandıktan sonra üstte başlık ve geri sayım göster
var chapterTitleContainer = new Container();
// Başlık yazısı
var titleText = new Text2("Chapter 1: Meet With ???", {
size: 110,
fill: "#fff"
});
titleText.anchor.set(0.5, 0);
titleText.x = GAME_W / 2;
titleText.y = 120;
chapterTitleContainer.addChild(titleText);
// Geri sayım yazısı
var countdownText = new Text2("3", {
size: 180,
fill: "#fff"
});
countdownText.anchor.set(0.5, 0);
countdownText.x = GAME_W / 2;
countdownText.y = titleText.y + titleText.height + 40;
chapterTitleContainer.addChild(countdownText);
// Ekrana ekle
game.addChild(chapterTitleContainer);
// Geri sayım başlat
var countdownVals = ["3", "2", "1"];
var countdownIdx = 0;
function doCountdown() {
if (countdownIdx < countdownVals.length) {
countdownText.setText(countdownVals[countdownIdx]);
countdownIdx++;
LK.setTimeout(doCountdown, 900);
} else {
// Mermi spawn fonksiyonu
var _fireNextBullet = function fireNextBullet() {
if (!isGameActive) {
// Oyun biterse timerı temizle
if (chapter1BulletTimer) {
LK.clearTimeout(chapter1BulletTimer);
chapter1BulletTimer = null;
}
return;
}
// SADECE YUKARIDAN DİKİNE GELEN MERMİ OLUŞTUR (OYUNCUYA DOĞRU DEĞİL, DİKİNE)
// chapter 1-1'de sadece üstten aşağıya, sidespawn yok!
var bullet = new LightBullet();
// Reduce bullet size by 50% for chapter 1-1
if (bullet && bullet.children && bullet.children.length > 0) {
var bulletAsset = bullet.children[0];
bulletAsset.scaleX = 0.5;
bulletAsset.scaleY = 0.5;
// Update width/height for collision/out-of-bounds logic
bullet.width = bulletAsset.width * 0.5;
bullet.height = bulletAsset.height * 0.5;
}
// Sadece üstten rastgele X'e spawnla
var startX = Math.random() * GAME_W;
var startY = -bullet.height;
// Hız (her 5 mermide bir hızlanacak şekilde ayarlanacak)
var baseSpeed = 13;
var speedStep = 2.2;
var speed = baseSpeed + Math.floor(bulletsFired / 5) * speedStep + Math.random() * 2.5;
bullet.x = startX;
bullet.y = startY;
bullet.speedX = 0;
bullet.speedY = speed;
// Merminin ucunu aşağıya bakacak şekilde döndür (PI radian = aşağı)
bullet.rotation = Math.PI;
bullets.push(bullet);
game.addChild(bullet);
bulletsFired++;
// Skor göstergesini güncelle (kalan mermi)
if (scoreTxt) {
scoreTxt.setText(totalBullets - bulletsFired + "");
}
// 50 mermiye ulaşıldıysa bitir
if (bulletsFired >= totalBullets) {
// Son mermi atıldıktan sonra kısa bir süre bekle, sonra outro diyaloğu göster
LK.setTimeout(function () {
isGameActive = false;
// CHAPTER 1-1 OUTRO DİYALOĞU
// --- Bölüm geçildi, ilerlemeyi kaydet ---
storage.chapter = 1;
storage.section = 2;
showDialogue(["You’re alive… For now. But the", "enemy will hit harder. Prepare."], function () {
// CHAPTER 1-2 INTRO DİYALOĞU
showDialogue("The enemy’s tracking you, Shadow. Trick", "them with your Shadow Clone. Move.", function () {
// CHAPTER 1-2 BAŞLAT
startChapter1_2Bullets();
});
});
}, 900);
return;
}
// Her 5 mermide bir hızlan
if (bulletsFired % 5 === 0 && currentInterval > minInterval) {
currentInterval = Math.max(minInterval, currentInterval - intervalStep);
}
// Sonraki mermiyi zamanla
chapter1BulletTimer = LK.setTimeout(_fireNextBullet, currentInterval);
}; // İlk mermiyi başlat
// Geri sayım bitti, başlığı kaldır ve oyunu başlat
chapterTitleContainer.destroy();
// Geri sayım bittiğinde kalan mermi sayısını göster
if (scoreTxt) {
scoreTxt.visible = true;
}
// 50 mermilik bölüm başlasın
var totalBullets = 50;
var bulletsFired = 0;
var baseInterval = 900; // ilk mermi aralığı (ms)
var minInterval = 250; // minimum aralık
var intervalStep = 90; // her 5 mermide bir azaltılacak miktar
var currentInterval = baseInterval;
var chapter1BulletTimer = null;
// Oyun aktif
isGameActive = true;
menuActive = false;
// Skor göstergesi: kalan mermi sayısı
if (scoreTxt) {
scoreTxt.setText(totalBullets + "");
}
_fireNextBullet();
}
}
doCountdown();
});
}
// Chapter 1-2 bullet pattern and logic
function startChapter1_2Bullets() {
// Skor metni sıfırla ve göster
if (scoreTxt) {
scoreTxt.setText("50");
scoreTxt.visible = true;
}
// Tüm eski mermileri temizle
for (var i = 0; i < bullets.length; i++) {
if (bullets[i]) {
bullets[i].destroy();
}
}
bullets = [];
// Ayarlar
var totalBullets = 50;
var bulletsFired = 0;
var interval = 1000; // 1 saniyede bir
var chapter1_2BulletTimer = null;
isGameActive = true;
menuActive = false;
// Mermi spawn fonksiyonu
function fireNextBullet_1_2() {
if (!isGameActive) {
if (chapter1_2BulletTimer) {
LK.clearTimeout(chapter1_2BulletTimer);
chapter1_2BulletTimer = null;
}
return;
}
// Rastgele sağdan veya soldan gelsin
var fromLeft = Math.random() < 0.5;
var bullet = new LightBullet();
var startY = shadow ? shadow.y : GAME_H * 0.5;
// Clamp startY to visible area
if (startY < bullet.height / 2) {
startY = bullet.height / 2;
}
if (startY > GAME_H - bullet.height / 2) {
startY = GAME_H - bullet.height / 2;
}
var startX = fromLeft ? -bullet.width : GAME_W + bullet.width;
bullet.x = startX;
bullet.y = startY;
// Hedef oyuncunun o anki konumu
var targetX = shadow ? shadow.x : GAME_W / 2;
var targetY = shadow ? shadow.y : GAME_H * 0.8;
var dx = targetX - startX;
var dy = targetY - startY;
var angle = Math.atan2(dy, dx);
var speed = 15 + Math.random() * 3;
bullet.speedX = Math.cos(angle) * speed;
bullet.speedY = Math.sin(angle) * speed;
// Merminin ucunu oyuncuya bakacak şekilde döndür
bullet.rotation = angle + Math.PI / 2;
bullets.push(bullet);
game.addChild(bullet);
bulletsFired++;
// Skor göstergesini güncelle (kalan mermi)
if (scoreTxt) {
scoreTxt.setText(totalBullets - bulletsFired + "");
}
// 50 mermiye ulaşıldıysa bitir
if (bulletsFired >= totalBullets) {
LK.setTimeout(function () {
isGameActive = false;
// CHAPTER 1-2 OUTRO DİYALOĞU
// --- Bölüm geçildi, ilerlemeyi kaydet ---
storage.chapter = 2;
storage.section = 1;
showDialogue("Your clone worked. But these enemies don’t quit. More are coming.", function () {
// Sonraki bölüme geçiş veya menüye dönüş burada yapılabilir
LK.showYouWin();
});
}, 900);
return;
}
// Sonraki mermiyi zamanla
chapter1_2BulletTimer = LK.setTimeout(fireNextBullet_1_2, interval);
}
// İlk mermiyi başlat
fireNextBullet_1_2();
}
// Endless Run başlat
function startEndlessRun() {
clearGameObjects();
// --- Endless Run Mod State ---
window._endlessRunState = {
unlockLevel: 0,
// 0: base, 1: first unlock, 2: second unlock, etc.
lastUnlockScore: 0,
bulletSpeedBase: 13,
bulletSpeedStep: 1.2,
bulletSizeScale: 1,
chasingBulletSpeedBase: 10,
chasingBulletSpeedStep: 1.0,
chasingBulletActive: false
};
// Karakteri oluştur ve ortala
shadow = new Shadow();
shadow.x = GAME_W / 2;
shadow.y = GAME_H * 0.8;
game.addChild(shadow);
// Skor (endless modda skor gösterilir)
score = 0;
if (scoreTxt) {
scoreTxt.destroy();
}
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
isGameActive = false;
menuActive = false;
// --- Endless Run: Show unlock info if any ---
// Removed endless run unlock/difficulty popup text as requested
// --- Endless Run: 3-2-1 geri sayım ekle ---
if (game._endlessCountdownContainer) {
game._endlessCountdownContainer.destroy();
game._endlessCountdownContainer = null;
}
var countdownContainer = new Container();
var countdownText = new Text2("3", {
size: 220,
fill: "#fff"
});
countdownText.anchor.set(0.5, 0.5);
countdownText.x = GAME_W / 2;
countdownText.y = GAME_H / 2;
countdownContainer.addChild(countdownText);
game.addChild(countdownContainer);
game._endlessCountdownContainer = countdownContainer;
var countdownVals = ["3", "2", "1"];
var countdownIdx = 0;
function doEndlessCountdown() {
if (countdownIdx < countdownVals.length) {
countdownText.setText(countdownVals[countdownIdx]);
countdownIdx++;
LK.setTimeout(doEndlessCountdown, 900);
} else {
// Geri sayım bittiğinde container'ı kaldır ve oyunu başlat
if (game._endlessCountdownContainer) {
game._endlessCountdownContainer.destroy();
game._endlessCountdownContainer = null;
}
isGameActive = true;
startBulletTimer();
startChasingBulletTimer();
}
}
doEndlessCountdown();
}
// Oyun objelerini temizle
function clearGameObjects() {
// Karakteri sil
if (shadow) {
shadow.destroy();
shadow = null;
}
// Mermileri sil
for (var i = 0; i < bullets.length; i++) {
if (bullets[i]) {
bullets[i].destroy();
}
}
bullets = [];
for (var i = 0; i < chasingBullets.length; i++) {
if (chasingBullets[i]) {
chasingBullets[i].destroy();
}
}
chasingBullets = [];
// Skor metni sil
if (scoreTxt) {
scoreTxt.destroy();
scoreTxt = null;
}
// Joystick ve paneli sil
if (controlPanel) {
controlPanel.destroy();
controlPanel = null;
}
joystick = null;
isPanelActive = false;
dragNode = null;
joystickDirX = 0;
joystickDirY = 0;
joystickActive = false;
}
// Oyun ilk açıldığında menüyü göster
showMenu();
// --- Endless Run: Level Type System ---
// Her 25 skorda bir rastgele oyun modu seçilecek
var endlessLevelTypes = [
// 0: Sadece yukarıdan dikine mermiler
function spawnTopDownBullet(bullet) {
var startX = Math.random() * GAME_W;
var startY = -bullet.height;
var dx = 0;
var dy = 1;
var angle = Math.PI / 2;
bullet.x = startX;
bullet.y = startY;
// Ucunu aşağı bakacak şekilde döndür (PI radian)
bullet.rotation = Math.PI;
// Hız vektörü ayarla (aşağıya)
bullet.speedX = 0;
bullet.speedY = Math.abs(bullet.speedY) > 0 ? bullet.speedY : 15 + Math.random() * 3;
return {
angle: angle,
startX: startX,
startY: startY
};
},
// 1: Sadece sağdan veya soldan yatay mermiler (karışık)
function spawnSideBullet(bullet) {
var fromLeft = Math.random() < 0.5;
var startY = Math.random() * GAME_H;
var startX, dx, dy, angle;
if (fromLeft) {
startX = -bullet.width;
dx = 1;
dy = 0;
} else {
startX = GAME_W + bullet.width;
dx = -1;
dy = 0;
}
angle = Math.atan2(dy, dx);
bullet.x = startX;
bullet.y = startY;
bullet.rotation = angle + Math.PI / 2;
return {
angle: angle,
startX: startX,
startY: startY
};
},
// 2: Kenardan rastgele, oyuncuya doğru (herhangi bir kenar)
function spawnEdgeToPlayerBullet(bullet) {
var safeDist = 400;
var tries = 0;
var edge, startX, startY;
do {
edge = Math.floor(Math.random() * 4);
if (edge === 0) {
startX = Math.random() * GAME_W;
startY = -bullet.height;
} else if (edge === 1) {
startX = GAME_W + bullet.width;
startY = Math.random() * GAME_H;
} else if (edge === 2) {
startX = Math.random() * GAME_W;
startY = GAME_H + bullet.height;
} else {
startX = -bullet.width;
startY = Math.random() * GAME_H;
}
tries++;
var dist = Math.sqrt((shadow.x - startX) * (shadow.x - startX) + (shadow.y - startY) * (shadow.y - startY));
if (dist > safeDist || tries > 8) break;
} while (true);
var dx = shadow.x - startX;
var dy = shadow.y - startY;
var angle = Math.atan2(dy, dx);
bullet.x = startX;
bullet.y = startY;
bullet.rotation = angle + Math.PI / 2;
return {
angle: angle,
startX: startX,
startY: startY
};
},
// 3: Kenardan rastgele, sabit bir açıyla (oyuncuya bakmadan)
function spawnEdgeRandomAngleBullet(bullet) {
var edge = Math.floor(Math.random() * 4);
var startX, startY, dx, dy, angle;
if (edge === 0) {
startX = Math.random() * GAME_W;
startY = -bullet.height;
dx = 0;
dy = 1;
} else if (edge === 1) {
startX = GAME_W + bullet.width;
startY = Math.random() * GAME_H;
dx = -1;
dy = 0;
} else if (edge === 2) {
startX = Math.random() * GAME_W;
startY = GAME_H + bullet.height;
dx = 0;
dy = -1;
} else {
startX = -bullet.width;
startY = Math.random() * GAME_H;
dx = 1;
dy = 0;
}
angle = Math.atan2(dy, dx);
bullet.x = startX;
bullet.y = startY;
bullet.rotation = angle + Math.PI / 2;
return {
angle: angle,
startX: startX,
startY: startY
};
}];
// Endless run için aktif seviye türü ve skor takibi
var endlessCurrentLevelType = 0;
var endlessLevelTypeName = "";
var endlessLevelTypeNames = ["Top-Down Bullets", "Side Bullets", "Edge-to-Player Bullets", "Edge-Random-Angle Bullets"];
var endlessLastLevelScore = 0;
var endlessLevelTypeIndex = 0;
// Mermi oluşturma fonksiyonu
function spawnBullet() {
var isEndless = currentMode === "endless";
var bullet = new LightBullet();
var angle, startX, startY, speed, dx, dy;
if (isEndless && shadow) {
// Her 25 skorda bir rastgele oyun modu seç
var levelTypeChanged = false;
// Her zaman, skorun 0 veya 25'in katı olduğu anda yeni bir mod seç
if (score === 0 || score > 0 && score % 25 === 0 && score !== endlessLastLevelScore) {
var prevType = endlessCurrentLevelType;
var newType;
do {
newType = Math.floor(Math.random() * endlessLevelTypes.length);
} while (newType === prevType && endlessLevelTypes.length > 1);
endlessLevelTypeIndex = newType;
endlessCurrentLevelType = endlessLevelTypeIndex;
endlessLastLevelScore = score;
levelTypeChanged = true;
}
// Oyun modu adı 3 saniye ekranda gösterilsin
if (levelTypeChanged) {
if (game._endlessLevelTypeText) {
game._endlessLevelTypeText.destroy();
game._endlessLevelTypeText = null;
}
var typeName = endlessLevelTypeNames[endlessCurrentLevelType] || "Unknown";
var typeText = new Text2(typeName, {
size: 120,
fill: "#ff0"
});
typeText.anchor.set(0.5, 0.5);
typeText.x = GAME_W / 2;
typeText.y = 320;
typeText.alpha = 0.0;
game.addChild(typeText);
game._endlessLevelTypeText = typeText;
tween(typeText, {
alpha: 1
}, {
duration: 350,
onFinish: function onFinish() {
tween(typeText, {
alpha: 0
}, {
duration: 600,
delay: 2400,
// 3 saniye toplam
onFinish: function onFinish() {
if (game._endlessLevelTypeText) {
game._endlessLevelTypeText.destroy();
game._endlessLevelTypeText = null;
}
}
});
}
});
}
// Seçili seviye fonksiyonunu uygula
var result = endlessLevelTypes[endlessCurrentLevelType](bullet);
angle = result.angle;
startX = result.startX;
startY = result.startY;
// Eğer bullet spawn fonksiyonu bir pozisyon döndürmediyse (ör: spawn fonksiyonu bug'luysa), bullet'ı spawnlama
if (typeof angle !== "number" || typeof startX !== "number" || typeof startY !== "number" || isNaN(angle) || isNaN(startX) || isNaN(startY)) {
return;
}
// Hedef oyuncu ise, ona göre ayarla
if (endlessCurrentLevelType === 2) {
var dist = Math.sqrt((shadow.x - startX) * (shadow.x - startX) + (shadow.y - startY) * (shadow.y - startY));
if (dist > 0.01) {
dx = (shadow.x - startX) / dist;
dy = (shadow.y - startY) / dist;
} else {
dx = Math.cos(angle);
dy = Math.sin(angle);
}
} else {
dx = Math.cos(angle);
dy = Math.sin(angle);
}
} else {
// Story mode veya fallback: yukarıdan aşağıya
startX = Math.random() * GAME_W;
startY = -bullet.height;
angle = Math.PI / 2;
dx = Math.cos(angle);
dy = Math.sin(angle);
bullet.x = startX;
bullet.y = startY;
bullet.rotation = angle;
}
// --- Endless Run: Dynamic bullet speed/size ---
var sizeScale = 1;
if (isEndless && window._endlessRunState) {
var base = window._endlessRunState.bulletSpeedBase || 13;
var step = window._endlessRunState.bulletSpeedStep || 1.2;
var unlock = window._endlessRunState.unlockLevel || 0;
// Edge-to-player bullets (type 2) should be slower
if (endlessCurrentLevelType === 2) {
speed = (base + unlock * step + Math.random() * 3.5) * 0.68;
} else {
speed = base + unlock * step + Math.random() * 3.5;
}
sizeScale = Math.max(0.32, 1 - unlock * 0.16);
if (bullet && bullet.children && bullet.children.length > 0) {
var bulletAsset = bullet.children[0];
bulletAsset.scaleX = sizeScale;
bulletAsset.scaleY = sizeScale;
bullet.width = bulletAsset.width * sizeScale;
bullet.height = bulletAsset.height * sizeScale;
}
} else {
speed = 13 + Math.random() * 5;
}
// Hız vektörü
bullet.speedX = dx * speed;
bullet.speedY = dy * speed;
// Sadece gerçekten ekrana mermi ekleniyorsa ekle
var bulletSpawned = true;
// Top-down modunda (0) bullet y pozisyonu ekrandan büyükse spawnlama
if (endlessCurrentLevelType === 0 && bullet.y > GAME_H) {
bulletSpawned = false;
}
// Side modunda (1) bullet x pozisyonu ekrandan tamamen dışarıdaysa spawnlama
if (endlessCurrentLevelType === 1 && (bullet.x < -bullet.width || bullet.x > GAME_W + bullet.width)) {
bulletSpawned = false;
}
// Edge-to-player (2) ve edge-random (3) modlarında bullet ekrandan tamamen dışarıdaysa spawnlama
if ((endlessCurrentLevelType === 2 || endlessCurrentLevelType === 3) && (bullet.x < -bullet.width || bullet.x > GAME_W + bullet.width || bullet.y < -bullet.height || bullet.y > GAME_H + bullet.height)) {
bulletSpawned = false;
}
if (bulletSpawned) {
bullets.push(bullet);
game.addChild(bullet);
// Endless run modunda skor sadece mermi yaratıldığında artsın
if (currentMode === "endless") {
updateScore(score + 1);
}
}
}
// Takipçi mermi oluşturma fonksiyonu
function spawnChasingBullet() {
var isEndless = currentMode === "endless";
// --- Endless Run: Unlock chasing bullets at 50 score ---
if (isEndless && window._endlessRunState) {
if (!window._endlessRunState.chasingBulletActive && score >= 50) {
window._endlessRunState.chasingBulletActive = true;
// Removed endless run unlock/difficulty popup text as requested
}
if (!window._endlessRunState.chasingBulletActive) {
// Not unlocked yet
return;
}
} else {
// Sadece skor 10 veya üstü ise oluştur
if (score < 10) {
return;
}
}
// Kenardan rastgele bir yerden başlat
var edge = Math.floor(Math.random() * 4);
var bullet = new ChasingLightBullet();
var startX, startY;
if (edge === 0) {
startX = Math.random() * GAME_W;
startY = -bullet.height;
} else if (edge === 1) {
startX = GAME_W + bullet.width;
startY = Math.random() * GAME_H;
} else if (edge === 2) {
startX = Math.random() * GAME_W;
startY = GAME_H + bullet.height;
} else {
startX = -bullet.width;
startY = Math.random() * GAME_H;
}
bullet.x = startX;
bullet.y = startY;
// --- Endless Run: Dynamic chasing bullet speed/size ---
if (isEndless && window._endlessRunState) {
var unlock = window._endlessRunState.unlockLevel || 0;
var base = window._endlessRunState.chasingBulletSpeedBase || 10;
var step = window._endlessRunState.chasingBulletSpeedStep || 1.0;
var speed = base + unlock * step + Math.random() * 2.5;
bullet.speed = speed;
// Size shrinks a bit with unlocks, but not too small (minimum 0.45 for better dodging)
var sizeScale = Math.max(0.45, 1 - unlock * 0.12);
if (bullet && bullet.children && bullet.children.length > 0) {
var bulletAsset = bullet.children[0];
bulletAsset.scaleX = sizeScale;
bulletAsset.scaleY = sizeScale;
bullet.width = bulletAsset.width * sizeScale;
bullet.height = bulletAsset.height * sizeScale;
}
}
// Başlangıçta oyuncuya bakacak şekilde döndür
var dx = shadow.x - bullet.x;
var dy = shadow.y - bullet.y;
bullet.rotation = Math.atan2(dy, dx) + Math.PI / 2;
chasingBullets.push(bullet);
game.addChild(bullet);
// Endless run modunda skor mermi sayısı olarak güncellensin
if (currentMode === "endless") {
updateScore(score + 1);
}
}
// Mermi oluşturma zamanlayıcısı başlat
function startBulletTimer() {
if (bulletTimer) {
LK.clearInterval(bulletTimer);
}
bulletTimer = LK.setInterval(function () {
if (isGameActive) {
spawnBullet();
}
}, bulletInterval);
}
// startBulletTimer(); // Chapter 1'de otomatik mermi spawn edilmesin
// Takipçi mermi zamanlayıcısı başlat
function startChasingBulletTimer() {
if (chasingBulletTimer) {
LK.clearInterval(chasingBulletTimer);
}
chasingBulletTimer = LK.setInterval(function () {
if (isGameActive && score >= 10) {
spawnChasingBullet();
}
}, chasingBulletInterval);
}
// startChasingBulletTimer(); // Chapter 1'de otomatik takipçi mermi spawn edilmesin
// Skoru güncelle
function updateScore(val) {
// Sadece endless modda skor güncellensin
if (currentMode === "endless") {
score = val;
if (scoreTxt) {
scoreTxt.setText(score);
}
}
}
// Oyun bittiğinde
function gameOver() {
isGameActive = false;
// Ekranı kırmızıya flashla
LK.effects.flashScreen(0xff2222, 800);
// Chapter modunda ise ilerlemeyi kaydet
if (currentMode === "chapter") {
// Skora göre chapter ilerletme örneği (ileride özelleştirilebilir)
if (score > 0) {
var nextChapter = (storage.chapter || 1) + 1;
storage.chapter = nextChapter;
}
}
// Endless modda high score zaten update'de kaydediliyor
// Oyun bitişini göster
LK.showGameOver();
// Kısa bir gecikmeden sonra menüyü tekrar göster
LK.setTimeout(function () {
showMenu();
}, 1200);
}
// Sürükleme işlemleri
// Joystick yönünü ve mesafesini globalde tut
var joystickDirX = 0;
var joystickDirY = 0;
var joystickActive = false;
function handleMove(x, y, obj) {
if (!isGameActive || menuActive) {
return;
}
if (isPanelActive && controlPanel && joystick) {
// Joystick merkezine göre delta
var dx = x - panelStartX;
var dy = y - panelStartY;
var dist = Math.sqrt(dx * dx + dy * dy);
// Knob'u panel sınırında tut
var maxDist = joystickRadius - joystickKnobRadius;
if (dist > maxDist) {
var angle = Math.atan2(dy, dx);
dx = Math.cos(angle) * maxDist;
dy = Math.sin(angle) * maxDist;
}
joystick.x = dx;
joystick.y = dy;
// Joystick yönünü ve oranını kaydet
if (dist > 10) {
// ölü bölge
joystickDirX = dx / maxDist;
joystickDirY = dy / maxDist;
joystickActive = true;
} else {
joystickDirX = 0;
joystickDirY = 0;
joystickActive = false;
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (!isGameActive || menuActive) {
return;
}
// Kontrol paneli zaten varsa kaldır
if (controlPanel) {
controlPanel.destroy();
controlPanel = null;
joystick = null;
isPanelActive = false;
}
// Kontrol panelini oluştur
controlPanel = new Container();
controlPanel.x = x;
controlPanel.y = y;
// Panel arka planı (şeffaf daire)
var panelBg = LK.getAsset('centerCircle', {
width: joystickRadius * 2,
height: joystickRadius * 2,
color: 0x222222,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.25
});
controlPanel.addChild(panelBg);
// Joystick knob'u (küçük daire)
joystick = LK.getAsset('centerCircle', {
width: joystickKnobRadius * 2,
height: joystickKnobRadius * 2,
color: 0xffffff,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
joystick.x = 0;
joystick.y = 0;
controlPanel.addChild(joystick);
// Paneli oyuna ekle
game.addChild(controlPanel);
isPanelActive = true;
panelStartX = x;
panelStartY = y;
knobStartX = 0;
knobStartY = 0;
};
game.up = function (x, y, obj) {
if (menuActive) {
return;
}
// Kontrol panelini kaldır
if (controlPanel) {
controlPanel.destroy();
controlPanel = null;
joystick = null;
isPanelActive = false;
}
dragNode = null;
// Joystick bırakıldığında hareketi durdur
joystickDirX = 0;
joystickDirY = 0;
joystickActive = false;
};
// Oyun güncelleme döngüsü
game.update = function () {
if (!isGameActive || menuActive) {
return;
}
// Karakteri joystick yönünde sürekli hareket ettir
if (joystickActive) {
var moveSpeed = 22;
shadow.x += joystickDirX * moveSpeed;
shadow.y += joystickDirY * moveSpeed;
var r = shadow.getRadius();
if (shadow.x < r) {
shadow.x = r;
}
if (shadow.x > GAME_W - r) {
shadow.x = GAME_W - r;
}
if (shadow.y < r) {
shadow.y = r;
}
if (shadow.y > GAME_H - r) {
shadow.y = GAME_H - r;
}
}
// Mermileri güncelle
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
if (b.isOutOfBounds()) {
b.destroy();
bullets.splice(i, 1);
continue;
}
var intersecting = b.intersects(shadow);
if (!b.lastIntersecting && intersecting) {
// Oyuncu mermiye çarptıysa, oyunu durdur ve "Yeniden Başla" butonu göster
isGameActive = false;
// Tüm mermileri temizle
for (var k = 0; k < bullets.length; k++) {
if (bullets[k]) {
bullets[k].destroy();
}
}
bullets = [];
for (var k = 0; k < chasingBullets.length; k++) {
if (chasingBullets[k]) {
chasingBullets[k].destroy();
}
}
chasingBullets = [];
// Karakteri sil
if (shadow) {
shadow.destroy();
shadow = null;
}
// Skor metni sil
if (scoreTxt) {
scoreTxt.destroy();
scoreTxt = null;
}
// Joystick ve paneli sil
if (controlPanel) {
controlPanel.destroy();
controlPanel = null;
}
joystick = null;
isPanelActive = false;
dragNode = null;
joystickDirX = 0;
joystickDirY = 0;
joystickActive = false;
// --- YENİDEN BAŞLA VE ANA MENÜ BUTONLARI EKLE ---
if (game._retryButtonContainer) {
game._retryButtonContainer.destroy();
game._retryButtonContainer = null;
}
var retryButtonContainer = new Container();
// Ortalanmış konumlar
var btnW = 600;
var btnH = 180;
var btnSpacing = 80;
var centerY = GAME_H / 2;
var restartY = centerY - btnH / 2 - btnSpacing / 2;
var menuY = centerY + btnH / 2 + btnSpacing / 2;
// --- Endless Run: Skor ve süreyi göster ---
if (currentMode === "endless") {
// Skor
var scoreLabel = new Text2("Score: " + score, {
size: 110,
fill: "#fff"
});
scoreLabel.anchor.set(0.5, 1);
scoreLabel.x = GAME_W / 2;
scoreLabel.y = restartY - 120;
retryButtonContainer.addChild(scoreLabel);
// Hayatta kalma süresi (saniye ve dakika)
// Her 30 tick = 0.5 saniye, 60 tick = 1 saniye (FPS 60)
// Skor her 30 tickte 1 artıyor, yani skor = yarım saniye
// Oyun update'inde: updateScore(score + 1); if (LK.ticks % 30 === 0)
// Yani score/2 = saniye
var survivedSeconds = Math.floor(score / 2);
var min = Math.floor(survivedSeconds / 60);
var sec = survivedSeconds % 60;
var timeStr = min > 0 ? min + "m " + (sec < 10 ? "0" : "") + sec + "s" : sec + "s";
var timeLabel = new Text2("Survived: " + timeStr, {
size: 90,
fill: "#fff"
});
timeLabel.anchor.set(0.5, 1);
timeLabel.x = GAME_W / 2;
timeLabel.y = scoreLabel.y - 100;
retryButtonContainer.addChild(timeLabel);
}
// Restart butonu arka planı
var btnBgRestart = LK.getAsset('blackbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_W / 2,
y: restartY,
width: btnW,
height: btnH
});
retryButtonContainer.addChild(btnBgRestart);
// Restart buton metni
var btnTextRestart = new Text2("Restart", {
size: 80,
fill: "#fff"
});
btnTextRestart.anchor.set(0.5, 0.5);
btnTextRestart.x = GAME_W / 2;
btnTextRestart.y = restartY;
retryButtonContainer.addChild(btnTextRestart);
// Main Menu butonu arka planı
var btnBgMenu = LK.getAsset('blackbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_W / 2,
y: menuY,
width: btnW,
height: btnH
});
retryButtonContainer.addChild(btnBgMenu);
// Main Menu buton metni
var btnTextMenu = new Text2("Main Menu", {
size: 80,
fill: "#fff"
});
btnTextMenu.anchor.set(0.5, 0.5);
btnTextMenu.x = GAME_W / 2;
btnTextMenu.y = menuY;
retryButtonContainer.addChild(btnTextMenu);
// Restart butonu etkileşimi
btnBgRestart.interactive = true;
btnBgRestart.buttonMode = true;
btnBgRestart.down = function (x, y, obj) {
if (game._retryButtonContainer) {
game._retryButtonContainer.destroy();
game._retryButtonContainer = null;
}
if (currentMode === "endless") {
startEndlessRun();
} else {
startGameFromChapter();
}
};
// Main Menu butonu etkileşimi
btnBgMenu.interactive = true;
btnBgMenu.buttonMode = true;
btnBgMenu.down = function (x, y, obj) {
if (game._retryButtonContainer) {
game._retryButtonContainer.destroy();
game._retryButtonContainer = null;
}
showMenu();
};
game.addChild(retryButtonContainer);
game._retryButtonContainer = retryButtonContainer;
return;
}
b.lastIntersecting = intersecting;
}
// Takipçi mermileri güncelle
for (var i = chasingBullets.length - 1; i >= 0; i--) {
var cb = chasingBullets[i];
cb.update();
if (cb.isOutOfBounds()) {
cb.destroy();
chasingBullets.splice(i, 1);
continue;
}
var intersecting = cb.intersects(shadow);
if (!cb.lastIntersecting && intersecting) {
// Oyuncu takipçi mermiye çarptıysa, oyunu durdur ve "Yeniden Başla" butonu göster
isGameActive = false;
// Tüm mermileri temizle
for (var k = 0; k < bullets.length; k++) {
if (bullets[k]) {
bullets[k].destroy();
}
}
bullets = [];
for (var k = 0; k < chasingBullets.length; k++) {
if (chasingBullets[k]) {
chasingBullets[k].destroy();
}
}
chasingBullets = [];
// Karakteri sil
if (shadow) {
shadow.destroy();
shadow = null;
}
// Skor metni sil
if (scoreTxt) {
scoreTxt.destroy();
scoreTxt = null;
}
// Joystick ve paneli sil
if (controlPanel) {
controlPanel.destroy();
controlPanel = null;
}
joystick = null;
isPanelActive = false;
dragNode = null;
joystickDirX = 0;
joystickDirY = 0;
joystickActive = false;
// --- YENİDEN BAŞLA VE ANA MENÜ BUTONLARI EKLE ---
if (game._retryButtonContainer) {
game._retryButtonContainer.destroy();
game._retryButtonContainer = null;
}
var retryButtonContainer = new Container();
// Ortalanmış konumlar
var btnW = 600;
var btnH = 180;
var btnSpacing = 80;
var centerY = GAME_H / 2;
var restartY = centerY - btnH / 2 - btnSpacing / 2;
var menuY = centerY + btnH / 2 + btnSpacing / 2;
// --- Endless Run: Skor ve süreyi göster ---
if (currentMode === "endless") {
// Skor
var scoreLabel = new Text2("Score: " + score, {
size: 110,
fill: "#fff"
});
scoreLabel.anchor.set(0.5, 1);
scoreLabel.x = GAME_W / 2;
scoreLabel.y = restartY - 120;
retryButtonContainer.addChild(scoreLabel);
// Hayatta kalma süresi (saniye ve dakika)
var survivedSeconds = Math.floor(score / 2);
var min = Math.floor(survivedSeconds / 60);
var sec = survivedSeconds % 60;
var timeStr = min > 0 ? min + "m " + (sec < 10 ? "0" : "") + sec + "s" : sec + "s";
var timeLabel = new Text2("Survived: " + timeStr, {
size: 90,
fill: "#fff"
});
timeLabel.anchor.set(0.5, 1);
timeLabel.x = GAME_W / 2;
timeLabel.y = scoreLabel.y - 100;
retryButtonContainer.addChild(timeLabel);
}
// Restart butonu arka planı
var btnBgRestart = LK.getAsset('blackbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_W / 2,
y: restartY,
width: btnW,
height: btnH
});
retryButtonContainer.addChild(btnBgRestart);
// Restart buton metni
var btnTextRestart = new Text2("Restart", {
size: 80,
fill: "#fff"
});
btnTextRestart.anchor.set(0.5, 0.5);
btnTextRestart.x = GAME_W / 2;
btnTextRestart.y = restartY;
retryButtonContainer.addChild(btnTextRestart);
// Main Menu butonu arka planı
var btnBgMenu = LK.getAsset('blackbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_W / 2,
y: menuY,
width: btnW,
height: btnH
});
retryButtonContainer.addChild(btnBgMenu);
// Main Menu buton metni
var btnTextMenu = new Text2("Main Menu", {
size: 80,
fill: "#fff"
});
btnTextMenu.anchor.set(0.5, 0.5);
btnTextMenu.x = GAME_W / 2;
btnTextMenu.y = menuY;
retryButtonContainer.addChild(btnTextMenu);
// Restart butonu etkileşimi
btnBgRestart.interactive = true;
btnBgRestart.buttonMode = true;
btnBgRestart.down = function (x, y, obj) {
if (game._retryButtonContainer) {
game._retryButtonContainer.destroy();
game._retryButtonContainer = null;
}
if (currentMode === "endless") {
startEndlessRun();
} else {
startGameFromChapter();
}
};
// Main Menu butonu etkileşimi
btnBgMenu.interactive = true;
btnBgMenu.buttonMode = true;
btnBgMenu.down = function (x, y, obj) {
if (game._retryButtonContainer) {
game._retryButtonContainer.destroy();
game._retryButtonContainer = null;
}
showMenu();
};
game.addChild(retryButtonContainer);
game._retryButtonContainer = retryButtonContainer;
return;
}
cb.lastIntersecting = intersecting;
}
// Işık mermileri birbirine çarptı mı kontrol et ve patlat
for (var i = bullets.length - 1; i >= 0; i--) {
var b1 = bullets[i];
for (var j = i - 1; j >= 0; j--) {
var b2 = bullets[j];
if (!b1._destroyed && !b2._destroyed && b1.intersects(b2)) {
b1.destroy();
b2.destroy();
bullets.splice(i, 1);
bullets.splice(j, 1);
i--;
break;
}
}
}
// Takipçi mermiler ile normal mermiler çarpışırsa patlat
for (var i = chasingBullets.length - 1; i >= 0; i--) {
var cb = chasingBullets[i];
for (var j = bullets.length - 1; j >= 0; j--) {
var b = bullets[j];
if (!cb._destroyed && !b._destroyed && cb.intersects(b)) {
cb.destroy();
b.destroy();
chasingBullets.splice(i, 1);
bullets.splice(j, 1);
i--;
break;
}
}
}
// Takipçi mermiler birbirine çarparsa patlat
for (var i = chasingBullets.length - 1; i >= 0; i--) {
var cb1 = chasingBullets[i];
for (var j = i - 1; j >= 0; j--) {
var cb2 = chasingBullets[j];
if (!cb1._destroyed && !cb2._destroyed && cb1.intersects(cb2)) {
cb1.destroy();
cb2.destroy();
chasingBullets.splice(i, 1);
chasingBullets.splice(j, 1);
i--;
break;
}
}
}
// Endless run modunda skor mermi yaratıldığında artıyor, burada skor artışı yapılmaz
if (currentMode === "endless") {
// Endless modda high score kaydet
if (score > (storage.endlessHighScore || 0)) {
storage.endlessHighScore = score;
}
// --- Endless Run: Unlock new features every 25 score ---
if (window._endlessRunState) {
var unlockLevel = window._endlessRunState.unlockLevel || 0;
var nextUnlockScore = (unlockLevel + 1) * 25;
if (score >= nextUnlockScore) {
window._endlessRunState.unlockLevel = unlockLevel + 1;
window._endlessRunState.lastUnlockScore = score;
// Removed endless run unlock/difficulty popup text as requested
if (window._endlessRunState.unlockLevel === 1) {
window._endlessRunState.chasingBulletActive = true;
} else if (window._endlessRunState.unlockLevel === 2) {
window._endlessRunState.bulletSpeedBase += 2;
} else if (window._endlessRunState.unlockLevel === 3) {
// No direct effect, but sizeScale will shrink more
} else if (window._endlessRunState.unlockLevel === 4) {
window._endlessRunState.chasingBulletSpeedBase += 2;
} else if (window._endlessRunState.unlockLevel === 5) {
window._endlessRunState.bulletSpeedBase += 1;
window._endlessRunState.chasingBulletSpeedBase += 1;
}
}
}
}
};
// Oyun yeniden başladığında sıfırla
game.on('reset', function () {
// Tüm oyun objelerini temizle
clearGameObjects();
// Menüye dön
showMenu();
}); ===================================================================
--- original.js
+++ change.js
@@ -1166,30 +1166,19 @@
var angle, startX, startY, speed, dx, dy;
if (isEndless && shadow) {
// Her 25 skorda bir rastgele oyun modu seç
var levelTypeChanged = false;
- if (score === 0) {
- endlessLevelTypeIndex = Math.floor(Math.random() * endlessLevelTypes.length);
+ // Her zaman, skorun 0 veya 25'in katı olduğu anda yeni bir mod seç
+ if (score === 0 || score > 0 && score % 25 === 0 && score !== endlessLastLevelScore) {
+ var prevType = endlessCurrentLevelType;
+ var newType;
+ do {
+ newType = Math.floor(Math.random() * endlessLevelTypes.length);
+ } while (newType === prevType && endlessLevelTypes.length > 1);
+ endlessLevelTypeIndex = newType;
endlessCurrentLevelType = endlessLevelTypeIndex;
- endlessLastLevelScore = 0;
+ endlessLastLevelScore = score;
levelTypeChanged = true;
- } else if (score - endlessLastLevelScore >= 25) {
- // Sadece tam olarak 25'in katında değiştir (ve sadece bir kez)
- if (score % 25 === 0) {
- // Yeni rastgele bir mod seç, bir öncekiyle aynı olmasın
- var prevType = endlessCurrentLevelType;
- var newType;
- do {
- newType = Math.floor(Math.random() * endlessLevelTypes.length);
- } while (newType === prevType && endlessLevelTypes.length > 1);
- endlessLevelTypeIndex = newType;
- endlessCurrentLevelType = endlessLevelTypeIndex;
- endlessLastLevelScore = score;
- levelTypeChanged = true;
- } else {
- // 25'in katı değilse, levelTypeChanged olmasın
- levelTypeChanged = false;
- }
}
// Oyun modu adı 3 saniye ekranda gösterilsin
if (levelTypeChanged) {
if (game._endlessLevelTypeText) {
@@ -1233,11 +1222,9 @@
angle = result.angle;
startX = result.startX;
startY = result.startY;
// Eğer bullet spawn fonksiyonu bir pozisyon döndürmediyse (ör: spawn fonksiyonu bug'luysa), bullet'ı spawnlama
- // Ayrıca: Eğer levelTypeChanged ise, bir sonraki frame'de tekrar bullet spawn etmeye çalış (yani bu frame'de spawn etme)
- if (typeof angle !== "number" || typeof startX !== "number" || typeof startY !== "number" || isNaN(angle) || isNaN(startX) || isNaN(startY) || typeof levelTypeChanged !== "undefined" && levelTypeChanged) {
- // Eğer levelTypeChanged ise, bir sonraki frame'de tekrar spawn etmeye çalışmak için return et
+ if (typeof angle !== "number" || typeof startX !== "number" || typeof startY !== "number" || isNaN(angle) || isNaN(startX) || isNaN(startY)) {
return;
}
// Hedef oyuncu ise, ona göre ayarla
if (endlessCurrentLevelType === 2) {
@@ -1385,22 +1372,9 @@
LK.clearInterval(bulletTimer);
}
bulletTimer = LK.setInterval(function () {
if (isGameActive) {
- // Her zaman bir bullet spawn etmeye çalış
spawnBullet();
- // Eğer bir önceki spawn'da levelTypeChanged olduysa, bu frame'de bullet spawn edilmemiş olabilir.
- // Bu durumda, bir sonraki frame'de tekrar spawn etmeye çalışmak için bir flag ile kontrol et.
- // spawnBullet fonksiyonu içinde levelTypeChanged flag'i set ediliyor.
- // Burada, eğer levelTypeChanged olduysa, bir kez daha spawnBullet çağır.
- if (typeof endlessCurrentLevelType !== "undefined" && typeof endlessLevelTypeIndex !== "undefined") {
- // spawnBullet fonksiyonu içinde levelTypeChanged flag'i set ediliyor
- // Burada, eğer bir önceki spawn'da levelTypeChanged olduysa, tekrar spawn et
- // Bunu anlamak için endlessLastLevelScore ile score'u karşılaştırabiliriz
- if (score === endlessLastLevelScore && isGameActive) {
- spawnBullet();
- }
- }
}
}, bulletInterval);
}
// startBulletTimer(); // Chapter 1'de otomatik mermi spawn edilmesin
gri bir yuvarlak. In-Game asset. 2d. High contrast. No shadows
white bullet. In-Game asset. 2d. High contrast. No shadows
kırmızı gözler, gözler sadece kırmızı, gözden başka hiçbir şey olmayacak ve 2 tane göz olacak yan yana aynı boyutta, dümdüz kırmızı gözleri ekstra efekt yok, yazı yok, gözler dikine dikdörtgen şeklinde olacak gerçek göz gibi olmayacaklar, gözlerin boşlukları olmayacak gözler köşeli olacak dikine dikdörtgen ve köşeli. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
white color circle. In-Game asset. 2d. High contrast. No shadows
rectangle with rounded edges (color white) (the edges will white too). In-Game asset. 2d. High contrast. No shadows
black walkie talkie. In-Game asset. 2d. High contrast. No shadows
only text no image, agent shadow text logo for agent game. In-Game asset. 2d. High contrast. No shadows
black rectangle (rounded corners). In-Game asset. 2d. High contrast. No shadows
lock icon. no text. only white color. In-Game asset. 2d. High contrast. No shadows
ünlem işareti kırmızı. In-Game asset. 2d. High contrast. No shadows
game skill logo, skill name is "shadow". no text. only image.. In-Game asset. 2d. High contrast. No shadows