/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // ChoiceButton: Large button for a story choice var ChoiceButton = Container.expand(function () { var self = Container.call(this); // Arguments: label, index var label = arguments[0] || ''; var idx = arguments[1] || 0; // Button background var btnBg = self.attachAsset('choiceBtn', { anchorX: 0.5, anchorY: 0.5 }); btnBg.x = 0; btnBg.y = 0; // Button text var btnText = new Text2(label, { size: 70, fill: 0x4A90E2, wordWrap: true, wordWrapWidth: 1200, align: 'center' }); btnText.anchor.set(0.5, 0.5); btnText.x = 0; btnText.y = 0; self.addChild(btnText); // Visual feedback on press self.down = function (x, y, obj) { tween(btnBg, { tint: 0xBEE3F8 }, { duration: 80, onFinish: function onFinish() { tween(btnBg, { tint: 0xffffff }, { duration: 120 }); } }); }; return self; }); /**** * Story Data ****/ /* A simple branching story structure. Each scene has: - id: unique string - text: narrative text - choices: array of { text: string, next: id } If choices is empty or undefined, it's an ending. */ // StoryScene: Represents a single scene in the story var StoryScene = Container.expand(function () { var self = Container.call(this); // Scene background var bg = self.attachAsset('sceneBg', { anchorX: 0.5, anchorY: 0.5 }); bg.x = 0; bg.y = 0; // Scene text var text = new Text2('', { size: 90, fill: 0xFFFFFF, wordWrap: true, wordWrapWidth: 1400, align: 'center' }); text.anchor.set(0.5, 0); text.x = 0; text.y = -350; self.addChild(text); // Holds choice button objects self.choiceButtons = []; // Set scene content self.setScene = function (sceneData) { text.setText(sceneData.text); // Remove old choice buttons for (var i = 0; i < self.choiceButtons.length; i++) { self.choiceButtons[i].destroy(); } self.choiceButtons = []; // Add new choice buttons var choices = sceneData.choices || []; var btnYStart = 200; var btnSpacing = 250; for (var i = 0; i < choices.length; i++) { var btn = new ChoiceButton(choices[i].text, i); btn.x = 0; btn.y = btnYStart + i * btnSpacing; self.addChild(btn); self.choiceButtons.push(btn); // Attach event (function (idx) { btn.down = function (x, y, obj) { if (typeof self.onChoice === 'function') { self.onChoice(idx); } }; })(i); } }; // Animate in self.animateIn = function () { self.alpha = 0; tween(self, { alpha: 1 }, { duration: 400, easing: tween.easeOut }); }; // Animate out self.animateOut = function (onFinish) { tween(self, { alpha: 0 }, { duration: 300, easing: tween.easeIn, onFinish: onFinish }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222244 }); /**** * Game Code ****/ // Center of the game area /* We use simple shapes for story scenes and buttons. Each scene uses a colored rectangle as a background, and choices are large rounded rectangles. */ /**** * Story Data ****/ /* A simple branching story structure. Each scene has: - id: unique string - text: narrative text - choices: array of { text: string, next: id } If choices is empty or undefined, it's an ending. */ var storyData = { start: { id: 'start', text: "You wake up in a mysterious forest. The sun is rising, and you hear birds singing. There are two paths ahead.\n\nWhat do you do?", choices: [{ text: "Take the left path", next: "leftPath" }, { text: "Take the right path", next: "rightPath" }] }, leftPath: { id: 'leftPath', text: "You walk down the left path and find a sparkling river. A wooden bridge crosses it, but it looks old.\n\nWhat do you do?", choices: [{ text: "Cross the bridge", next: "bridgeCross" }, { text: "Follow the river", next: "followRiver" }] }, rightPath: { id: 'rightPath', text: "The right path leads you deeper into the woods. You spot a friendly-looking fox watching you.\n\nWhat do you do?", choices: [{ text: "Approach the fox", next: "foxFriend" }, { text: "Ignore the fox and keep walking", next: "deepWoods" }] }, bridgeCross: { id: 'bridgeCross', text: "You step onto the bridge. It creaks, but holds. On the other side, you find a treasure chest!\n\nYou open it and discover gold and a map. Congratulations, you found a happy ending!", choices: [] }, followRiver: { id: 'followRiver', text: "You follow the river and soon get lost. Night falls, and you hear strange noises. Maybe next time you'll choose differently.\n\nThe End.", choices: [] }, foxFriend: { id: 'foxFriend', text: "You approach the fox. It smiles and leads you to a hidden clearing where a picnic is set up just for you. What a magical day!\n\nThe End.", choices: [] }, deepWoods: { id: 'deepWoods', text: "You walk deeper into the woods and soon realize you're going in circles. Eventually, you find your way back to where you started.\n\nDo you want to try again?", choices: [{ text: "Yes, restart the adventure", next: "start" }] } }; var centerX = 2048 / 2; var centerY = 2732 / 2; // Current scene container var currentScene = null; // Title text (shown at the top) var titleText = new Text2("Story Crafter", { size: 120, fill: 0xFFFFFF, align: 'center' }); titleText.anchor.set(0.5, 0); titleText.x = centerX; titleText.y = 120; LK.gui.top.addChild(titleText); // Settings button (top-right, avoids top-left menu) var settingsBtn = LK.getAsset('choiceBtn', { anchorX: 1, anchorY: 0, width: 300, height: 120, x: 2048 - 40, y: 40 }); var settingsTxt = new Text2("Settings", { size: 48, fill: 0x4A90E2, align: 'center' }); settingsTxt.anchor.set(0.5, 0.5); settingsTxt.x = settingsBtn.width / 2 - 150; settingsTxt.y = settingsBtn.height / 2 - 60; settingsBtn.addChild(settingsTxt); LK.gui.topRight.addChild(settingsBtn); // Visual feedback and placeholder for settings action settingsBtn.down = function (x, y, obj) { tween(settingsBtn, { tint: 0xBEE3F8 }, { duration: 80, onFinish: function onFinish() { tween(settingsBtn, { tint: 0xffffff }, { duration: 120 }); } }); // Placeholder: Show a simple settings message (could be replaced with real settings) if (!settingsBtn._settingsMsg) { var msg = new Text2("Settings coming soon!", { size: 70, fill: 0xffffff, align: 'center' }); msg.anchor.set(0.5, 0.5); msg.x = centerX; msg.y = centerY; game.addChild(msg); settingsBtn._settingsMsg = msg; LK.setTimeout(function () { if (msg && msg.parent) msg.destroy(); settingsBtn._settingsMsg = null; }, 1200); } }; // Helper: Show a scene by id function showScene(sceneId) { var sceneData = storyData[sceneId]; if (!sceneData) return; // Remove previous scene with fade out if (currentScene) { currentScene.animateOut(function () { currentScene.destroy(); createAndShowScene(sceneData); }); } else { createAndShowScene(sceneData); } } // Helper: Create and show a scene function createAndShowScene(sceneData) { var scene = new StoryScene(); scene.x = centerX; scene.y = centerY; scene.setScene(sceneData); // Handle choice selection scene.onChoice = function (choiceIdx) { var choices = sceneData.choices || []; if (choices[choiceIdx] && choices[choiceIdx].next) { showScene(choices[choiceIdx].next); } }; game.addChild(scene); scene.animateIn(); currentScene = scene; } // Intro animation: Show 'firegames', then 'story crafter', then 'start the story', then start the story var introText = new Text2("firegames", { size: 180, fill: 0xff6600, align: 'center' }); introText.anchor.set(0.5, 0.5); introText.x = centerX; introText.y = centerY; introText.alpha = 0; game.addChild(introText); // Animate in 'firegames' tween(introText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Hold for 900ms, then fade out LK.setTimeout(function () { tween(introText, { alpha: 0, scaleX: 1, scaleY: 1 }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { // After 'firegames', show 'story crafter' if (introText && introText.parent) introText.destroy(); var storyCrafterText = new Text2("Story Crafter", { size: 180, fill: 0xffffff, align: 'center' }); storyCrafterText.anchor.set(0.5, 0.5); storyCrafterText.x = centerX; storyCrafterText.y = centerY; storyCrafterText.alpha = 0; game.addChild(storyCrafterText); tween(storyCrafterText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Hold for 900ms, then fade out LK.setTimeout(function () { tween(storyCrafterText, { alpha: 0, scaleX: 1, scaleY: 1 }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { if (storyCrafterText && storyCrafterText.parent) storyCrafterText.destroy(); // Show 'start the story' var startStoryText = new Text2("Start the story", { size: 120, fill: 0x4A90E2, align: 'center' }); startStoryText.anchor.set(0.5, 0.5); startStoryText.x = centerX; startStoryText.y = centerY; startStoryText.alpha = 0; game.addChild(startStoryText); tween(startStoryText, { alpha: 1, scaleX: 1.1, scaleY: 1.1 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Hold for 900ms, then fade out and start the story LK.setTimeout(function () { tween(startStoryText, { alpha: 0, scaleX: 1, scaleY: 1 }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { if (startStoryText && startStoryText.parent) startStoryText.destroy(); showScene('start'); } }); }, 900); } }); } }); }, 900); } }); } }); }, 900); } }); // No update loop needed for this game, but placeholder for future features game.update = function () { // No per-frame logic needed for MVP };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// ChoiceButton: Large button for a story choice
var ChoiceButton = Container.expand(function () {
var self = Container.call(this);
// Arguments: label, index
var label = arguments[0] || '';
var idx = arguments[1] || 0;
// Button background
var btnBg = self.attachAsset('choiceBtn', {
anchorX: 0.5,
anchorY: 0.5
});
btnBg.x = 0;
btnBg.y = 0;
// Button text
var btnText = new Text2(label, {
size: 70,
fill: 0x4A90E2,
wordWrap: true,
wordWrapWidth: 1200,
align: 'center'
});
btnText.anchor.set(0.5, 0.5);
btnText.x = 0;
btnText.y = 0;
self.addChild(btnText);
// Visual feedback on press
self.down = function (x, y, obj) {
tween(btnBg, {
tint: 0xBEE3F8
}, {
duration: 80,
onFinish: function onFinish() {
tween(btnBg, {
tint: 0xffffff
}, {
duration: 120
});
}
});
};
return self;
});
/****
* Story Data
****/
/*
A simple branching story structure.
Each scene has:
- id: unique string
- text: narrative text
- choices: array of { text: string, next: id }
If choices is empty or undefined, it's an ending.
*/
// StoryScene: Represents a single scene in the story
var StoryScene = Container.expand(function () {
var self = Container.call(this);
// Scene background
var bg = self.attachAsset('sceneBg', {
anchorX: 0.5,
anchorY: 0.5
});
bg.x = 0;
bg.y = 0;
// Scene text
var text = new Text2('', {
size: 90,
fill: 0xFFFFFF,
wordWrap: true,
wordWrapWidth: 1400,
align: 'center'
});
text.anchor.set(0.5, 0);
text.x = 0;
text.y = -350;
self.addChild(text);
// Holds choice button objects
self.choiceButtons = [];
// Set scene content
self.setScene = function (sceneData) {
text.setText(sceneData.text);
// Remove old choice buttons
for (var i = 0; i < self.choiceButtons.length; i++) {
self.choiceButtons[i].destroy();
}
self.choiceButtons = [];
// Add new choice buttons
var choices = sceneData.choices || [];
var btnYStart = 200;
var btnSpacing = 250;
for (var i = 0; i < choices.length; i++) {
var btn = new ChoiceButton(choices[i].text, i);
btn.x = 0;
btn.y = btnYStart + i * btnSpacing;
self.addChild(btn);
self.choiceButtons.push(btn);
// Attach event
(function (idx) {
btn.down = function (x, y, obj) {
if (typeof self.onChoice === 'function') {
self.onChoice(idx);
}
};
})(i);
}
};
// Animate in
self.animateIn = function () {
self.alpha = 0;
tween(self, {
alpha: 1
}, {
duration: 400,
easing: tween.easeOut
});
};
// Animate out
self.animateOut = function (onFinish) {
tween(self, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn,
onFinish: onFinish
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222244
});
/****
* Game Code
****/
// Center of the game area
/*
We use simple shapes for story scenes and buttons.
Each scene uses a colored rectangle as a background, and choices are large rounded rectangles.
*/
/****
* Story Data
****/
/*
A simple branching story structure.
Each scene has:
- id: unique string
- text: narrative text
- choices: array of { text: string, next: id }
If choices is empty or undefined, it's an ending.
*/
var storyData = {
start: {
id: 'start',
text: "You wake up in a mysterious forest. The sun is rising, and you hear birds singing. There are two paths ahead.\n\nWhat do you do?",
choices: [{
text: "Take the left path",
next: "leftPath"
}, {
text: "Take the right path",
next: "rightPath"
}]
},
leftPath: {
id: 'leftPath',
text: "You walk down the left path and find a sparkling river. A wooden bridge crosses it, but it looks old.\n\nWhat do you do?",
choices: [{
text: "Cross the bridge",
next: "bridgeCross"
}, {
text: "Follow the river",
next: "followRiver"
}]
},
rightPath: {
id: 'rightPath',
text: "The right path leads you deeper into the woods. You spot a friendly-looking fox watching you.\n\nWhat do you do?",
choices: [{
text: "Approach the fox",
next: "foxFriend"
}, {
text: "Ignore the fox and keep walking",
next: "deepWoods"
}]
},
bridgeCross: {
id: 'bridgeCross',
text: "You step onto the bridge. It creaks, but holds. On the other side, you find a treasure chest!\n\nYou open it and discover gold and a map. Congratulations, you found a happy ending!",
choices: []
},
followRiver: {
id: 'followRiver',
text: "You follow the river and soon get lost. Night falls, and you hear strange noises. Maybe next time you'll choose differently.\n\nThe End.",
choices: []
},
foxFriend: {
id: 'foxFriend',
text: "You approach the fox. It smiles and leads you to a hidden clearing where a picnic is set up just for you. What a magical day!\n\nThe End.",
choices: []
},
deepWoods: {
id: 'deepWoods',
text: "You walk deeper into the woods and soon realize you're going in circles. Eventually, you find your way back to where you started.\n\nDo you want to try again?",
choices: [{
text: "Yes, restart the adventure",
next: "start"
}]
}
};
var centerX = 2048 / 2;
var centerY = 2732 / 2;
// Current scene container
var currentScene = null;
// Title text (shown at the top)
var titleText = new Text2("Story Crafter", {
size: 120,
fill: 0xFFFFFF,
align: 'center'
});
titleText.anchor.set(0.5, 0);
titleText.x = centerX;
titleText.y = 120;
LK.gui.top.addChild(titleText);
// Settings button (top-right, avoids top-left menu)
var settingsBtn = LK.getAsset('choiceBtn', {
anchorX: 1,
anchorY: 0,
width: 300,
height: 120,
x: 2048 - 40,
y: 40
});
var settingsTxt = new Text2("Settings", {
size: 48,
fill: 0x4A90E2,
align: 'center'
});
settingsTxt.anchor.set(0.5, 0.5);
settingsTxt.x = settingsBtn.width / 2 - 150;
settingsTxt.y = settingsBtn.height / 2 - 60;
settingsBtn.addChild(settingsTxt);
LK.gui.topRight.addChild(settingsBtn);
// Visual feedback and placeholder for settings action
settingsBtn.down = function (x, y, obj) {
tween(settingsBtn, {
tint: 0xBEE3F8
}, {
duration: 80,
onFinish: function onFinish() {
tween(settingsBtn, {
tint: 0xffffff
}, {
duration: 120
});
}
});
// Placeholder: Show a simple settings message (could be replaced with real settings)
if (!settingsBtn._settingsMsg) {
var msg = new Text2("Settings coming soon!", {
size: 70,
fill: 0xffffff,
align: 'center'
});
msg.anchor.set(0.5, 0.5);
msg.x = centerX;
msg.y = centerY;
game.addChild(msg);
settingsBtn._settingsMsg = msg;
LK.setTimeout(function () {
if (msg && msg.parent) msg.destroy();
settingsBtn._settingsMsg = null;
}, 1200);
}
};
// Helper: Show a scene by id
function showScene(sceneId) {
var sceneData = storyData[sceneId];
if (!sceneData) return;
// Remove previous scene with fade out
if (currentScene) {
currentScene.animateOut(function () {
currentScene.destroy();
createAndShowScene(sceneData);
});
} else {
createAndShowScene(sceneData);
}
}
// Helper: Create and show a scene
function createAndShowScene(sceneData) {
var scene = new StoryScene();
scene.x = centerX;
scene.y = centerY;
scene.setScene(sceneData);
// Handle choice selection
scene.onChoice = function (choiceIdx) {
var choices = sceneData.choices || [];
if (choices[choiceIdx] && choices[choiceIdx].next) {
showScene(choices[choiceIdx].next);
}
};
game.addChild(scene);
scene.animateIn();
currentScene = scene;
}
// Intro animation: Show 'firegames', then 'story crafter', then 'start the story', then start the story
var introText = new Text2("firegames", {
size: 180,
fill: 0xff6600,
align: 'center'
});
introText.anchor.set(0.5, 0.5);
introText.x = centerX;
introText.y = centerY;
introText.alpha = 0;
game.addChild(introText);
// Animate in 'firegames'
tween(introText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold for 900ms, then fade out
LK.setTimeout(function () {
tween(introText, {
alpha: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
// After 'firegames', show 'story crafter'
if (introText && introText.parent) introText.destroy();
var storyCrafterText = new Text2("Story Crafter", {
size: 180,
fill: 0xffffff,
align: 'center'
});
storyCrafterText.anchor.set(0.5, 0.5);
storyCrafterText.x = centerX;
storyCrafterText.y = centerY;
storyCrafterText.alpha = 0;
game.addChild(storyCrafterText);
tween(storyCrafterText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold for 900ms, then fade out
LK.setTimeout(function () {
tween(storyCrafterText, {
alpha: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
if (storyCrafterText && storyCrafterText.parent) storyCrafterText.destroy();
// Show 'start the story'
var startStoryText = new Text2("Start the story", {
size: 120,
fill: 0x4A90E2,
align: 'center'
});
startStoryText.anchor.set(0.5, 0.5);
startStoryText.x = centerX;
startStoryText.y = centerY;
startStoryText.alpha = 0;
game.addChild(startStoryText);
tween(startStoryText, {
alpha: 1,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold for 900ms, then fade out and start the story
LK.setTimeout(function () {
tween(startStoryText, {
alpha: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
if (startStoryText && startStoryText.parent) startStoryText.destroy();
showScene('start');
}
});
}, 900);
}
});
}
});
}, 900);
}
});
}
});
}, 900);
}
});
// No update loop needed for this game, but placeholder for future features
game.update = function () {
// No per-frame logic needed for MVP
};