User prompt
Use the uploaded alphabet text assets to animate the words “NOCTURNE CITY” fading in, then out across the top of the screen upon loading the game. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Import of sound asset
User prompt
Please try again. Of the most recently requested changes, it appears only one was implemented partially correctly. Player now maintains a consistent altitude when initiating flight mode, although movement direction while flying is still limited from left to right (regardless of which direction pushed on the trackpad). All other requested changes remain in need of updates. Thank you ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Player still continues gradually ascending when in flight mode. They should, instead, maintain a consistent altitude upon detecting the second tap is being held. Also, player character currently only moves to the right when in flight mode, regardless of which direction on the trackpad is pressed. When in flight mode, player should be able to move in every direction as indicated by the trackpad input. The combat menu cursor still isn’t sized and aligned correctly. The rectangular outline of the cursor should fully encompass the whichever of the four menu option rectangles within the cursor’s borders. Also, the tap detection appears reversed for the cast and enthrall options—trying to select “cast” initiates a capture attempt and tapping “enthrall” casts a spell effect. Dialogue text when selecting “speak” should be layered in front of the dialogue box asset, and text should fit neatly within the dialogue box (ignoring dialogue box’s tail portion, which simply visually indicates from which character the dialogue is spoken) Please avoid placing hunks in front of/overlapping doors, and please place some hunks within the interior overlays, giving the effect that they’re indoors. The player character should remain present throughout hunk encounters, remaining in the same place they were upon activating the combat menu. Currently, they fall through whichever platform they were on and fall off screen. Player should remain in the same spot they were once battle menu appeared, as well as once a hunk encounter had concluded. Currently, player is being teleported to the starting position of the level once an encounter concludes, which is undoing any progress made in exploration. Please implement hedonism as background music, as well as assorted sound effects during relevant actions and events. Thanks! ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
For the flight/levitation mechanic, rather than continuing to ascend when long-pressing the jump button, I’d simply like the player character to remain suspended at the height they are at the point when registering the held button. From that point, flight animation should loop (forward, in reverse, forward, etc) yet they should be able to navigate using the track pad. In this way they’re able to continue ascending via pressing that direction on the track pad. Also, it seems the game doesn’t currently recognize an attempt to fly unless the player is motionless, but I’d like to be able to seamlessly be walking, then double-jump but hold the jump button on second press to activate flight, all while continuing navigation left and right (up and down when flight mode is active). It’s still very difficult to initiate combat with hunk characters; the game only seems to register an indecipherable combination of tapping a small area of the hunk while standing directly on him—a more generous distance and area to tap, please. Also, Please resize and align the combat menu to neatly fit centered between the corner buttons, leaving a small border of empty space between the edge of each asset so that no assets appear to overlap. Then, attempt to better size and position the cursor, and attempt to better, more accurately calibrate which menu option is tapped. Please make dialogue text layered in front of the dialogue box while also within the border confines of the dialogue box. Resize and rearrange as is required. Player walk animation looks a bit warped and squished still. Avoid positioning hunks in front of doors, but feel free to place them “within” interior space overlays when tapping doors. Make sure interior overlays exit buttons aren’t concealed by the brostiary icon, which doesn’t need to be visible when an interior overlay is present. Thanks! ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'ReferenceError: Can't find variable: scoreText' in or related to this line: 'scoreText.setText('Hunks: ' + capturedHunks.length);' Line Number: 3717
User prompt
Player character should remain in the air for the duration of the second button press. While in the air, he should be able to navigate player character using the track pad, still in the air able to move all directions. He shouldn’t fall to the ground until the jump button is released. Careful attention should be paid to whether jump button is tapped, double tapped, or double tap-long pressed. If finger isn’t released on the second tap, player should remain in the air. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
While I see the animation works mostly, we definitely didn’t implement the core aspect of a flight mechanic, chiefly: the levitation part. Please use your best understanding of physics, gravity, and videogame mechanics to implement a rewardingly tactile, responsive parabolic arch, sensing the continuing press of the ju ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please remove the “Hunk” text/counter from the top center screen (the Brostiary is intended for that purpose) as well as from the shop menu. Shop assets are still garbled, illegible, and sized and aligned badly. Make sure text remains within the boundaries of dialogue box. For the jump button, please add additional function so that when double tapping but holding the jump button on second tap, for the duration of the finger press, player flight occurs. Use playerfly - playerfly6 as animation assets, looping forward, then in reverse, then forward, etc. For the duration the player remains holding jump button, they can move freely in all directions with the track pad. Make sure player faces direction they’re moving, with all assets uploaded to the face left by default. Upon removing finger from the jump button, player should drop down to the ground beneath them, with playerfly7 - playerfly8 as the animation while player drops back down to the ground. Initiating battle menu when tapping hunk is still very difficult, and appears to have a very narrow area that initiates menu. When the menu does appear, it is giant and stretched, rather than neatly fitting between trackpad and buttons on right, and needs resized and better alignment. Also, the touch detection and cursor alignment for the menu options aren’t very functional, with it registering an attempt to enthrall when trying to cast. Speech text is garbled and should fit within the boundary of dialoguebox. Resize xpmenu to be legible and its realign the text placement appearing over xpmenu so that it fits neatly within the boundaries of the asset’s fields also. Thanks! ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Import of sound asset
User prompt
Please fix the bug: 'ReferenceError: Can't find variable: scoreText' in or related to this line: 'scoreText.setText('Hunks: ' + capturedHunks.length);' Line Number: 3375
User prompt
Combat menu still isn’t appearing and functional when standing near and tapping hunk characters. Please use interior - interior8 as interior assets when standing in front of and tapping doors, using the same animation effect to transition to the interior overlay, which should have an exit button. Please make sure that platform assets appear in front of other assets, and that one is at the bottom of doors so there is a surface to stand on. Functional combat menu with casting, dialogue, and capture animations would be greatly appreciated, as well as the experience menu functioning upon a successful capture. All 3 kinds of cloud assets should appear drifting midground between characters and background assets, drifting across the screen, especially the bottom beneath the platform. Text legibility and spacing still an issue in shop, presumably should be anticipated in dialogue scenarios as well. Please also remove hunk counter text from top center, that is what Brostiary is meant to catalogue. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Combat menu is stretched and warped. It should fit neatly between trackpad and jump and cast buttons along the bottom center. The cursor should be adjusted to be wider and taller than each of the four options. Only 4 positions for the cursor , two upper and two lower. When tapping cast, the player (who should remain standing where they were, not fall through the platform) should perform cast animation. When tapping speak in the upper right option, dialogue should appear in dialogue box properly positioned to floating above the speaker’s head. When tapping enthrall, the cast animation should occur, with the addition of the capture object and capture effect an animated and timed to appear thrown by player character at hunk character. RNG based on hunk’s resistance meter should determine if capture attempt is successful. If successful , hunk character should appear to be sucked down into capture device. If unsuccessful, capture device should appear to bounce off hunk. Tapping leave should exit the combat menu. Player assets should remain present throughout entire encounter. Remain cognizant of coherent, appealing visual placement and timing. Please adjust shop overlay to be coherent and follow the same principles as well. Combat menu doesn’t appear at all now. City layout is still nonsensical mess currently. Refer to layout guide and make sure it platforms fill the space in a logical way both vertically as well as the horizontal. Incorporate unused assets to fill background and placeholder spaces with visual intrigue. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Combat menu is stretched and warped. It should fit neatly between trackpad and jump and cast buttons along the bottom center. The cursor should be adjusted to be wider and taller than each of the four options. Only 4 positions for the cursor , two upper and two lower. When tapping cast, the player (who should remain standing where they were, not fall through the platform) should perform cast animation. When tapping speak in the upper right option, dialogue should appear in dialogue box properly positioned to floating above the speaker’s head. When tapping enthrall, the cast animation should occur, with the addition of the capture object and capture effect an animated and timed to appear thrown by player character at hunk character. RNG based on hunk’s resistance meter should determine if capture attempt is successful. If successful , hunk character should appear to be sucked down into capture device. If unsuccessful, capture device should appear to bounce off hunk. Tapping leave should exit the combat menu. Player assets should remain present throughout entire encounter. Remain cognizant of coherent, appealing visual placement and timing. Please adjust shop overlay to be coherent and follow the same principles as well. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Combat menu isn’t appearing when within range and tapping hunks. City layout is a mess. Please keep bottom platform repeating in the different varieties of platform options across the bottom. Better conceal the edges of background assets by strategically layering additional assets like the multiple buildings and statues and neon signs. Please incorporate playerwalk9 - 12 into the walking animation, making sure to keep the height and size consistent across each frame, and fixing the frames that appear warped and squished too skinny or stretched too tall. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please refer to nocturnecitylayout as primary guide and blueprint to the level layout and structure. Then, add nocturnecity as an additional background asset, layered to expand the current level layout both vertically and a bit horizontally. Then, using nocturnecitylayout as a visual guide (please look at it, reviewing door and platform placement). Then place the corresponding foreground assets like the platforms, placing door assets where doors appear in the city layout guide. The the shop overlay and floating sign should remain the bottom left door position. Place other door assets where they appear in the layout guide asset, maintaining the mechanic where tapping the doors when standing in front of them cause an animation transition to a placeholder interior asset overlay. Make sure these interior overlays have exit buttons. Then, populate hunk characters less densely, making sure only one is visible onscreen at a time and that they don’t repeat, each hunk character appearing once in the level, outside of view of other hunk characters. When standing within range of hunk characters, tapping should cause an animation of the combatui stretching out along the bottom center or screen. Make sure cursor properly outlines the individual menu options, making each option have a clear visual function and effect. Make each option perform its eponymous task. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'The supplied index is out of bounds' in or related to this line: 'worldContainer.setChildIndex(cloud, 16 + i);' Line Number: 1451
User prompt
Please fix the bug: 'The supplied index is out of bounds' in or related to this line: 'worldContainer.setChildIndex(cloud, 16 + i);' Line Number: 1450
User prompt
Please fix the bug: 'The supplied index is out of bounds' in or related to this line: 'worldContainer.setChildIndex(cloud, 16 + i);' Line Number: 1450
User prompt
Please fix the bug: 'The supplied index is out of bounds' in or related to this line: 'worldContainer.setChildIndex(cloud, 16 + i);' Line Number: 1449
User prompt
Please fix the bug: 'The supplied index is out of bounds' in or related to this line: 'worldContainer.setChildIndex(cloud, 16 + i);' Line Number: 1449
User prompt
Please fix the bug: 'The supplied index is out of bounds' in or related to this line: 'worldContainer.setChildIndex(cloud, 16 + i);' Line Number: 1449
User prompt
Please fix the bug: 'The supplied index is out of bounds' in or related to this line: 'worldContainer.setChildIndex(cloud, 16 + i);' Line Number: 1449
User prompt
Please fix the bug: 'The supplied index is out of bounds' in or related to this line: 'worldContainer.setChildIndex(cloud, 16 + i);' Line Number: 1449
User prompt
Please fix the bug: 'The supplied index is out of bounds' in or related to this line: 'worldContainer.setChildIndex(cloud, 16 + i);' Line Number: 1449
/****
* 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: 0,
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
}));
// 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
}));
// 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
}));
self.idleAnimationDirection = 1; // 1 for forward, -1 for reverse
self.startIdleAnimation = function () {
if (self.isMoving) return;
// Hide main body and walking frames, show current idle frame
body.alpha = 0;
for (var i = 0; i < self.walkFrames.length; i++) {
self.walkFrames[i].alpha = 0;
}
for (var i = 0; i < self.idleFrames.length; i++) {
self.idleFrames[i].alpha = i === self.currentIdleFrame ? 1 : 0;
}
// Smoothly fade out current frame and fade in 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];
tween(currentFrame, {
alpha: 0
}, {
duration: 100,
easing: tween.easeInOut
});
tween(nextFrame, {
alpha: 1
}, {
duration: 100,
easing: tween.easeInOut
});
self.currentIdleFrame = nextFrameIndex;
// Schedule next frame change
self.idleAnimationTimer = LK.setTimeout(function () {
if (!self.isMoving) {
self.startIdleAnimation();
}
}, 150);
};
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();
}
}, 100);
};
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;
}
// Calculate which frame to show based on jump progress
var jumpProgress = 0;
if (self.velocityY < -15) {
jumpProgress = 0; // Start of jump
} else if (self.velocityY < -5) {
jumpProgress = 1; // Rising
} else if (self.velocityY < 5) {
jumpProgress = 2; // Peak
} else if (self.velocityY < 15) {
jumpProgress = 3; // Falling
} else {
jumpProgress = 4; // Landing
}
// Ensure we don't go past available frames
var frameIndex = Math.min(jumpProgress, self.jumpFrames.length - 1);
// 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.update = function () {
self.velocityY += self.gravity;
self.x += self.velocityX;
self.y += self.velocityY;
// Check if jumping
var wasJumping = self.isJumping;
self.isJumping = !self.grounded || Math.abs(self.velocityY) > 2;
// Handle animation state
var wasMoving = self.isMoving;
self.isMoving = Math.abs(self.velocityX) > 0.1;
if (self.isJumping) {
// Handle jump animation
if (!wasJumping) {
// Just started jumping
self.stopWalkingAnimation();
self.stopIdleAnimation();
}
self.startJumpAnimation();
} else {
// Not jumping
if (wasJumping) {
// Just landed
self.stopJumpAnimation();
if (self.isMoving) {
self.startWalkingAnimation();
} else {
self.startIdleAnimation();
}
} else {
// Ground movement
if (wasMoving && !self.isMoving) {
// Just stopped moving
self.stopWalkingAnimation();
self.startIdleAnimation();
} else if (!wasMoving && self.isMoving) {
// Just started moving
self.stopIdleAnimation();
}
}
}
};
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;
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 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) {
// Generate background elements
var bgAssets = ['bg2', 'bg3', 'bg4'];
// Add layered backgrounds
for (var layer = 0; layer < 3; layer++) {
var bgAsset = bgAssets[layer];
var bg = worldContainer.attachAsset(bgAsset, {
anchorX: 0.5,
anchorY: 1.0,
x: startX + 2048,
y: 2732,
scaleX: 2.0 - layer * 0.3,
scaleY: 2.0 - layer * 0.3
});
bg.alpha = 0.3 + layer * 0.2;
backgroundElements.push(bg);
worldContainer.setChildIndex(bg, layer);
}
// Add statue as decoration
if (Math.random() > 0.7) {
var statue = worldContainer.attachAsset('statue', {
anchorX: 0.5,
anchorY: 1.0,
x: startX + Math.random() * 2000,
y: 2000,
scaleX: 0.8,
scaleY: 0.8
});
statue.tint = 0x666666;
backgroundElements.push(statue);
// Ensure statue stays behind platforms
var statueIndex = worldContainer.getChildIndex(statue);
worldContainer.setChildIndex(statue, Math.max(0, statueIndex - 10));
}
// 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));
}
// 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 semi-translucent with varying opacity
cloud.alpha = 0.3 + 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 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
for (var i = 0; i < 6; i++) {
var platform = new Platform();
platform.x = startX + 300 + i * 500;
platform.y = 1600 - i % 3 * 200;
platforms.push(platform);
worldContainer.addChild(platform);
// Add hunks on some platforms
if (i % 2 === 1 && Math.random() > 0.3) {
var hunk = new Hunk();
hunk.x = platform.x;
hunk.y = platform.y - 20;
hunks.push(hunk);
worldContainer.addChild(hunk);
}
}
// Add more platforms and hunks for extended level
var extendedPlatforms = [{
x: startX + 400,
y: 1500
}, {
x: startX + 900,
y: 1300
}, {
x: startX + 1400,
y: 1100
}, {
x: startX + 1900,
y: 1400
}];
for (var p = 0; p < extendedPlatforms.length; p++) {
var platform = new Platform();
platform.x = extendedPlatforms[p].x;
platform.y = extendedPlatforms[p].y;
platforms.push(platform);
worldContainer.addChild(platform);
// Add single hunk every other extended platform
if (p % 2 === 1) {
var hunk = new Hunk();
hunk.x = platform.x;
hunk.y = platform.y - 20;
hunks.push(hunk);
worldContainer.addChild(hunk);
}
}
// Update last generated position
lastGeneratedX = startX + 2500;
}
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 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 expanded background layer
var nocturneCityBg = worldContainer.attachAsset('nocturnecity', {
anchorX: 0.5,
anchorY: 1.0,
x: 2500,
y: 2732,
scaleX: 1.5,
scaleY: 1.5
});
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 semi-translucent with varying opacity
cloud.alpha = 0.3 + Math.random() * 0.4;
// 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 < 8; i++) {
var building = new Building();
building.x = 200 + i * 600;
building.y = 2000; // Position at ground level so buildings meet platforms
building.scale.x = 0.8 + Math.random() * 0.4; // Vary building sizes
building.scale.y = 0.8 + Math.random() * 0.4;
buildings.push(building);
worldContainer.addChild(building);
}
var ground = new Platform();
ground.x = 1024;
ground.y = 2000;
ground.scale.x = 10;
platforms.push(ground);
worldContainer.addChild(ground);
// Create platform layout based on nocturnecitylayout guide
var platformPositions = [{
x: 400,
y: 1800
},
// Starting platform
{
x: 800,
y: 1600
},
// Lower left area
{
x: 1300,
y: 1400
},
// Shop door platform (bottom left in layout)
{
x: 1800,
y: 1200
},
// Mid level
{
x: 2400,
y: 1100
},
// Upper mid
{
x: 2900,
y: 1300
},
// Door platform (mid level)
{
x: 3400,
y: 1000
},
// Upper right area
{
x: 3900,
y: 1200
},
// Far right
{
x: 4400,
y: 1400
},
// Door platform (upper right)
{
x: 4900,
y: 1600
},
// Extended right
{
x: 5400,
y: 1100
},
// Final platform
{
x: 5900,
y: 1300
} // 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);
// Check if this platform should have a door
var doorInfo = doorPositions.find(function (d) {
return d.platformIndex === i;
});
if (doorInfo) {
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;
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 placeholder interior function
door.down = function () {
showPlaceholderInterior();
};
}
}
}
// Add single hunks at strategic positions - only one visible at a time
var hunkPositions = [{
x: 1800,
y: 1180,
platformIndex: 3
},
// First hunk after starting area
{
x: 2900,
y: 1280,
platformIndex: 5
},
// Near mid door
{
x: 4400,
y: 1380,
platformIndex: 8
},
// Near upper door
{
x: 5900,
y: 1280,
platformIndex: 11
} // Final hunk
];
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);
}
bloodmage = new Bloodmage();
bloodmage.x = 400; // Start on first platform
bloodmage.y = 1780;
worldContainer.addChild(bloodmage);
// Start idle animation initially
bloodmage.startIdleAnimation();
// 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) {
var _animateButtonBlink = 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;
blinkFrame.alpha = 0;
blinkFrame.scale.x = 0.8;
blinkFrame.scale.y = 0.8;
// Animate the blink frame with smooth scaling
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();
}
}
});
}
});
}
};
// Start button blink animation
var blinkFrames = ['buttonblink', 'buttonblink2', 'buttonblink3', 'buttonblink4'];
var currentBlinkFrame = 0;
_animateButtonBlink();
var currentTime = Date.now();
var timeSinceLastJump = currentTime - bloodmage.lastJumpTime;
// Check for double tap timing
if (timeSinceLastJump < doubleTapThreshold && bloodmage.jumpCount === 1 && bloodmage.grounded) {
// Double tap detected - perform higher jump
bloodmage.velocityY = -bloodmage.jumpPower * 1.5;
bloodmage.grounded = false;
bloodmage.doubleJumpAvailable = true;
LK.getSound('jump').play();
bloodmage.jumpCount = 0; // Reset for next sequence
} else {
// Normal jump attempt
var jumpSuccess = bloodmage.jump();
if (jumpSuccess) {
bloodmage.lastJumpTime = currentTime;
bloodmage.jumpCount = bloodmage.grounded ? 1 : 0; // Track if this was first jump
}
}
}
};
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();
}
}
};
var scoreText = new Text2('Hunks: ' + capturedHunks.length, {
size: 60,
fill: 0xFF00FF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// 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
var currencyText = new Text2('HUNKS: ' + capturedHunks.length, {
size: 100,
fill: 0x00FF00,
fontWeight: 'bold'
});
currencyText.anchor.set(0.5, 0.5);
currencyText.x = 1024;
currencyText.y = 2500;
currencyText.alpha = 0;
// Add hot pink glow
currencyText.style = {
fontSize: 100,
fill: 0x00FF00,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0xFF1493,
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 = 600 + localIndex * 400;
var itemY = 1800;
// Add appropriate display platform
if (item.useDisplay) {
// Display platform for crystal hearts
var displayPlatform = shopOverlay.attachAsset('displayplatform', {
anchorX: 0.5,
anchorY: 0.5,
x: itemX,
y: itemY + 20,
scaleX: 3,
scaleY: 3
});
} else if (item.useShelf) {
// Shelf for plush items
var platShelf = shopOverlay.attachAsset('platshelf', {
anchorX: 0.5,
anchorY: 0.5,
x: itemX,
y: itemY + 30,
scaleX: 4,
scaleY: 4
});
}
// Item asset
var itemAsset = shopOverlay.attachAsset(item.asset, {
anchorX: 0.5,
anchorY: 0.5,
x: itemX,
y: itemY - 50,
scaleX: 2,
scaleY: 2
});
// Create item name using alphabet assets
var nameLetters = item.name.toLowerCase();
var letterSpacing = 25;
var startX = itemX - nameLetters.length * letterSpacing / 2;
for (var j = 0; j < nameLetters.length; j++) {
var letter = nameLetters[j];
if (letter !== ' ') {
var letterAsset = shopOverlay.attachAsset(letter, {
anchorX: 0.5,
anchorY: 0.5,
x: startX + j * letterSpacing,
y: itemY + 80,
scaleX: 0.8,
scaleY: 0.8
});
}
}
// Item price
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 + 120;
// Add hot pink glow
itemPrice.style = {
fontSize: 56,
fill: 0xFFFF00,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0xFF1493,
dropShadowBlur: 15,
dropShadowDistance: 0
};
shopOverlay.addChild(itemPrice);
// Item description
var itemDesc = new Text2(item.description, {
size: 40,
fill: 0xFFFFFF,
fontWeight: 'bold',
wordWrap: true,
wordWrapWidth: 300
});
itemDesc.anchor.set(0.5, 0.5);
itemDesc.x = itemX;
itemDesc.y = itemY + 180;
// Add hot pink glow
itemDesc.style = {
fontSize: 40,
fill: 0xFFFFFF,
fontWeight: 'bold',
dropShadow: true,
dropShadowColor: 0xFF1493,
dropShadowBlur: 15,
dropShadowDistance: 0,
wordWrap: true,
wordWrapWidth: 300
};
shopOverlay.addChild(itemDesc);
// Buy button
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 + 240;
buyButton.interactive = true;
// 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 () {
purchaseItem(itemIndex);
};
}(i);
shopOverlay.addChild(buyButton);
}
// 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('HUNKS: ' + capturedHunks.length);
}
// Clean up transition
transitionOverlay.destroy();
}
});
}
function showShop() {
enterShop();
}
function purchaseItem(itemIndex) {
var item = shopItems[itemIndex];
if (capturedHunks.length >= item.price) {
// Remove hunks as currency
for (var i = 0; i < item.price; i++) {
capturedHunks.pop();
}
storage.capturedHunks = capturedHunks;
scoreText.setText('Hunks: ' + capturedHunks.length);
// 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;
cameraX += (targetX - cameraX) * 0.1;
worldContainer.x = cameraX;
}
}
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 stretched combat menu along bottom center
combatMenuUI = game.attachAsset('combatmenu', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2200,
scaleX: 8,
scaleY: 3
});
combatMenuUI.interactive = true;
// Animate combat UI stretching in from center
combatMenuUI.scaleX = 0.1;
tween(combatMenuUI, {
scaleX: 8
}, {
duration: 400,
easing: tween.easeOut
});
// Position cursor properly for 4 menu options with better spacing
cursorUI = game.attachAsset('cursor', {
anchorX: 0.5,
anchorY: 0.5,
x: 624,
y: 2200,
scaleX: 0.25,
scaleY: 0.4
});
cursorUI.alpha = 0.8;
// Add visual outline effect to cursor
tween(cursorUI, {
scaleX: 0.3,
scaleY: 0.5,
alpha: 1
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(cursorUI, {
scaleX: 0.25,
scaleY: 0.4,
alpha: 0.8
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
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.setText('Hunks: ' + capturedHunks.length);
// 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 stretched combat menu bounds (centered at 1024, 2200 with scale 8x3)
var menuLeft = 1024 - 800; // Full width of scaled menu (200 * 8 scale)
var menuRight = 1024 + 800;
var menuTop = 2200 - 270; // Full height of scaled menu (180 * 3 scale)
var menuBottom = 2200 + 270;
if (x >= menuLeft && x <= menuRight && y >= menuTop && y <= menuBottom) {
// Determine which option based on x position within menu (4 equal sections)
var relativeX = (x - menuLeft) / (menuRight - menuLeft);
var newOption = Math.floor(relativeX * 4);
newOption = Math.max(0, Math.min(3, newOption));
// Updated cursor positions for stretched menu with proper spacing
var positions = [{
x: 624,
y: 2200
}, {
x: 824,
y: 2200
}, {
x: 1224,
y: 2200
}, {
x: 1424,
y: 2200
}];
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
for (var i = 0; i < hunks.length; i++) {
var hunk = hunks[i];
if (!hunk.captured) {
var hunkDistance = Math.sqrt(Math.pow(worldPos.x - hunk.x, 2) + Math.pow(worldPos.y - hunk.y, 2));
if (hunkDistance < 150) {
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
}
// Convert game coordinates to local GUI coordinates for 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) {
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
if (timeSinceLastJump < doubleTapThreshold && bloodmage.jumpCount === 1 && bloodmage.grounded) {
// Double tap detected - perform higher jump
bloodmage.velocityY = -bloodmage.jumpPower * 1.5;
bloodmage.grounded = false;
bloodmage.doubleJumpAvailable = true;
LK.getSound('jump').play();
bloodmage.jumpCount = 0; // Reset for next sequence
} else {
// Normal jump attempt
var jumpSuccess = bloodmage.jump();
if (jumpSuccess) {
bloodmage.lastJumpTime = currentTime;
bloodmage.jumpCount = bloodmage.grounded ? 1 : 0; // Track if this was first jump
}
}
}
// 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 () {
trackpadPressed = false;
if (bloodmage) {
bloodmage.velocityX = 0;
}
trackpadThumb.x = 200;
trackpadThumb.y = -200;
};
function updateTrackpad(x, y) {
// x and y are already in local coordinates for GUI elements
var dx = x - 200;
var dy = y - -200;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 80) {
dx = dx / dist * 80;
dy = dy / dist * 80;
}
trackpadThumb.x = 200 + dx;
trackpadThumb.y = -200 + dy;
if (bloodmage && dist > 5) {
// Lower dead zone
// More responsive movement
bloodmage.velocityX = dx / 80 * bloodmage.speed * 1.5; // Increased responsiveness
}
}
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;
}
}
// 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) {
// Speak - show dialogue with player response
showDualDialogue();
} else if (selectedOption === 2) {
// Cast - perform casting animation
performBattleCast();
} else if (selectedOption === 3) {
// Leave - exit battle UI
hideBattleUI();
currentHunk = null;
}
}
function performCaptureAnimation() {
if (!currentHunk) return;
// 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() {
if (currentHunk.resistance < 30) {
// Success - shrink hunk into device
tween(currentHunk, {
scaleX: 0.1,
scaleY: 0.1,
x: captureDevice.x,
y: captureDevice.y
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
attemptCapture();
showXPMenu(true);
captureDevice.destroy();
captureEffect.destroy();
}
});
} else {
// Failed - 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();
captureDevice.destroy();
captureEffect.destroy();
}
});
}
}
});
}
});
}
function performBattleCast() {
if (!currentHunk) return;
// 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();
// Play cast animation on bloodmage
castPlayerSpell();
// Damage hunk
castSpell(25);
// Clean up after 2 seconds
LK.setTimeout(function () {
if (spellBehind) spellBehind.destroy();
if (spellPulse) spellPulse.destroy();
}, 2000);
}
function showDualDialogue() {
if (!currentHunk) return;
// Player dialogue
var playerDialogue = worldContainer.attachAsset('dialoguebox', {
anchorX: 0.5,
anchorY: 0.5,
x: bloodmage.x,
y: bloodmage.y - 200,
scaleX: 6,
scaleY: 4
});
var playerText = new Text2('SUBMIT TO MY WILL!', {
size: 28,
fill: 0xFF00FF,
fontWeight: 'bold',
wordWrap: true,
wordWrapWidth: 180
});
playerText.anchor.set(0.5, 0.5);
playerDialogue.addChild(playerText);
// Hunk dialogue
var hunkDialogue = worldContainer.attachAsset('dialoguebox', {
anchorX: 0.5,
anchorY: 0.5,
x: currentHunk.x,
y: currentHunk.y - 200,
scaleX: 8,
scaleY: 6
});
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: 24,
fill: 0xFFFFFF,
fontWeight: 'bold',
wordWrap: true,
wordWrapWidth: 200
});
hunkText.anchor.set(0.5, 0.5);
hunkText.y = 20;
hunkDialogue.addChild(hunkText);
// Auto-hide both dialogues after 4 seconds
LK.setTimeout(function () {
if (playerDialogue) playerDialogue.destroy();
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: 8,
scaleY: 8,
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
var xpText = new Text2('EXPERIENCE GAINED: ' + totalXP, {
size: 80,
fill: 0x00FF00,
fontWeight: 'bold'
});
xpText.anchor.set(0.5, 0.5);
xpText.x = 1024;
xpText.y = 1200;
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('cyberpunk');
;
var placeholderInteriorShowing = false;
var placeholderOverlay = null;
function showPlaceholderInterior() {
if (placeholderInteriorShowing) return;
placeholderInteriorShowing = true;
// Create interior overlay
placeholderOverlay = game.addChild(new Container());
// Add dark background
var interiorBg = placeholderOverlay.attachAsset('bg3', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 3,
scaleY: 4,
alpha: 0
});
interiorBg.tint = 0x1a1a1a;
tween(interiorBg, {
alpha: 0.9
}, {
duration: 500,
easing: tween.easeOut
});
// Add placeholder text
var placeholderText = new Text2('INTERIOR COMING SOON...', {
size: 120,
fill: 0xFFFFFF,
fontWeight: 'bold'
});
placeholderText.anchor.set(0.5, 0.5);
placeholderText.x = 1024;
placeholderText.y = 1366;
placeholderText.alpha = 0;
placeholderOverlay.addChild(placeholderText);
tween(placeholderText, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
// Add exit button
var exitBtn = placeholderOverlay.attachAsset('x', {
anchorX: 0.5,
anchorY: 0.5,
x: 1800,
y: 400,
scaleX: 2,
scaleY: 2,
alpha: 0
});
exitBtn.interactive = true;
exitBtn.down = function () {
closePlaceholderInterior();
};
tween(exitBtn, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
}
function closePlaceholderInterior() {
if (!placeholderInteriorShowing || !placeholderOverlay) return;
placeholderInteriorShowing = false;
placeholderOverlay.destroy();
placeholderOverlay = null;
} ===================================================================
--- original.js
+++ change.js
@@ -289,89 +289,60 @@
self.walkFrames.push(self.attachAsset('playerwalk', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
- scaleX: baseWalkScale * 1.10,
+ scaleX: baseWalkScale * 0.70,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk2', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
- scaleX: baseWalkScale * 1.75,
+ scaleX: baseWalkScale * 1.05,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk3', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
- scaleX: baseWalkScale * 0.95,
+ scaleX: baseWalkScale * 0.85,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk4', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
- scaleX: baseWalkScale * 1.05,
+ scaleX: baseWalkScale * 0.60,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk5', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
- scaleX: baseWalkScale * 1.48,
+ scaleX: baseWalkScale * 0.60,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk6', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
- scaleX: baseWalkScale * 1.09,
+ scaleX: baseWalkScale * 0.58,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk7', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
- scaleX: baseWalkScale * 1.35,
+ scaleX: baseWalkScale * 1.20,
scaleY: baseWalkScale
}));
self.walkFrames.push(self.attachAsset('playerwalk8', {
anchorX: 0.5,
anchorY: 1.0,
alpha: 0,
- scaleX: baseWalkScale * 1.05,
+ scaleX: baseWalkScale * 0.60,
scaleY: baseWalkScale
}));
- // Add new walking frames with consistent scaling
- self.walkFrames.push(self.attachAsset('playerwalk9', {
- anchorX: 0.5,
- anchorY: 1.0,
- alpha: 0,
- scaleX: baseWalkScale * 1.05,
- scaleY: baseWalkScale
- }));
- self.walkFrames.push(self.attachAsset('playerwalk10', {
- anchorX: 0.5,
- anchorY: 1.0,
- alpha: 0,
- scaleX: baseWalkScale * 1.62,
- scaleY: baseWalkScale
- }));
- self.walkFrames.push(self.attachAsset('playerwalk11', {
- anchorX: 0.5,
- anchorY: 1.0,
- alpha: 0,
- scaleX: baseWalkScale * 2.64,
- scaleY: baseWalkScale
- }));
- self.walkFrames.push(self.attachAsset('playerwalk12', {
- anchorX: 0.5,
- anchorY: 1.0,
- alpha: 0,
- scaleX: baseWalkScale * 1.37,
- 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,
@@ -1054,10 +1025,11 @@
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 and characters - force to specific layer
- worldContainer.setChildIndex(building, Math.min(worldContainer.children.length - 1, 5));
+ // Ensure buildings stay behind platforms
+ var buildingIndex = worldContainer.getChildIndex(building);
+ worldContainer.setChildIndex(building, Math.max(0, buildingIndex - 10));
}
// Generate lower clouds (beneath platforms)
for (var i = 0; i < 6; i++) {
var cloudAssets = ['cloud1', 'cloud2', 'cloud3'];
@@ -1078,13 +1050,9 @@
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);
- var maxIndex = worldContainer.children.length - 1;
- var targetIndex = Math.min(Math.max(4, cloudIndex), maxIndex);
- if (targetIndex >= 0 && targetIndex <= maxIndex && maxIndex >= 0) {
- worldContainer.setChildIndex(cloud, targetIndex);
- }
+ worldContainer.setChildIndex(cloud, Math.max(4, cloudIndex - 5));
}
// Generate upper clouds (among tall buildings)
for (var i = 0; i < 4; i++) {
var cloudAssets = ['cloud1', 'cloud2', 'cloud3'];
@@ -1105,71 +1073,57 @@
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);
- var maxIndex = worldContainer.children.length - 1;
- // Calculate safe index that doesn't exceed container bounds
- var desiredIndex = 16 + i;
- var safeIndex = Math.min(desiredIndex, maxIndex);
- if (safeIndex >= 0 && safeIndex <= maxIndex && maxIndex >= 0 && safeIndex < worldContainer.children.length) {
- worldContainer.setChildIndex(cloud, safeIndex);
- }
+ worldContainer.setChildIndex(cloud, Math.max(4, cloudIndex - 5));
}
// Generate platforms AFTER backgrounds and clouds to ensure they're in foreground
- for (var i = 0; i < 10; i++) {
+ for (var i = 0; i < 6; i++) {
var platform = new Platform();
- // Create more varied platform layouts
- if (i < 6) {
- // Standard progression platforms
- platform.x = startX + 300 + i * 500;
- platform.y = 1600 - i % 3 * 200;
- } else {
- // Additional elevated platforms for more complex navigation
- platform.x = startX + 200 + (i - 6) * 700;
- platform.y = 1000 - (i - 6) % 4 * 180;
- platform.scale.x = 0.7 + Math.random() * 0.6;
- platform.scale.y = 0.8 + Math.random() * 0.4;
- }
+ platform.x = startX + 300 + i * 500;
+ platform.y = 1600 - i % 3 * 200;
platforms.push(platform);
worldContainer.addChild(platform);
- // Add single hunk only if none visible onscreen
- var visibleHunks = 0;
- for (var j = 0; j < hunks.length; j++) {
- var existingHunk = hunks[j];
- if (!existingHunk.captured && Math.abs(existingHunk.x - cameraX) < 2048) {
- visibleHunks++;
- }
+ // Add hunks on some platforms
+ if (i % 2 === 1 && Math.random() > 0.3) {
+ var hunk = new Hunk();
+ hunk.x = platform.x;
+ hunk.y = platform.y - 20;
+ hunks.push(hunk);
+ worldContainer.addChild(hunk);
}
- // Only add hunk if no others are visible and this is a good platform
- if (visibleHunks === 0 && i === 5 && Math.random() > 0.7) {
+ }
+ // Add more platforms and hunks for extended level
+ var extendedPlatforms = [{
+ x: startX + 400,
+ y: 1500
+ }, {
+ x: startX + 900,
+ y: 1300
+ }, {
+ x: startX + 1400,
+ y: 1100
+ }, {
+ x: startX + 1900,
+ y: 1400
+ }];
+ for (var p = 0; p < extendedPlatforms.length; p++) {
+ var platform = new Platform();
+ platform.x = extendedPlatforms[p].x;
+ platform.y = extendedPlatforms[p].y;
+ platforms.push(platform);
+ worldContainer.addChild(platform);
+ // Add single hunk every other extended platform
+ if (p % 2 === 1) {
var hunk = new Hunk();
hunk.x = platform.x;
hunk.y = platform.y - 20;
hunks.push(hunk);
worldContainer.addChild(hunk);
- // Ensure hunk stays in foreground
- var hunkIndex = worldContainer.getChildIndex(hunk);
- worldContainer.setChildIndex(hunk, Math.max(70, hunkIndex));
}
}
- // Add environmental doors/portals for area transitions
- if (Math.random() > 0.6) {
- var environmentDoor = worldContainer.attachAsset('door', {
- anchorX: 0.5,
- anchorY: 1.0,
- x: startX + 1500 + Math.random() * 1000,
- y: 1800,
- scaleX: 0.6,
- scaleY: 0.6
- });
- environmentDoor.tint = 0x444444 + Math.floor(Math.random() * 0x555555);
- backgroundElements.push(environmentDoor);
- // Ensure doors stay behind characters
- var doorIndex = worldContainer.getChildIndex(environmentDoor);
- worldContainer.setChildIndex(environmentDoor, Math.max(0, doorIndex - 10));
- }
// Update last generated position
- lastGeneratedX = startX + 3000;
+ lastGeneratedX = startX + 2500;
}
function cleanupOldElements() {
var cleanupThreshold = cameraX - 3000;
// Cleanup old background elements
@@ -1208,337 +1162,280 @@
}
}
}
function createLevel() {
- // Add nocturnecity as the primary background with seamless layering
- var nocturneBg = worldContainer.attachAsset('nocturnecity', {
+ // Add nocturnecity as the expanded background layer
+ var nocturneCityBg = worldContainer.attachAsset('nocturnecity', {
anchorX: 0.5,
anchorY: 1.0,
x: 2500,
y: 2732,
- scaleX: 1.2,
- scaleY: 1.2
+ scaleX: 1.5,
+ scaleY: 1.5
});
- nocturneBg.alpha = 0.95;
- worldContainer.setChildIndex(nocturneBg, 0); // Put behind everything
- // Add layered backgrounds for depth without visible seams
+ 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: 1000,
+ x: 2048,
y: 2732,
- scaleX: 1.8,
- scaleY: 1.8
+ scaleX: 2.0,
+ scaleY: 2.0
});
- background2.alpha = 0.4;
- worldContainer.setChildIndex(background2, 1);
- var background3 = worldContainer.attachAsset('bg3', {
+ background2.alpha = 0.7;
+ // Add main background layer on top
+ var background = worldContainer.attachAsset('bg', {
anchorX: 0.5,
anchorY: 1.0,
- x: 4000,
+ x: 2048,
y: 2732,
- scaleX: 1.5,
- scaleY: 1.5
+ scaleX: 0.8,
+ scaleY: 0.8
});
- background3.alpha = 0.3;
- worldContainer.setChildIndex(background3, 2);
- // Create buildings first for background layer
+ 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 building = new Building();
- building.x = 300 + i * 400;
- building.y = 2000; // Position at ground level
- building.scale.x = 0.7 + Math.random() * 0.5;
- building.scale.y = 0.7 + Math.random() * 0.6;
- buildings.push(building);
- worldContainer.addChild(building);
- // Force buildings to background layer (index 3-15)
- worldContainer.setChildIndex(building, 3 + i);
- }
- // Create atmospheric clouds in background
- for (var i = 0; i < 15; 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() * 5000,
- y: 800 + Math.random() * 1200
+ x: Math.random() * 4096 - 1024,
+ // Spread across wider area
+ y: 2100 + Math.random() * 400 // Position below starting platform (1800-2000)
});
- var cloudScale = 0.4 + Math.random() * 1.2;
+ // Vary cloud sizes for depth
+ var cloudScale = 0.5 + Math.random() * 1.0;
cloud.scale.x = cloudScale;
cloud.scale.y = cloudScale;
- cloud.alpha = 0.2 + Math.random() * 0.4;
- cloud.driftSpeed = 0.3 + Math.random() * 1.0;
+ // Make clouds semi-translucent with varying opacity
+ cloud.alpha = 0.3 + Math.random() * 0.4;
+ // 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 in background layer (index 16-30)
- worldContainer.setChildIndex(cloud, 16 + i);
+ // Put clouds behind platforms but in front of backgrounds
+ worldContainer.setChildIndex(cloud, 4 + i);
}
- // Add neon signs and statues as background elements
- for (var i = 0; i < 6; i++) {
- var statue = worldContainer.attachAsset('statue', {
- anchorX: 0.5,
- anchorY: 1.0,
- x: 600 + i * 800,
- y: 2000,
- scaleX: 0.8 + Math.random() * 0.4,
- scaleY: 0.8 + Math.random() * 0.4
- });
- statue.tint = 0x555555 + Math.floor(Math.random() * 0x333333);
- backgroundElements.push(statue);
- // Force statues to background layer
- worldContainer.setChildIndex(statue, 31 + i);
+ // Create more buildings with varied positioning to fill the midground
+ for (var i = 0; i < 8; i++) {
+ var building = new Building();
+ building.x = 200 + i * 600;
+ building.y = 2000; // Position at ground level so buildings meet platforms
+ building.scale.x = 0.8 + Math.random() * 0.4; // Vary building sizes
+ building.scale.y = 0.8 + Math.random() * 0.4;
+ buildings.push(building);
+ worldContainer.addChild(building);
}
- // Add blinking neon signs to background
- for (var i = 0; i < 6; i++) {
- var neonSign = worldContainer.attachAsset('neonSign', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: 500 + i * 700,
- y: 1000 + Math.random() * 600,
- scaleX: 0.6 + Math.random() * 0.4,
- scaleY: 0.6 + Math.random() * 0.4
- });
- neonSign.tint = [0xFF00FF, 0x00FFFF, 0xFFFF00, 0xFF0088, 0x88FF00, 0xFF8800][i];
- // Force neon signs to background layer
- worldContainer.setChildIndex(neonSign, 37 + i);
- // Animate blinking
- var _animateNeon = function animateNeon(sign) {
- tween(sign, {
- alpha: 0.3
- }, {
- duration: 1000 + Math.random() * 1000,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- tween(sign, {
- alpha: 1
- }, {
- duration: 1000 + Math.random() * 1000,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- _animateNeon(sign);
- }
- });
- }
- });
- };
- _animateNeon(neonSign);
- }
- // NOW CREATE PLATFORMS - guided by nocturnecitylayout
- // These will be in the foreground (index 43+)
- // Ground level platforms matching layout guide
- var platforms_data = [
- // Bottom level (street level)
+ var ground = new Platform();
+ ground.x = 1024;
+ ground.y = 2000;
+ ground.scale.x = 10;
+ platforms.push(ground);
+ worldContainer.addChild(ground);
+ // Create platform layout based on nocturnecitylayout guide
+ var platformPositions = [{
+ x: 400,
+ y: 1800
+ },
+ // Starting platform
{
- x: 250,
- y: 1900,
- scale: 1.0
- }, {
- x: 750,
- y: 1900,
- scale: 1.2
+ x: 800,
+ y: 1600
},
- // Shop platform
+ // Lower left area
{
- x: 1250,
- y: 1900,
- scale: 1.0
- }, {
- x: 1750,
- y: 1900,
- scale: 1.0
- }, {
- x: 2250,
- y: 1900,
- scale: 1.0
- }, {
- x: 2750,
- y: 1900,
- scale: 1.0
+ x: 1300,
+ y: 1400
},
- // Second level
+ // Shop door platform (bottom left in layout)
{
- x: 400,
- y: 1450,
- scale: 0.9
- }, {
- x: 900,
- y: 1450,
- scale: 1.0
- }, {
- x: 1400,
- y: 1450,
- scale: 0.8
- }, {
- x: 1900,
- y: 1450,
- scale: 0.9
- }, {
+ x: 1800,
+ y: 1200
+ },
+ // Mid level
+ {
x: 2400,
- y: 1450,
- scale: 0.8
+ y: 1100
},
- // Third level
+ // Upper mid
{
- x: 650,
- y: 1000,
- scale: 0.7
- }, {
- x: 1150,
- y: 1000,
- scale: 0.8
- }, {
- x: 1650,
- y: 1000,
- scale: 0.9
- }, {
- x: 2150,
- y: 1000,
- scale: 0.7
+ x: 2900,
+ y: 1300
},
- // Top level
+ // Door platform (mid level)
{
- x: 800,
- y: 550,
- scale: 0.6
- }, {
- x: 1300,
- y: 450,
- scale: 0.7
- }, {
- x: 1800,
- y: 550,
- scale: 0.6
- }];
- for (var i = 0; i < platforms_data.length; i++) {
- var platData = platforms_data[i];
+ x: 3400,
+ y: 1000
+ },
+ // Upper right area
+ {
+ x: 3900,
+ y: 1200
+ },
+ // Far right
+ {
+ x: 4400,
+ y: 1400
+ },
+ // Door platform (upper right)
+ {
+ x: 4900,
+ y: 1600
+ },
+ // Extended right
+ {
+ x: 5400,
+ y: 1100
+ },
+ // Final platform
+ {
+ x: 5900,
+ y: 1300
+ } // 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 = platData.x;
- platform.y = platData.y;
- platform.scale.x = platData.scale;
- platform.scale.y = platData.scale;
+ platform.x = platformPositions[i].x;
+ platform.y = platformPositions[i].y;
platforms.push(platform);
worldContainer.addChild(platform);
- // Ensure platforms are in foreground (index 43+)
- worldContainer.setChildIndex(platform, 43 + i);
- }
- // Add doors AFTER platforms to ensure proper layering
- // Shop door at designated shop platform (index 1)
- var shopPlatform = platforms[1];
- doorAsset = worldContainer.attachAsset('door', {
- anchorX: 0.5,
- anchorY: 1.0,
- x: shopPlatform.x,
- y: shopPlatform.y - 20,
- scaleX: 0.8,
- scaleY: 0.8
- });
- doorAsset.interactive = true;
- // Ensure door is in front of buildings but behind player
- worldContainer.setChildIndex(doorAsset, 60);
- // Add hidesign floating over shop door - ONLY for shop
- var hideSign = worldContainer.attachAsset('hidesign', {
- anchorX: 0.5,
- anchorY: 1.0,
- x: shopPlatform.x,
- y: shopPlatform.y - 300,
- scaleX: 3,
- scaleY: 3
- });
- hideSign.interactive = true;
- hideSign.down = function (x, y, obj) {
- enterShop();
- };
- // Ensure sign is in front of everything except player
- worldContainer.setChildIndex(hideSign, 61);
- // Animate sign floating
- var _animateSign = function animateSign() {
- tween(hideSign, {
- y: shopPlatform.y - 280
- }, {
- duration: 2000,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- tween(hideSign, {
- y: shopPlatform.y - 320
- }, {
- duration: 2000,
- easing: tween.easeInOut,
- onFinish: _animateSign
+ // Check if this platform should have a door
+ var doorInfo = doorPositions.find(function (d) {
+ return d.platformIndex === i;
+ });
+ if (doorInfo) {
+ 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;
+ 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 placeholder interior function
+ door.down = function () {
+ showPlaceholderInterior();
+ };
}
- });
- };
- _animateSign();
- // Add other doors as placeholders only - matching layout positions
- var doorPositions = [{
- x: 1400,
- y: 1430,
- type: 'TAVERN'
+ }
+ }
+ // Add single hunks at strategic positions - only one visible at a time
+ var hunkPositions = [{
+ x: 1800,
+ y: 1180,
+ platformIndex: 3
},
- // Mid-level
+ // First hunk after starting area
{
- x: 1650,
- y: 980,
- type: 'GUILD'
+ x: 2900,
+ y: 1280,
+ platformIndex: 5
},
- // Upper level
+ // Near mid door
{
- x: 1300,
- y: 430,
- type: 'TEMPLE'
+ x: 4400,
+ y: 1380,
+ platformIndex: 8
},
- // Top level
+ // Near upper door
{
- x: 2400,
- y: 1430,
- type: 'ARMORY'
- },
- // Mid-level right
- {
- x: 2150,
- y: 980,
- type: 'LIBRARY'
- } // Upper right
+ x: 5900,
+ y: 1280,
+ platformIndex: 11
+ } // Final hunk
];
- for (var i = 0; i < doorPositions.length; i++) {
- var doorPos = doorPositions[i];
- var door = worldContainer.attachAsset('door', {
- anchorX: 0.5,
- anchorY: 1.0,
- x: doorPos.x,
- y: doorPos.y,
- scaleX: 0.6,
- scaleY: 0.6
- });
- door.interactive = true;
- door.doorType = doorPos.type;
- door.down = function (doorType) {
- return function () {
- showPlaceholderArea(doorType);
- };
- }(door.doorType);
- // Ensure doors are in foreground
- worldContainer.setChildIndex(door, 62 + i);
+ 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 SINGLE hunk only - properly spaced
- var hunk = new Hunk();
- hunk.x = 1150; // Position on accessible platform
- hunk.y = 980;
- hunks.push(hunk);
- worldContainer.addChild(hunk);
- // Ensure hunk is in foreground
- worldContainer.setChildIndex(hunk, 70);
- // Add player character
bloodmage = new Bloodmage();
- bloodmage.x = 250;
- bloodmage.y = 1880;
+ bloodmage.x = 400; // Start on first platform
+ bloodmage.y = 1780;
worldContainer.addChild(bloodmage);
- // Ensure player is in very front
- worldContainer.setChildIndex(bloodmage, 71);
// Start idle animation initially
bloodmage.startIdleAnimation();
// Track initial generation position
- lastGeneratedX = 4000;
+ lastGeneratedX = 6500; // Extended to accommodate new layout
}
createLevel();
var trackpadBg = LK.gui.bottomLeft.attachAsset('trackpadBg', {
anchorX: 0.5,
@@ -2061,30 +1958,35 @@
}, {
duration: 500,
easing: tween.easeOut
});
- // Add Mandroid's name using alphabet assets
- var nameLetters = 'hardware';
- var nameLetterSpacing = 60;
- var nameStartX = 350 - nameLetters.length * nameLetterSpacing / 2;
- for (var i = 0; i < nameLetters.length; i++) {
- var letter = nameLetters[i];
- var letterAsset = shopOverlay.attachAsset(letter, {
- anchorX: 0.5,
- anchorY: 0.5,
- x: nameStartX + i * nameLetterSpacing,
- y: 1200,
- scaleX: 3.0,
- scaleY: 3.0,
- alpha: 0
- });
- tween(letterAsset, {
- alpha: 1
- }, {
- duration: 600 + i * 100,
- 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, {
@@ -2240,93 +2142,128 @@
// 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; // Show 3 items per page for better variety
+ 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 = 400 + localIndex * 500; // Better spacing between items
- var itemY = 1500; // Positioned to avoid UI overlap
- // Add appropriate display platform with proper sizing
+ var itemX = 600 + localIndex * 400;
+ var itemY = 1800;
+ // Add appropriate display platform
if (item.useDisplay) {
// Display platform for crystal hearts
var displayPlatform = shopOverlay.attachAsset('displayplatform', {
anchorX: 0.5,
anchorY: 0.5,
x: itemX,
- y: itemY + 80,
- scaleX: 2.0,
- scaleY: 2.0
+ y: itemY + 20,
+ scaleX: 3,
+ scaleY: 3
});
} else if (item.useShelf) {
// Shelf for plush items
var platShelf = shopOverlay.attachAsset('platshelf', {
anchorX: 0.5,
anchorY: 0.5,
x: itemX,
- y: itemY + 90,
- scaleX: 2.5,
- scaleY: 2.5
+ y: itemY + 30,
+ scaleX: 4,
+ scaleY: 4
});
}
- // Item asset with appropriate scaling
+ // Item asset
var itemAsset = shopOverlay.attachAsset(item.asset, {
anchorX: 0.5,
anchorY: 0.5,
x: itemX,
- y: itemY - 30,
- scaleX: 1.5,
- scaleY: 1.5
+ y: itemY - 50,
+ scaleX: 2,
+ scaleY: 2
});
- // Create item name using alphabet assets with proper spacing
+ // Create item name using alphabet assets
var nameLetters = item.name.toLowerCase();
- var letterSpacing = 45; // Proper letter spacing for readability
+ var letterSpacing = 25;
var startX = itemX - nameLetters.length * letterSpacing / 2;
for (var j = 0; j < nameLetters.length; j++) {
var letter = nameLetters[j];
if (letter !== ' ') {
var letterAsset = shopOverlay.attachAsset(letter, {
anchorX: 0.5,
anchorY: 0.5,
x: startX + j * letterSpacing,
- y: itemY + 140,
- scaleX: 1.2,
- scaleY: 1.2
+ y: itemY + 80,
+ scaleX: 0.8,
+ scaleY: 0.8
});
}
}
- // Item price with proper text rendering
+ // Item price
var itemPrice = new Text2('PRICE: ' + item.price, {
- size: 36,
- fill: 0xFFFF00
+ size: 56,
+ fill: 0xFFFF00,
+ fontWeight: 'bold'
});
itemPrice.anchor.set(0.5, 0.5);
itemPrice.x = itemX;
- itemPrice.y = itemY + 180;
+ itemPrice.y = itemY + 120;
+ // Add hot pink glow
+ itemPrice.style = {
+ fontSize: 56,
+ fill: 0xFFFF00,
+ fontWeight: 'bold',
+ dropShadow: true,
+ dropShadowColor: 0xFF1493,
+ dropShadowBlur: 15,
+ dropShadowDistance: 0
+ };
shopOverlay.addChild(itemPrice);
- // Item description with word wrap
+ // Item description
var itemDesc = new Text2(item.description, {
- size: 24,
+ size: 40,
fill: 0xFFFFFF,
+ fontWeight: 'bold',
wordWrap: true,
- wordWrapWidth: 180
+ wordWrapWidth: 300
});
itemDesc.anchor.set(0.5, 0.5);
itemDesc.x = itemX;
- itemDesc.y = itemY + 220;
+ itemDesc.y = itemY + 180;
+ // Add hot pink glow
+ itemDesc.style = {
+ fontSize: 40,
+ fill: 0xFFFFFF,
+ fontWeight: 'bold',
+ dropShadow: true,
+ dropShadowColor: 0xFF1493,
+ dropShadowBlur: 15,
+ dropShadowDistance: 0,
+ wordWrap: true,
+ wordWrapWidth: 300
+ };
shopOverlay.addChild(itemDesc);
// Buy button
var buyButton = new Text2('BUY', {
- size: 48,
- fill: capturedHunks.length >= item.price ? 0x00FF00 : 0xFF0000
+ size: 80,
+ fill: capturedHunks.length >= item.price ? 0x00FF00 : 0xFF0000,
+ fontWeight: 'bold'
});
buyButton.anchor.set(0.5, 0.5);
buyButton.x = itemX;
- buyButton.y = itemY + 280;
+ buyButton.y = itemY + 240;
buyButton.interactive = true;
+ // 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 () {
purchaseItem(itemIndex);
};
@@ -2334,9 +2271,9 @@
shopOverlay.addChild(buyButton);
}
// Update navigation buttons
prevButton.alpha = currentShopPage > 0 ? 1 : 0.3;
- var maxPages = Math.ceil(shopItems.length / itemsPerPage) - 1;
+ var maxPages = Math.ceil(shopItems.length / 3) - 1;
nextButton.alpha = currentShopPage < maxPages ? 1 : 0.3;
// Update currency display
currencyText.setText('HUNKS: ' + capturedHunks.length);
}
@@ -2384,93 +2321,8 @@
shopShowing = false;
shopOverlay.destroy();
shopOverlay = null;
}
-function showPlaceholderArea(areaType) {
- if (shopShowing || bropageShowing || battleMenuVisible) return;
- // Create placeholder overlay
- var placeholderOverlay = game.addChild(new Container());
- var placeholderBg = placeholderOverlay.attachAsset('shop', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: 1024,
- y: 1366,
- scaleX: 2.0,
- scaleY: 2.7,
- alpha: 0.8
- });
- placeholderBg.tint = 0x444444;
- // Create area title using alphabet assets
- var titleLetters = areaType.toLowerCase();
- var titleSpacing = 100;
- var titleStartX = 1024 - titleLetters.length * titleSpacing / 2;
- for (var i = 0; i < titleLetters.length; i++) {
- var letter = titleLetters[i];
- if (letter !== ' ') {
- var letterAsset = placeholderOverlay.attachAsset(letter, {
- anchorX: 0.5,
- anchorY: 0.5,
- x: titleStartX + i * titleSpacing,
- y: 800,
- scaleX: 4.0,
- scaleY: 4.0,
- alpha: 0
- });
- tween(letterAsset, {
- alpha: 1
- }, {
- duration: 300 + i * 50,
- easing: tween.easeOut
- });
- }
- }
- // Coming soon message
- var comingSoonText = new Text2('COMING SOON!', {
- size: 150,
- fill: 0xFFFF00,
- fontWeight: 'bold'
- });
- comingSoonText.anchor.set(0.5, 0.5);
- comingSoonText.x = 1024;
- comingSoonText.y = 1400;
- comingSoonText.alpha = 0;
- placeholderOverlay.addChild(comingSoonText);
- tween(comingSoonText, {
- alpha: 1
- }, {
- duration: 800,
- easing: tween.easeOut
- });
- // Exit button
- var exitButton = placeholderOverlay.attachAsset('x', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: 1800,
- y: 400,
- scaleX: 2.0,
- scaleY: 2.0
- });
- exitButton.interactive = true;
- exitButton.down = function () {
- placeholderOverlay.destroy();
- };
- // Add descriptive exit text
- var exitText = new Text2('EXIT', {
- size: 60,
- fill: 0xFFFFFF,
- fontWeight: 'bold'
- });
- exitText.anchor.set(0.5, 0.5);
- exitText.x = 1800;
- exitText.y = 500;
- placeholderOverlay.addChild(exitText);
- // Auto-close after 5 seconds (extended time)
- LK.setTimeout(function () {
- if (placeholderOverlay) {
- placeholderOverlay.destroy();
- }
- }, 5000);
-}
function castPlayerSpell() {
if (!bloodmage || isCasting) return;
isCasting = true;
LK.getSound('spell').play();
@@ -2617,25 +2469,54 @@
var cursorUI = null;
function showBattleUI() {
if (battleMenuVisible || !currentHunk) return;
battleMenuVisible = true;
- // Create combat menu system positioned between trackpad and buttons
+ // Create stretched combat menu along bottom center
combatMenuUI = game.attachAsset('combatmenu', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2200,
- scaleX: 4,
- scaleY: 4
+ scaleX: 8,
+ scaleY: 3
});
combatMenuUI.interactive = true;
+ // Animate combat UI stretching in from center
+ combatMenuUI.scaleX = 0.1;
+ tween(combatMenuUI, {
+ scaleX: 8
+ }, {
+ duration: 400,
+ easing: tween.easeOut
+ });
+ // Position cursor properly for 4 menu options with better spacing
cursorUI = game.attachAsset('cursor', {
anchorX: 0.5,
anchorY: 0.5,
- x: 724,
+ x: 624,
y: 2200,
+ scaleX: 0.25,
+ scaleY: 0.4
+ });
+ cursorUI.alpha = 0.8;
+ // Add visual outline effect to cursor
+ tween(cursorUI, {
scaleX: 0.3,
- scaleY: 0.3
+ scaleY: 0.5,
+ alpha: 1
+ }, {
+ duration: 300,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ tween(cursorUI, {
+ scaleX: 0.25,
+ scaleY: 0.4,
+ alpha: 0.8
+ }, {
+ duration: 300,
+ easing: tween.easeInOut
+ });
+ }
});
window.currentSelectedOption = 0;
// Store global references for cleanup
window.currentCombatMenu = combatMenuUI;
@@ -2718,39 +2599,42 @@
}
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 4)
- var menuLeft = 1024 - 400; // Full width of scaled menu (200 * 4 scale)
- var menuRight = 1024 + 400;
- var menuTop = 2200 - 360; // Full height of scaled menu (180 * 4 scale)
- var menuBottom = 2200 + 360;
+ // Check if clicking within stretched combat menu bounds (centered at 1024, 2200 with scale 8x3)
+ var menuLeft = 1024 - 800; // Full width of scaled menu (200 * 8 scale)
+ var menuRight = 1024 + 800;
+ var menuTop = 2200 - 270; // Full height of scaled menu (180 * 3 scale)
+ var menuBottom = 2200 + 270;
if (x >= menuLeft && x <= menuRight && y >= menuTop && y <= menuBottom) {
- // Determine which option based on x position within menu
+ // Determine which option based on x position within menu (4 equal sections)
var relativeX = (x - menuLeft) / (menuRight - menuLeft);
var newOption = Math.floor(relativeX * 4);
newOption = Math.max(0, Math.min(3, newOption));
- // Update cursor position with correct positions for 4 options
+ // Updated cursor positions for stretched menu with proper spacing
var positions = [{
- x: 724,
+ x: 624,
y: 2200
}, {
- x: 874,
+ x: 824,
y: 2200
}, {
- x: 1174,
+ x: 1224,
y: 2200
}, {
- x: 1324,
+ x: 1424,
y: 2200
}];
if (newOption === window.currentSelectedOption) {
- // Same option clicked - select it
+ // Same option clicked - select it with visual feedback
+ LK.effects.flashObject(window.currentCursor, 0x00FF00, 200);
selectCurrentOption();
} else {
- // Different option - move cursor there
+ // 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
});
@@ -2779,24 +2663,17 @@
enterShop();
return;
}
}
- // Check if tapping on a hunk to start battle - improved detection
+ // Check if tapping on a hunk to start battle
for (var i = 0; i < hunks.length; i++) {
var hunk = hunks[i];
if (!hunk.captured) {
- // Check if player is near hunk first
- var playerDistance = Math.sqrt(Math.pow(bloodmage.x - hunk.x, 2) + Math.pow(bloodmage.y - hunk.y, 2));
- if (playerDistance < 500) {
- // Player must be close to hunk - increased range
- var hunkDistance = Math.sqrt(Math.pow(worldPos.x - hunk.x, 2) + Math.pow(worldPos.y - hunk.y, 2));
- if (hunkDistance < 400) {
- // Further increased tap detection range
- currentHunk = hunk;
- battleHunk = hunk;
- showBattleUI();
- return;
- }
+ var hunkDistance = Math.sqrt(Math.pow(worldPos.x - hunk.x, 2) + Math.pow(worldPos.y - hunk.y, 2));
+ if (hunkDistance < 150) {
+ currentHunk = hunk;
+ showBattleUI();
+ return;
}
}
}
// Check if near door for action button press
@@ -3054,22 +2931,13 @@
performCaptureAnimation();
} else if (selectedOption === 1) {
// Speak - show dialogue with player response
showDualDialogue();
- // Show XP menu after dialogue
- LK.setTimeout(function () {
- showXPMenu(false);
- }, 4500);
} else if (selectedOption === 2) {
// Cast - perform casting animation
performBattleCast();
- // Show XP menu after casting
- LK.setTimeout(function () {
- showXPMenu(false);
- }, 3000);
} else if (selectedOption === 3) {
- // Leave - exit battle UI and show XP
- showXPMenu(false);
+ // Leave - exit battle UI
hideBattleUI();
currentHunk = null;
}
}
@@ -3384,5 +3252,73 @@
}
}, 3000);
}
LK.playMusic('cyberpunk');
-;
\ No newline at end of file
+;
+var placeholderInteriorShowing = false;
+var placeholderOverlay = null;
+function showPlaceholderInterior() {
+ if (placeholderInteriorShowing) return;
+ placeholderInteriorShowing = true;
+ // Create interior overlay
+ placeholderOverlay = game.addChild(new Container());
+ // Add dark background
+ var interiorBg = placeholderOverlay.attachAsset('bg3', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 1024,
+ y: 1366,
+ scaleX: 3,
+ scaleY: 4,
+ alpha: 0
+ });
+ interiorBg.tint = 0x1a1a1a;
+ tween(interiorBg, {
+ alpha: 0.9
+ }, {
+ duration: 500,
+ easing: tween.easeOut
+ });
+ // Add placeholder text
+ var placeholderText = new Text2('INTERIOR COMING SOON...', {
+ size: 120,
+ fill: 0xFFFFFF,
+ fontWeight: 'bold'
+ });
+ placeholderText.anchor.set(0.5, 0.5);
+ placeholderText.x = 1024;
+ placeholderText.y = 1366;
+ placeholderText.alpha = 0;
+ placeholderOverlay.addChild(placeholderText);
+ tween(placeholderText, {
+ alpha: 1
+ }, {
+ duration: 600,
+ easing: tween.easeOut
+ });
+ // Add exit button
+ var exitBtn = placeholderOverlay.attachAsset('x', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 1800,
+ y: 400,
+ scaleX: 2,
+ scaleY: 2,
+ alpha: 0
+ });
+ exitBtn.interactive = true;
+ exitBtn.down = function () {
+ closePlaceholderInterior();
+ };
+ tween(exitBtn, {
+ alpha: 1
+ }, {
+ duration: 500,
+ easing: tween.easeOut
+ });
+}
+function closePlaceholderInterior() {
+ if (!placeholderInteriorShowing || !placeholderOverlay) return;
+ placeholderInteriorShowing = false;
+ placeholderOverlay.destroy();
+ placeholderOverlay = null;
+}
\ No newline at end of file
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