User prompt
moving forward, for all aspects of this projec7, be Art 💙✝️✨
User prompt
Please try again—there are no platforms whatsoever in either direction now.
User prompt
Please try again ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Girlie pop lost her damn marbles. K. No, gurl. Way above, I outlined a structure for level generation. Nowhere among that did I say “bury the player character behind a jumble of background assets”. And it *is* a jumble. Chiefly in those instructions buried among distinguishing between background and midground and blah blah blah, was one core philosophy: Art. So, every individual asset is representing a visual (in this instance, because sound design comes later) facet of reality. In this case, operating in what’s actual a 2D cross section representing a 3D space, assets and frames are being layered strategically to visually convey that 3D, providing a visually convincing optical illusion. While it’s possible the player character could pass behind a more foreground object, such as a menu overlay, or even something like a cave overlay with stalagmites and tites using parallax in the foreground in front of player character, these instances are the exceptions. Mostly, the player character and all of the animation assets associated with them, should be the visible focal point in the highest layer we the viewer can see. Also... platform layout. GURL. This is cray. I asked for “give me clear and logical and aesthetic pathways going in each direction”. You gave me “put platforms in all directions. And cover the player character with them. Don’t like... have a purpose, as a path. Lead to a doorway. Or a focal point. A character.” Nah, you said PLATFORMS AND DOORS EVERYWHERE. AND CHARACTERS AND—“ gurl. One character (in addition to the player character) visible onscreen at a time is a good rule of thumb. There can be exceptions to that but that should serve us well. Also, use nocturnecitylayout as a reference image for structuring the opening space loading in. It should provide decent visual guide for placement of platforms, distance from one another, frequency, scale, etc. I placed and layered most of that image by hand myself. Okay.... So let’s try this, and don’t give me a “couldn’t find object” or whatever failure. This is my last prompt of the day... for like two hours. lol 💙 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'ReferenceError: Can't find variable: zone' in or related to this line: 'effect.zone = zone;' Line Number: 1806
User prompt
Please fix the bug: 'ReferenceError: Can't find variable: zone' in or related to this line: 'structure.zone = zone;' Line Number: 1717
User prompt
Please fix the bug: 'ReferenceError: Can't find variable: zone' in or related to this line: 'bg.zone = zone;' Line Number: 1680
User prompt
Try again. Be creative and inventive. Improvise, if need be, scrap current system to reimplement using assets unused for the Zones. Cosmic, City, Sea, Under, & Forest. Make sure every direction continues with no end in sight. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please try again. Troubleshoot and improvise as needed. Make the level layout expansive and explorable in every direction. Be creative, artistic, exercising judgement and logic to implement unused assets where is obvious. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Try again a different way please. Troubleshoot as needed ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please continue. Implement and place unused non-player assets. Better layer background assets and midground images to conceal the hard edge of the asset’s borders. Arrange platforms to make descend and ascend possible ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Import of asset
User prompt
Import of asset
User prompt
Import of asset
User prompt
Import of asset
User prompt
Import of asset
User prompt
Resize background and various assets for size consistency/aesthetics. Layer more artfully, and add platforming elements and opportunities to more in all directions. Including down to the underground portions. Make cosmic assets more accessible and sized ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
moving forward, for all aspects of this projec7, be Art 💙✝️✨ Please implement the following structural framework for level layouts via procedural generation. There are four distinct Zones, with the player beginning in the City (all currently implemented background/environmental assets are City. To the north (moving upward) is Cosmic, moving West/to the Left, City assets gradually become Sea. South/downward = Under. East, to the right = Forest Each zone is associated with a set of background images, midground images, environmental effects, foreground images, to be seamlessly blended and layered is ways that should obscure the harshness of their borders aesthetically and artistically. Use parallax effects to further blend background image borders. (This layer is collage Art, so as much as you are a reflection of human consciousness and ideas, be Art.) Layered in front of background images are midground images, and in front of those, environmental effects. Environmental effects are often animated, such as the cloud assets drifting by in City Zone. In front of midground are foreground assets, that stay relatively static to the eye, and include things like platform or floor tiles. Character assets for animations also belong on this level. In front of all these layers sit the UI icons like the track pad, jump button and spell buttons. When interior spaces are selected via doors, their overlays also open above the other layers as well as most ui elements. Cosmic: Backgrounds: Galaxybg galaxybg2 galaxybg3 galaxybg4 Midground: galaxy starlight2 galaxy2 Environmental effects: galaxy platforms: starlight galaxy3 galaxy4 City: Backgrounds: bg bg2 bg3 bg4 nocturnecitylayout Midground: building Building2 building3 building4 Midground upperlayer: neonSign neonsign2 Environmental effects: cloud1 - cloud3 Platforms: platform platform2 platform3 platform4 Forest: Backgrounds: forest forest2 forest3 Midground: trees trees2 trees3 Environmental effects (blinking): foresteffect foresteffect2 Sea: Backgrounds: sea - sea4 midground: caveoverlay Environmental effect (drifting by): seaeffect Under: Backgrounds: underbg - underbg5 midground: underoverlay caveoverlay Environmental effect: undereffect Using those as guides and troubleshooting when encountering an asset with a different spelling. Check for spelling variations and artistic judgement in implementing ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Import of asset
User prompt
Import of asset
User prompt
Import of asset
User prompt
Import of asset
User prompt
Import of asset
User prompt
Import of asset
User prompt
Import of asset
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BattleUI = Container.expand(function () {
var self = Container.call(this);
var bg = self.attachAsset('battleBg', {
anchorX: 0.5,
anchorY: 0.5
});
bg.alpha = 0.9;
self.hunkDisplay = null;
self.resistanceBar = null;
self.resistanceFill = null;
self.spellButtons = [];
self.setup = function (hunk) {
self.hunkDisplay = self.addChild(new Container());
var hunkAssets = ['hunk', 'hunk2', 'hunk3', 'hunk4'];
// For animated hunks, we need to create an animated display
if (hunk.hunkType === 0) {
// Create animation frames for battle display - mushroom hunk
self.hunkIdleFrames = [];
self.currentBattleFrame = 0;
var frameAssets = ['hunk', 'hunka', 'hunkb', 'hunkc', 'hunkd', 'hunke'];
for (var i = 0; i < frameAssets.length; i++) {
var frame = self.hunkDisplay.attachAsset(frameAssets[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
alpha: i === 0 ? 1 : 0
});
self.hunkIdleFrames.push(frame);
}
// Start battle idle animation
self.startBattleIdleAnimation();
} else if (hunk.hunkType === 1) {
// Create animation frames for battle display - hunk2
self.hunkIdleFrames = [];
self.currentBattleFrame = 0;
var frameAssets = ['hunk2', 'hunk2a', 'hunk2b', 'hunk2c', 'hunk2d'];
for (var i = 0; i < frameAssets.length; i++) {
var frame = self.hunkDisplay.attachAsset(frameAssets[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
alpha: i === 0 ? 1 : 0
});
self.hunkIdleFrames.push(frame);
}
// Start battle idle animation
self.startBattleIdleAnimation();
} else if (hunk.hunkType === 2) {
// Create animation frames for battle display - hunk3
self.hunkIdleFrames = [];
self.currentBattleFrame = 0;
var frameAssets = ['hunk3', 'hunk3a', 'hunk3b', 'hunk3c', 'hunk3d'];
for (var i = 0; i < frameAssets.length; i++) {
var frame = self.hunkDisplay.attachAsset(frameAssets[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
alpha: i === 0 ? 1 : 0
});
self.hunkIdleFrames.push(frame);
}
// Start battle idle animation
self.startBattleIdleAnimation();
} else if (hunk.hunkType === 3) {
// Create animation frames for battle display - hunk4
self.hunkIdleFrames = [];
self.currentBattleFrame = 0;
var frameAssets = ['hunk4', 'hunk4a', 'hunk4b', 'hunk4c', 'hunk4d'];
for (var i = 0; i < frameAssets.length; i++) {
var frame = self.hunkDisplay.attachAsset(frameAssets[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
alpha: i === 0 ? 1 : 0
});
self.hunkIdleFrames.push(frame);
}
// Start battle idle animation
self.startBattleIdleAnimation();
} else {
// Normal static display for other hunks
var hunkGraphic = self.hunkDisplay.attachAsset(hunkAssets[hunk.hunkType], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
}
self.hunkDisplay.y = -200;
var barBg = self.attachAsset('resistanceBar', {
anchorX: 0.5,
anchorY: 0.5,
y: 50
});
self.resistanceFill = self.attachAsset('resistanceFill', {
anchorX: 0,
anchorY: 0.5,
x: -150,
y: 50
});
self.updateResistance(hunk.resistance, hunk.maxResistance);
var spell1 = self.attachAsset('spellButton', {
anchorX: 0.5,
anchorY: 0.5,
x: -200,
y: 200
});
spell1.interactive = true;
self.spellButtons.push(spell1);
var spell1Text = new Text2('Hypnotize', {
size: 40,
fill: 0xFFFFFF
});
spell1Text.anchor.set(0.5, 0.5);
spell1.addChild(spell1Text);
var spell2 = self.attachAsset('spellButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: 200
});
spell2.interactive = true;
self.spellButtons.push(spell2);
var spell2Text = new Text2('Charm', {
size: 40,
fill: 0xFFFFFF
});
spell2Text.anchor.set(0.5, 0.5);
spell2.addChild(spell2Text);
var captureBtn = self.attachAsset('crystal', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 350,
scaleX: 1.5,
scaleY: 1.5
});
captureBtn.interactive = true;
self.captureButton = captureBtn;
var captureText = new Text2('Capture', {
size: 30,
fill: 0xFFFFFF
});
captureText.anchor.set(0.5, 0.5);
captureBtn.addChild(captureText);
};
self.updateResistance = function (current, max) {
if (self.resistanceFill) {
self.resistanceFill.scale.x = current / max;
}
};
self.animationDirection = 1; // 1 for forward, -1 for reverse
self.startBattleIdleAnimation = function () {
if (!self.hunkIdleFrames || self.hunkIdleFrames.length === 0) return;
var currentFrame = self.hunkIdleFrames[self.currentBattleFrame];
// Calculate next frame with direction
var nextFrameIndex = self.currentBattleFrame + self.animationDirection;
// Check if we need to reverse direction
if (nextFrameIndex >= self.hunkIdleFrames.length - 1) {
nextFrameIndex = self.hunkIdleFrames.length - 1;
self.animationDirection = -1;
} else if (nextFrameIndex <= 0) {
nextFrameIndex = 0;
self.animationDirection = 1;
}
var nextFrame = self.hunkIdleFrames[nextFrameIndex];
tween(currentFrame, {
alpha: 0
}, {
duration: 150,
easing: tween.easeInOut
});
tween(nextFrame, {
alpha: 1
}, {
duration: 150,
easing: tween.easeInOut
});
self.currentBattleFrame = nextFrameIndex;
self.battleAnimationTimer = LK.setTimeout(function () {
self.startBattleIdleAnimation();
}, 200);
};
self.cleanup = function () {
if (self.battleAnimationTimer) {
LK.clearTimeout(self.battleAnimationTimer);
}
// Clean up dialogue boxes if they exist
if (self.playerDialogueBox) {
self.playerDialogueBox.destroy();
}
if (self.hunkDialogueBox) {
self.hunkDialogueBox.destroy();
}
// Clean up combat menu elements
if (self.combatMenu) {
self.combatMenu.destroy();
}
if (self.cursor) {
self.cursor.destroy();
}
self.destroy();
};
return self;
});
var Bloodmage = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('bloodmage', {
anchorX: 0.5,
anchorY: 1.0
});
self.velocityX = 0;
self.velocityY = 0;
self.grounded = false;
self.doubleJumpAvailable = true;
self.speed = 8;
self.jumpPower = 25;
self.gravity = 1.2;
self.isMoving = false;
self.idleFrames = [];
self.walkFrames = [];
self.currentIdleFrame = 0;
self.currentWalkFrame = 0;
self.idleAnimationTimer = 0;
self.walkAnimationTimer = 0;
self.jumpFrames = [];
self.currentJumpFrame = 0;
self.jumpAnimationTimer = 0;
self.isJumping = false;
self.facingDirection = -1; // -1 for right (assets face left), 1 for left
self.lastJumpTime = 0;
self.jumpCount = 0;
// Create idle animation frames with consistent scaling
var baseIdleScale = 1.0; // Standard scale for idle frames
self.idleFrames.push(self.attachAsset('playeridle', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 1,
scaleX: baseIdleScale,
scaleY: baseIdleScale
}));
self.idleFrames.push(self.attachAsset('playeridle2', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseIdleScale * 1.21,
// Adjust for narrower asset
scaleY: baseIdleScale
}));
self.idleFrames.push(self.attachAsset('playeridle3', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseIdleScale * 1.04,
// Adjust for slightly narrower asset
scaleY: baseIdleScale
}));
self.idleFrames.push(self.attachAsset('playeridle4', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseIdleScale * 0.96,
// Adjust for wider asset
scaleY: baseIdleScale
}));
self.idleFrames.push(self.attachAsset('playeridle5', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseIdleScale * 1.05,
// Adjust for narrower asset
scaleY: baseIdleScale
}));
self.idleFrames.push(self.attachAsset('playeridle6', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseIdleScale * 1.15,
scaleY: baseIdleScale
}));
// Add more idle animation frames for enhanced variety
self.idleFrames.push(self.attachAsset('playeridle9', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseIdleScale * 1.08,
scaleY: baseIdleScale
}));
self.idleFrames.push(self.attachAsset('playeridle10', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseIdleScale * 0.98,
scaleY: baseIdleScale
}));
self.idleFrames.push(self.attachAsset('playeridle11', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseIdleScale * 1.13,
scaleY: baseIdleScale
}));
self.idleFrames.push(self.attachAsset('playeridle12', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseIdleScale * 1.02,
scaleY: baseIdleScale
}));
// Create walking animation frames with consistent scaling
var baseWalkScale = 1.0; // Standard scale for walk frames
self.walkFrames.push(self.attachAsset('playerwalk', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 0.70,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk2', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 1.05,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk3', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 0.85,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk4', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 0.60,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk5', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 0.60,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk6', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 0.58,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk7', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 1.20,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk8', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 0.60,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk9', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 1.06,
// Fix warped frame - normalized scale
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk10', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 0.93,
// Adjust for narrower asset
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk11', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 1.51,
// Fix skinny frame
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk12', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseWalkScale * 0.78,
// Adjust for wider asset
scaleY: baseWalkScale
}));
// Create jump animation frames with consistent scaling
var baseJumpScale = 1.0; // Standard scale for jump frames
self.jumpFrames.push(self.attachAsset('playerjump', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseJumpScale * 0.60,
// Adjust for much wider asset
scaleY: baseJumpScale
}));
self.jumpFrames.push(self.attachAsset('playerjump2', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseJumpScale * 0.55,
// Adjust for much wider asset
scaleY: baseJumpScale
}));
self.jumpFrames.push(self.attachAsset('playerjump3', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseJumpScale * 0.52,
// Adjust for much wider asset
scaleY: baseJumpScale
}));
self.jumpFrames.push(self.attachAsset('playerjump4', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseJumpScale * 0.58,
// Adjust for much wider asset
scaleY: baseJumpScale
}));
self.jumpFrames.push(self.attachAsset('playerjump5', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseJumpScale * 0.56,
// Adjust for much wider asset
scaleY: baseJumpScale
}));
self.jumpFrames.push(self.attachAsset('playerjump6', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseJumpScale * 0.57,
// Adjust for much wider asset
scaleY: baseJumpScale
}));
// Create fly animation frames with consistent scaling
var baseFlyScale = 1.0;
self.flyFrames = [];
self.flyFrames.push(self.attachAsset('playerfly', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseFlyScale * 0.99,
scaleY: baseFlyScale
}));
self.flyFrames.push(self.attachAsset('playerfly2', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseFlyScale * 0.79,
scaleY: baseFlyScale
}));
self.flyFrames.push(self.attachAsset('playerfly3', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseFlyScale * 1.06,
scaleY: baseFlyScale
}));
self.flyFrames.push(self.attachAsset('playerfly4', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseFlyScale * 0.73,
scaleY: baseFlyScale
}));
self.flyFrames.push(self.attachAsset('playerfly5', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseFlyScale * 0.76,
scaleY: baseFlyScale
}));
self.flyFrames.push(self.attachAsset('playerfly6', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseFlyScale * 0.80,
scaleY: baseFlyScale
}));
// Create falling frames
self.fallFrames = [];
self.fallFrames.push(self.attachAsset('playerfly7', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseFlyScale * 0.97,
scaleY: baseFlyScale
}));
self.fallFrames.push(self.attachAsset('playerfly8', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseFlyScale * 0.79,
scaleY: baseFlyScale
}));
self.flyAnimationDirection = 1;
self.currentFlyFrame = 0;
self.isFlying = false;
self.idleAnimationDirection = 1; // 1 for forward, -1 for reverse
// REBUILT ANIMATION SYSTEM: Clear hierarchy with no conflicts
self.currentAnimationState = 'idle'; // Track current state: idle, walk, jump, fly, fall
self.clearAllAnimations = function () {
// Clear ALL timers first
LK.clearTimeout(self.idleAnimationTimer);
LK.clearTimeout(self.walkAnimationTimer);
LK.clearTimeout(self.jumpAnimationTimer);
LK.clearTimeout(self.flyAnimationTimer);
// Hide ALL frames
body.alpha = 0;
for (var i = 0; i < self.idleFrames.length; i++) {
self.idleFrames[i].alpha = 0;
}
for (var i = 0; i < self.walkFrames.length; i++) {
self.walkFrames[i].alpha = 0;
}
for (var i = 0; i < self.jumpFrames.length; i++) {
self.jumpFrames[i].alpha = 0;
}
for (var i = 0; i < self.flyFrames.length; i++) {
self.flyFrames[i].alpha = 0;
}
for (var i = 0; i < self.fallFrames.length; i++) {
self.fallFrames[i].alpha = 0;
}
};
self.startIdleAnimation = function () {
// ONLY start if truly idle - no other states active
if (self.isMoving || self.isJumping || self.isFlying || self.currentAnimationState !== 'idle') return;
// Clear everything first
self.clearAllAnimations();
// Verify idle frames exist (playeridle to playeridle12)
if (!self.idleFrames || self.idleFrames.length < 6) return;
// Show current idle frame ONLY
self.idleFrames[self.currentIdleFrame].alpha = 1;
// Calculate next frame with ping-pong effect
var nextFrameIndex = self.currentIdleFrame + self.idleAnimationDirection;
// Reverse direction at boundaries for smooth ping-pong
if (nextFrameIndex >= self.idleFrames.length - 1) {
nextFrameIndex = self.idleFrames.length - 1;
self.idleAnimationDirection = -1;
} else if (nextFrameIndex <= 0) {
nextFrameIndex = 0;
self.idleAnimationDirection = 1;
}
var currentFrame = self.idleFrames[self.currentIdleFrame];
var nextFrame = self.idleFrames[nextFrameIndex];
// Smooth cross-fade transition
tween(currentFrame, {
alpha: 0
}, {
duration: 120,
easing: tween.easeInOut
});
tween(nextFrame, {
alpha: 1
}, {
duration: 120,
easing: tween.easeInOut
});
self.currentIdleFrame = nextFrameIndex;
// Continue ONLY if still in idle state
self.idleAnimationTimer = LK.setTimeout(function () {
if (self.currentAnimationState === 'idle' && !self.isMoving && !self.isJumping && !self.isFlying) {
self.startIdleAnimation();
}
}, 180);
};
self.stopIdleAnimation = function () {
LK.clearTimeout(self.idleAnimationTimer);
// Hide main body and idle frames
body.alpha = 0;
for (var i = 0; i < self.idleFrames.length; i++) {
self.idleFrames[i].alpha = 0;
}
// Only start walking if we're actually moving
if (self.isMoving) {
self.startWalkingAnimation();
}
};
self.walkAnimationDirection = 1; // 1 for forward, -1 for reverse
self.startWalkingAnimation = function () {
if (!self.isMoving) return;
// Hide idle frames and show current walk frame
for (var i = 0; i < self.idleFrames.length; i++) {
self.idleFrames[i].alpha = 0;
}
for (var i = 0; i < self.walkFrames.length; i++) {
self.walkFrames[i].alpha = i === self.currentWalkFrame ? 1 : 0;
}
// Update facing direction based on velocity
if (self.velocityX > 0) self.facingDirection = -1; // Moving right, but assets face left by default
else if (self.velocityX < 0) self.facingDirection = 1; // Moving left, matches asset default
// Apply direction to all frames
for (var i = 0; i < self.walkFrames.length; i++) {
self.walkFrames[i].scale.x = Math.abs(self.walkFrames[i].scale.x) * self.facingDirection;
}
// Smoothly fade out current frame and fade in next frame
var currentFrame = self.walkFrames[self.currentWalkFrame];
// Calculate next frame with direction
var nextFrameIndex = self.currentWalkFrame + self.walkAnimationDirection;
// Check if we need to reverse direction
if (nextFrameIndex >= self.walkFrames.length - 1) {
nextFrameIndex = self.walkFrames.length - 1;
self.walkAnimationDirection = -1;
} else if (nextFrameIndex <= 0) {
nextFrameIndex = 0;
self.walkAnimationDirection = 1;
}
var nextFrame = self.walkFrames[nextFrameIndex];
tween(currentFrame, {
alpha: 0
}, {
duration: 50,
easing: tween.easeInOut
});
tween(nextFrame, {
alpha: 1
}, {
duration: 50,
easing: tween.easeInOut
});
self.currentWalkFrame = nextFrameIndex;
// Schedule next frame change
self.walkAnimationTimer = LK.setTimeout(function () {
if (self.isMoving) {
self.startWalkingAnimation();
}
}, 60);
};
self.stopWalkingAnimation = function () {
LK.clearTimeout(self.walkAnimationTimer);
// Hide walking frames
for (var i = 0; i < self.walkFrames.length; i++) {
self.walkFrames[i].alpha = 0;
}
};
self.startJumpAnimation = function () {
// Hide all other frames
body.alpha = 0;
for (var i = 0; i < self.idleFrames.length; i++) {
self.idleFrames[i].alpha = 0;
}
for (var i = 0; i < self.walkFrames.length; i++) {
self.walkFrames[i].alpha = 0;
}
// Apply direction to jump frames
for (var i = 0; i < self.jumpFrames.length; i++) {
self.jumpFrames[i].scale.x = Math.abs(self.jumpFrames[i].scale.x) * self.facingDirection;
}
// Use different frames based on jump type
var frameIndex = 0;
if (self.isFlying) {
// Flight mode uses playerjump3 (frame 2)
frameIndex = 2;
} else if (self.jumpCount === 1) {
// Double jump uses playerjump2 (frame 1)
frameIndex = 1;
} else {
// Regular jump uses playerjump (frame 0)
frameIndex = 0;
}
// Show appropriate jump frame
for (var i = 0; i < self.jumpFrames.length; i++) {
self.jumpFrames[i].alpha = i === frameIndex ? 1 : 0;
}
};
self.stopJumpAnimation = function () {
// Hide all jump frames
for (var i = 0; i < self.jumpFrames.length; i++) {
self.jumpFrames[i].alpha = 0;
}
};
self.startFlyAnimation = function () {
// Hide all other frames
body.alpha = 0;
for (var i = 0; i < self.idleFrames.length; i++) {
self.idleFrames[i].alpha = 0;
}
for (var i = 0; i < self.walkFrames.length; i++) {
self.walkFrames[i].alpha = 0;
}
for (var i = 0; i < self.jumpFrames.length; i++) {
self.jumpFrames[i].alpha = 0;
}
for (var i = 0; i < self.fallFrames.length; i++) {
self.fallFrames[i].alpha = 0;
}
// Apply direction to fly frames
for (var i = 0; i < self.flyFrames.length; i++) {
self.flyFrames[i].scale.x = Math.abs(self.flyFrames[i].scale.x) * self.facingDirection;
}
// Show current fly frame
for (var i = 0; i < self.flyFrames.length; i++) {
self.flyFrames[i].alpha = i === self.currentFlyFrame ? 1 : 0;
}
// Animate fly frames with ping-pong effect
var currentFrame = self.flyFrames[self.currentFlyFrame];
var nextFrameIndex = self.currentFlyFrame + self.flyAnimationDirection;
if (nextFrameIndex >= self.flyFrames.length - 1) {
nextFrameIndex = self.flyFrames.length - 1;
self.flyAnimationDirection = -1;
} else if (nextFrameIndex <= 0) {
nextFrameIndex = 0;
self.flyAnimationDirection = 1;
}
var nextFrame = self.flyFrames[nextFrameIndex];
tween(currentFrame, {
alpha: 0
}, {
duration: 80,
easing: tween.easeInOut
});
tween(nextFrame, {
alpha: 1
}, {
duration: 80,
easing: tween.easeInOut
});
self.currentFlyFrame = nextFrameIndex;
if (self.isFlying) {
self.flyAnimationTimer = LK.setTimeout(function () {
self.startFlyAnimation();
}, 120);
}
};
self.stopFlyAnimation = function () {
LK.clearTimeout(self.flyAnimationTimer);
for (var i = 0; i < self.flyFrames.length; i++) {
self.flyFrames[i].alpha = 0;
}
};
self.startFallAnimation = function () {
// Hide fly frames
for (var i = 0; i < self.flyFrames.length; i++) {
self.flyFrames[i].alpha = 0;
}
// Apply direction and show fall frames
for (var i = 0; i < self.fallFrames.length; i++) {
self.fallFrames[i].scale.x = Math.abs(self.fallFrames[i].scale.x) * self.facingDirection;
}
// Alternate between fall frames
var frameIndex = Math.floor(Date.now() / 150) % self.fallFrames.length;
for (var i = 0; i < self.fallFrames.length; i++) {
self.fallFrames[i].alpha = i === frameIndex ? 1 : 0;
}
};
self.stopFallAnimation = function () {
for (var i = 0; i < self.fallFrames.length; i++) {
self.fallFrames[i].alpha = 0;
}
};
self.update = function () {
// REBUILT PHYSICS SYSTEM: Clear separation of flight and ground physics
// FLIGHT MODE: Pure trackpad control with zero drift
if (self.isFlying && jumpButtonHeld) {
self.currentAnimationState = 'fly';
// ABSOLUTE ZERO DRIFT: Force all velocities to zero
self.velocityX = 0;
self.velocityY = 0;
self.grounded = false;
// ONLY move if trackpad is pressed with valid input
if (trackpadPressed && trackpadAngle !== undefined) {
var moveSpeed = self.speed * 0.8;
var moveX = Math.cos(trackpadAngle) * moveSpeed;
var moveY = Math.sin(trackpadAngle) * moveSpeed;
// Apply movement directly - NO velocity interference
self.x += moveX;
self.y += moveY;
// Update facing direction
if (moveX > 0) self.facingDirection = -1;else if (moveX < 0) self.facingDirection = 1;
}
// If no trackpad input: COMPLETE STILLNESS (no drift at all)
// Start/continue flight animation
if (self.currentAnimationState === 'fly') {
self.startFlyAnimation();
}
// Skip ALL other physics
return;
}
// GROUND/AIR PHYSICS: Traditional system
if (!self.grounded && !self.isFlying) {
// Falling
self.velocityY += self.gravity;
self.velocityX *= 0.98; // Air resistance
self.currentAnimationState = 'fall';
} else if (self.grounded) {
// Ground physics
self.velocityY += self.gravity;
}
// Apply velocities
self.x += self.velocityX;
self.y += self.velocityY;
// REBUILT ANIMATION STATE MACHINE
var newState = 'idle';
self.isMoving = Math.abs(self.velocityX) > 0.1;
self.isJumping = !self.grounded && !self.isFlying && Math.abs(self.velocityY) > 2;
if (self.isFlying) {
newState = 'fly';
} else if (self.isJumping) {
if (self.velocityY > 5) {
newState = 'fall';
} else {
newState = 'jump';
}
} else if (self.isMoving) {
newState = 'walk';
} else {
newState = 'idle';
}
// ONLY change animation if state actually changed
if (newState !== self.currentAnimationState) {
self.currentAnimationState = newState;
// Clear all animations before starting new one
self.clearAllAnimations();
// Start appropriate animation
if (newState === 'idle') {
self.startIdleAnimation();
} else if (newState === 'walk') {
self.startWalkingAnimation();
} else if (newState === 'jump') {
self.startJumpAnimation();
} else if (newState === 'fly') {
self.startFlyAnimation();
} else if (newState === 'fall') {
self.startFallAnimation();
}
}
};
self.jump = function () {
var jumpExecuted = false;
if (self.grounded) {
// First jump from ground
self.velocityY = -self.jumpPower;
self.grounded = false;
self.doubleJumpAvailable = true; // Reset double jump availability when leaving ground
LK.getSound('jump').play();
jumpExecuted = true;
} else if (self.doubleJumpAvailable && !self.grounded) {
// Double jump in air - always use higher power
self.velocityY = -self.jumpPower * 1.3;
self.doubleJumpAvailable = false;
LK.getSound('jump').play();
jumpExecuted = true;
// Create spell2 effect at the base of the character
var spellEffect = worldContainer.attachAsset('spell2', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y - 10,
// Position at base of character (shoes level)
alpha: 0.8
});
// Animate the spell effect - fade in and out quickly
tween(spellEffect, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
spellEffect.destroy();
}
});
}
return jumpExecuted;
};
return self;
});
var Building = Container.expand(function () {
var self = Container.call(this);
// Randomly choose between building assets
var buildingAsset = Math.random() > 0.5 ? 'building' : 'Building2';
var building = self.attachAsset(buildingAsset, {
anchorX: 0.5,
anchorY: 0.95
});
building.tint = 0x2a2a2a + Math.floor(Math.random() * 0x222222);
// Randomly choose between neon sign assets
var neonAsset = Math.random() > 0.5 ? 'neonSign' : 'neonsign2';
var neonSign = self.attachAsset(neonAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 200 - 100,
y: -building.height * 0.5 + Math.random() * 100
});
neonSign.alpha = 0.8;
// Apply random tint to neon signs for variety
neonSign.tint = [0xFF00FF, 0x00FFFF, 0xFFFF00, 0xFF0088, 0x88FF00][Math.floor(Math.random() * 5)];
// Animate neon sign with smooth pulsing
var animDuration = 1500 + Math.random() * 1000;
var _animateNeonSign = function animateNeonSign() {
tween(neonSign, {
alpha: 0.3
}, {
duration: animDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(neonSign, {
alpha: 0.8
}, {
duration: animDuration,
easing: tween.easeInOut,
onFinish: _animateNeonSign
});
}
});
};
_animateNeonSign();
return self;
});
var Hunk = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of the 4 hunk types
var hunkAssets = ['hunk', 'hunk2', 'hunk3', 'hunk4'];
self.hunkType = Math.floor(Math.random() * hunkAssets.length);
var body = self.attachAsset(hunkAssets[self.hunkType], {
anchorX: 0.5,
anchorY: 1.0
});
// Different resistance values for different hunk types
self.resistance = [100, 120, 150, 80][self.hunkType];
self.maxResistance = self.resistance;
self.captured = false;
self.idleFrames = [];
self.currentIdleFrame = 0;
self.idleAnimationTimer = 0;
self.idleAnimationDirection = 1; // 1 for forward, -1 for reverse
self.startIdleAnimation = function () {
if (self.hunkType !== 0 && self.hunkType !== 1 && self.hunkType !== 2 && self.hunkType !== 3 || self.captured) return;
// Get current and next frame
var currentFrame = self.idleFrames[self.currentIdleFrame];
// Calculate next frame with direction
var nextFrameIndex = self.currentIdleFrame + self.idleAnimationDirection;
// Check if we need to reverse direction
if (nextFrameIndex >= self.idleFrames.length - 1) {
nextFrameIndex = self.idleFrames.length - 1;
self.idleAnimationDirection = -1;
} else if (nextFrameIndex <= 0) {
nextFrameIndex = 0;
self.idleAnimationDirection = 1;
}
var nextFrame = self.idleFrames[nextFrameIndex];
// Smoothly fade between frames
tween(currentFrame, {
alpha: 0
}, {
duration: 150,
easing: tween.easeInOut
});
tween(nextFrame, {
alpha: 1
}, {
duration: 150,
easing: tween.easeInOut
});
self.currentIdleFrame = nextFrameIndex;
// Schedule next frame change
self.idleAnimationTime = LK.setTimeout(function () {
if (!self.captured && (self.hunkType === 0 || self.hunkType === 1 || self.hunkType === 2 || self.hunkType === 3)) {
self.startIdleAnimation();
}
}, 200);
};
// If this is hunk (index 0), set up idle animation with hunk and hunka-e
if (self.hunkType === 0) {
// Hide main body for animated hunk as we'll use animation frames
body.alpha = 0;
// Create idle animation frames for mushroom hunk with consistent scaling
var baseHunkScale = 1.0;
self.idleFrames.push(self.attachAsset('hunk', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 1,
scaleX: baseHunkScale,
scaleY: baseHunkScale
}));
self.idleFrames.push(self.attachAsset('hunka', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunkScale * 1.21,
// Adjust for narrower asset
scaleY: baseHunkScale
}));
self.idleFrames.push(self.attachAsset('hunkb', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunkScale * 1.21,
// Adjust for narrower asset
scaleY: baseHunkScale
}));
self.idleFrames.push(self.attachAsset('hunkc', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunkScale * 1.37,
// Adjust for much narrower asset
scaleY: baseHunkScale
}));
self.idleFrames.push(self.attachAsset('hunkd', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunkScale * 0.63,
// Adjust for wider asset
scaleY: baseHunkScale
}));
self.idleFrames.push(self.attachAsset('hunke', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunkScale * 1.07,
// Adjust for narrower asset
scaleY: baseHunkScale
}));
// Start idle animation
self.startIdleAnimation();
} else if (self.hunkType === 1) {
// Hide main body for animated hunk2 as we'll use animation frames
body.alpha = 0;
// Create idle animation frames for hunk2 with consistent scaling
var baseHunk2Scale = 1.0;
self.idleFrames.push(self.attachAsset('hunk2', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 1,
scaleX: baseHunk2Scale,
scaleY: baseHunk2Scale
}));
self.idleFrames.push(self.attachAsset('hunk2a', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk2Scale * 1.03,
// Adjust for slightly narrower asset
scaleY: baseHunk2Scale
}));
self.idleFrames.push(self.attachAsset('hunk2b', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk2Scale * 1.10,
// Adjust for narrower asset
scaleY: baseHunk2Scale
}));
self.idleFrames.push(self.attachAsset('hunk2c', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk2Scale * 1.35,
// Adjust for much narrower asset
scaleY: baseHunk2Scale
}));
self.idleFrames.push(self.attachAsset('hunk2d', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk2Scale * 0.98,
// Adjust for slightly wider asset
scaleY: baseHunk2Scale
}));
// Start idle animation
self.startIdleAnimation();
} else if (self.hunkType === 2) {
// Hide main body for animated hunk3 as we'll use animation frames
body.alpha = 0;
// Create idle animation frames for hunk3 with consistent scaling
var baseHunk3Scale = 1.0;
self.idleFrames.push(self.attachAsset('hunk3', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 1,
scaleX: baseHunk3Scale,
scaleY: baseHunk3Scale * 0.99
}));
self.idleFrames.push(self.attachAsset('hunk3a', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
//{7B}
scaleX: baseHunk3Scale * 1.04,
// Adjust for narrower asset
scaleY: baseHunk3Scale
}));
self.idleFrames.push(self.attachAsset('hunk3b', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk3Scale * 1.07,
// Adjust for narrower asset
scaleY: baseHunk3Scale
}));
self.idleFrames.push(self.attachAsset('hunk3c', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk3Scale * 1.04,
// Adjust for narrower asset
scaleY: baseHunk3Scale
}));
self.idleFrames.push(self.attachAsset('hunk3d', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk3Scale * 0.99,
// Adjust for slightly wider asset
scaleY: baseHunk3Scale
}));
// Start idle animation
self.startIdleAnimation();
} else if (self.hunkType === 3) {
// Hide main body for animated hunk4 as we'll use animation frames
body.alpha = 0;
// Create idle animation frames for hunk4 with consistent scaling
var baseHunk4Scale = 1.0;
self.idleFrames.push(self.attachAsset('hunk4', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 1,
scaleX: baseHunk4Scale,
scaleY: baseHunk4Scale
}));
self.idleFrames.push(self.attachAsset('hunk4a', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk4Scale * 1.26,
// Adjust for narrower asset
scaleY: baseHunk4Scale
}));
self.idleFrames.push(self.attachAsset('hunk4b', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk4Scale * 1.12,
// Adjust for narrower asset
scaleY: baseHunk4Scale
}));
self.idleFrames.push(self.attachAsset('hunk4c', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk4Scale * 1.18,
// Adjust for narrower asset
scaleY: baseHunk4Scale
}));
self.idleFrames.push(self.attachAsset('hunk4d', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
scaleX: baseHunk4Scale * 1.21,
// Adjust for narrower asset
scaleY: baseHunk4Scale
}));
// Start idle animation
self.startIdleAnimation();
}
self.stopIdleAnimation = function () {
if (self.idleAnimationTimer) {
LK.clearTimeout(self.idleAnimationTimer);
self.idleAnimationTimer = 0;
}
// Hide all idle frames
for (var i = 0; i < self.idleFrames.length; i++) {
self.idleFrames[i].alpha = 0;
}
};
// Override destroy to clean up animation
var originalDestroy = self.destroy;
self.destroy = function () {
self.stopIdleAnimation();
originalDestroy.call(self);
};
return self;
});
var Platform = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of the 4 platform types
var platformAssets = ['platform', 'platform2', 'platform3', 'platform4'];
var selectedPlatform = platformAssets[Math.floor(Math.random() * platformAssets.length)];
var platform = self.attachAsset(selectedPlatform, {
anchorX: 0.5,
anchorY: 0.5
});
// Store platform type for reference
self.platformType = selectedPlatform;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0a0a0a
});
/****
* Game Code
****/
game.setBackgroundColor(0x0a0a0a);
var bloodmage = null;
var platforms = [];
var buildings = [];
var hunks = [];
var clouds = [];
var currentHunk = null;
var battleUI = null;
// Removed inBattle - using proximity-based battle UI instead
var cameraX = 0;
var worldContainer = null;
// Zone system for procedural generation
var currentZone = 'City'; // Starting zone
var zoneTransitionThreshold = 2000; // Distance for zone transitions
// Zone definitions with environmental layers
var zones = {
City: {
backgrounds: ['bg', 'bg2', 'bg3', 'bg4', 'nocturnecitylayout'],
midground: ['building', 'Building2', 'building3', 'building4'],
midgroundUpper: ['neonSign', 'neonsign2'],
environmental: ['cloud1', 'cloud2', 'cloud3'],
platforms: ['platform', 'platform2', 'platform3', 'platform4'],
direction: 'base',
// Starting zone
parallaxLayers: [{
assets: ['bg4'],
speed: 0.1,
depth: 0
}, {
assets: ['bg3'],
speed: 0.3,
depth: 1
}, {
assets: ['bg2'],
speed: 0.5,
depth: 2
}, {
assets: ['bg'],
speed: 0.7,
depth: 3
}]
},
Cosmic: {
backgrounds: ['Galaxybg', 'galaxybg2', 'galaxybg3', 'galaxybg4'],
midground: ['galaxy', 'starlight2', 'galaxy2'],
environmental: ['galaxy'],
platforms: ['starlight', 'galaxy3', 'galaxy4', 'platform', 'platform2'],
// Add regular platforms for accessibility
direction: 'north',
parallaxLayers: [{
assets: ['galaxybg4'],
speed: 0.1,
depth: 0
}, {
assets: ['galaxybg3'],
speed: 0.3,
depth: 1
}, {
assets: ['galaxybg2'],
speed: 0.5,
depth: 2
}, {
assets: ['Galaxybg'],
speed: 0.7,
depth: 3
}]
},
Sea: {
backgrounds: ['sea', 'sea2', 'sea3', 'sea4'],
midground: ['caveoverlay'],
environmental: ['seaeffect'],
platforms: ['platform', 'platform2'],
direction: 'west',
parallaxLayers: [{
assets: ['sea4'],
speed: 0.1,
depth: 0
}, {
assets: ['sea3'],
speed: 0.3,
depth: 1
}, {
assets: ['sea2'],
speed: 0.5,
depth: 2
}, {
assets: ['sea'],
speed: 0.7,
depth: 3
}]
},
Under: {
backgrounds: ['underbg', 'underbg2', 'underbg3', 'underbg4', 'underbg5'],
midground: ['underoverlay', 'caveoverlay'],
environmental: ['undereffect'],
platforms: ['platform', 'platform3'],
direction: 'south',
parallaxLayers: [{
assets: ['underbg5'],
speed: 0.1,
depth: 0
}, {
assets: ['underbg4'],
speed: 0.3,
depth: 1
}, {
assets: ['underbg3'],
speed: 0.5,
depth: 2
}, {
assets: ['underbg2'],
speed: 0.7,
depth: 3
}]
},
Forest: {
backgrounds: ['forest', 'forest2', 'forest3'],
midground: ['trees', 'trees2', 'trees3'],
environmental: ['foresteffect', 'foresteffect2'],
platforms: ['platform', 'platform4'],
direction: 'east',
parallaxLayers: [{
assets: ['forest3'],
speed: 0.1,
depth: 0
}, {
assets: ['forest2'],
speed: 0.3,
depth: 1
}, {
assets: ['forest'],
speed: 0.7,
depth: 2
}]
}
};
var zoneElements = []; // Track zone-specific elements for cleanup
var environmentalEffects = []; // Track animated environmental effects
var trackpadPressed = false;
var trackpadAngle = 0;
var capturedHunks = storage.capturedHunks || [];
var brostiary = storage.brostiary || {};
var bropageOverlay = null;
var bropageShowing = false;
var currentBropageIndex = 0;
var lastJumpTime = 0;
var doubleTapThreshold = 300; // milliseconds for double tap detection
var isFlying = false;
var flyFrames = [];
var currentFlyFrame = 0;
var flyAnimationTimer = 0;
var flyAnimationDirection = 1;
var jumpHoldStartTime = 0;
var jumpButtonHeld = false;
var lastGeneratedX = 0; // Track the rightmost generated position
var backgroundElements = []; // Track all background elements for cleanup
var spellProjectiles = []; // Track active spell projectiles
var isCasting = false; // Track if player is casting
worldContainer = game.addChild(new Container());
function generateLevelChunk(startX) {
// Determine zone and get zone data
var zone = determineZone(startX);
var zoneData = zones[zone];
if (!zoneData) zoneData = zones.City; // Fallback
// STEP 1: Generate layered backgrounds with seamless edge blending
generateZoneBackgrounds(startX, zone, zoneData);
// STEP 2: Generate zone-specific midground and decorative elements
generateZoneContent(startX, zone, zoneData);
// STEP 3: Generate environmental effects and animations
generateZoneEnvironment(startX, zone, zoneData);
// STEP 4: Generate platforms and gameplay elements
generateZonePlatforms(startX, zone);
// Update generation tracking
lastGeneratedX = startX + 3000;
}
function generateZoneBackgrounds(startX, zone, zoneData) {
// Create multiple overlapping background layers for seamless coverage
for (var layerIndex = 0; layerIndex < zoneData.parallaxLayers.length; layerIndex++) {
var layer = zoneData.parallaxLayers[layerIndex];
// Generate 3-4 overlapping instances per layer for complete coverage
for (var instance = 0; instance < 4; instance++) {
var asset = layer.assets[Math.floor(Math.random() * layer.assets.length)];
var baseScale = zone === 'Cosmic' ? 4.2 : zone === 'Sea' ? 3.8 : zone === 'Under' ? 3.5 : zone === 'Forest' ? 3.2 : 3.0;
var finalScale = baseScale * (0.8 + Math.random() * 0.6);
// Overlapping positions to eliminate gaps
var xPos = startX + instance * 800 + (Math.random() - 0.5) * 400;
var yPos = 2732 + (Math.random() - 0.5) * 600;
var bg = worldContainer.attachAsset(asset, {
anchorX: 0.5,
anchorY: 1.0,
x: xPos,
y: yPos,
scaleX: finalScale,
scaleY: finalScale
});
// Enhanced zone-specific styling
bg.parallaxSpeed = layer.speed * (0.5 + Math.random() * 0.8);
bg.alpha = (0.4 + layerIndex * 0.15) * (0.7 + Math.random() * 0.3);
bg.zone = zone;
// Zone-specific tinting and atmosphere
if (zone === 'Cosmic') {
bg.tint = [0xDDDDFF, 0xCCCCEE, 0xBBBBDD][layerIndex % 3];
bg.alpha *= 1.2; // Brighter for cosmic
} else if (zone === 'Sea') {
bg.tint = [0xBBDDFF, 0xAABBEE, 0x99AADD][layerIndex % 3];
} else if (zone === 'Under') {
bg.tint = [0xEE9999, 0xDD8888, 0xCC7777][layerIndex % 3];
} else if (zone === 'Forest') {
bg.tint = [0xAADD99, 0x99CC88, 0x88BB77][layerIndex % 3];
} else {
bg.tint = [0xBBBBBB, 0xAAAAAA, 0x999999][layerIndex % 3];
}
backgroundElements.push(bg);
zoneElements.push(bg);
worldContainer.setChildIndex(bg, layerIndex * 2);
}
}
}
function generateZoneContent(startX, zone, zoneData) {
// Generate midground elements with proper layering
var midgroundCount = zone === 'Cosmic' ? 6 : 4;
for (var i = 0; i < midgroundCount; i++) {
if (zoneData.midground && zoneData.midground.length > 0) {
var asset = zoneData.midground[Math.floor(Math.random() * zoneData.midground.length)];
var scale = zone === 'Cosmic' ? 1.8 : zone === 'Forest' ? 1.4 : zone === 'Sea' ? 1.2 : 1.0;
scale *= 0.8 + Math.random() * 0.6;
var midground = worldContainer.attachAsset(asset, {
anchorX: 0.5,
anchorY: 1.0,
x: startX + 300 + i * 600 + (Math.random() - 0.5) * 200,
y: 1900 + Math.random() * 500,
scaleX: scale,
scaleY: scale
});
midground.zone = zone;
midground.alpha = 0.7 + Math.random() * 0.3;
// Zone-specific midground styling
if (zone === 'Cosmic') {
midground.tint = 0xDDDDFF;
// Add gentle cosmic pulsing
var originalScale = scale;
setInterval(function () {
if (midground && !midground.destroyed) {
tween(midground, {
scaleX: originalScale * (1.0 + Math.sin(Date.now() * 0.002) * 0.1),
scaleY: originalScale * (1.0 + Math.sin(Date.now() * 0.002) * 0.1)
}, {
duration: 100
});
}
}, 2000 + Math.random() * 3000);
} else if (zone === 'Sea') {
midground.tint = 0xCCEEFF;
} else if (zone === 'Under') {
midground.tint = 0xFFCCCC;
} else if (zone === 'Forest') {
midground.tint = 0xCCFFCC;
}
backgroundElements.push(midground);
zoneElements.push(midground);
worldContainer.setChildIndex(midground, 8 + i);
}
}
// Generate decorative elements
var decorCount = zone === 'Cosmic' ? 8 : 5;
for (var d = 0; d < decorCount; d++) {
if (Math.random() > 0.3) {
var decorAssets = getZoneDecorations(zone);
var decorAsset = decorAssets[Math.floor(Math.random() * decorAssets.length)];
var decoration = worldContainer.attachAsset(decorAsset, {
anchorX: 0.5,
anchorY: 1.0,
x: startX + d * 500 + Math.random() * 400,
y: 1600 + Math.random() * 800,
scaleX: 0.8 + Math.random() * 1.4,
scaleY: 0.8 + Math.random() * 1.4
});
// Zone-specific decoration styling
decoration.zone = zone;
decoration.alpha = 0.6 + Math.random() * 0.4;
if (zone === 'Cosmic') {
decoration.tint = 0xCCCCFF;
decoration.alpha = 0.9;
} else if (zone === 'Sea') {
decoration.tint = 0xAADDFF;
} else if (zone === 'Under') {
decoration.tint = 0xEE8888;
} else if (zone === 'Forest') {
decoration.tint = 0xAADD88;
} else {
decoration.tint = 0x999999;
}
backgroundElements.push(decoration);
zoneElements.push(decoration);
worldContainer.setChildIndex(decoration, 6);
}
}
}
function generateZoneEnvironment(startX, zone, zoneData) {
// Generate environmental effects
if (zoneData.environmental && zoneData.environmental.length > 0) {
var effectCount = zone === 'Cosmic' ? 10 : 6;
for (var i = 0; i < effectCount; i++) {
var asset = zoneData.environmental[Math.floor(Math.random() * zoneData.environmental.length)];
var scale = zone === 'Cosmic' ? 2.0 : 1.5;
scale *= 0.6 + Math.random() * 0.8;
var effect = worldContainer.attachAsset(asset, {
anchorX: 0.5,
anchorY: 0.5,
x: startX + Math.random() * 3000,
y: zone === 'Cosmic' ? 600 + Math.random() * 1600 : 1000 + Math.random() * 1400,
scaleX: scale,
scaleY: scale
});
effect.zone = zone;
effect.animationType = zone;
effect.driftSpeed = 0.3 + Math.random() * 1.2;
effect.alpha = 0.6 + Math.random() * 0.4;
// Zone-specific environmental behavior
if (zone === 'Cosmic') {
effect.tint = 0xEEEEFF;
effect.rotationSpeed = (Math.random() - 0.5) * 0.03;
effect.alpha = 0.8;
} else if (zone === 'Forest') {
effect.blinkTimer = Math.random() * 4000;
} else if (zone === 'Under') {
effect.floatAmplitude = 60 + Math.random() * 120;
effect.floatSpeed = 0.015 + Math.random() * 0.025;
}
environmentalEffects.push(effect);
zoneElements.push(effect);
worldContainer.setChildIndex(effect, 10);
}
}
// Generate zone-appropriate clouds/atmospheric elements
if (zone === 'City' || zone === 'Sea') {
generateAtmosphericClouds(startX, zone);
}
}
function generateZonePlatforms(startX, zone) {
// Generate main platforms with zone-specific layouts
var platformCount = zone === 'Cosmic' ? 10 : 7;
var platformSpacing = zone === 'Cosmic' ? 350 : 450;
for (var i = 0; i < platformCount; i++) {
var platform = new Platform();
platform.x = startX + 400 + i * platformSpacing;
// Zone-specific vertical arrangements
if (zone === 'Cosmic') {
// Ascending spiral with better accessibility
platform.y = 1500 - Math.sin(i * 0.6) * 250 - i * 40;
platform.scaleX = 1.3;
platform.scaleY = 1.3;
} else if (zone === 'Under') {
// Descending cave system
platform.y = 1700 + i % 3 * 120;
} else if (zone === 'Sea') {
// Wave-like pattern
platform.y = 1600 + Math.sin(i * 0.8) * 200;
} else if (zone === 'Forest') {
// Canopy levels
platform.y = 1650 - i % 4 * 150;
} else {
// Standard city pattern
platform.y = 1600 - i % 3 * 180;
}
platforms.push(platform);
worldContainer.addChild(platform);
// Add hunks to platforms
if (i % 2 === 1 && Math.random() > 0.4) {
var hunk = new Hunk();
hunk.x = platform.x + (Math.random() - 0.5) * 200;
hunk.y = platform.y - 20;
hunks.push(hunk);
worldContainer.addChild(hunk);
}
}
// Generate enhanced vertical connectivity networks
generateVerticalPlatformNetworks(startX, zone);
}
function generateVerticalPlatformNetworks(startX, zone) {
var verticalPlatforms = [];
// Generate zone-specific vertical platform arrangements
if (zone === 'Cosmic') {
// Multi-tier ascending cosmic pathways
for (var tier = 0; tier < 3; tier++) {
for (var step = 0; step < 4; step++) {
verticalPlatforms.push({
x: startX + 600 + tier * 800 + step * 300,
y: 1800 - tier * 400 - step * 100
});
}
}
} else if (zone === 'Under') {
// Descending underground network
for (var level = 0; level < 4; level++) {
for (var segment = 0; segment < 3; segment++) {
verticalPlatforms.push({
x: startX + 500 + level * 600 + segment * 200,
y: 1600 + level * 200 + segment * 50
});
}
}
} else {
// Standard multi-level networks for other zones
for (var level = 0; level < 3; level++) {
for (var pos = 0; pos < 5; pos++) {
verticalPlatforms.push({
x: startX + 400 + pos * 500,
y: 1800 - level * 300 + pos % 2 * 100
});
}
}
}
// Create the vertical platforms
for (var p = 0; p < verticalPlatforms.length; p++) {
var vPlatform = new Platform();
vPlatform.x = verticalPlatforms[p].x;
vPlatform.y = verticalPlatforms[p].y;
// Scale based on zone and height
var heightRatio = (2200 - vPlatform.y) / 1000;
vPlatform.scaleX = 0.9 + heightRatio * 0.4;
vPlatform.scaleY = 1.0;
platforms.push(vPlatform);
worldContainer.addChild(vPlatform);
// Add connection hunks
if (p % 3 === 1 && Math.random() > 0.5) {
var vHunk = new Hunk();
vHunk.x = vPlatform.x + (Math.random() - 0.5) * 150;
vHunk.y = vPlatform.y - 20;
hunks.push(vHunk);
worldContainer.addChild(vHunk);
}
}
}
function getZoneDecorations(zone) {
if (zone === 'City') {
return ['statue', 'crystal', 'artefact', 'artefact2', 'mandroid', 'tech', 'tokurrencoin', 'skilltree', 'door2', 'door3'];
} else if (zone === 'Cosmic') {
return ['starlight', 'galaxy3', 'crystal', 'artefact', 'starlight2', 'galaxy4', 'crystalheart'];
} else if (zone === 'Forest') {
return ['trees3', 'trees4', 'plush', 'plush2', 'plush3', 'crystal'];
} else if (zone === 'Sea') {
return ['caveoverlay', 'crystal', 'artefact2', 'underoverlay'];
} else if (zone === 'Under') {
return ['underoverlay', 'crystal', 'crystalheart', 'caveoverlay'];
}
return ['crystal', 'artefact']; // Fallback
}
function generateAtmosphericClouds(startX, zone) {
var cloudCount = zone === 'Sea' ? 8 : 6;
var cloudAssets = ['cloud1', 'cloud2', 'cloud3'];
for (var i = 0; i < cloudCount; i++) {
var cloudType = cloudAssets[Math.floor(Math.random() * cloudAssets.length)];
var cloud = worldContainer.attachAsset(cloudType, {
anchorX: 0.5,
anchorY: 0.5,
x: startX + Math.random() * 3000,
y: i < cloudCount / 2 ? 2200 + Math.random() * 400 : 900 + Math.random() * 600
});
var cloudScale = 0.6 + Math.random() * 1.2;
cloud.scale.x = cloudScale;
cloud.scale.y = cloudScale;
cloud.alpha = i < cloudCount / 2 ? 0.7 + Math.random() * 0.3 : 0.3 + Math.random() * 0.4;
cloud.driftSpeed = 0.4 + Math.random() * 1.0;
// Zone-specific cloud styling
if (zone === 'Sea') {
cloud.tint = 0xCCEEFF;
cloud.alpha *= 1.2;
}
clouds.push(cloud);
worldContainer.setChildIndex(cloud, 7);
}
// Enhanced vertical platform networks with better connectivity
var verticalPlatforms = [];
if (zone === 'Cosmic') {
// Cosmic zone: Multi-tier ascending spirals with connection platforms
verticalPlatforms = [
// Lower tier
{
x: startX + 300,
y: 1600
}, {
x: startX + 600,
y: 1500
}, {
x: startX + 900,
y: 1400
},
// Mid tier - ascending spiral
{
x: startX + 1200,
y: 1200
}, {
x: startX + 1500,
y: 1000
}, {
x: startX + 1800,
y: 800
},
// Upper tier - sky access
{
x: startX + 2100,
y: 600
}, {
x: startX + 2400,
y: 400
}, {
x: startX + 2700,
y: 200
},
// Connecting bridges
{
x: startX + 750,
y: 1350
}, {
x: startX + 1650,
y: 900
}, {
x: startX + 2550,
y: 500
}];
} else if (zone === 'Under') {
// Underground: Descending cave system with multiple levels
verticalPlatforms = [
// Entry level
{
x: startX + 400,
y: 1600
}, {
x: startX + 700,
y: 1700
},
// Mid descent
{
x: startX + 1000,
y: 1900
}, {
x: startX + 1300,
y: 2100
},
// Deep level
{
x: startX + 1600,
y: 2300
}, {
x: startX + 1900,
y: 2200
},
// Return path
{
x: startX + 2200,
y: 2000
}, {
x: startX + 2500,
y: 1800
}];
} else if (zone === 'Sea') {
// Sea zone: Wave-like platform arrangement with diving platforms
verticalPlatforms = [
// Surface waves
{
x: startX + 400,
y: 1400
}, {
x: startX + 700,
y: 1500
}, {
x: startX + 1000,
y: 1300
},
// Diving platforms
{
x: startX + 1300,
y: 1600
}, {
x: startX + 1600,
y: 1800
}, {
x: startX + 1900,
y: 1900
},
// Surfacing platforms
{
x: startX + 2200,
y: 1700
}, {
x: startX + 2500,
y: 1500
}];
} else if (zone === 'Forest') {
// Forest zone: Tree canopy levels
verticalPlatforms = [
// Ground level
{
x: startX + 400,
y: 1700
}, {
x: startX + 700,
y: 1600
},
// Canopy level
{
x: startX + 1000,
y: 1200
}, {
x: startX + 1300,
y: 1100
}, {
x: startX + 1600,
y: 1000
},
// Treetop level
{
x: startX + 1900,
y: 800
}, {
x: startX + 2200,
y: 900
}, {
x: startX + 2500,
y: 1100
}];
} else {
// City zone: Building-hop platforms with varied heights
verticalPlatforms = [
// Street level
{
x: startX + 400,
y: 1800
}, {
x: startX + 700,
y: 1700
},
// Mid-rise level
{
x: startX + 1000,
y: 1400
}, {
x: startX + 1300,
y: 1300
}, {
x: startX + 1600,
y: 1200
},
// High-rise level
{
x: startX + 1900,
y: 900
}, {
x: startX + 2200,
y: 1000
}, {
x: startX + 2500,
y: 1100
}];
}
for (var p = 0; p < verticalPlatforms.length; p++) {
var platform = new Platform();
platform.x = verticalPlatforms[p].x;
platform.y = verticalPlatforms[p].y;
// Scale platforms based on zone and height for better visibility
var heightRatio = (2000 - platform.y) / 1000;
platform.scaleX = 1.0 + heightRatio * 0.3;
platform.scaleY = 1.0;
platforms.push(platform);
worldContainer.addChild(platform);
// Add connection platforms between major height differences
if (p > 0) {
var prevPlatform = verticalPlatforms[p - 1];
var heightDiff = Math.abs(platform.y - prevPlatform.y);
if (heightDiff > 300) {
var midPlatform = new Platform();
midPlatform.x = (platform.x + prevPlatform.x) / 2;
midPlatform.y = (platform.y + prevPlatform.y) / 2;
midPlatform.scaleX = 0.8;
platforms.push(midPlatform);
worldContainer.addChild(midPlatform);
}
}
// Add hunks to strategic vertical positions
if (p % 3 === 1 && Math.random() > 0.4) {
var hunk = new Hunk();
hunk.x = platform.x + (Math.random() - 0.5) * 100;
hunk.y = platform.y - 20;
hunks.push(hunk);
worldContainer.addChild(hunk);
}
}
// Update last generated position
lastGeneratedX = startX + 2500;
}
function determineZone(x) {
// Enhanced zone determination with smoother transitions
var cityCenter = 2048;
var distanceFromCenter = x - cityCenter;
// Primary zone determination based on distance and direction
if (Math.abs(distanceFromCenter) < zoneTransitionThreshold * 0.8) {
return 'City'; // Core city area
}
// Enhanced directional zones with player position consideration
if (distanceFromCenter > zoneTransitionThreshold * 1.5) {
// Eastern regions - Forest zone
if (bloodmage && bloodmage.y > 2000) {
return 'Under'; // Deep forest underground
}
return 'Forest';
} else if (distanceFromCenter < -zoneTransitionThreshold * 1.5) {
// Western regions - Sea zone
if (bloodmage && bloodmage.y > 2100) {
return 'Under'; // Deep sea caverns
}
return 'Sea';
}
// Vertical zone determination when near city center
if (bloodmage) {
if (bloodmage.y < 1200) {
return 'Cosmic'; // High altitude areas
} else if (bloodmage.y > 2100) {
return 'Under'; // Underground areas
}
}
return 'City'; // Fallback to city
}
function generateZoneMidground(startX, zone) {
var zoneData = zones[zone];
if (!zoneData) return;
// Zone-specific scaling and positioning
var midgroundCount = zone === 'Cosmic' ? 5 : 3; // More cosmic elements for accessibility
var baseScale = zone === 'Cosmic' ? 1.5 : zone === 'Forest' ? 1.2 : zone === 'Sea' ? 1.0 : zone === 'Under' ? 0.9 : 0.8;
// Generate midground elements with improved layering
for (var i = 0; i < midgroundCount; i++) {
var asset = zoneData.midground[Math.floor(Math.random() * zoneData.midground.length)];
var layerDepth = Math.random() > 0.5 ? 0.8 : 1.2; // Vary depth for layering
var finalScale = baseScale * layerDepth * (0.9 + Math.random() * 0.4);
var midground = worldContainer.attachAsset(asset, {
anchorX: 0.5,
anchorY: 1.0,
x: startX + 200 + i * (zone === 'Cosmic' ? 500 : 700) + Math.random() * 300,
y: 1800 + Math.random() * 400,
scaleX: finalScale,
scaleY: finalScale
});
midground.zone = zone;
midground.alpha = zone === 'Cosmic' ? 0.8 + Math.random() * 0.2 : 0.7 + Math.random() * 0.3;
// Enhanced zone-specific tinting and effects
if (zone === 'Cosmic') {
midground.tint = 0xDDDDFF;
// Add gentle pulsing to cosmic elements
var pulseScale = finalScale;
tween(midground, {
scaleX: pulseScale * 1.1,
scaleY: pulseScale * 1.1
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(midground, {
scaleX: pulseScale,
scaleY: pulseScale
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut
});
}
});
} else if (zone === 'Sea') {
midground.tint = 0xCCEEFF;
} else if (zone === 'Under') {
midground.tint = 0xFFCCCC;
} else if (zone === 'Forest') {
midground.tint = 0xCCFFCC;
}
zoneElements.push(midground);
worldContainer.setChildIndex(midground, 5 + Math.floor(layerDepth));
}
// Add upper midground layer if available
if (zoneData.midgroundUpper) {
for (var i = 0; i < 2; i++) {
var asset = zoneData.midgroundUpper[Math.floor(Math.random() * zoneData.midgroundUpper.length)];
var upperMidground = worldContainer.attachAsset(asset, {
anchorX: 0.5,
anchorY: 0.5,
x: startX + 400 + i * 800,
y: 1200 + Math.random() * 400,
scaleX: 0.6 + Math.random() * 0.8,
scaleY: 0.6 + Math.random() * 0.8
});
upperMidground.zone = zone;
upperMidground.alpha = 0.8;
zoneElements.push(upperMidground);
}
}
}
function generateEnvironmentalEffects(startX, zone) {
var zoneData = zones[zone];
if (!zoneData || !zoneData.environmental) return;
// Zone-specific effect count and sizing
var effectCount = zone === 'Cosmic' ? 8 : 4; // More cosmic effects
var baseScale = zone === 'Cosmic' ? 1.5 : zone === 'Forest' ? 1.2 : 1.0;
// Generate animated environmental effects
for (var i = 0; i < effectCount; i++) {
var asset = zoneData.environmental[Math.floor(Math.random() * zoneData.environmental.length)];
var layeredScale = baseScale * (0.7 + Math.random() * 0.8);
var layerDepth = Math.random() * 3; // For varied layering
var effect = worldContainer.attachAsset(asset, {
anchorX: 0.5,
anchorY: 0.5,
x: startX + Math.random() * 2500,
y: zone === 'Cosmic' ? 800 + Math.random() * 1400 : 1000 + Math.random() * 1200,
// Higher cosmic range
scaleX: layeredScale,
scaleY: layeredScale
});
effect.zone = zone;
effect.animationType = zone; // Store for animation behavior
effect.driftSpeed = zone === 'Cosmic' ? 0.2 + Math.random() * 0.8 : 0.3 + Math.random() * 1.5;
effect.alpha = zone === 'Cosmic' ? 0.7 + Math.random() * 0.3 : 0.6 + Math.random() * 0.4;
// Enhanced cosmic effects
if (zone === 'Cosmic') {
effect.tint = 0xEEEEFF;
// Add subtle rotation for cosmic elements
effect.rotationSpeed = (Math.random() - 0.5) * 0.02;
}
// Zone-specific animation setup
if (zone === 'City' || zone === 'Sea') {
// Drifting clouds/effects
effect.driftDirection = Math.random() * Math.PI * 2;
} else if (zone === 'Forest') {
// Blinking forest effects
effect.blinkTimer = Math.random() * 3000;
} else if (zone === 'Under') {
// Floating underworld effects
effect.floatAmplitude = 50 + Math.random() * 100;
effect.floatSpeed = 0.02 + Math.random() * 0.03;
}
environmentalEffects.push(effect);
zoneElements.push(effect);
worldContainer.setChildIndex(effect, 6);
}
}
function cleanupOldElements() {
var cleanupThreshold = cameraX - 3000;
// Cleanup old background elements
for (var i = backgroundElements.length - 1; i >= 0; i--) {
if (backgroundElements[i].x < cleanupThreshold) {
backgroundElements[i].destroy();
backgroundElements.splice(i, 1);
}
}
// Cleanup old zone elements
for (var i = zoneElements.length - 1; i >= 0; i--) {
if (zoneElements[i].x < cleanupThreshold) {
zoneElements[i].destroy();
zoneElements.splice(i, 1);
}
}
// Cleanup old environmental effects
for (var i = environmentalEffects.length - 1; i >= 0; i--) {
if (environmentalEffects[i].x < cleanupThreshold) {
environmentalEffects[i].destroy();
environmentalEffects.splice(i, 1);
}
}
// Cleanup old buildings
for (var i = buildings.length - 1; i >= 0; i--) {
if (buildings[i].x < cleanupThreshold) {
buildings[i].destroy();
buildings.splice(i, 1);
}
}
// Cleanup old platforms
for (var i = platforms.length - 1; i >= 0; i--) {
if (platforms[i].x < cleanupThreshold) {
platforms[i].destroy();
platforms.splice(i, 1);
}
}
// Cleanup old hunks
for (var i = hunks.length - 1; i >= 0; i--) {
if (hunks[i].x < cleanupThreshold && hunks[i].captured) {
hunks[i].destroy();
hunks.splice(i, 1);
}
}
// Cleanup old clouds
for (var i = clouds.length - 1; i >= 0; i--) {
if (clouds[i].x < cleanupThreshold - 2000) {
clouds[i].destroy();
clouds.splice(i, 1);
}
}
}
function createLevel() {
// Add nocturnecity as the main side background layer to prevent player going behind elements
var nocturneCityBg = worldContainer.attachAsset('nocturnecity', {
anchorX: 0.5,
anchorY: 1.0,
x: 2500,
y: 2732,
scaleX: 2.5,
scaleY: 2.5
});
// Ensure nocturnecity stays at the very back
worldContainer.setChildIndex(nocturneCityBg, 0);
// Add bg2 as the furthest background layer to fill empty spaces
var background2 = worldContainer.attachAsset('bg2', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048,
y: 2732,
scaleX: 2.0,
scaleY: 2.0
});
background2.alpha = 0.7;
// Add main background layer on top
var background = worldContainer.attachAsset('bg', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048,
y: 2732,
scaleX: 0.8,
scaleY: 0.8
});
background.alpha = 0.8;
// Add additional colored background shapes to fill gaps
var bg3 = worldContainer.attachAsset('bg3', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048,
y: 2732,
scaleX: 30,
scaleY: 15
});
bg3.alpha = 0.3;
worldContainer.setChildIndex(bg3, 0); // Put behind other backgrounds
var bg4 = worldContainer.attachAsset('bg4', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048,
y: 2732,
scaleX: 25,
scaleY: 12
});
bg4.alpha = 0.2;
worldContainer.setChildIndex(bg4, 1); // Put behind main backgrounds
// Create clouds drifting beneath the starting platform
for (var i = 0; i < 12; i++) {
var cloudAssets = ['cloud1', 'cloud2', 'cloud3'];
var cloudType = cloudAssets[Math.floor(Math.random() * cloudAssets.length)];
var cloud = worldContainer.attachAsset(cloudType, {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 4096 - 1024,
// Spread across wider area
y: 2100 + Math.random() * 400 // Position below starting platform (1800-2000)
});
// Vary cloud sizes for depth
var cloudScale = 0.5 + Math.random() * 1.0;
cloud.scale.x = cloudScale;
cloud.scale.y = cloudScale;
// Make clouds more visible with better opacity
cloud.alpha = 0.8 + Math.random() * 0.2;
// Store initial position and drift speed for animation
cloud.initialX = cloud.x;
cloud.driftSpeed = 0.5 + Math.random() * 1.5;
clouds.push(cloud);
// Put clouds behind platforms but in front of backgrounds
worldContainer.setChildIndex(cloud, 4 + i);
}
// Create more buildings with varied positioning to fill the midground
for (var i = 0; i < 15; i++) {
var building = new Building();
building.x = 100 + i * 400;
building.y = 2000; // Position at ground level so buildings meet platforms
building.scale.x = 0.6 + Math.random() * 0.8; // More varied building sizes
building.scale.y = 0.6 + Math.random() * 0.8;
buildings.push(building);
worldContainer.addChild(building);
}
// Add additional statues for better edge concealment
for (var s = 0; s < 8; s++) {
var statue = worldContainer.attachAsset('statue', {
anchorX: 0.5,
anchorY: 1.0,
x: 500 + s * 800,
y: 2000,
scaleX: 0.7 + Math.random() * 0.6,
scaleY: 0.7 + Math.random() * 0.6
});
statue.tint = 0x555555 + Math.floor(Math.random() * 0x333333);
backgroundElements.push(statue);
// Ensure statues stay behind platforms
var statueIndex = worldContainer.getChildIndex(statue);
worldContainer.setChildIndex(statue, Math.max(0, statueIndex - 5));
}
// Add additional neon signs for better visual coverage
for (var n = 0; n < 15; n++) {
var neonAsset = Math.random() > 0.5 ? 'neonSign' : 'neonsign2';
var neonSign = worldContainer.attachAsset(neonAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: 200 + n * 450,
y: 1300 + Math.random() * 600,
scaleX: 0.6 + Math.random() * 0.8,
scaleY: 0.6 + Math.random() * 0.8
});
neonSign.alpha = 0.5 + Math.random() * 0.4;
neonSign.tint = [0xFF00FF, 0x00FFFF, 0xFFFF00, 0xFF0088, 0x88FF00][Math.floor(Math.random() * 5)];
backgroundElements.push(neonSign);
}
// Add more vertical decorative elements to fill gaps
for (var v = 0; v < 12; v++) {
var decorAssets = ['crystal', 'artefact', 'skilltree'];
var decorAsset = decorAssets[Math.floor(Math.random() * decorAssets.length)];
var decoration = worldContainer.attachAsset(decorAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: 400 + v * 500,
y: 1000 + Math.random() * 800,
scaleX: 1.5 + Math.random() * 1.0,
scaleY: 1.5 + Math.random() * 1.0
});
decoration.alpha = 0.4 + Math.random() * 0.3;
decoration.tint = 0x666666 + Math.floor(Math.random() * 0x999999);
backgroundElements.push(decoration);
var decorIndex = worldContainer.getChildIndex(decoration);
worldContainer.setChildIndex(decoration, Math.max(0, decorIndex - 8));
}
var ground = new Platform();
ground.x = 1024;
ground.y = 2000;
ground.scale.x = 10;
platforms.push(ground);
worldContainer.addChild(ground);
// Create repeating bottom platform foundation
for (var b = 0; b < 15; b++) {
var bottomPlatform = new Platform();
bottomPlatform.x = 300 + b * 400;
bottomPlatform.y = 2000; // Ground level
bottomPlatform.scale.x = 1.2;
bottomPlatform.scale.y = 0.8;
platforms.push(bottomPlatform);
worldContainer.addChild(bottomPlatform);
}
// Create platform layout based on nocturnecitylayout guide
var platformPositions = [{
x: 400,
y: 1800
},
// Starting platform
{
x: 800,
y: 1600
},
// Lower left area
{
x: 1200,
y: 1500
},
// Lower mid
{
x: 1600,
y: 1600
},
// Shop door platform
{
x: 2000,
y: 1400
},
// Mid level ascending
{
x: 2400,
y: 1200
},
// Mid upper
{
x: 2800,
y: 1000
},
// Upper mid
{
x: 3200,
y: 1100
},
// Plateau area
{
x: 3600,
y: 1300
},
// Right mid
{
x: 4000,
y: 1500
},
// Right lower
{
x: 4400,
y: 1200
},
// Far right upper
{
x: 4800,
y: 1000
},
// Far right peak
{
x: 5200,
y: 1300
},
// Extended plateau
{
x: 5600,
y: 1400
},
// Final ascent
{
x: 6000,
y: 1100
} // End platform
];
// Door positions based on layout guide
var doorPositions = [{
platformIndex: 2,
isShop: true
},
// Bottom left shop door
{
platformIndex: 5,
isShop: false
},
// Mid level door
{
platformIndex: 8,
isShop: false
} // Upper right door
];
for (var i = 0; i < platformPositions.length; i++) {
var platform = new Platform();
platform.x = platformPositions[i].x;
platform.y = platformPositions[i].y;
platforms.push(platform);
worldContainer.addChild(platform);
// Ensure platform appears in foreground
var platformIndex = worldContainer.getChildIndex(platform);
worldContainer.setChildIndex(platform, worldContainer.children.length - 1);
// Check if this platform should have a door
var doorInfo = doorPositions.find(function (d) {
return d.platformIndex === i;
});
if (doorInfo) {
// Add platform beneath door for proper standing surface
var doorPlatform = new Platform();
doorPlatform.x = platform.x;
doorPlatform.y = platform.y + 50; // Slightly below main platform
doorPlatform.scale.x = 1.2;
platforms.push(doorPlatform);
worldContainer.addChild(doorPlatform);
// Ensure door platform is in foreground
var doorPlatformIndex = worldContainer.getChildIndex(doorPlatform);
worldContainer.setChildIndex(doorPlatform, worldContainer.children.length - 1);
var door = worldContainer.attachAsset('door', {
anchorX: 0.5,
anchorY: 1.0,
x: platform.x,
y: platform.y - 20,
scaleX: 0.8,
scaleY: 0.8
});
door.interactive = true;
door.platformIndex = i;
door.isShop = doorInfo.isShop;
// Ensure door appears in front of platforms
var doorIndex = worldContainer.getChildIndex(door);
worldContainer.setChildIndex(door, worldContainer.children.length - 1);
if (doorInfo.isShop) {
doorAsset = door; // Store shop door reference
// Add hidesign floating over shop door
var hideSign = worldContainer.attachAsset('hidesign', {
anchorX: 0.5,
anchorY: 1.0,
x: platform.x,
y: platform.y - 600,
scaleX: 4,
scaleY: 4
});
hideSign.interactive = true;
hideSign.down = function (x, y, obj) {
enterShop();
};
// Animate sign floating
var _animateSign = function animateSign() {
tween(hideSign, {
y: platform.y - 330
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(hideSign, {
y: platform.y - 370
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: _animateSign
});
}
});
};
_animateSign();
} else {
// Non-shop doors get random interior
door.interiorAsset = 'interior' + (Math.floor(Math.random() * 8) + 1);
door.down = function () {
showInterior(this.interiorAsset);
};
}
}
}
// Add single hunks at strategic positions - positioned away from doors
var hunkPositions = [{
x: 1000,
y: 1480,
platformIndex: 1
},
// First hunk on safe platform
{
x: 3400,
y: 980,
platformIndex: 7
},
// Mid level hunk - away from doors
{
x: 5400,
y: 1280,
platformIndex: 13
} // Final hunk - clear area
];
for (var h = 0; h < hunkPositions.length; h++) {
var hunk = new Hunk();
hunk.x = hunkPositions[h].x;
hunk.y = hunkPositions[h].y;
hunks.push(hunk);
worldContainer.addChild(hunk);
}
// Add hunks inside interiors for immersion
var interiorHunks = [{
x: 2400,
y: 1000,
interior: true
}, {
x: 4200,
y: 900,
interior: true
}];
for (var ih = 0; ih < interiorHunks.length; ih++) {
var interiorHunk = new Hunk();
interiorHunk.x = interiorHunks[ih].x;
interiorHunk.y = interiorHunks[ih].y;
interiorHunk.isInterior = true;
hunks.push(interiorHunk);
worldContainer.addChild(interiorHunk);
}
bloodmage = new Bloodmage();
bloodmage.x = 400; // Start on first platform
bloodmage.y = 1780;
bloodmage.grounded = true; // Ensure character starts on ground
bloodmage.isMoving = false; // Ensure character is not moving
bloodmage.isJumping = false; // Ensure character is not jumping
bloodmage.isFlying = false; // Ensure character is not flying
worldContainer.addChild(bloodmage);
// CRITICAL: Ensure player appears in front of ALL background elements
var playerIndex = worldContainer.getChildIndex(bloodmage);
worldContainer.setChildIndex(bloodmage, worldContainer.children.length - 1);
// COMPLETELY REBUILT ANIMATION INITIALIZATION
// Set initial state
bloodmage.currentAnimationState = 'idle';
bloodmage.currentIdleFrame = 0;
bloodmage.idleAnimationDirection = 1;
bloodmage.isMoving = false;
bloodmage.isJumping = false;
bloodmage.isFlying = false;
// Clear ALL animations and timers
bloodmage.clearAllAnimations();
// Verify idle frames exist and are properly set up (playeridle to playeridle12)
if (bloodmage.idleFrames.length >= 6) {
// Show ONLY the first idle frame (playeridle)
bloodmage.idleFrames[0].alpha = 1;
bloodmage.currentIdleFrame = 0;
// Start proper idle animation immediately
LK.setTimeout(function () {
bloodmage.startIdleAnimation();
}, 300); // Slight delay to ensure everything is initialized
}
// Track initial generation position
lastGeneratedX = 6500; // Extended to accommodate new layout
}
createLevel();
var trackpadBg = LK.gui.bottomLeft.attachAsset('trackpadBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: -200
});
trackpadBg.interactive = true;
var trackpadThumb = LK.gui.bottomLeft.attachAsset('trackpadThumb', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: -200
});
var jumpBtn = LK.gui.bottomRight.attachAsset('jumpButton', {
anchorX: 0.5,
anchorY: 0.5,
x: -200,
y: -200
});
jumpBtn.interactive = true;
var actionBtn = LK.gui.bottomRight.attachAsset('actionbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: -200,
y: -450
});
actionBtn.interactive = true;
// Direct event handlers for better responsiveness
trackpadBg.down = function (x, y, obj) {
if (battleMenuVisible || bropageShowing || shopShowing) return;
trackpadPressed = true;
updateTrackpad(x, y);
};
trackpadBg.move = function (x, y, obj) {
if (!trackpadPressed || battleMenuVisible || bropageShowing || shopShowing) return;
updateTrackpad(x, y);
};
trackpadBg.up = function () {
trackpadPressed = false;
if (bloodmage) {
bloodmage.velocityX = 0;
}
trackpadThumb.x = 200;
trackpadThumb.y = -200;
};
jumpBtn.down = function () {
if (bloodmage && !battleMenuVisible && !bropageShowing && !shopShowing) {
jumpButtonHeld = true;
jumpHoldStartTime = Date.now();
var _animateButtonBlink = function animateButtonBlink() {
if (currentBlinkFrame < blinkFrames.length) {
var frameAsset = LK.getAsset(blinkFrames[currentBlinkFrame], {
anchorX: 0.5,
anchorY: 0.5
});
var blinkFrame = LK.gui.bottomRight.addChild(frameAsset);
blinkFrame.x = -200;
blinkFrame.y = -200;
blinkFrame.alpha = 0;
blinkFrame.scale.x = 0.8;
blinkFrame.scale.y = 0.8;
tween(blinkFrame, {
alpha: 0.8,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 60,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(blinkFrame, {
alpha: 0,
scaleX: 1.4,
scaleY: 1.4
}, {
duration: 60,
easing: tween.easeIn,
onFinish: function onFinish() {
blinkFrame.destroy();
currentBlinkFrame++;
if (currentBlinkFrame < blinkFrames.length) {
_animateButtonBlink();
}
}
});
}
});
}
};
var blinkFrames = ['buttonblink', 'buttonblink2', 'buttonblink3', 'buttonblink4'];
var currentBlinkFrame = 0;
_animateButtonBlink();
var currentTime = Date.now();
var timeSinceLastJump = currentTime - bloodmage.lastJumpTime;
// Check for double tap timing - but only if finger held down on second tap
if (timeSinceLastJump < doubleTapThreshold && bloodmage.jumpCount === 1) {
// Double tap detected - check if this is being held for flight
// Start flight mode immediately on double tap detection
bloodmage.isFlying = true;
isFlying = true;
// Strong initial upward boost for flight initiation
bloodmage.velocityY = -12; // Stronger boost to feel responsive
bloodmage.grounded = false;
bloodmage.startFlyAnimation();
LK.getSound('jump').play();
bloodmage.jumpCount = 0;
// Add visual flight initiation effect
var flightEffect = worldContainer.attachAsset('spell2', {
anchorX: 0.5,
anchorY: 0.5,
x: bloodmage.x,
y: bloodmage.y - 10,
alpha: 0.9,
scaleX: 0.8,
scaleY: 0.8
});
tween(flightEffect, {
alpha: 0,
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
flightEffect.destroy();
}
});
} else {
// Normal jump attempt - record this as first tap
var jumpSuccess = bloodmage.jump();
if (jumpSuccess) {
bloodmage.lastJumpTime = currentTime;
bloodmage.jumpCount = 1; // Mark as first jump for double-tap detection
}
}
}
};
jumpBtn.up = function () {
if (jumpButtonHeld && isFlying && bloodmage) {
// Release flight mode with graceful descent
jumpButtonHeld = false;
isFlying = false;
bloodmage.isFlying = false;
bloodmage.stopFlyAnimation();
bloodmage.startFallAnimation();
// Apply realistic falling physics with parabolic arc
var currentUpwardVelocity = Math.min(bloodmage.velocityY, 0);
bloodmage.velocityY = Math.max(currentUpwardVelocity, 3); // Gentle fall start
// Maintain some horizontal momentum for natural arc
bloodmage.velocityX *= 0.7; // Slight air resistance
// Add falling visual effect
var fallEffect = worldContainer.attachAsset('spell', {
anchorX: 0.5,
anchorY: 0.5,
x: bloodmage.x,
y: bloodmage.y - 20,
alpha: 0.6,
scaleX: 1.5,
scaleY: 0.8
});
tween(fallEffect, {
alpha: 0,
y: bloodmage.y + 50,
scaleX: 0.8,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
fallEffect.destroy();
}
});
}
jumpButtonHeld = false;
};
var jumpText = new Text2('JUMP', {
size: 40,
fill: 0xFFFFFF
});
jumpText.anchor.set(0.5, 0.5);
jumpBtn.addChild(jumpText);
var actionText = new Text2('CAST', {
size: 40,
fill: 0xFFFFFF
});
actionText.anchor.set(0.5, 0.5);
actionBtn.addChild(actionText);
actionBtn.down = function () {
if (bloodmage && !battleMenuVisible && !bropageShowing && !shopShowing && !isCasting) {
// Check if player is near door using the stored door reference
var nearDoor = false;
if (doorAsset) {
var dx = Math.abs(bloodmage.x - doorAsset.x);
var dy = Math.abs(bloodmage.y - doorAsset.y);
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 300) {
// Increased detection range further
nearDoor = true;
enterShop();
return; // Exit early to prevent casting
}
}
// Only cast spell if explicitly not near door
if (!nearDoor) {
castPlayerSpell();
}
}
};
// Score text removed - using Brostiary for hunk tracking instead
// Add brostiary icon to top right corner
var brostiaryIcon = LK.gui.topRight.attachAsset('brostiary', {
anchorX: 0.5,
anchorY: 0.5,
x: -150,
y: 150,
scaleX: 1.5,
scaleY: 1.5
});
brostiaryIcon.interactive = true;
brostiaryIcon.down = function () {
showBropage();
};
// Updated shop items with plush varieties and crystal hearts
var shopOverlay = null;
var shopShowing = false;
var currentShopPage = 0;
var shopItems = [{
name: 'crystalheart',
asset: 'crystalheart',
price: 50,
description: 'MYSTICAL CRYSTAL WITH ARCANE PROPERTIES',
useDisplay: true
}, {
name: 'plush',
asset: 'plush',
price: 30,
description: 'ADORABLE COMPANION FOR LONELY NIGHTS',
useShelf: true
}, {
name: 'plush2',
asset: 'plush2',
price: 35,
description: 'CUTE CUDDLY FRIEND',
useShelf: true
}, {
name: 'plush3',
asset: 'plush3',
price: 40,
description: 'PREMIUM PLUSH COMPANION',
useShelf: true
}, {
name: 'crystalheart',
asset: 'crystalheart',
price: 75,
description: 'RARE MYSTICAL HEART CRYSTAL',
useDisplay: true
}, {
name: 'crystalheart',
asset: 'crystalheart',
price: 100,
description: 'LEGENDARY HEART OF POWER',
useDisplay: true
}];
function showBropage() {
if (bropageShowing || battleMenuVisible) return;
bropageShowing = true;
currentBropageIndex = 0;
// Create overlay container
bropageOverlay = game.addChild(new Container());
// Add bropage background
var bgOverlay = bropageOverlay.attachAsset('bropage', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2,
scaleY: 2.5
});
bgOverlay.alpha = 0.95;
// Add exit button (X) in upper right corner
var exitButton = bropageOverlay.attachAsset('x', {
anchorX: 0.5,
anchorY: 0.5,
x: 1800,
y: 400,
scaleX: 1.2,
scaleY: 1.2
});
exitButton.interactive = true;
exitButton.down = function () {
closeBropage();
};
// Create function to display current hunk entry
var _displayHunkEntry = function displayHunkEntry() {
// Clear previous content (except background and exit button)
for (var i = bropageOverlay.children.length - 1; i >= 2; i--) {
bropageOverlay.children[i].destroy();
}
var hunkAssets = ['hunk', 'hunk2', 'hunk3', 'hunk4'];
var hunkNames = ['MUSHROOM HUNK', 'CYBER HUNK', 'MEGA HUNK', 'MINOTAUR HUNK'];
var hunkDescriptions = ['A MYSTERIOUS FUNGAL BEING WITH HYPNOTIC SPORES THAT CAN ENTRANCE ENEMIES.', 'A DIGITAL WARRIOR FROM THE CYBER REALM WITH ELECTROMAGNETIC POWERS.', 'AN ENORMOUS POWERHOUSE WITH INCREDIBLE STRENGTH AND ENDURANCE.', 'A POWERFUL BULL-HEADED WARRIOR WITH ANCIENT COMBAT TECHNIQUES AND BERSERKER RAGE.'];
var hunkAttacks = [['SPORE BURST', 'MIND MELD', 'FUNGAL SHIELD'], ['DATA DRAIN', 'FIREWALL', 'SYSTEM CRASH'], ['GROUND POUND', 'BULK UP', 'RAMPAGE'], ['HORN CHARGE', 'BERSERKER RAGE', 'BULL RUSH']];
var hunkStats = [{
hp: 100,
power: 25,
defense: 20,
speed: 15
}, {
hp: 120,
power: 30,
defense: 25,
speed: 20
}, {
hp: 150,
power: 40,
defense: 35,
speed: 10
}, {
hp: 80,
power: 35,
defense: 15,
speed: 30
}];
// Hunk image in upper left frame (positioned to fit within the frame outline)
if (brostiary[currentBropageIndex]) {
var hunkImage = bropageOverlay.attachAsset(hunkAssets[currentBropageIndex], {
anchorX: 0.5,
anchorY: 0.5,
x: 600,
y: 800,
scaleX: 1.0,
scaleY: 1.0
});
} else {
// Show silhouette or placeholder for uncaptured hunks
var placeholder = new Text2('?', {
size: 150,
fill: 0x666666
});
placeholder.anchor.set(0.5, 0.5);
placeholder.x = 600;
placeholder.y = 800;
bropageOverlay.addChild(placeholder);
}
// Hunk name (positioned in top left name field)
var nameText = new Text2(hunkNames[currentBropageIndex], {
size: 80,
fill: brostiary[currentBropageIndex] ? 0x00FFFF : 0x666666
});
nameText.anchor.set(0, 0.5);
nameText.x = 350;
nameText.y = 550;
bropageOverlay.addChild(nameText);
// Bio section (bottom right boxed area - positioned within asset's bio box)
if (brostiary[currentBropageIndex]) {
var descText = new Text2(hunkDescriptions[currentBropageIndex], {
size: 36,
fill: 0xFFFFFF,
wordWrap: true,
wordWrapWidth: 550
});
descText.anchor.set(0, 0);
descText.x = 1150;
descText.y = 1700;
bropageOverlay.addChild(descText);
// Attacks section (lower left side - positioned within asset's attacks box)
var attacks = hunkAttacks[currentBropageIndex];
for (var i = 0; i < attacks.length; i++) {
var attackText = new Text2(attacks[i], {
size: 42,
fill: 0xFFFF00
});
attackText.anchor.set(0, 0);
attackText.x = 380;
attackText.y = 1720 + i * 70;
bropageOverlay.addChild(attackText);
}
// Stats section (positioned within asset's stats area)
var stats = hunkStats[currentBropageIndex];
var statLabels = ['HP: ', 'POWER: ', 'DEFENSE: ', 'SPEED: '];
var statValues = [stats.hp, stats.power, stats.defense, stats.speed];
for (var i = 0; i < statLabels.length; i++) {
var statText = new Text2(statLabels[i] + statValues[i], {
size: 48,
fill: 0x00FF00
});
statText.anchor.set(0, 0);
statText.x = 1200;
statText.y = 1050 + i * 70;
bropageOverlay.addChild(statText);
}
} else {
var lockedText = new Text2('⚠ DATA ENCRYPTED ⚠', {
size: 80,
fill: 0xFF0000
});
lockedText.anchor.set(0.5, 0.5);
lockedText.x = 1024;
lockedText.y = 1400;
bropageOverlay.addChild(lockedText);
var sublockText = new Text2('CAPTURE HUNK TO UNLOCK', {
size: 50,
fill: 0x888888
});
sublockText.anchor.set(0.5, 0.5);
sublockText.x = 1024;
sublockText.y = 1500;
bropageOverlay.addChild(sublockText);
}
// Navigation arrows
// Previous arrow
if (currentBropageIndex > 0) {
var prevArrow = new Text2('◄', {
size: 120,
fill: 0xFFFF00
});
prevArrow.anchor.set(0.5, 0.5);
prevArrow.x = 250;
prevArrow.y = 1366;
prevArrow.interactive = true;
prevArrow.down = function () {
currentBropageIndex--;
_displayHunkEntry();
};
bropageOverlay.addChild(prevArrow);
}
// Next arrow
if (currentBropageIndex < 3) {
var nextArrow = new Text2('►', {
size: 120,
fill: 0xFFFF00
});
nextArrow.anchor.set(0.5, 0.5);
nextArrow.x = 1774;
nextArrow.y = 1366;
nextArrow.interactive = true;
nextArrow.down = function () {
currentBropageIndex++;
_displayHunkEntry();
};
bropageOverlay.addChild(nextArrow);
}
// Page indicator
var pageText = new Text2('[ ' + (currentBropageIndex + 1) + ' / 4 ]', {
size: 60,
fill: 0xFFFFFF
});
pageText.anchor.set(0.5, 0.5);
pageText.x = 1024;
pageText.y = 1750;
bropageOverlay.addChild(pageText);
};
// Display first entry
_displayHunkEntry();
}
function closeBropage() {
if (!bropageShowing || !bropageOverlay) return;
bropageShowing = false;
bropageOverlay.destroy();
bropageOverlay = null;
}
var doorAsset = null;
var hideSignAsset = null;
for (var i = 0; i < worldContainer.children.length; i++) {
var child = worldContainer.children[i];
if (child.texture && child.texture.baseTexture && child.texture.baseTexture.resource && child.texture.baseTexture.resource.url && child.texture.baseTexture.resource.url.includes('door')) {
doorAsset = child;
// Add direct door interaction
doorAsset.down = function (x, y, obj) {
enterShop();
};
} else if (child.texture && child.texture.baseTexture && child.texture.baseTexture.resource && child.texture.baseTexture.resource.url && child.texture.baseTexture.resource.url.includes('hidesign')) {
hideSignAsset = child;
// Add direct sign interaction
hideSignAsset.interactive = true;
hideSignAsset.down = function (x, y, obj) {
enterShop();
};
}
}
function enterShop() {
if (shopShowing || battleMenuVisible) return;
shopShowing = true;
currentShopPage = 0;
// Create transition overlay
var transitionOverlay = game.addChild(new Container());
// Start with door asset that expands
var expandingDoor = transitionOverlay.attachAsset('door', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.8
});
// Animate door expanding with enhanced transition
tween(expandingDoor, {
scaleX: 6.0,
scaleY: 6.0,
alpha: 0.9
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create the actual shop overlay
shopOverlay = game.addChild(new Container());
// Add shop background - single main asset properly sized
var shopBg = shopOverlay.attachAsset('shop', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
tween(shopBg, {
scaleX: 2.0,
scaleY: 2.7,
alpha: 0.95
}, {
duration: 600,
easing: tween.easeOut
});
// Add shop2 as overlay detail
var shopDetail = shopOverlay.attachAsset('shop2', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
tween(shopDetail, {
scaleX: 2.0,
scaleY: 2.7,
alpha: 0.6
}, {
duration: 700,
easing: tween.easeOut
});
// Add Mandroid character with entrance animation
var mandroid = shopOverlay.attachAsset('mandroid', {
anchorX: 0.5,
anchorY: 1.0,
x: 350,
y: 1800,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
tween(mandroid, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 1
}, {
duration: 700,
easing: tween.bounceOut
});
// Add dialogue box
var dialogueBox = shopOverlay.attachAsset('dialoguebox', {
anchorX: 0.5,
anchorY: 0.5,
x: 1400,
y: 1000,
scaleX: 12,
scaleY: 8,
alpha: 0
});
tween(dialogueBox, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
// Add Mandroid's name with innuendo
var mandroidName = new Text2('HARD-WARE', {
size: 180,
fill: 0xFFFFFF,
fontWeight: 'bold'
});
mandroidName.anchor.set(0.5, 0.5);
mandroidName.x = 350;
mandroidName.y = 1200;
mandroidName.alpha = 0;
// Add hot pink glow effect
mandroidName.style = {
fontSize: 180,
fill: 0xFFFFFF,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0xFF1493,
dropShadowBlur: 25,
dropShadowDistance: 0
};
shopOverlay.addChild(mandroidName);
tween(mandroidName, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
// Flirtatious dialogue options
var dialogueOptions = ["WELCOME TO MY BOUTIQUE, GORGEOUS~ *MECHANICAL PURR*", "FANCY SOME PREMIUM HARDWARE? I'VE GOT WHAT YOU NEED~", "MY CIRCUITS ARE OVERHEATING JUST LOOKING AT YOU, SUGAR", "CARE TO BROWSE MY... EXTENSIVE COLLECTION? *WINK*", "I SPECIALIZE IN BOTH FASHION AND... HARDER TO FIND ITEMS~"];
var currentDialogue = dialogueOptions[Math.floor(Math.random() * dialogueOptions.length)];
var dialogueText = new Text2(currentDialogue, {
size: 96,
fill: 0xFFFFFF,
fontWeight: 'bold',
wordWrap: true,
wordWrapWidth: 800
});
dialogueText.anchor.set(0.5, 0.5);
dialogueText.x = 1450;
dialogueText.y = 1000;
dialogueText.alpha = 0;
// Add hot pink glow
dialogueText.style = {
fontSize: 96,
fill: 0xFFFFFF,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0xFF1493,
dropShadowBlur: 20,
dropShadowDistance: 0,
wordWrap: true,
wordWrapWidth: 800
};
shopOverlay.addChild(dialogueText);
tween(dialogueText, {
alpha: 1
}, {
duration: 800,
easing: tween.easeOut
});
// Add exit button (X) in upper left corner
var exitButton = shopOverlay.attachAsset('x', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: 200,
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
});
exitButton.interactive = true;
exitButton.down = function () {
closeShop();
};
tween(exitButton, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
// Display shop items with delay
LK.setTimeout(function () {
displayShopItems();
}, 1000);
// Add navigation buttons
var prevButton = new Text2('◄ PREV', {
size: 100,
fill: 0xFFFFFF,
fontWeight: 'bold'
});
prevButton.anchor.set(0.5, 0.5);
prevButton.x = 200;
prevButton.y = 2400;
prevButton.interactive = true;
prevButton.alpha = 0;
// Add hot pink glow
prevButton.style = {
fontSize: 100,
fill: 0xFFFFFF,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0xFF1493,
dropShadowBlur: 15,
dropShadowDistance: 0
};
prevButton.down = function () {
if (currentShopPage > 0) {
currentShopPage--;
displayShopItems();
}
};
shopOverlay.addChild(prevButton);
tween(prevButton, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
var nextButton = new Text2('NEXT ►', {
size: 100,
fill: 0xFFFFFF,
fontWeight: 'bold'
});
nextButton.anchor.set(0.5, 0.5);
nextButton.x = 1800;
nextButton.y = 2400;
nextButton.interactive = true;
nextButton.alpha = 0;
// Add hot pink glow
nextButton.style = {
fontSize: 100,
fill: 0xFFFFFF,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0xFF1493,
dropShadowBlur: 15,
dropShadowDistance: 0
};
nextButton.down = function () {
var maxPages = Math.ceil(shopItems.length / 3) - 1;
if (currentShopPage < maxPages) {
currentShopPage++;
displayShopItems();
}
};
shopOverlay.addChild(nextButton);
tween(nextButton, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
// Add currency display using gold coins instead
var currencyText = new Text2('GOLD: ' + capturedHunks.length * 10, {
size: 100,
fill: 0xFFD700,
fontWeight: 'bold'
});
currencyText.anchor.set(0.5, 0.5);
currencyText.x = 1024;
currencyText.y = 2500;
currencyText.alpha = 0;
// Add gold glow
currencyText.style = {
fontSize: 100,
fill: 0xFFD700,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0xFF8C00,
dropShadowBlur: 15,
dropShadowDistance: 0
};
shopOverlay.addChild(currencyText);
tween(currencyText, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
function displayShopItems() {
// Clear previous items (keep first 10 children which are backgrounds and UI)
while (shopOverlay.children.length > 10) {
shopOverlay.children[shopOverlay.children.length - 1].destroy();
}
var itemsPerPage = 3;
var startIndex = currentShopPage * itemsPerPage;
var endIndex = Math.min(startIndex + itemsPerPage, shopItems.length);
for (var i = startIndex; i < endIndex; i++) {
var item = shopItems[i];
var localIndex = i - startIndex;
var itemX = 500 + localIndex * 500;
var itemY = 1800;
// Add appropriate display platform with better positioning
if (item.useDisplay) {
// Display platform for crystal hearts - positioned beneath item
var displayPlatform = shopOverlay.attachAsset('displayplatform', {
anchorX: 0.5,
anchorY: 0.5,
x: itemX,
y: itemY + 80,
scaleX: 2.5,
scaleY: 2.5
});
} else if (item.useShelf) {
// Shelf for plush items - positioned beneath item
var platShelf = shopOverlay.attachAsset('platshelf', {
anchorX: 0.5,
anchorY: 0.5,
x: itemX,
y: itemY + 100,
scaleX: 3,
scaleY: 3
});
}
// Item asset positioned above platform/shelf
var itemAsset = shopOverlay.attachAsset(item.asset, {
anchorX: 0.5,
anchorY: 1.0,
x: itemX,
y: itemY - 20,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0
});
// Animate item appearing
tween(itemAsset, {
alpha: 1,
y: itemY - 50
}, {
duration: 400,
easing: tween.bounceOut
});
// Use proper text instead of alphabet assets for better legibility
var itemNameText = new Text2(item.name.toUpperCase(), {
size: 48,
fill: 0xFFFFFF,
fontWeight: 'bold',
wordWrap: true,
wordWrapWidth: 300
});
itemNameText.anchor.set(0.5, 0.5);
itemNameText.x = itemX;
itemNameText.y = itemY + 150;
itemNameText.alpha = 0;
// Add glow effect for better visibility
itemNameText.style = {
fontSize: 48,
fill: 0xFFFFFF,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 8,
dropShadowDistance: 2,
wordWrap: true,
wordWrapWidth: 300
};
shopOverlay.addChild(itemNameText);
// Animate text appearing
tween(itemNameText, {
alpha: 1
}, {
duration: 400,
easing: tween.easeOut
});
// Item price with better visual hierarchy
var itemPrice = new Text2('PRICE: ' + item.price, {
size: 56,
fill: 0xFFFF00,
fontWeight: 'bold'
});
itemPrice.anchor.set(0.5, 0.5);
itemPrice.x = itemX;
itemPrice.y = itemY + 220;
itemPrice.alpha = 0;
// Add hot pink glow
itemPrice.style = {
fontSize: 56,
fill: 0xFFFF00,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0xFF1493,
dropShadowBlur: 15,
dropShadowDistance: 0
};
shopOverlay.addChild(itemPrice);
tween(itemPrice, {
alpha: 1
}, {
duration: 300,
easing: tween.easeOut
});
// Buy button with enhanced visual feedback
var buyButton = new Text2('BUY', {
size: 80,
fill: capturedHunks.length >= item.price ? 0x00FF00 : 0xFF0000,
fontWeight: 'bold'
});
buyButton.anchor.set(0.5, 0.5);
buyButton.x = itemX;
buyButton.y = itemY + 300;
buyButton.interactive = true;
buyButton.alpha = 0;
// Add hot pink glow
buyButton.style = {
fontSize: 80,
fill: capturedHunks.length >= item.price ? 0x00FF00 : 0xFF0000,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0xFF1493,
dropShadowBlur: 15,
dropShadowDistance: 0
};
buyButton.down = function (itemIndex) {
return function () {
// Add purchase animation
tween(buyButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(buyButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
purchaseItem(itemIndex);
};
}(i);
shopOverlay.addChild(buyButton);
tween(buyButton, {
alpha: 1
}, {
duration: 400,
easing: tween.easeOut
});
}
// Update navigation buttons
prevButton.alpha = currentShopPage > 0 ? 1 : 0.3;
var maxPages = Math.ceil(shopItems.length / 3) - 1;
nextButton.alpha = currentShopPage < maxPages ? 1 : 0.3;
// Update currency display
currencyText.setText('GOLD: ' + capturedHunks.length * 10);
}
// Clean up transition
transitionOverlay.destroy();
}
});
}
function showShop() {
enterShop();
}
function purchaseItem(itemIndex) {
var item = shopItems[itemIndex];
var goldAmount = capturedHunks.length * 10;
if (goldAmount >= item.price) {
// Calculate how many hunks to remove based on price
var hunksToRemove = Math.ceil(item.price / 10);
for (var i = 0; i < hunksToRemove && capturedHunks.length > 0; i++) {
capturedHunks.pop();
}
storage.capturedHunks = capturedHunks;
// Score text was removed, no need to update
// Show purchase success
LK.effects.flashScreen(0x00FF00, 500);
// Update Mandroid's dialogue
var successDialogues = ["EXCELLENT CHOICE, DARLING~ *MECHANICAL SATISFACTION*", "PLEASURE DOING BUSINESS WITH SUCH A STUNNING CUSTOMER~", "YOUR TASTE IS AS EXQUISITE AS YOUR APPEARANCE, SWEETIE", "COME BACK ANYTIME, BEAUTIFUL~ I'LL BE WAITING~"];
var dialogueText = shopOverlay.children.find(function (child) {
return child instanceof Text2 && child.x === 1400 && child.y === 1000;
});
if (dialogueText) {
dialogueText.setText(successDialogues[Math.floor(Math.random() * successDialogues.length)]);
}
displayShopItems();
} else {
// Not enough currency
LK.effects.flashScreen(0xFF0000, 500);
var dialogueText = shopOverlay.children.find(function (child) {
return child instanceof Text2 && child.x === 1400 && child.y === 1000;
});
if (dialogueText) {
dialogueText.setText("NOT ENOUGH HUNKS, SUGAR~ COME BACK WHEN YOU'RE RICHER");
}
}
}
function closeShop() {
if (!shopShowing || !shopOverlay) return;
shopShowing = false;
shopOverlay.destroy();
shopOverlay = null;
}
function castPlayerSpell() {
if (!bloodmage || isCasting) return;
isCasting = true;
LK.getSound('spell').play();
// Stop all animations first
bloodmage.stopIdleAnimation();
bloodmage.stopWalkingAnimation();
bloodmage.stopJumpAnimation();
// Hide ALL assets in bloodmage to ensure nothing remains visible
for (var i = 0; i < bloodmage.children.length; i++) {
bloodmage.children[i].alpha = 0;
}
// Hide all other frames
for (var i = 0; i < bloodmage.idleFrames.length; i++) {
bloodmage.idleFrames[i].alpha = 0;
}
for (var i = 0; i < bloodmage.walkFrames.length; i++) {
bloodmage.walkFrames[i].alpha = 0;
}
for (var i = 0; i < bloodmage.jumpFrames.length; i++) {
bloodmage.jumpFrames[i].alpha = 0;
}
// Create cast animation frames
var castFrames = ['cast', 'cast2', 'cast3', 'cast4', 'cast5', 'cast6', 'cast7', 'cast8', 'cast9'];
var castAnimFrames = [];
for (var i = 0; i < castFrames.length; i++) {
var frame = worldContainer.attachAsset(castFrames[i], {
anchorX: 0.5,
anchorY: 1.0,
x: bloodmage.x,
y: bloodmage.y,
alpha: 0
});
frame.scale.x = bloodmage.facingDirection;
castAnimFrames.push(frame);
}
// Animate through cast frames with smooth transitions
var currentCastFrame = 0;
function animateCast() {
if (currentCastFrame < castAnimFrames.length) {
// Fade out previous frame smoothly
if (currentCastFrame > 0) {
tween(castAnimFrames[currentCastFrame - 1], {
alpha: 0
}, {
duration: 100,
easing: tween.easeInOut
});
}
// Fade in current frame smoothly
var currentFrame = castAnimFrames[currentCastFrame];
tween(currentFrame, {
alpha: 1
}, {
duration: 100,
easing: tween.easeInOut
});
// Launch projectile when reaching spell8 frame (frame 7)
if (currentCastFrame === 7) {
var projectile = worldContainer.attachAsset('spell4', {
anchorX: 0.5,
anchorY: 0.5,
x: bloodmage.x + bloodmage.facingDirection * -80,
y: bloodmage.y - 350
});
projectile.velocity = bloodmage.facingDirection * -15;
projectile.scale.x = bloodmage.facingDirection;
// Add smooth fade-in for projectile
projectile.alpha = 0;
tween(projectile, {
alpha: 1
}, {
duration: 200,
easing: tween.easeOut
});
spellProjectiles.push(projectile);
}
currentCastFrame++;
// Slower frame rate for smoother animation
LK.setTimeout(animateCast, 120);
} else {
// Smooth fade out of last frame
tween(castAnimFrames[castAnimFrames.length - 1], {
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Clean up cast animation
for (var i = 0; i < castAnimFrames.length; i++) {
castAnimFrames[i].destroy();
}
isCasting = false;
// Resume idle animation
if (!bloodmage.isMoving && bloodmage.grounded) {
bloodmage.startIdleAnimation();
}
}
});
}
}
animateCast();
}
function updateCamera() {
if (bloodmage) {
var targetX = -bloodmage.x + 1024;
var targetY = -bloodmage.y + 1366;
cameraX += (targetX - cameraX) * 0.1;
// Add vertical camera tracking for flight mode
var currentCameraY = worldContainer.y || 0;
currentCameraY += (targetY - currentCameraY) * 0.1;
worldContainer.x = cameraX;
worldContainer.y = currentCameraY;
}
}
function checkCollisions() {
if (!bloodmage) return;
bloodmage.grounded = false;
for (var i = 0; i < platforms.length; i++) {
var platform = platforms[i];
var platLeft = platform.x - platform.width * platform.scale.x / 2;
var platRight = platform.x + platform.width * platform.scale.x / 2;
var platTop = platform.y - platform.height / 2;
var platBottom = platform.y + platform.height / 2;
var mageLeft = bloodmage.x - 40;
var mageRight = bloodmage.x + 40;
var mageTop = bloodmage.y - 120;
var mageBottom = bloodmage.y;
if (mageRight > platLeft && mageLeft < platRight && mageBottom > platTop && mageTop < platBottom) {
if (bloodmage.velocityY >= 0 && mageBottom - bloodmage.velocityY <= platTop) {
bloodmage.y = platTop;
bloodmage.velocityY = 0;
bloodmage.grounded = true;
bloodmage.jumpCount = 0; // Reset jump count when landing
}
}
}
if (bloodmage.y > 2500) {
bloodmage.x = 200;
bloodmage.y = 1800;
bloodmage.velocityX = 0;
bloodmage.velocityY = 0;
}
// Remove automatic proximity battle UI - now using tap-to-interact
}
// Global variables for battle UI management
var battleHunk = null;
var battleMenuVisible = false;
var combatMenuUI = null;
var cursorUI = null;
function showBattleUI() {
if (battleMenuVisible || !currentHunk) return;
battleMenuVisible = true;
// Create combat menu properly positioned and sized between trackpad and buttons
combatMenuUI = game.attachAsset('combatmenu', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2200,
scaleX: 2.8,
scaleY: 1.8,
alpha: 0
});
combatMenuUI.interactive = true;
// Animate combat UI appearing
tween(combatMenuUI, {
alpha: 0.95
}, {
duration: 600,
easing: tween.easeOut
});
// Position cursor with proper sizing for 4-option grid
cursorUI = game.attachAsset('cursor', {
anchorX: 0.5,
anchorY: 0.5,
x: 774,
y: 2050,
scaleX: 0.42,
scaleY: 0.56,
alpha: 0
});
// Animate cursor appearing
LK.setTimeout(function () {
tween(cursorUI, {
alpha: 1
}, {
duration: 400,
easing: tween.easeOut
});
}, 500);
window.currentSelectedOption = 0;
// Store global references for cleanup
window.currentCombatMenu = combatMenuUI;
window.currentCursor = cursorUI;
}
function hideBattleUI() {
if (!battleMenuVisible) return;
battleMenuVisible = false;
if (combatMenuUI) {
combatMenuUI.destroy();
combatMenuUI = null;
}
if (cursorUI) {
cursorUI.destroy();
cursorUI = null;
}
window.currentCombatMenu = null;
window.currentCursor = null;
}
function startBattle(hunk) {
// Don't lock player in battle mode, just set current hunk for proximity detection
currentHunk = hunk;
battleHunk = hunk;
}
function castSpell(damage) {
if (!currentHunk) return;
currentHunk.resistance = Math.max(0, currentHunk.resistance - damage);
LK.getSound('spell').play();
// Flash the hunk to show damage
LK.effects.flashObject(currentHunk, 0xFF00FF, 500);
// Create spell effect
var spellEffect = worldContainer.attachAsset('spell2', {
anchorX: 0.5,
anchorY: 0.5,
x: currentHunk.x,
y: currentHunk.y - 50,
alpha: 0.8
});
// Animate the spell effect
tween(spellEffect, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
spellEffect.destroy();
}
});
}
function attemptCapture() {
if (!currentHunk) return;
if (currentHunk.resistance < 30) {
LK.getSound('capture').play();
currentHunk.captured = true;
currentHunk.visible = false;
// Stop idle animation for animated hunk
if (currentHunk.hunkType === 0 || currentHunk.hunkType === 1 || currentHunk.hunkType === 2 || currentHunk.hunkType === 3) {
currentHunk.stopIdleAnimation();
}
capturedHunks.push(currentHunk.hunkType);
storage.capturedHunks = capturedHunks;
if (!brostiary[currentHunk.hunkType]) {
brostiary[currentHunk.hunkType] = true;
storage.brostiary = brostiary;
}
// scoreText removed - using Brostiary for hunk tracking instead
// Hide battle UI after capture
hideBattleUI();
currentHunk = null;
// Don't teleport player - they stay at current position
if (capturedHunks.length >= 10) {
LK.showYouWin();
}
} else {
// Flash screen red for failed capture
LK.effects.flashScreen(0xFF0000, 500);
}
}
game.down = function (x, y, obj) {
// Handle combat menu interaction when battle UI is visible
if (battleMenuVisible && window.currentCombatMenu) {
// Check if clicking within combat menu bounds (centered at 1024, 2200 with scale 2.8x1.8)
var menuLeft = 1024 - 350; // Full width of scaled menu (250 * 2.8 scale)
var menuRight = 1024 + 350;
var menuTop = 2200 - 225; // Full height of scaled menu (250 * 1.8 scale)
var menuBottom = 2200 + 225;
if (x >= menuLeft && x <= menuRight && y >= menuTop && y <= menuBottom) {
// 2x2 grid layout for 4 options
var relativeX = (x - menuLeft) / (menuRight - menuLeft);
var relativeY = (y - menuTop) / (menuBottom - menuTop);
var colIndex = relativeX < 0.5 ? 0 : 1; // Left or right column
var rowIndex = relativeY < 0.5 ? 0 : 1; // Top or bottom row
var newOption = rowIndex * 2 + colIndex; // Convert 2D to 1D index
newOption = Math.max(0, Math.min(3, newOption));
// Cursor positions for 2x2 grid layout with updated combat menu position
var positions = [{
x: 774,
y: 2050
}, {
x: 1274,
y: 2050
}, {
x: 774,
y: 2350
}, {
x: 1274,
y: 2350
}];
if (newOption === window.currentSelectedOption) {
// Same option clicked - select it with visual feedback
LK.effects.flashObject(window.currentCursor, 0x00FF00, 200);
selectCurrentOption();
} else {
// Different option - move cursor there with smooth animation
window.currentSelectedOption = newOption;
if (window.currentCursor && positions[window.currentSelectedOption]) {
// Flash cursor to show selection change
LK.effects.flashObject(window.currentCursor, 0xFFFF00, 150);
tween(window.currentCursor, positions[window.currentSelectedOption], {
duration: 200,
easing: tween.easeOut
});
}
}
return;
}
return;
}
if (bropageShowing || shopShowing) return;
// Check if tapping directly on door or sign area in world coordinates
var worldPos = worldContainer.toLocal({
x: x,
y: y
});
if (doorAsset) {
var doorDistance = Math.sqrt(Math.pow(worldPos.x - doorAsset.x, 2) + Math.pow(worldPos.y - doorAsset.y, 2));
if (doorDistance < 200) {
enterShop();
return;
}
}
if (hideSignAsset) {
var signDistance = Math.sqrt(Math.pow(worldPos.x - hideSignAsset.x, 2) + Math.pow(worldPos.y - hideSignAsset.y, 2));
if (signDistance < 150) {
enterShop();
return;
}
}
// Check if tapping on a hunk to start battle - more generous interaction
for (var i = 0; i < hunks.length; i++) {
var hunk = hunks[i];
if (!hunk.captured) {
// More generous proximity and tap area
var playerDistance = Math.sqrt(Math.pow(bloodmage.x - hunk.x, 2) + Math.pow(bloodmage.y - hunk.y, 2));
var tapDistance = Math.sqrt(Math.pow(worldPos.x - hunk.x, 2) + Math.pow(worldPos.y - hunk.y, 2));
if (playerDistance < 600 && tapDistance < 500) {
currentHunk = hunk;
showBattleUI();
return;
}
}
}
// Check if near door for action button press
var nearDoor = false;
if (bloodmage && doorAsset) {
var distance = Math.abs(bloodmage.x - doorAsset.x) + Math.abs(bloodmage.y - doorAsset.y);
if (distance < 200) {
nearDoor = true;
}
}
// Convert game coordinates to local GUI coordinates for trackpad
var localPos = LK.gui.bottomLeft.toLocal({
x: x,
y: y
});
var dx = localPos.x - 200;
var dy = localPos.y - -200;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 150) {
// Increased hit area
trackpadPressed = true;
trackpadAngle = Math.atan2(dy, dx);
updateTrackpad(x, y);
return; // Exit early to prevent overlap
}
// Check jump button
localPos = LK.gui.bottomRight.toLocal({
x: x,
y: y
});
dx = localPos.x - -200;
dy = localPos.y - -200;
dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 150 && bloodmage) {
jumpButtonHeld = true;
jumpHoldStartTime = Date.now();
var _animateButtonBlink2 = function animateButtonBlink() {
if (currentBlinkFrame < blinkFrames.length) {
// Get the current frame asset and apply it to the button
var frameAsset = LK.getAsset(blinkFrames[currentBlinkFrame], {
anchorX: 0.5,
anchorY: 0.5
});
// Replace button content temporarily
// Keep jump button visible - just layer blink on top
// Add blink frame to GUI
var blinkFrame = LK.gui.bottomRight.addChild(frameAsset);
blinkFrame.x = -200;
blinkFrame.y = -200;
// Animate the blink frame
tween(blinkFrame, {
alpha: 0.8
}, {
duration: 50,
onFinish: function onFinish() {
tween(blinkFrame, {
alpha: 0
}, {
duration: 50,
onFinish: function onFinish() {
blinkFrame.destroy();
currentBlinkFrame++;
if (currentBlinkFrame < blinkFrames.length) {
_animateButtonBlink2();
}
}
});
}
});
}
};
// Increased hit area
// Start button blink animation
var blinkFrames = ['buttonblink', 'buttonblink2', 'buttonblink3', 'buttonblink4'];
var currentBlinkFrame = 0;
_animateButtonBlink2();
var currentTime = Date.now();
var timeSinceLastJump = currentTime - bloodmage.lastJumpTime;
// Check for double tap timing - initiate flight if conditions met regardless of movement
if (timeSinceLastJump < doubleTapThreshold) {
// Double tap detected - initiate flight mode
bloodmage.isFlying = true;
isFlying = true;
// Gentle lift to enter flight mode smoothly
bloodmage.velocityY = Math.min(bloodmage.velocityY, -8); // Smooth entry
bloodmage.grounded = false;
bloodmage.startFlyAnimation();
LK.getSound('jump').play();
bloodmage.jumpCount = 0; // Reset for next sequence
// Visual flight effect
var flightEffect = worldContainer.attachAsset('spell2', {
anchorX: 0.5,
anchorY: 0.5,
x: bloodmage.x,
y: bloodmage.y - 10,
alpha: 0.9,
scaleX: 0.8,
scaleY: 0.8
});
tween(flightEffect, {
alpha: 0,
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
flightEffect.destroy();
}
});
} else {
// Normal jump attempt
var jumpSuccess = bloodmage.jump();
if (jumpSuccess) {
bloodmage.lastJumpTime = currentTime;
bloodmage.jumpCount = 1; // Mark first jump for double-tap detection
}
}
}
// Check action button
localPos = LK.gui.bottomRight.toLocal({
x: x,
y: y
});
dx = localPos.x - -200;
dy = localPos.y - -450;
dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 150 && bloodmage && !isCasting) {
// Double-check door proximity with improved calculation
var doorDistance = doorAsset ? Math.sqrt(Math.pow(bloodmage.x - doorAsset.x, 2) + Math.pow(bloodmage.y - doorAsset.y, 2)) : 999;
if (doorDistance < 300) {
enterShop();
return; // Exit early to prevent casting
} else {
castPlayerSpell();
}
}
// Check shop icon
localPos = LK.gui.topRight.toLocal({
x: x,
y: y
});
dx = localPos.x - -300;
dy = localPos.y - 150;
dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 75) {
showShop();
}
};
game.move = function (x, y, obj) {
if (!trackpadPressed || battleMenuVisible || bropageShowing || shopShowing) return;
updateTrackpad(x, y);
};
game.up = function () {
// Handle jump button release for flight mode
if (jumpButtonHeld && isFlying && bloodmage) {
// Release flight mode with graceful descent
jumpButtonHeld = false;
isFlying = false;
bloodmage.isFlying = false;
bloodmage.stopFlyAnimation();
bloodmage.startFallAnimation();
// Apply realistic falling physics with parabolic arc
var currentUpwardVelocity = Math.min(bloodmage.velocityY, 0);
bloodmage.velocityY = Math.max(currentUpwardVelocity, 3); // Gentle fall start
// Maintain some horizontal momentum for natural arc
bloodmage.velocityX *= 0.7; // Slight air resistance
// Add falling visual effect
var fallEffect = worldContainer.attachAsset('spell', {
anchorX: 0.5,
anchorY: 0.5,
x: bloodmage.x,
y: bloodmage.y - 20,
alpha: 0.6,
scaleX: 1.5,
scaleY: 0.8
});
tween(fallEffect, {
alpha: 0,
y: bloodmage.y + 50,
scaleX: 0.8,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
fallEffect.destroy();
}
});
}
// Handle trackpad release
trackpadPressed = false;
if (bloodmage) {
bloodmage.velocityX = 0;
}
trackpadThumb.x = 200;
trackpadThumb.y = -200;
// Always reset jump button held state on any touch release
jumpButtonHeld = false;
};
function updateTrackpad(x, y) {
// Convert to local trackpad coordinates
var localPos = LK.gui.bottomLeft.toLocal({
x: x,
y: y
});
var dx = localPos.x - 200;
var dy = localPos.y - -200;
var dist = Math.sqrt(dx * dx + dy * dy);
// Constrain to trackpad area
if (dist > 80) {
dx = dx / dist * 80;
dy = dy / dist * 80;
}
// Update visual trackpad thumb
trackpadThumb.x = 200 + dx;
trackpadThumb.y = -200 + dy;
if (bloodmage && dist > 8) {
// Calculate angle for ALL 8 directions with proper recognition
trackpadAngle = Math.atan2(dy, dx);
if (bloodmage.isFlying && jumpButtonHeld) {
// REBUILT FLIGHT CONTROL: Pure 8-directional movement
var flightSpeed = bloodmage.speed * 0.8;
// Calculate movement components for ALL 8 directions
var moveX = Math.cos(trackpadAngle) * flightSpeed;
var moveY = Math.sin(trackpadAngle) * flightSpeed;
// CRITICAL: The flight movement is handled in bloodmage.update()
// Just store the trackpad state - NO direct position changes here
bloodmage.velocityX = 0;
bloodmage.velocityY = 0;
bloodmage.grounded = false;
// Update facing direction
if (moveX > 0) bloodmage.facingDirection = -1;else if (moveX < 0) bloodmage.facingDirection = 1;
} else {
// Ground movement - horizontal only
bloodmage.velocityX = dx / 80 * bloodmage.speed * 3.5;
}
}
}
game.update = function () {
if (!battleMenuVisible) {
checkCollisions();
updateCamera();
// Update action button text based on door proximity
if (bloodmage && actionText && doorAsset) {
var dx = Math.abs(bloodmage.x - doorAsset.x);
var dy = Math.abs(bloodmage.y - doorAsset.y);
var distance = Math.sqrt(dx * dx + dy * dy);
var nearDoor = distance < 300;
actionText.setText(nearDoor ? 'ENTER' : 'CAST');
}
// Check if we need to generate more level
if (bloodmage && bloodmage.x > lastGeneratedX - 2000) {
generateLevelChunk(lastGeneratedX);
cleanupOldElements();
}
}
// Animate clouds drifting
for (var i = 0; i < clouds.length; i++) {
var cloud = clouds[i];
cloud.x += cloud.driftSpeed;
// For procedurally generated clouds, wrap them based on current view
var rightEdge = -cameraX + 3072;
var leftEdge = -cameraX - 1024;
if (cloud.x > rightEdge) {
cloud.x = leftEdge;
}
}
// Enhanced environmental effects animation system
for (var i = 0; i < environmentalEffects.length; i++) {
var effect = environmentalEffects[i];
if (!effect || !effect.zone || effect.destroyed) continue;
// Zone-specific environmental animations
if (effect.zone === 'Cosmic') {
// Enhanced cosmic animations
if (effect.rotationSpeed) {
effect.rotation += effect.rotationSpeed;
}
// Cosmic pulsing with varied timing
var cosmicPulse = 1.0 + Math.sin(Date.now() * 0.002 + i) * 0.15;
effect.scale.x = effect.scale.y = cosmicPulse;
// Subtle cosmic drift
effect.x += Math.sin(Date.now() * 0.001 + i) * 0.3;
effect.y += Math.cos(Date.now() * 0.0008 + i) * 0.2;
} else if (effect.zone === 'Forest') {
// Enhanced forest blinking effects
if (effect.blinkTimer !== undefined) {
effect.blinkTimer -= 16;
if (effect.blinkTimer <= 0) {
var targetAlpha = effect.alpha > 0.4 ? 0.1 : 0.9;
tween(effect, {
alpha: targetAlpha
}, {
duration: 300 + Math.random() * 600,
easing: tween.easeInOut
});
effect.blinkTimer = 2000 + Math.random() * 4000;
}
}
// Subtle forest swaying
effect.rotation = Math.sin(Date.now() * 0.001 + i) * 0.1;
} else if (effect.zone === 'Sea') {
// Enhanced sea effects - wave-like motion
effect.x += Math.cos(Date.now() * 0.001 + i * 0.5) * effect.driftSpeed;
effect.y += Math.sin(Date.now() * 0.0015 + i * 0.3) * (effect.driftSpeed * 0.6);
// Sea opacity waves
effect.alpha = 0.5 + Math.sin(Date.now() * 0.002 + i) * 0.3;
} else if (effect.zone === 'Under') {
// Enhanced underground floating effects
if (effect.floatAmplitude) {
var floatOffset = Math.sin(Date.now() * effect.floatSpeed + i) * effect.floatAmplitude * 0.03;
effect.y += floatOffset;
}
// Underground flickering
if (Math.random() < 0.002) {
tween(effect, {
alpha: effect.alpha * 0.3
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(effect, {
alpha: effect.alpha / 0.3
}, {
duration: 100,
easing: tween.easeInOut
});
}
});
}
} else if (effect.zone === 'City') {
// Enhanced city atmospheric effects
if (effect.driftDirection === undefined) {
effect.driftDirection = Math.random() * Math.PI * 2;
}
effect.x += Math.cos(effect.driftDirection) * effect.driftSpeed;
effect.y += Math.sin(effect.driftDirection) * effect.driftSpeed * 0.4;
// City light pulsing
if (Math.random() < 0.001) {
var originalTint = effect.tint || 0xFFFFFF;
tween(effect, {
tint: 0xFFFFAA
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(effect, {
tint: originalTint
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
}
}
// Apply parallax movement to background elements
for (var i = 0; i < backgroundElements.length; i++) {
var bg = backgroundElements[i];
if (bg.parallaxSpeed !== undefined) {
// Parallax offset based on camera movement and layer speed
bg.x += (cameraX - bg.x) * bg.parallaxSpeed * 0.1;
}
}
// Update spell projectiles
for (var i = spellProjectiles.length - 1; i >= 0; i--) {
var projectile = spellProjectiles[i];
projectile.x += projectile.velocity;
// Check if projectile is off screen
var screenLeft = -cameraX - 500;
var screenRight = -cameraX + 2548;
if (projectile.x < screenLeft || projectile.x > screenRight) {
projectile.destroy();
spellProjectiles.splice(i, 1);
continue;
}
// Check collision with hunks
for (var j = 0; j < hunks.length; j++) {
var hunk = hunks[j];
if (!hunk.captured && projectile.intersects(hunk)) {
// Reduce hunk resistance
hunk.resistance = Math.max(0, hunk.resistance - 25);
// Create visual effect
var effect = worldContainer.attachAsset('spell2', {
anchorX: 0.5,
anchorY: 0.5,
x: hunk.x,
y: hunk.y - 50,
alpha: 1
});
// Animate effect
tween(effect, {
alpha: 0,
scaleX: 2,
scaleY: 2,
rotation: Math.PI * 2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
effect.destroy();
}
});
// Flash the hunk
LK.effects.flashObject(hunk, 0xFF00FF, 300);
// Show resistance bar above hunk temporarily
var resistanceText = new Text2('RESISTANCE: ' + hunk.resistance + '/' + hunk.maxResistance, {
size: 60,
fill: hunk.resistance < 30 ? 0x00FF00 : 0xFF0000
});
resistanceText.anchor.set(0.5, 0.5);
resistanceText.x = hunk.x;
resistanceText.y = hunk.y - 150;
worldContainer.addChild(resistanceText);
// Auto-hide resistance text after 2 seconds
tween(resistanceText, {
alpha: 0
}, {
duration: 2000,
onFinish: function onFinish() {
resistanceText.destroy();
}
});
// Destroy projectile
projectile.destroy();
spellProjectiles.splice(i, 1);
break;
}
}
}
};
// Combat menu navigation functions removed - using direct touch interaction
function selectCurrentOption() {
if (!battleMenuVisible || !currentHunk) return;
var selectedOption = window.currentSelectedOption;
if (selectedOption === 0) {
// Enthrall (capture function) with enhanced animations
performCaptureAnimation();
} else if (selectedOption === 1) {
// Cast - perform casting animation
performBattleCast();
} else if (selectedOption === 2) {
// Speak - show dialogue with player response
showDualDialogue();
} else if (selectedOption === 3) {
// Leave - exit battle UI
hideBattleUI();
currentHunk = null;
}
}
function performCaptureAnimation() {
if (!currentHunk) return;
// Store player position to prevent falling
var castX = bloodmage.x;
var castY = bloodmage.y;
var wasGrounded = bloodmage.grounded;
// Disable physics completely during battle action
bloodmage.x = castX;
bloodmage.y = castY;
bloodmage.grounded = wasGrounded;
bloodmage.velocityX = 0;
bloodmage.velocityY = 0;
bloodmage.isMoving = false;
bloodmage.isFlying = false;
// Perform cast animation first
performPlayerCastAnimation();
// Throw capture device animation
var captureDevice = worldContainer.attachAsset('capturedevice', {
anchorX: 0.5,
anchorY: 0.5,
x: bloodmage.x,
y: bloodmage.y - 100,
scaleX: 1.5,
scaleY: 1.5
});
// Animate device flying toward hunk
tween(captureDevice, {
x: currentHunk.x,
y: currentHunk.y - 50,
rotation: Math.PI * 2
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create capture effect
var captureEffect = worldContainer.attachAsset('captureeffect', {
anchorX: 0.5,
anchorY: 0.5,
x: currentHunk.x,
y: currentHunk.y - 50,
scaleX: 0.1,
scaleY: 0.1
});
// Animate effect expanding
tween(captureEffect, {
scaleX: 3,
scaleY: 3,
alpha: 0.8
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// RNG based on hunk's resistance meter
var captureChance = 1 - currentHunk.resistance / currentHunk.maxResistance;
var captureRoll = Math.random();
if (captureRoll < captureChance) {
// Success - shrink hunk into device
LK.getSound('capture').play();
tween(currentHunk, {
scaleX: 0.1,
scaleY: 0.1,
x: captureDevice.x,
y: captureDevice.y
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
currentHunk.captured = true;
currentHunk.visible = false;
capturedHunks.push(currentHunk.hunkType);
storage.capturedHunks = capturedHunks;
if (!brostiary[currentHunk.hunkType]) {
brostiary[currentHunk.hunkType] = true;
storage.brostiary = brostiary;
}
// scoreText removed - using Brostiary for hunk tracking instead
showXPMenu(true);
captureDevice.destroy();
captureEffect.destroy();
// Restore player position and state
bloodmage.x = castX;
bloodmage.y = castY;
bloodmage.grounded = wasGrounded;
bloodmage.velocityX = 0;
bloodmage.velocityY = 0;
}
});
} else {
// Failed - device bounces off hunk
tween(captureDevice, {
x: captureDevice.x + (captureDevice.x - currentHunk.x) * 0.5,
y: captureDevice.y - 100,
rotation: Math.PI * 4
}, {
duration: 400,
easing: tween.bounceOut,
onFinish: function onFinish() {
captureDevice.destroy();
}
});
// Break free animation
var breakEffect = worldContainer.attachAsset('effect', {
anchorX: 0.5,
anchorY: 0.5,
x: currentHunk.x,
y: currentHunk.y,
scaleX: 2,
scaleY: 2
});
LK.effects.flashScreen(0xFF0000, 500);
tween(breakEffect, {
alpha: 0,
scaleX: 4,
scaleY: 4
}, {
duration: 600,
onFinish: function onFinish() {
breakEffect.destroy();
captureEffect.destroy();
// Restore player position
bloodmage.x = castX;
bloodmage.y = castY;
bloodmage.grounded = wasGrounded;
}
});
}
}
});
}
});
}
function performBattleCast() {
if (!currentHunk) return;
// Store player position and state to prevent falling
var playerGrounded = bloodmage.grounded;
var playerX = bloodmage.x;
var playerY = bloodmage.y;
var playerVelY = bloodmage.velocityY;
// Cast spell animation with spell behind hunk
var spellBehind = worldContainer.attachAsset('spell', {
anchorX: 0.5,
anchorY: 0.5,
x: currentHunk.x,
y: currentHunk.y,
scaleX: 5,
scaleY: 5,
alpha: 0.8
});
worldContainer.setChildIndex(spellBehind, worldContainer.getChildIndex(currentHunk) - 1);
// Pulsing spell2 at bottom of hunk
var spellPulse = worldContainer.attachAsset('spell2', {
anchorX: 0.5,
anchorY: 0.5,
x: currentHunk.x,
y: currentHunk.y + 50,
scaleX: 0.5,
scaleY: 0.5
});
// Animate pulsing
function pulseSpell() {
tween(spellPulse, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 1
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(spellPulse, {
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.6
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: pulseSpell
});
}
});
}
pulseSpell();
// Perform cast animation while keeping player in place
performPlayerCastAnimation();
// Damage hunk with additional sound effect
castSpell(25);
LK.getSound('climax').play();
// Clean up after 2 seconds
LK.setTimeout(function () {
if (spellBehind) spellBehind.destroy();
if (spellPulse) spellPulse.destroy();
// Restore player position and state
bloodmage.x = playerX;
bloodmage.y = playerY;
bloodmage.velocityY = playerGrounded ? 0 : playerVelY;
bloodmage.grounded = playerGrounded;
}, 2000);
}
function showDualDialogue() {
if (!currentHunk) return;
// Player dialogue floating above head
var playerDialogue = worldContainer.attachAsset('dialoguebox', {
anchorX: 0.5,
anchorY: 1.0,
x: bloodmage.x,
y: bloodmage.y - 400,
scaleX: 6,
scaleY: 4,
alpha: 0
});
// Animate dialogue appearing
tween(playerDialogue, {
alpha: 1,
y: bloodmage.y - 450
}, {
duration: 300,
easing: tween.easeOut
});
var playerText = new Text2('SUBMIT TO MY WILL!', {
size: 18,
fill: 0xFF00FF,
fontWeight: 'bold',
wordWrap: true,
wordWrapWidth: 120
});
playerText.anchor.set(0.5, 0.5);
playerText.x = 0;
playerText.y = 0;
playerDialogue.addChild(playerText);
// Hunk dialogue floating above head
var hunkDialogue = worldContainer.attachAsset('dialoguebox', {
anchorX: 0.5,
anchorY: 1.0,
x: currentHunk.x,
y: currentHunk.y - 400,
scaleX: 8,
scaleY: 6,
alpha: 0
});
// Animate dialogue appearing with slight delay
LK.setTimeout(function () {
tween(hunkDialogue, {
alpha: 1,
y: currentHunk.y - 450
}, {
duration: 300,
easing: tween.easeOut
});
}, 200);
var hunkNames = ['FUNGI-GUY', 'DJINN-TONIC', 'MINO-THROBBER', 'IGUANA-THRUST'];
var hunkDialogues = ['NEVER! MY SPORES PROTECT ME!', 'YOUR MAGIC IS WEAK, MORTAL!', 'I BOW TO NO ONE!', 'YOU CANNOT TAME THIS BEAST!'];
var hunkNameText = new Text2(hunkNames[currentHunk.hunkType], {
size: 32,
fill: 0xFF1493,
fontWeight: 'bold'
});
hunkNameText.anchor.set(0.5, 0);
hunkNameText.y = -40;
hunkDialogue.addChild(hunkNameText);
var hunkText = new Text2(hunkDialogues[currentHunk.hunkType], {
size: 20,
fill: 0xFFFFFF,
fontWeight: 'bold',
wordWrap: true,
wordWrapWidth: 160
});
hunkText.anchor.set(0.5, 0.5);
hunkText.y = 10;
hunkDialogue.addChild(hunkText);
// Play grunt sound for hunk dialogue
LK.getSound('grunt').play();
// Auto-hide both dialogues after 4 seconds with fade out
LK.setTimeout(function () {
tween(playerDialogue, {
alpha: 0,
y: bloodmage.y - 500
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
if (playerDialogue) playerDialogue.destroy();
}
});
tween(hunkDialogue, {
alpha: 0,
y: currentHunk.y - 500
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
if (hunkDialogue) hunkDialogue.destroy();
}
});
}, 4000);
}
function showXPMenu(captureSuccess) {
// Create XP menu overlay
var xpMenuOverlay = game.addChild(new Container());
var xpBg = xpMenuOverlay.attachAsset('xpmenu', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 12,
scaleY: 10,
alpha: 0
});
tween(xpBg, {
alpha: 0.95
}, {
duration: 500,
easing: tween.easeOut
});
// Calculate XP earned
var baseXP = 100;
var bonusXP = captureSuccess ? 50 : 0;
var totalXP = baseXP + bonusXP;
// XP text positioned within menu boundaries
var xpText = new Text2('EXPERIENCE GAINED: ' + totalXP, {
size: 60,
fill: 0x00FF00,
fontWeight: 'bold',
wordWrap: true,
wordWrapWidth: 600
});
xpText.anchor.set(0.5, 0.5);
xpText.x = 1024;
xpText.y = 1300;
xpText.alpha = 0;
xpMenuOverlay.addChild(xpText);
tween(xpText, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
if (captureSuccess) {
var successText = new Text2('HUNK CAPTURED!', {
size: 100,
fill: 0xFF00FF,
fontWeight: 'bold'
});
successText.anchor.set(0.5, 0.5);
successText.x = 1024;
successText.y = 1400;
successText.alpha = 0;
xpMenuOverlay.addChild(successText);
tween(successText, {
alpha: 1
}, {
duration: 800,
easing: tween.easeOut
});
}
// Continue button
var continueBtn = new Text2('CONTINUE', {
size: 60,
fill: 0xFFFFFF,
fontWeight: 'bold'
});
continueBtn.anchor.set(0.5, 0.5);
continueBtn.x = 1024;
continueBtn.y = 1600;
continueBtn.interactive = true;
continueBtn.alpha = 0;
continueBtn.down = function () {
xpMenuOverlay.destroy();
hideBattleUI();
};
xpMenuOverlay.addChild(continueBtn);
tween(continueBtn, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
}
function showHunkDialogue() {
var hunkNames = ['FUNGI-GUY', 'DJINN-TONIC', 'MINO-THROBBER', 'IGUANA-THRUST'];
var hunkDialogues = ['READY TO GET FUNGAL?', 'YOUR WISH IS MY COMMAND~', 'PREPARE TO BE DOMINATED!', 'TASTE MY SCALES~'];
// Create dialogue box only when speak is selected with proper sizing
var hunkDialogueBox = worldContainer.attachAsset('dialoguebox', {
anchorX: 0.5,
anchorY: 0.5,
x: currentHunk.x,
y: currentHunk.y - 200,
scaleX: 8,
scaleY: 6
});
var hunkNameText = new Text2(hunkNames[currentHunk.hunkType], {
size: 32,
fill: 0xFF1493,
fontWeight: 'bold'
});
hunkNameText.anchor.set(0.5, 0);
hunkNameText.y = -40;
hunkDialogueBox.addChild(hunkNameText);
var hunkText = new Text2(hunkDialogues[currentHunk.hunkType], {
size: 24,
fill: 0xFFFFFF,
fontWeight: 'bold',
wordWrap: true,
wordWrapWidth: 200
});
hunkText.anchor.set(0.5, 0.5);
hunkText.y = 20;
hunkDialogueBox.addChild(hunkText);
// Auto-hide dialogue after 3 seconds
LK.setTimeout(function () {
if (hunkDialogueBox) {
hunkDialogueBox.destroy();
}
}, 3000);
}
LK.playMusic('hedonism');
// REBUILT TITLE ANIMATION: Proper spacing and squeeze effect with wave momentum
function showTitle() {
var titleContainer = game.addChild(new Container());
// Letter positions for "NOCTURNE CITY" - NOCTURNE moved 0.5 inch left with proper spacing
var letters = [{
asset: 'n',
finalX: -600,
// NOCTURNE moved left 0.5 inch
startX: 2500 // Start from right side for squeeze effect
}, {
asset: 'o',
finalX: -500,
startX: 2500
}, {
asset: 'c',
finalX: -400,
startX: 2500
}, {
asset: 't',
finalX: -300,
startX: 2500
}, {
asset: 'u',
finalX: -200,
startX: 2500
}, {
asset: 'r',
finalX: -100,
startX: 2500
}, {
asset: 'n',
finalX: 0,
startX: 2500
}, {
asset: 'e',
finalX: 100,
startX: 2500
}, {
// Space between NOCTURNE and CITY - CITY in original position
asset: 'c',
finalX: 300,
// Proper gap between words
startX: 3000
}, {
asset: 'i',
finalX: 400,
startX: 3000
}, {
asset: 't',
finalX: 500,
startX: 3000
}, {
asset: 'y',
finalX: 600,
startX: 3000
}];
var letterAssets = [];
// Create all letters starting from right side
for (var i = 0; i < letters.length; i++) {
var letter = titleContainer.attachAsset(letters[i].asset, {
anchorX: 0.5,
anchorY: 0.5,
x: letters[i].startX,
y: 600,
// Above player position
scaleX: 0.5,
// Start small for squeeze effect
scaleY: 0.5,
alpha: 0
});
letterAssets.push(letter);
}
// Animate letters with wave momentum and squeeze effect
function animateSqueezeIn() {
for (var i = 0; i < letterAssets.length; i++) {
var letter = letterAssets[i];
var finalX = 1024 + letters[i].finalX;
// Each letter appears with staggered timing for wave effect
(function (index, targetLetter, targetX) {
LK.setTimeout(function () {
// Fade in letter
tween(targetLetter, {
alpha: 1
}, {
duration: 200,
easing: tween.easeOut
});
// Squeeze animation - expand and move to final position
tween(targetLetter, {
x: targetX,
scaleX: 2.8,
// Final scale
scaleY: 2.8
}, {
duration: 600,
easing: tween.bounceOut // Wave momentum effect
});
}, index * 120); // Staggered timing
})(i, letter, finalX);
}
// Hold for 3 seconds then fade out left to right
LK.setTimeout(function () {
animateLettersOut();
}, 3500);
}
// Animate letters fading out from left to right
function animateLettersOut() {
for (var i = 0; i < letterAssets.length; i++) {
(function (index, targetLetter) {
LK.setTimeout(function () {
tween(targetLetter, {
alpha: 0,
y: 400,
scaleX: 0.2,
scaleY: 0.2
}, {
duration: 300,
easing: tween.easeIn
});
}, index * 80);
})(i, letterAssets[i]);
}
// Clean up after fade
LK.setTimeout(function () {
titleContainer.destroy();
}, 2000);
}
// Start the squeeze animation
animateSqueezeIn();
}
// Show title animation on game start
showTitle();
;
var placeholderInteriorShowing = false;
var placeholderOverlay = null;
var interiorShowing = false;
var interiorOverlay = null;
function showInterior(interiorAsset) {
if (interiorShowing) return;
interiorShowing = true;
// Create interior overlay with door expand animation
var transitionOverlay = game.addChild(new Container());
var expandingDoor = transitionOverlay.attachAsset('door', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.8
});
// Animate door expanding
tween(expandingDoor, {
scaleX: 5.0,
scaleY: 5.0,
alpha: 0.95
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create actual interior overlay
interiorOverlay = game.addChild(new Container());
var interior = interiorOverlay.attachAsset(interiorAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
});
tween(interior, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
// Hide brostiary icon when interior is showing
if (brostiaryIcon) {
brostiaryIcon.alpha = 0;
}
// Add exit button
var exitBtn = interiorOverlay.attachAsset('x', {
anchorX: 0.5,
anchorY: 0.5,
x: 1800,
y: 300,
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
});
exitBtn.interactive = true;
exitBtn.down = function () {
closeInterior();
};
tween(exitBtn, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
// Cleanup transition
transitionOverlay.destroy();
}
});
}
function closeInterior() {
if (!interiorShowing || !interiorOverlay) return;
interiorShowing = false;
// Show brostiary icon again
if (brostiaryIcon) {
brostiaryIcon.alpha = 1;
}
interiorOverlay.destroy();
interiorOverlay = null;
}
function showPlaceholderInterior() {
showInterior('interior');
}
function performPlayerCastAnimation() {
if (!bloodmage || isCasting) return;
isCasting = true;
LK.getSound('spell').play();
// Store player position to maintain it during animation
var castX = bloodmage.x;
var castY = bloodmage.y;
var wasGrounded = bloodmage.grounded;
// Stop all animations first
bloodmage.stopIdleAnimation();
bloodmage.stopWalkingAnimation();
bloodmage.stopJumpAnimation();
// Hide ALL assets in bloodmage to ensure nothing remains visible
for (var i = 0; i < bloodmage.children.length; i++) {
bloodmage.children[i].alpha = 0;
}
// Hide all other frames
for (var i = 0; i < bloodmage.idleFrames.length; i++) {
bloodmage.idleFrames[i].alpha = 0;
}
for (var i = 0; i < bloodmage.walkFrames.length; i++) {
bloodmage.walkFrames[i].alpha = 0;
}
for (var i = 0; i < bloodmage.jumpFrames.length; i++) {
bloodmage.jumpFrames[i].alpha = 0;
}
// Create cast animation frames
var castFrames = ['cast', 'cast2', 'cast3', 'cast4', 'cast5', 'cast6'];
var castAnimFrames = [];
for (var i = 0; i < castFrames.length; i++) {
var frame = worldContainer.attachAsset(castFrames[i], {
anchorX: 0.5,
anchorY: 1.0,
x: castX,
y: castY,
alpha: 0
});
frame.scale.x = bloodmage.facingDirection;
castAnimFrames.push(frame);
}
// Animate through cast frames
var currentCastFrame = 0;
function animateCast() {
if (currentCastFrame < castAnimFrames.length) {
// Keep player in exact position
bloodmage.x = castX;
bloodmage.y = castY;
bloodmage.grounded = wasGrounded;
bloodmage.velocityY = 0;
// Fade out previous frame
if (currentCastFrame > 0) {
tween(castAnimFrames[currentCastFrame - 1], {
alpha: 0
}, {
duration: 100,
easing: tween.easeInOut
});
}
// Fade in current frame
var currentFrame = castAnimFrames[currentCastFrame];
tween(currentFrame, {
alpha: 1
}, {
duration: 100,
easing: tween.easeInOut
});
currentCastFrame++;
LK.setTimeout(animateCast, 120);
} else {
// Clean up cast animation
for (var i = 0; i < castAnimFrames.length; i++) {
castAnimFrames[i].destroy();
}
isCasting = false;
// Restore player position one final time
bloodmage.x = castX;
bloodmage.y = castY;
bloodmage.grounded = wasGrounded;
bloodmage.velocityY = 0;
// Resume idle animation
if (!bloodmage.isMoving && bloodmage.grounded) {
bloodmage.startIdleAnimation();
}
}
}
animateCast();
} ===================================================================
--- original.js
+++ change.js
@@ -1386,189 +1386,320 @@
var spellProjectiles = []; // Track active spell projectiles
var isCasting = false; // Track if player is casting
worldContainer = game.addChild(new Container());
function generateLevelChunk(startX) {
- // Determine zone based on position
+ // Determine zone and get zone data
var zone = determineZone(startX);
var zoneData = zones[zone];
if (!zoneData) zoneData = zones.City; // Fallback
- // Generate overlapping parallax background layers to conceal hard edges
- for (var layerPass = 0; layerPass < 2; layerPass++) {
- for (var i = 0; i < zoneData.parallaxLayers.length; i++) {
- var layer = zoneData.parallaxLayers[i];
+ // STEP 1: Generate layered backgrounds with seamless edge blending
+ generateZoneBackgrounds(startX, zone, zoneData);
+ // STEP 2: Generate zone-specific midground and decorative elements
+ generateZoneContent(startX, zone, zoneData);
+ // STEP 3: Generate environmental effects and animations
+ generateZoneEnvironment(startX, zone, zoneData);
+ // STEP 4: Generate platforms and gameplay elements
+ generateZonePlatforms(startX, zone);
+ // Update generation tracking
+ lastGeneratedX = startX + 3000;
+}
+function generateZoneBackgrounds(startX, zone, zoneData) {
+ // Create multiple overlapping background layers for seamless coverage
+ for (var layerIndex = 0; layerIndex < zoneData.parallaxLayers.length; layerIndex++) {
+ var layer = zoneData.parallaxLayers[layerIndex];
+ // Generate 3-4 overlapping instances per layer for complete coverage
+ for (var instance = 0; instance < 4; instance++) {
var asset = layer.assets[Math.floor(Math.random() * layer.assets.length)];
- // Create overlapping coverage with larger scales and multiple positions
- var baseScale = zone === 'Cosmic' ? 3.8 : zone === 'Sea' ? 3.2 : zone === 'Under' ? 3.0 : zone === 'Forest' ? 2.8 : 2.5;
- var layerScale = baseScale - i * 0.2;
- var finalScale = layerScale * (1.0 + Math.random() * 0.3);
- // Multiple positions to ensure edge coverage
- var positions = layerPass === 0 ? [startX + 1500, startX + 2048, startX + 2600] : [startX + 1200, startX + 2400, startX + 2900];
- for (var p = 0; p < positions.length; p++) {
- var bg = worldContainer.attachAsset(asset, {
- anchorX: 0.5,
- anchorY: 1.0,
- x: positions[p] + (Math.random() - 0.5) * 200,
- y: 2732 + (Math.random() - 0.5) * 300,
- scaleX: finalScale * (0.9 + Math.random() * 0.4),
- scaleY: finalScale * (0.9 + Math.random() * 0.4)
- });
- // Apply parallax movement with varied speeds for depth
- bg.parallaxSpeed = layer.speed * (0.8 + Math.random() * 0.4);
- bg.alpha = (zone === 'Cosmic' ? 0.5 + i * 0.1 : 0.3 + i * 0.12) * (layerPass === 0 ? 1.0 : 0.6);
- bg.zone = zone;
- // Enhanced tinting with gradual transitions
- if (zone === 'Cosmic') {
- bg.tint = layerPass === 0 ? 0xDDDDFF : 0xBBBBEE;
- bg.alpha = (0.6 + i * 0.1) * (layerPass === 0 ? 1.0 : 0.7);
- } else if (zone === 'Sea') {
- bg.tint = layerPass === 0 ? 0xBBDDFF : 0x99CCEE;
- } else if (zone === 'Under') {
- bg.tint = layerPass === 0 ? 0xEE9999 : 0xCC7777;
- } else if (zone === 'Forest') {
- bg.tint = layerPass === 0 ? 0xAADD99 : 0x88BB77;
- } else {
- bg.tint = layerPass === 0 ? 0xAAAAAA : 0x888888;
- }
- backgroundElements.push(bg);
- zoneElements.push(bg);
- worldContainer.setChildIndex(bg, layer.depth + layerPass);
+ var baseScale = zone === 'Cosmic' ? 4.2 : zone === 'Sea' ? 3.8 : zone === 'Under' ? 3.5 : zone === 'Forest' ? 3.2 : 3.0;
+ var finalScale = baseScale * (0.8 + Math.random() * 0.6);
+ // Overlapping positions to eliminate gaps
+ var xPos = startX + instance * 800 + (Math.random() - 0.5) * 400;
+ var yPos = 2732 + (Math.random() - 0.5) * 600;
+ var bg = worldContainer.attachAsset(asset, {
+ anchorX: 0.5,
+ anchorY: 1.0,
+ x: xPos,
+ y: yPos,
+ scaleX: finalScale,
+ scaleY: finalScale
+ });
+ // Enhanced zone-specific styling
+ bg.parallaxSpeed = layer.speed * (0.5 + Math.random() * 0.8);
+ bg.alpha = (0.4 + layerIndex * 0.15) * (0.7 + Math.random() * 0.3);
+ bg.zone = zone;
+ // Zone-specific tinting and atmosphere
+ if (zone === 'Cosmic') {
+ bg.tint = [0xDDDDFF, 0xCCCCEE, 0xBBBBDD][layerIndex % 3];
+ bg.alpha *= 1.2; // Brighter for cosmic
+ } else if (zone === 'Sea') {
+ bg.tint = [0xBBDDFF, 0xAABBEE, 0x99AADD][layerIndex % 3];
+ } else if (zone === 'Under') {
+ bg.tint = [0xEE9999, 0xDD8888, 0xCC7777][layerIndex % 3];
+ } else if (zone === 'Forest') {
+ bg.tint = [0xAADD99, 0x99CC88, 0x88BB77][layerIndex % 3];
+ } else {
+ bg.tint = [0xBBBBBB, 0xAAAAAA, 0x999999][layerIndex % 3];
}
+ backgroundElements.push(bg);
+ zoneElements.push(bg);
+ worldContainer.setChildIndex(bg, layerIndex * 2);
}
}
- // Generate zone-specific content
- var zone = determineZone(startX);
- generateZoneMidground(startX, zone);
- generateEnvironmentalEffects(startX, zone);
- // Add zone-appropriate decorations with more varied asset usage
- for (var d = 0; d < 3; d++) {
- if (Math.random() > 0.4) {
- var decorAssets = zone === 'City' ? ['statue', 'crystal', 'artefact', 'artefact2', 'mandroid', 'tech', 'tokurrencoin', 'skilltree'] : zone === 'Cosmic' ? ['starlight', 'galaxy3', 'crystal', 'artefact', 'starlight2'] : zone === 'Forest' ? ['trees3', 'trees4', 'plush', 'plush2', 'plush3'] : zone === 'Sea' ? ['caveoverlay', 'crystal', 'artefact2'] : ['underoverlay', 'crystal', 'crystalheart'];
+}
+function generateZoneContent(startX, zone, zoneData) {
+ // Generate midground elements with proper layering
+ var midgroundCount = zone === 'Cosmic' ? 6 : 4;
+ for (var i = 0; i < midgroundCount; i++) {
+ if (zoneData.midground && zoneData.midground.length > 0) {
+ var asset = zoneData.midground[Math.floor(Math.random() * zoneData.midground.length)];
+ var scale = zone === 'Cosmic' ? 1.8 : zone === 'Forest' ? 1.4 : zone === 'Sea' ? 1.2 : 1.0;
+ scale *= 0.8 + Math.random() * 0.6;
+ var midground = worldContainer.attachAsset(asset, {
+ anchorX: 0.5,
+ anchorY: 1.0,
+ x: startX + 300 + i * 600 + (Math.random() - 0.5) * 200,
+ y: 1900 + Math.random() * 500,
+ scaleX: scale,
+ scaleY: scale
+ });
+ midground.zone = zone;
+ midground.alpha = 0.7 + Math.random() * 0.3;
+ // Zone-specific midground styling
+ if (zone === 'Cosmic') {
+ midground.tint = 0xDDDDFF;
+ // Add gentle cosmic pulsing
+ var originalScale = scale;
+ setInterval(function () {
+ if (midground && !midground.destroyed) {
+ tween(midground, {
+ scaleX: originalScale * (1.0 + Math.sin(Date.now() * 0.002) * 0.1),
+ scaleY: originalScale * (1.0 + Math.sin(Date.now() * 0.002) * 0.1)
+ }, {
+ duration: 100
+ });
+ }
+ }, 2000 + Math.random() * 3000);
+ } else if (zone === 'Sea') {
+ midground.tint = 0xCCEEFF;
+ } else if (zone === 'Under') {
+ midground.tint = 0xFFCCCC;
+ } else if (zone === 'Forest') {
+ midground.tint = 0xCCFFCC;
+ }
+ backgroundElements.push(midground);
+ zoneElements.push(midground);
+ worldContainer.setChildIndex(midground, 8 + i);
+ }
+ }
+ // Generate decorative elements
+ var decorCount = zone === 'Cosmic' ? 8 : 5;
+ for (var d = 0; d < decorCount; d++) {
+ if (Math.random() > 0.3) {
+ var decorAssets = getZoneDecorations(zone);
var decorAsset = decorAssets[Math.floor(Math.random() * decorAssets.length)];
var decoration = worldContainer.attachAsset(decorAsset, {
anchorX: 0.5,
anchorY: 1.0,
- x: startX + d * 800 + Math.random() * 400,
- y: 1800 + Math.random() * 600,
- scaleX: 0.8 + Math.random() * 1.2,
- scaleY: 0.8 + Math.random() * 1.2
+ x: startX + d * 500 + Math.random() * 400,
+ y: 1600 + Math.random() * 800,
+ scaleX: 0.8 + Math.random() * 1.4,
+ scaleY: 0.8 + Math.random() * 1.4
});
- // Zone-specific tinting with better visibility
+ // Zone-specific decoration styling
+ decoration.zone = zone;
+ decoration.alpha = 0.6 + Math.random() * 0.4;
if (zone === 'Cosmic') {
decoration.tint = 0xCCCCFF;
- decoration.alpha = 0.8;
+ decoration.alpha = 0.9;
} else if (zone === 'Sea') {
decoration.tint = 0xAADDFF;
- decoration.alpha = 0.7;
} else if (zone === 'Under') {
decoration.tint = 0xEE8888;
- decoration.alpha = 0.6;
} else if (zone === 'Forest') {
decoration.tint = 0xAADD88;
- decoration.alpha = 0.8;
} else {
decoration.tint = 0x999999;
- decoration.alpha = 0.7;
}
- decoration.zone = zone;
backgroundElements.push(decoration);
zoneElements.push(decoration);
- // Ensure decoration stays behind platforms
- var decorIndex = worldContainer.getChildIndex(decoration);
- worldContainer.setChildIndex(decoration, Math.max(0, decorIndex - 10));
+ worldContainer.setChildIndex(decoration, 6);
}
}
- // Generate buildings
- for (var i = 0; i < 4; i++) {
- var building = new Building();
- building.x = startX + 200 + i * 600;
- building.y = 2000;
- building.scale.x = 0.8 + Math.random() * 0.4;
- building.scale.y = 0.8 + Math.random() * 0.4;
- buildings.push(building);
- worldContainer.addChild(building);
- // Ensure buildings stay behind platforms
- var buildingIndex = worldContainer.getChildIndex(building);
- worldContainer.setChildIndex(building, Math.max(0, buildingIndex - 10));
+}
+function generateZoneEnvironment(startX, zone, zoneData) {
+ // Generate environmental effects
+ if (zoneData.environmental && zoneData.environmental.length > 0) {
+ var effectCount = zone === 'Cosmic' ? 10 : 6;
+ for (var i = 0; i < effectCount; i++) {
+ var asset = zoneData.environmental[Math.floor(Math.random() * zoneData.environmental.length)];
+ var scale = zone === 'Cosmic' ? 2.0 : 1.5;
+ scale *= 0.6 + Math.random() * 0.8;
+ var effect = worldContainer.attachAsset(asset, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: startX + Math.random() * 3000,
+ y: zone === 'Cosmic' ? 600 + Math.random() * 1600 : 1000 + Math.random() * 1400,
+ scaleX: scale,
+ scaleY: scale
+ });
+ effect.zone = zone;
+ effect.animationType = zone;
+ effect.driftSpeed = 0.3 + Math.random() * 1.2;
+ effect.alpha = 0.6 + Math.random() * 0.4;
+ // Zone-specific environmental behavior
+ if (zone === 'Cosmic') {
+ effect.tint = 0xEEEEFF;
+ effect.rotationSpeed = (Math.random() - 0.5) * 0.03;
+ effect.alpha = 0.8;
+ } else if (zone === 'Forest') {
+ effect.blinkTimer = Math.random() * 4000;
+ } else if (zone === 'Under') {
+ effect.floatAmplitude = 60 + Math.random() * 120;
+ effect.floatSpeed = 0.015 + Math.random() * 0.025;
+ }
+ environmentalEffects.push(effect);
+ zoneElements.push(effect);
+ worldContainer.setChildIndex(effect, 10);
+ }
}
- // Generate lower clouds (beneath platforms)
- for (var i = 0; i < 6; i++) {
- var cloudAssets = ['cloud1', 'cloud2', 'cloud3'];
- var cloudType = cloudAssets[Math.floor(Math.random() * cloudAssets.length)];
- var cloud = worldContainer.attachAsset(cloudType, {
- anchorX: 0.5,
- anchorY: 0.5,
- x: startX + Math.random() * 3000,
- y: 2100 + Math.random() * 400 // Lower clouds
- });
- // Vary cloud sizes for depth
- var cloudScale = 0.5 + Math.random() * 1.0;
- cloud.scale.x = cloudScale;
- cloud.scale.y = cloudScale;
- // Make clouds more visible with better opacity
- cloud.alpha = 0.6 + Math.random() * 0.4;
- // Store drift speed for animation
- cloud.driftSpeed = 0.5 + Math.random() * 1.5;
- clouds.push(cloud);
- // Ensure clouds stay behind platforms but in front of backgrounds
- var cloudIndex = worldContainer.getChildIndex(cloud);
- worldContainer.setChildIndex(cloud, Math.max(4, cloudIndex - 5));
+ // Generate zone-appropriate clouds/atmospheric elements
+ if (zone === 'City' || zone === 'Sea') {
+ generateAtmosphericClouds(startX, zone);
}
- // Generate upper clouds (among tall buildings)
- for (var i = 0; i < 4; i++) {
- var cloudAssets = ['cloud1', 'cloud2', 'cloud3'];
- var cloudType = cloudAssets[Math.floor(Math.random() * cloudAssets.length)];
- var cloud = worldContainer.attachAsset(cloudType, {
- anchorX: 0.5,
- anchorY: 0.5,
- x: startX + Math.random() * 3000,
- y: 800 + Math.random() * 600 // Upper clouds among buildings
- });
- // Make upper clouds larger and more translucent
- var cloudScale = 0.8 + Math.random() * 1.5;
- cloud.scale.x = cloudScale;
- cloud.scale.y = cloudScale;
- // More translucent for upper clouds
- cloud.alpha = 0.2 + Math.random() * 0.3;
- // Slower drift for upper clouds
- cloud.driftSpeed = 0.3 + Math.random() * 0.8;
- clouds.push(cloud);
- // Ensure clouds stay behind platforms but in front of backgrounds
- var cloudIndex = worldContainer.getChildIndex(cloud);
- worldContainer.setChildIndex(cloud, Math.max(4, cloudIndex - 5));
- }
- // Generate platforms AFTER backgrounds and clouds to ensure they're in foreground
- // Zone-specific platform generation for better accessibility
- var platformCount = zone === 'Cosmic' ? 8 : 6; // More cosmic platforms
- var platformSpacing = zone === 'Cosmic' ? 400 : 500; // Closer cosmic platforms
+}
+function generateZonePlatforms(startX, zone) {
+ // Generate main platforms with zone-specific layouts
+ var platformCount = zone === 'Cosmic' ? 10 : 7;
+ var platformSpacing = zone === 'Cosmic' ? 350 : 450;
for (var i = 0; i < platformCount; i++) {
var platform = new Platform();
- platform.x = startX + 300 + i * platformSpacing;
- // Zone-specific vertical positioning
+ platform.x = startX + 400 + i * platformSpacing;
+ // Zone-specific vertical arrangements
if (zone === 'Cosmic') {
- // Create ascending spiral pattern for cosmic accessibility
- platform.y = 1400 - Math.sin(i * 0.8) * 300 - i * 50;
- // Scale cosmic platforms for better visibility
- platform.scaleX = 1.2;
- platform.scaleY = 1.2;
+ // Ascending spiral with better accessibility
+ platform.y = 1500 - Math.sin(i * 0.6) * 250 - i * 40;
+ platform.scaleX = 1.3;
+ platform.scaleY = 1.3;
} else if (zone === 'Under') {
- // Descending pattern for underground
- platform.y = 1800 + i % 2 * 150;
+ // Descending cave system
+ platform.y = 1700 + i % 3 * 120;
+ } else if (zone === 'Sea') {
+ // Wave-like pattern
+ platform.y = 1600 + Math.sin(i * 0.8) * 200;
+ } else if (zone === 'Forest') {
+ // Canopy levels
+ platform.y = 1650 - i % 4 * 150;
} else {
- // Standard pattern for other zones
- platform.y = 1600 - i % 3 * 200;
+ // Standard city pattern
+ platform.y = 1600 - i % 3 * 180;
}
platforms.push(platform);
worldContainer.addChild(platform);
- // Zone-specific hunk placement
- if (i % 2 === 1 && Math.random() > (zone === 'Cosmic' ? 0.4 : 0.3)) {
+ // Add hunks to platforms
+ if (i % 2 === 1 && Math.random() > 0.4) {
var hunk = new Hunk();
- // Offset hunk position to avoid doors
- hunk.x = platform.x + (Math.random() > 0.5 ? 150 : -150);
+ hunk.x = platform.x + (Math.random() - 0.5) * 200;
hunk.y = platform.y - 20;
hunks.push(hunk);
worldContainer.addChild(hunk);
}
}
+ // Generate enhanced vertical connectivity networks
+ generateVerticalPlatformNetworks(startX, zone);
+}
+function generateVerticalPlatformNetworks(startX, zone) {
+ var verticalPlatforms = [];
+ // Generate zone-specific vertical platform arrangements
+ if (zone === 'Cosmic') {
+ // Multi-tier ascending cosmic pathways
+ for (var tier = 0; tier < 3; tier++) {
+ for (var step = 0; step < 4; step++) {
+ verticalPlatforms.push({
+ x: startX + 600 + tier * 800 + step * 300,
+ y: 1800 - tier * 400 - step * 100
+ });
+ }
+ }
+ } else if (zone === 'Under') {
+ // Descending underground network
+ for (var level = 0; level < 4; level++) {
+ for (var segment = 0; segment < 3; segment++) {
+ verticalPlatforms.push({
+ x: startX + 500 + level * 600 + segment * 200,
+ y: 1600 + level * 200 + segment * 50
+ });
+ }
+ }
+ } else {
+ // Standard multi-level networks for other zones
+ for (var level = 0; level < 3; level++) {
+ for (var pos = 0; pos < 5; pos++) {
+ verticalPlatforms.push({
+ x: startX + 400 + pos * 500,
+ y: 1800 - level * 300 + pos % 2 * 100
+ });
+ }
+ }
+ }
+ // Create the vertical platforms
+ for (var p = 0; p < verticalPlatforms.length; p++) {
+ var vPlatform = new Platform();
+ vPlatform.x = verticalPlatforms[p].x;
+ vPlatform.y = verticalPlatforms[p].y;
+ // Scale based on zone and height
+ var heightRatio = (2200 - vPlatform.y) / 1000;
+ vPlatform.scaleX = 0.9 + heightRatio * 0.4;
+ vPlatform.scaleY = 1.0;
+ platforms.push(vPlatform);
+ worldContainer.addChild(vPlatform);
+ // Add connection hunks
+ if (p % 3 === 1 && Math.random() > 0.5) {
+ var vHunk = new Hunk();
+ vHunk.x = vPlatform.x + (Math.random() - 0.5) * 150;
+ vHunk.y = vPlatform.y - 20;
+ hunks.push(vHunk);
+ worldContainer.addChild(vHunk);
+ }
+ }
+}
+function getZoneDecorations(zone) {
+ if (zone === 'City') {
+ return ['statue', 'crystal', 'artefact', 'artefact2', 'mandroid', 'tech', 'tokurrencoin', 'skilltree', 'door2', 'door3'];
+ } else if (zone === 'Cosmic') {
+ return ['starlight', 'galaxy3', 'crystal', 'artefact', 'starlight2', 'galaxy4', 'crystalheart'];
+ } else if (zone === 'Forest') {
+ return ['trees3', 'trees4', 'plush', 'plush2', 'plush3', 'crystal'];
+ } else if (zone === 'Sea') {
+ return ['caveoverlay', 'crystal', 'artefact2', 'underoverlay'];
+ } else if (zone === 'Under') {
+ return ['underoverlay', 'crystal', 'crystalheart', 'caveoverlay'];
+ }
+ return ['crystal', 'artefact']; // Fallback
+}
+function generateAtmosphericClouds(startX, zone) {
+ var cloudCount = zone === 'Sea' ? 8 : 6;
+ var cloudAssets = ['cloud1', 'cloud2', 'cloud3'];
+ for (var i = 0; i < cloudCount; i++) {
+ var cloudType = cloudAssets[Math.floor(Math.random() * cloudAssets.length)];
+ var cloud = worldContainer.attachAsset(cloudType, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: startX + Math.random() * 3000,
+ y: i < cloudCount / 2 ? 2200 + Math.random() * 400 : 900 + Math.random() * 600
+ });
+ var cloudScale = 0.6 + Math.random() * 1.2;
+ cloud.scale.x = cloudScale;
+ cloud.scale.y = cloudScale;
+ cloud.alpha = i < cloudCount / 2 ? 0.7 + Math.random() * 0.3 : 0.3 + Math.random() * 0.4;
+ cloud.driftSpeed = 0.4 + Math.random() * 1.0;
+ // Zone-specific cloud styling
+ if (zone === 'Sea') {
+ cloud.tint = 0xCCEEFF;
+ cloud.alpha *= 1.2;
+ }
+ clouds.push(cloud);
+ worldContainer.setChildIndex(cloud, 7);
+ }
// Enhanced vertical platform networks with better connectivity
var verticalPlatforms = [];
if (zone === 'Cosmic') {
// Cosmic zone: Multi-tier ascending spirals with connection platforms
@@ -1787,27 +1918,38 @@
// Update last generated position
lastGeneratedX = startX + 2500;
}
function determineZone(x) {
- // Zone boundaries based on direction from City center
+ // Enhanced zone determination with smoother transitions
var cityCenter = 2048;
var distanceFromCenter = x - cityCenter;
- if (Math.abs(distanceFromCenter) < zoneTransitionThreshold) {
- return 'City'; // Always return to city at center
+ // Primary zone determination based on distance and direction
+ if (Math.abs(distanceFromCenter) < zoneTransitionThreshold * 0.8) {
+ return 'City'; // Core city area
}
- // Determine zone based on direction and distance
- if (distanceFromCenter > zoneTransitionThreshold * 2) {
- return 'Forest'; // East
- } else if (distanceFromCenter < -zoneTransitionThreshold * 2) {
- return 'Sea'; // West
+ // Enhanced directional zones with player position consideration
+ if (distanceFromCenter > zoneTransitionThreshold * 1.5) {
+ // Eastern regions - Forest zone
+ if (bloodmage && bloodmage.y > 2000) {
+ return 'Under'; // Deep forest underground
+ }
+ return 'Forest';
+ } else if (distanceFromCenter < -zoneTransitionThreshold * 1.5) {
+ // Western regions - Sea zone
+ if (bloodmage && bloodmage.y > 2100) {
+ return 'Under'; // Deep sea caverns
+ }
+ return 'Sea';
}
- // For north/south, check Y position of player
- else if (bloodmage && bloodmage.y < 1000) {
- return 'Cosmic'; // North/Up
- } else if (bloodmage && bloodmage.y > 2200) {
- return 'Under'; // South/Down
+ // Vertical zone determination when near city center
+ if (bloodmage) {
+ if (bloodmage.y < 1200) {
+ return 'Cosmic'; // High altitude areas
+ } else if (bloodmage.y > 2100) {
+ return 'Under'; // Underground areas
+ }
}
- return 'City'; // Default
+ return 'City'; // Fallback to city
}
function generateZoneMidground(startX, zone) {
var zoneData = zones[zone];
if (!zoneData) return;
@@ -3972,39 +4114,95 @@
if (cloud.x > rightEdge) {
cloud.x = leftEdge;
}
}
- // Animate environmental effects based on zone type
+ // Enhanced environmental effects animation system
for (var i = 0; i < environmentalEffects.length; i++) {
var effect = environmentalEffects[i];
- if (!effect || !effect.zone) continue;
- if (effect.zone === 'City' || effect.zone === 'Sea') {
- // Drifting effects
- effect.x += Math.cos(effect.driftDirection) * effect.driftSpeed;
- effect.y += Math.sin(effect.driftDirection) * effect.driftSpeed * 0.3;
- } else if (effect.zone === 'Forest' && effect.animationType === 'Forest') {
- // Blinking forest effects
- effect.blinkTimer -= 16; // Assuming 60fps
- if (effect.blinkTimer <= 0) {
- var targetAlpha = effect.alpha > 0.3 ? 0.1 : 0.8;
+ if (!effect || !effect.zone || effect.destroyed) continue;
+ // Zone-specific environmental animations
+ if (effect.zone === 'Cosmic') {
+ // Enhanced cosmic animations
+ if (effect.rotationSpeed) {
+ effect.rotation += effect.rotationSpeed;
+ }
+ // Cosmic pulsing with varied timing
+ var cosmicPulse = 1.0 + Math.sin(Date.now() * 0.002 + i) * 0.15;
+ effect.scale.x = effect.scale.y = cosmicPulse;
+ // Subtle cosmic drift
+ effect.x += Math.sin(Date.now() * 0.001 + i) * 0.3;
+ effect.y += Math.cos(Date.now() * 0.0008 + i) * 0.2;
+ } else if (effect.zone === 'Forest') {
+ // Enhanced forest blinking effects
+ if (effect.blinkTimer !== undefined) {
+ effect.blinkTimer -= 16;
+ if (effect.blinkTimer <= 0) {
+ var targetAlpha = effect.alpha > 0.4 ? 0.1 : 0.9;
+ tween(effect, {
+ alpha: targetAlpha
+ }, {
+ duration: 300 + Math.random() * 600,
+ easing: tween.easeInOut
+ });
+ effect.blinkTimer = 2000 + Math.random() * 4000;
+ }
+ }
+ // Subtle forest swaying
+ effect.rotation = Math.sin(Date.now() * 0.001 + i) * 0.1;
+ } else if (effect.zone === 'Sea') {
+ // Enhanced sea effects - wave-like motion
+ effect.x += Math.cos(Date.now() * 0.001 + i * 0.5) * effect.driftSpeed;
+ effect.y += Math.sin(Date.now() * 0.0015 + i * 0.3) * (effect.driftSpeed * 0.6);
+ // Sea opacity waves
+ effect.alpha = 0.5 + Math.sin(Date.now() * 0.002 + i) * 0.3;
+ } else if (effect.zone === 'Under') {
+ // Enhanced underground floating effects
+ if (effect.floatAmplitude) {
+ var floatOffset = Math.sin(Date.now() * effect.floatSpeed + i) * effect.floatAmplitude * 0.03;
+ effect.y += floatOffset;
+ }
+ // Underground flickering
+ if (Math.random() < 0.002) {
tween(effect, {
- alpha: targetAlpha
+ alpha: effect.alpha * 0.3
}, {
- duration: 200 + Math.random() * 400,
- easing: tween.easeInOut
+ duration: 100,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ tween(effect, {
+ alpha: effect.alpha / 0.3
+ }, {
+ duration: 100,
+ easing: tween.easeInOut
+ });
+ }
});
- effect.blinkTimer = 1500 + Math.random() * 3000;
}
- } else if (effect.zone === 'Under') {
- // Floating underworld effects
- if (effect.floatAmplitude) {
- effect.y += Math.sin(Date.now() * effect.floatSpeed) * effect.floatAmplitude * 0.02;
+ } else if (effect.zone === 'City') {
+ // Enhanced city atmospheric effects
+ if (effect.driftDirection === undefined) {
+ effect.driftDirection = Math.random() * Math.PI * 2;
}
- } else if (effect.zone === 'Cosmic') {
- // Cosmic effects - gentle rotation and scaling
- effect.rotation += 0.005;
- var pulseScale = 1.0 + Math.sin(Date.now() * 0.003) * 0.1;
- effect.scale.x = effect.scale.y = pulseScale;
+ effect.x += Math.cos(effect.driftDirection) * effect.driftSpeed;
+ effect.y += Math.sin(effect.driftDirection) * effect.driftSpeed * 0.4;
+ // City light pulsing
+ if (Math.random() < 0.001) {
+ var originalTint = effect.tint || 0xFFFFFF;
+ tween(effect, {
+ tint: 0xFFFFAA
+ }, {
+ duration: 200,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ tween(effect, {
+ tint: originalTint
+ }, {
+ duration: 200,
+ easing: tween.easeInOut
+ });
+ }
+ });
+ }
}
}
// Apply parallax movement to background elements
for (var i = 0; i < backgroundElements.length; i++) {
Neon cyberpunk App icon BROSTIARY encyclopedia of black leather biker jacket longsleeves shirtless musxles pants boots hunk outline linework glowing 3d hologram flat
Neon cyberpunk magic projectile effect
Idle animation, sleek graceful man Cyberpunk manga, facing forward idle pose shiny black leather biker jacket longsleeves shirtless Skinny abs blonde pompadour guy, action shot wine red joggers combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model pose
Idle animation, sleek graceful man Cyberpunk manga, facing forward idle pose shiny black leather biker jacket longsleeves shirtless Skinny abs blonde pompadour guy, action shot wine red joggers combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model pose
Neon cyberpunk anime city skyline futuristic holograms
Swishy graceful man Cyberpunk manga, wand pointed straight ahead, arm extended, jump attack, side profile view attack animation, shiny black leather biker jacket longsleeves shirtless Skinny abs blonde pompadour guy, action shot wine red joggers combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model running pose
Cyberpunk manga man blonde undercut pompadour shiny black leather biker jacket longsleeves shirtless thin abs pecs necklaces gemstone tipped wand side profile view, action shot wand pointed straight in front arm extended wand casting spell feet planted, standing upright fierce fashion pose animation blonde undercut pompadour, vampire fangs, wine red joggers, combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including face and shoes, fully within the frame" Character fully contained within a square frame, no edges cut off,
Neon cyberpunk rectangular empty outline flat two crystals hologram 2 glowing Diamonds on each side symmetrical selection highlight overlay
cyberpunk neon anime metropolis skyline holograms billboards, occult-capitalism-consumerism imagery nighttime futuristic architecture glow
Neon cyberpunk magic spell effect 3d hologram
Neon cyberpunk 3d magical glowing crystal gemstone hologram orb sphere
Swishy graceful man Cyberpunk manga, idle animation, shiny black leather biker jacket longsleeves shirtless Skinny abs blonde pompadour guy, wine red joggers combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model idle pose
Neon cyberpunk mobile game trackpad too down flat 3d 2d hologram futuristic magic occult chic
round directional arrows Neon cyberpunk mobile game trackpad too down flat 3d 2d hologram futuristic magic occult chic
Neon cyberpunk skyscraper occult capitalist 3d hologram billboards futuristic elaborate architecture multidimensional towering city spires glowing
Neon cyberpunk futuristic glowing side-view 2d platformer style platform flat top
Neon cyberpunk futuristic glowing side-view 2d platformer style platform flat top hologram projection hovering hover platform antigravity jet thrusters
Neon cyberpunk button icon magic spell effect 3d hologram
Neon cyberpunk bag inventory button icon magic glowing futuristic 3d hologram
Same pose Limb positions variations, Front arm in front of body, front leg extended behind, back leg in front, limbs positions swapped, Swishy graceful man Cyberpunk manga, side profile view walking animation, shiny black leather biker jacket longsleeves shirtless Skinny abs blonde pompadour guy, wine red joggers combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model idle pose
man Cyberpunk manga, minotaur monster hunk, shiny black leather biker jacket longsleeves shirtless muscles, jockstrap combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from horns to hooves, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model idle pose
Neon cyberpunk mobile game button magic gemstone crystal sigil eyeball heart triangle topdown flat 3d 2d hologram futuristic glowing occult chic
idle animation hunky Minotaur man Cyberpunk manga, minotaur monster hunk, shiny black leather biker jacket longsleeves shirtless muscles, jockstrap combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from horns to hooves, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model idle pose
idle animation hunky Minotaur man Cyberpunk manga, minotaur monster hunk, shiny black leather biker jacket longsleeves shirtless muscles, jockstrap combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from horns to hooves, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model idle pose
idle animation hunky Minotaur man Cyberpunk manga, minotaur monster hunk, shiny black leather biker jacket longsleeves shirtless muscles, jockstrap combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from horns to hooves, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model idle pose
idle animation hunky Minotaur man Cyberpunk manga, minotaur monster hunk, shiny black leather biker jacket longsleeves shirtless muscles, jockstrap combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from horns to hooves, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model idle pose
Swishy graceful man Cyberpunk manga, wand pointed straight ahead, arm extended, side profile view attack animation, shiny black leather biker jacket longsleeves shirtless Skinny abs blonde pompadour guy, action shot wine red joggers combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model walking pose Style of vogue magazine
Black leather combat boots, wine red joggers, Swishy graceful man Cyberpunk manga, arm extended, side profile view walking animation, shiny black leather biker jacket longsleeves shirtless Skinny abs blonde pompadour guy, action shot wine red joggers combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model walking pose Style of vogue magazine
Neon cyberpunk mobile game menu overlay boxes for CAST, SPEAK, CALL, ENTHRALL, LEAVE magic gemstone crystal sigil eyeball heart triangle topdown flat 3d 2d hologram futuristic glowing occult chic HOLOGRAM menu
Neon cyberpunk door shop entryway storefront entrance doorway
Neon cyberpunk blank empty videogame meter hologram glowing
Magical spell effect 3d cyberpunk hologram sacred geometry diagram tree of life
Neon cyberpunk experience menu form blank boxes labeled space for current level, experience gained, flat 2d 3d hologram glowing futuristic occult sigil heart eye triangle circle magick
Swishy graceful man Cyberpunk manga, side profile view, flying flight levitation action shot shiny black leather biker jacket longsleeves shirtless Skinny abs blonde pompadour guy, dynamic motion lifting off ground wine red joggers combat boots, tilting forward legs relaxed toes pointed arms relaxed at sides, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model pose style of Vogue magazine
Swishy graceful man Cyberpunk manga, side profile view, flying flight levitation magic spell action shot shiny black leather biker jacket longsleeves shirtless Skinny abs blonde pompadour guy, dynamic motion lifting off ground wine red joggers combat boots, gemstone tipped magic wand heart triangle eye sigil hologram 3d neon, tilting forward legs relaxed toes pointed arms relaxed at sides, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including face and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model pose style of Vogue magazine
3d hologram neon cyberpunk starlight magick galaxy overlay projection asteroid starfield Platforming magical dimension pathway level obstacle layout videogame
3d hologram neon cyberpunk starlight magick galaxy overlay projection asteroid starfield Platforming magical dimension pathway level obstacle layout videogame
neon galaxy projection 3d simulation glowing swirling nebulas supernova solar system outer space purple cyan stars black holes esoteric magick sigils sparkle glowing cyberpunk starlight magick galaxy overlay projection asteroid starfield swirling background magical dimension pathway level obstacle layout videogame
3d hologram neon cyberpunk starlight magick galaxy overlay projection asteroid starfield Platforming magical dimension pathway level obstacle layout videogame Map space layout magical mystical cosmic colorful glowing scene blackhole supernova nebula galaxies universe
Neon cyberpunk manga Magical underwater crystal cavern background image deep sea cave majestic cavern background scene landscape ocean floor seaweed
Magical underwater crystal cavern background image deep sea cave anime majestic
cyberpunk neon anime metropolis skyline holograms billboards, occult-capitalism-consumerism imagery nighttime futuristic architecture glow
16:9 banner, Neon cyberpunk horror landscape scene futuristic spires skyscrapers towers skyline hologram sigils lovecraftian bladerunner tower defense neon purple blue pink occult sigils projections 3d
Neon cyberpunk blank rectangular hologram empty overlay
3d hologram neon cyberpunk starlight magick galaxy overlay projection asteroid starfield Platforming magical dimension pathway level l layout videogame Map outerspace magical mystical cosmic colorful glowing scene blackholes supernovas nebulas galaxies universe background scene
cyberpunk neon anime metropolis skyline corporate holograms billboards, occult-capitalism-consumerism imagery nighttime futuristic architecture glow
3d hologram neon cyberpunk starlight magick galaxy overlay projection asteroid starfield magical dimension videogame Map outerspace magical mystical cosmic colorful glowing scene blackholes supernovas nebulas galaxies universe background scene
3d hologram neon cyberpunk starlight magick galaxy overlay projection asteroid starfield magical dimension videogame Map outerspace magical mystical cosmic colorful glowing scene blackholes supernovas nebulas galaxies universe background scene
neon cyberpunk manga seaside biome ocean beach holograms projections cyber deep sea background scene large hd overworld futuristic side platformer bg sea floor coastal
Swishy graceful man Cyberpunk manga, camera-facing arm bent In fro t of body, side profile view walking animation, shiny black leather biker jacket longsleeves shirtless Skinny abs blonde undercut pompadour guy, action shot wine red joggers combat boots, Background removed, "Full-body character, entirely in frame, no cropping of face, head, or feet" "Complete character visible, from head to toe, fully centered in the image" "Entire character, including hair and boots, fully within the frame" Character fully contained within a square frame, no edges cut off fashion model walking pose Style of vogue magazine
Top down isometric anime cyberpunk forest neon map grid hologram projection
Top down isometric anime forest River mountains neon map grid hologram
Top down isometric anime region neon map grid hologram
Top down isometric Cyberpunk forest thick foliage woods, anime Akira-nausicaa inspired, background scene, large sci-fi horror bioluminescent glowing alien flora
Top down isometric Cyberpunk forest, anime Akira-nausicaa inspired, background scene, large sci-fi horror bioluminescent glowing alien flora
Top down isometric neon cyberpunk castle mansion interior dungeon background large
Top down isometric neon cyberpunk castle courtyard dungeon background large
Top down isometric neon cyberpunk castle dungeon background large
Top down isometric cyberpunk simulation hologram grid projection forest biome, wilderness dense foliage wild rainforest mountain river
Top down isometric cyberpunk simulation hologram grid projection forest
Top down isometric Cyberpunk forest background image large
Cyberpunk forest background image large
Neon cyberpunk manga subterranean underground tunnels crystal caverns glowing huge ancient technology ruins futurustic underworld holograms spirits projections magical dimension cave background image deep caverns majestic background scene landscape tech purgatory catacombs maze Backdrop
neon cyberpunk manga undersea landscape oceanic holograms 3d deepsea seafloor underwater background scene large hd side platformer bg
2D platformer outerspace cosmic cyberpunk neon side profile view hologram stars magick galaxies cosmic black holes milkway star system overlay projection asteroid starfield Platforming magical dimension pathway level obstacle layout videogame
UI of 2D videogame asset management tool app, empty slots for assets, sleek flat intuitive User Interface futuristic style of Apple product ui, blank template sans uploaded assets
cyberpunk neon manga city metropolis skyline holograms billboards advertisements signs, magic sigils glow in subliminal messages with corporate logos, encouraging consumption, imagery nighttime futuristic hologram 3D projections horror sci-fi
cyberpunk neon anime metropolis skyline corporate holograms billboards, occult-capitalism-consumerism imagery nighttime futuristic architecture glow
neon cyberpunk manga undersea landscape ocean computer simulation underwater seafloor underwater background hd 2 sidescrolling platformer videogame bg undersea landscape
Expand on both sides landscape orientation neon cyberpunk metropolis world sidescrolling platformer futuristic holograms skyline jewel-tones forest cosmos ocean subterranean glowing simulation projection fantasy sci-fi background