User prompt
Migrate to the latest version of LK
Code edit (4 edits merged)
Please save this source code
User prompt
Create a new Debris class extending ConfigContainer. It should have an attached asset, with the following details: id derived from config.image, an x position derived from config.offset, a random rotation, and a random scale between 0.8 and 1.2
Code edit (1 edits merged)
Please save this source code
Code edit (6 edits merged)
Please save this source code
User prompt
In the ship class, tint the asset FFE4B2
Code edit (9 edits merged)
Please save this source code
User prompt
Apply a slight orange tint to the FarmNode's farmGraphics
Code edit (1 edits merged)
Please save this source code
User prompt
the autoweeder should slowly move around the planet at a speed of AUTOWEEDER_SPEED, maintaining a distance of the planets radius
Code edit (1 edits merged)
Please save this source code
User prompt
In the Planet class update, call the autoweeder update if it exists
User prompt
Add a new global AUTOWEEDER_ROTATION = 0.01 (under the existing Autoweeder settings). In the Autowheel class's update function, increase the rotation of the autoweederWheel asset by the AUTOWEEDER_ROTATION
User prompt
When creating an autoweeder, add it to the planet background instead of the planet itself
User prompt
The Autoweeder class needs an asset with id autoweederWheel
User prompt
The Autoweeder class needs an asset with id autoweederWheel, which slowly rotates at a speed of AUTOWEEDER_ROTATION = 0.01 (new global variable under "Autoweeder settings")
Code edit (2 edits merged)
Please save this source code
User prompt
In the NavigationButton's update function, in addition to updating the buttonGraphics. Rotate the planetGraphics by the amount specified in the relevant PLANET_DETAILS
Code edit (1 edits merged)
Please save this source code
User prompt
In the NavigationButton's update function, check if the buttonGraphics rotation is not equal to the targetRotation and rotate (the buttonGraphics) towards the targetRotation at a speed of NAVIGATION_ROTATE_SPEED
Code edit (1 edits merged)
Please save this source code
User prompt
Add a local targetRotation var = 0 to the NavigationButton class
Code edit (1 edits merged)
Please save this source code
User prompt
Add NAVIGATION_ROTATE_SPEED = 0.01 and NAVIGATION_ROTATE_TARGET = MATH_HALF_PI as global variables under the existing "Interface settings" heading
User prompt
Add NAVIGATION_ROTATE_SPEED = 0.01 and NAVIGATION_ROTATE_TARGET = MATH_HALF_PI as global variables under the existing Interface settings heading
/****
* Classes
****/
var Background = Container.expand(function () {
var self = Container.call(this);
var backgroundGraphics = self.attachAsset('background', {
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
backgroundGraphics.width = GAME_WIDTH * 1.05;
backgroundGraphics.height = GAME_WIDTH * 1.05;
;
self.refresh = refresh;
;
function refresh() {
backgroundGraphics.rotation += Math.PI / 2;
}
});
var ConfigContainer = Container.expand(function (config) {
var self = Container.call(this);
config = config || {};
;
self.x = config.x || 0;
self.y = config.y || 0;
self.rotation = config.rotation || 0;
;
return self;
});
var TraderDialogue = ConfigContainer.expand(function (callback, trade, config) {
var self = ConfigContainer.call(this, config);
config = config || {};
var flipX = config.flipX;
var dialogueBackground = self.attachAsset('traderDialogue', {
anchorX: TRADER_FRAME_OFFSET_X,
anchorY: 1,
scaleX: flipX
});
var centerFrameX = dialogueBackground.width * (0.5 - TRADER_FRAME_OFFSET_X) * flipX;
var centerFrameY = dialogueBackground.height * TRADER_FRAME_OFFSET_Y;
var tradeSymbols = self.addChild(new Container());
var tradeArrow = tradeSymbols.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.4,
scaleY: 1.4,
rotation: -MATH_HALF_PI
});
var tradeSell = createTradePart(trade.sellName, trade.sellAmount, {
x: -tradeArrow.height * 0.7 - TRADER_DETAIL_MARGIN,
anchorX: 1
});
var tradeBuy = createTradePart(trade.buyName, trade.buyAmount, {
x: tradeArrow.height * 0.7 + TRADER_DETAIL_MARGIN,
anchorX: 0
});
self.addChild(new TraderButtons(callback, {
x: -(dialogueBackground.width * TRADER_FRAME_OFFSET_X + TRADER_DETAIL_MARGIN) * flipX,
y: centerFrameY,
anchorX: 0.5 + 0.5 * flipX
}));
tradeSymbols.x = centerFrameX;
tradeSymbols.y = centerFrameY;
;
self.setTint = setTint;
;
function setTint(tint) {
LK.effects.flashObject(dialogueBackground, tint, 500);
}
function createTradePart(name, amount, config) {
if (name === 'credits') {
return tradeSymbols.addChild(new SymbolText(amount, 'currencySymbol', {
x: config.x,
anchorX: config.anchorX,
anchorY: 0.5,
tint: true
}));
} else {
return tradeSymbols.addChild(new SymbolText(amount + '×', name, {
x: config.x,
anchorX: config.anchorX,
anchorY: 0.5,
suffix: true,
scale: SCALE_FRUIT_TRADE
}));
}
}
;
return self;
});
var TraderButtons = ConfigContainer.expand(function (callback, config) {
var self = ConfigContainer.call(this, config);
var checkButton = self.addChild(new BasicButton('check', function () {
callback(true);
}, {
y: -TRADER_DETAIL_MARGIN / 2,
anchorX: config.anchorX,
anchorY: 1
}));
var crossButton = self.addChild(new BasicButton('cross', function () {
callback(false);
}, {
y: TRADER_DETAIL_MARGIN / 2,
anchorX: config.anchorX,
anchorY: 0
}));
;
return self;
});
var Trader = ConfigContainer.expand(function (config, setTrade) {
var self = ConfigContainer.call(this, config);
var index = config.index;
var angle = Math.random() * MATH_2_PI;
var distance = Math.random() * TRADER_SPAWN_RADIUS;
var direction = 1;
var spawnTick = LK.ticks;
var counter = TRADER_SPAWN_TIME;
var traderGraphics = self.attachAsset('trader', {
anchorX: 0.5,
anchorY: 0.5,
x: Math.cos(angle) * distance,
y: Math.sin(angle) * distance,
scaleX: 0,
scaleY: 0
});
var moveX = -traderGraphics.x / TRADER_SPAWN_TIME;
var moveY = -traderGraphics.y / TRADER_SPAWN_TIME;
var trade = setTrade || generateTrade();
var traderDialogue;
;
self.update = update;
self.trade = trade;
self.handleTradeCallback = handleTradeCallback;
;
function update() {
if ((LK.ticks - spawnTick) % TRADER_MOVE_INTERVAL === 0) {
var anchorX = 0.5 + TRADER_MOVE_ANCHOR * (Math.random() * 2 - 1);
var anchorY = 0.5 + TRADER_MOVE_ANCHOR * (Math.random() * 2 - 1);
var rotation = TRADER_MOVE_ROTATION * (Math.random() * 2 - 1);
traderGraphics.anchor.set(anchorX, anchorY);
traderGraphics.rotation = rotation;
}
if (direction !== 0) {
counter -= direction;
traderGraphics.x += moveX * direction;
traderGraphics.y += moveY * direction;
traderGraphics.scale.set(1 - counter / TRADER_SPAWN_TIME);
if (counter <= 0) {
direction = 0;
traderDialogue = foreground.addChild(new TraderDialogue(handleTradeCallback, trade, {
x: self.x,
y: self.y - traderGraphics.height / 2 - TRADER_SHIP_SPACING,
flipX: self.x < GAME_WIDTH / 2 ? -1 : 1
}));
} else if (counter >= TRADER_SPAWN_TIME) {
traders[index] = undefined;
self.destroy();
}
}
}
function checkAutoweeder() {
if (planet.autoweeder) {
return false;
}
for (var i = 0; i < 4; i++) {
var trader = traders[i];
if (trader && trader.trade.buyName === 'autoweeder') {
return false;
}
}
return true;
}
function handleTradeCallback(accepted) {
var closeDialogue = false;
skipRetarget = true;
if (accepted) {
if (checkValue(trade.sellName, trade.sellAmount)) {
makeAdjustment(trade.sellName, -trade.sellAmount, false);
makeAdjustment(trade.buyName, trade.buyAmount, true);
stats.salesDone++;
closeDialogue = true;
} else {
traderDialogue.setTint(0xFF0000);
}
} else {
stats.salesRejected++;
closeDialogue = true;
}
if (closeDialogue) {
direction = -1;
if (traderDialogue) {
traderDialogue.destroy();
}
}
}
function generateTrade() {
// Try to offer auto-weeder
if (Math.random() < TRADER_AUTOWEEDER_CHANCE && checkAutoweeder()) {
return TRADER_AUTOWEEDER_TRADE;
} else {
// Determine what the trade items are available
var details = PLANET_DETAILS[planet.name];
var buyList = details.buy;
var buyName = buyList[randomInt(0, buyList.length)];
var sellList = details.sell.filter(function (item) {
return item !== buyName;
});
var sellName = sellList[randomInt(0, sellList.length)];
// Determine trade value and quantities
var buyAmount, sellAmount;
var valueFactor = 1 + TRADER_COST_VARIANCE * (Math.random() * 2 - 1);
if (sellName === 'credits') {
// Player buying Fruit with Credits
var value = valueFactor * FRUIT_DETAILS[buyName].value;
buyAmount = randomInt(TRADER_FRUIT_OFFER_MIN, TRADER_FRUIT_OFFER_MAX);
sellAmount = Math.round(buyAmount * value);
} else if (buyName === 'credits') {
// Player selling Fruit for Credits
var fruitDetails = FRUIT_DETAILS[sellName];
var minSell = fruitDetails.minSell || TRADER_MINIMUM_BUY;
var value = valueFactor * FRUIT_DETAILS[sellName].value;
var quantity = Math.max(1, inventory.getQuantity(sellName));
sellAmount = Math.max(minSell, randomInt(quantity * TRADER_INVENTORY_MIN, quantity * TRADER_INVENTORY_MAX));
buyAmount = Math.round(sellAmount * value);
} else {
// Fruit exchange
var multiplier = 1 + Math.random() * (TRADER_EXCHANGE_MULTIPLIER - 1);
var sellValue = valueFactor * FRUIT_DETAILS[buyName].value;
var buyValue = FRUIT_DETAILS[sellName].value;
var lowValue = Math.min(sellValue, buyValue);
sellAmount = Math.ceil(multiplier * sellValue / lowValue);
buyAmount = Math.ceil(multiplier * buyValue / lowValue);
}
return {
sellName: sellName,
sellAmount: sellAmount,
buyName: buyName,
buyAmount: buyAmount
};
}
}
function checkValue(name, amount) {
if (name === 'credits') {
return money >= amount;
} else {
return inventory.getQuantity(name) >= amount;
}
}
function makeAdjustment(name, amount, collectStats) {
if (name === 'credits') {
moneyDisplay.setText(money += amount);
if (collectStats) {
stats.creditsEarned += amount;
}
} else {
inventory.adjustItem(name, amount);
if (collectStats) {
stats.itemsCollected += amount;
}
}
}
;
return self;
});
/**
* var config = {
* x : Number || 0, // See: ConfigContainer
* y : Number || 0, // See: ConfigContainer
* rotation : Number || 0, // See: ConfigContainer
* prefix : Boolean || true,
* suffix : Boolean || false, // Overriden by prefix
* tint : Boolean || false, // Whether to tint the symbol
* anchorX : Number || 0,
* anchorY : Number || 1,
* margin : Number || TEXT_DEFAULT_MARGIN,
* size : Number || TEXT_DEFAULT_SIZE,
* scale : Number || undefined, // See: BorderedSymbol
* fill : String || undefined, // See: BorderedText, BorderedSymbol (tint)
* border : Number || undefined, // See: BorderedText, BorderedSymbol
* }
**/
var SymbolText = ConfigContainer.expand(function (text, symbol, config) {
var self = ConfigContainer.call(this, config);
config = config || {};
var prefix = !!config.prefix || !config.suffix;
var tint = !!config.tint;
var anchorX = config.anchorX !== undefined ? config.anchorX : 0;
var anchorY = config.anchorY !== undefined ? config.anchorY : 1;
var margin = config.margin !== undefined ? config.margin : TEXT_DEFAULT_MARGIN;
var size = config.size !== undefined ? config.size : TEXT_DEFAULT_SIZE;
var symbolHeight = config.scale === undefined ? size : undefined;
var textAsset = self.addChild(new BorderedText(text, {
anchorX: 0,
anchorY: 0.5,
font: config.font,
// Passthrough
fill: config.fill,
// Passthrough
border: config.border,
// Passthrough
size: size
}));
var symbolAsset = self.addChild(new BorderedSymbol(symbol, {
anchorX: 0,
anchorY: 0.5,
height: symbolHeight,
tint: tint ? config.fill : undefined,
border: config.border,
// Passthrough
scale: config.scale
}));
;
self.setText = setText;
self.setFill = setFill;
self.setSymbol = setSymbol;
;
function alignAssets() {
var first = prefix ? symbolAsset : textAsset;
var second = prefix ? textAsset : symbolAsset;
var totalWidth = symbolAsset.width + margin + textAsset.width;
var heightOffset = Math.max(first.height, second.height) * (0.5 - anchorY);
first.x = -totalWidth * anchorX;
first.y = heightOffset;
second.x = first.x + first.width + margin;
second.y = heightOffset;
}
function setText(newText) {
textAsset.setText(newText);
alignAssets();
}
function setFill(newFill) {
textAsset.setFill(newFill);
if (tint) {
symbolAsset.setTint(newFill);
}
}
function setSymbol(newSymbol) {
symbolAsset.setSymbol(newSymbol);
alignAssets();
}
;
alignAssets();
});
var Ship = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var background = self.addChild(new Container());
var shipGraphics = self.attachAsset('ship', {
anchorX: 0.10,
anchorY: 0.5
});
var counter = 0;
var particles = [];
background.rotation = -MATH_HALF_PI;
;
self.update = update;
self.launch = launch;
self.direction = -1;
;
function update() {
if (self.direction < 0) {
self.x -= ROCKET_SPEED_REVERSE;
if (self.x <= planet.radius) {
self.x = planet.radius;
self.direction = 0;
LK.setTimeout(function () {
player = planet.addChild(new Player({
rotation: ship.rotation,
x: ship.x,
y: ship.y
}));
}, PLAYER_SPAWN_DELAY);
}
} else if (self.direction > 0) {
counter++;
var speed = Math.pow(ROCKET_SPEED_BASE, counter / ROCKET_SPEED_DIV - ROCKET_SPEED_DELAY);
self.x += speed;
if (self.x > ROCKET_DIST_LEAVE) {
counter = 0;
self.direction = -1;
transitionPlanets();
}
}
if (self.direction) {
particles.push(background.addChild(new FlameParticle({
x: (Math.random() - 0.5) * ROCKET_FLAME_BREDTH
})));
}
updateList(particles);
}
function launch(direction) {
if (!self.direction) {
self.direction = direction;
}
}
});
var PopupEffect = ConfigContainer.expand(function (fruitName, config) {
var self = ConfigContainer.call(this, config);
var count = 1;
var lifetime = POPUP_DURATION;
var symbolText = self.addChild(new SymbolText(count + '×', fruitName, {
anchorX: 0.5,
anchorY: 1,
suffix: true,
scale: SCALE_FRUIT_TRADE
}));
;
self.update = update;
self.increment = increment;
;
function update() {
if (--lifetime > 0) {
symbolText.y -= 1;
symbolText.alpha = lifetime / POPUP_DURATION;
if (lifetime === POPUP_RECYCLE) {
popupMap[fruitName] = undefined;
}
} else {
self.destroy();
return true;
}
}
;
function increment() {
count++;
symbolText.setText(count + '×');
}
;
effectsList.push(planet.addChild(self));
popupMap[fruitName] = self;
return self;
});
var Player = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var actionCountdown = PLAYER_ACTION_INTERVAL;
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.75
});
;
self.update = update;
self.angle = 0;
;
function update() {
var currentAngle = Math.atan2(self.y, self.x);
var angleDifference = mod(targetAngle - currentAngle + Math.PI, MATH_2_PI) - Math.PI;
if (approxZero(angleDifference)) {
if (taskLaunch) {
self.destroy();
player = undefined;
LK.setTimeout(function () {
ship.launch(1);
}, PLAYER_SPAWN_DELAY);
} else if (--actionCountdown <= 0) {
performAction(currentAngle);
actionCountdown = PLAYER_ACTION_INTERVAL;
}
} else {
var direction = angleDifference / Math.abs(angleDifference);
var angleStep = PLAYER_SPEED / planet.radius;
var angleIncrease = Math.min(Math.abs(angleDifference), angleStep);
var newAngle = currentAngle + direction * angleIncrease;
self.x = Math.cos(newAngle) * planet.radius;
self.y = Math.sin(newAngle) * planet.radius;
self.angle = newAngle;
stats.distanceRun += angleIncrease * planet.radius;
playerGraphics.scale.x = direction < 0 ? -1 : 1;
self.rotation = newAngle + MATH_HALF_PI;
actionCountdown = 0; // Perform action immediately after stopping
}
}
function performAction(angle) {
var count = planet.nodes.length;
var baseIndex = mod(Math.round(angle / MATH_2_PI * count), count);
var baseNode = planet.nodes[baseIndex];
if (baseNode && !baseNode.tryAction()) {
var iterations = Math.floor(PLAYER_ACTION_DIST / (planet.radius * MATH_2_PI / planet.nodes.length));
var prev = baseNode.prev;
var next = baseNode.next;
while (iterations > 0) {
iterations--;
if (next && next.tryAction()) {
break;
}
if (prev && prev.tryAction()) {
break;
}
prev = prev ? prev.prev : null;
next = next ? next.next : null;
}
}
}
});
var PlantNode = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
;
self.update = update;
self.addPlant = addPlant;
self.removePlant = removePlant;
self.tryAction = tryAction;
self.plant;
self.farm;
self.prev;
self.next;
;
function update() {
if (self.plant) {
self.plant.update();
}
}
function addPlant(plantName, stage, overrun, potted) {
if (!self.plant || self.plant.uproot(overrun)) {
if (self.plant) {
self.removePlant();
}
var plantBlueprint = PLANT_DETAILS[plantName].blueprint;
self.plant = self.addChild(new plantBlueprint(stage, potted, self.parent, {
y: potted ? PLOT_FARM_OFFSET : PLOT_NODE_OFFSET
}));
}
}
function removePlant() {
self.plant.destroy();
self.plant = undefined;
}
function tryAction() {
return !!self.plant && self.plant.actionable && self.plant.action();
}
;
return self;
});
var WeedsNode = PlantNode.expand(function (config) {
var self = PlantNode.call(this, config);
var baseUpdate = self.update;
var baseAddPlant = self.addPlant;
var baseTryAction = self.tryAction;
;
self.update = update;
self.addPlant = addPlant;
self.tryAction = tryAction;
;
function update() {
baseUpdate();
if (!self.plant && --spawnCountdown <= 0) {
self.addPlant('plantWeeds', 1, false, false);
}
}
function addPlant(plantName, stage, overrun, potted) {
baseAddPlant(plantName, stage, overrun, potted);
refreshCountdown();
}
function tryAction() {
if (baseTryAction()) {
refreshCountdown();
return true;
}
return false;
}
function refreshCountdown() {
var baseTime = WEEDS_SPAWN_TIME + Math.random() * WEEDS_SPAWN_VARIANCE;
spawnCountdown = Math.floor(PLANT_GROWTH_FACTOR * baseTime);
}
;
refreshCountdown();
});
var FarmNode = PlantNode.expand(function (config) {
var self = PlantNode.call(this, config);
var baseUpdate = self.update;
var baseAddPlant = self.addPlant;
var baseTryAction = self.tryAction;
var targetAlpha = 0;
var planterButton;
var uprootButton;
var currentAlpha = targetAlpha;
var farmGraphics = self.attachAsset('farm', {
anchorX: 0.5,
anchorY: 0.6,
alpha: currentAlpha
});
;
self.update = update;
self.addPlant = addPlant;
self.tryAction = tryAction;
self.tryPlantFruit = tryPlantFruit;
;
function update() {
baseUpdate();
// Control the in/out fade when overrun
if (currentAlpha !== targetAlpha) {
if (currentAlpha < targetAlpha) {
currentAlpha = Math.min(targetAlpha, currentAlpha + PLOT_ALPHA_STEP);
} else {
currentAlpha = Math.max(targetAlpha, currentAlpha - PLOT_ALPHA_STEP);
}
farmGraphics.alpha = currentAlpha;
}
// Show/hide the planter button
var fruitName = inventory.getSelection();
if (planterRequirements(fruitName)) {
if (!planterButton) {
planterButton = self.addChild(new PlanterButton(fruitName, planterButtonCallback, {
scale: SCALE_FRUIT_PLANTER
}));
} else {
planterButton.updateImage(fruitName);
}
} else if (planterButton) {
planterButton.destroy();
planterButton = undefined;
}
// Show/hide the uproot button
if (uprootRequirements()) {
if (!uprootButton) {
uprootButton = self.addChild(new UprootButton(fruitName, uprootButtonCallback, {}));
}
} else if (uprootButton) {
uprootButton.destroy();
uprootButton = undefined;
}
}
function tryAction() {
if (baseTryAction()) {
targetAlpha = 1;
return true;
} else if (taskPlanter) {
tryPlantFruit(taskPlanter);
taskPlanter = false;
} else if (taskUproot) {
tryUproot();
taskPlanter = true;
}
}
function tryUproot() {
if (uprootRequirements()) {
if (self.plant.uproot(false)) {
self.removePlant();
}
}
}
function tryPlantFruit(fruitName) {
if (planterRequirements(fruitName)) {
var plantName = FRUIT_DETAILS[fruitName].plant;
self.addPlant(plantName, 1, false, true);
inventory.adjustItem(fruitName, -1);
stats.cropsPlanted++;
}
}
function addPlant(plantName, stage, overrun, potted) {
baseAddPlant(plantName, stage, overrun, potted);
targetAlpha = potted ? 1 : 0;
}
function planterRequirements(fruitName) {
return !self.plant && currentAlpha === 1 && inventory.allowed && inventory.getQuantity(fruitName) > 0;
}
function planterButtonCallback() {
resetTasks();
taskPlanter = inventory.getSelection();
targetAngle = Math.atan2(self.y, self.x);
skipRetarget = true;
}
function uprootRequirements() {
return !!self.plant && self.plant.uprootable && self.plant.potted;
}
function uprootButtonCallback() {
resetTasks();
taskUproot = true;
targetAngle = Math.atan2(self.y, self.x);
skipRetarget = true;
}
});
var Plant = ConfigContainer.expand(function (type, growthStage, potted, parentPlanet, config) {
var self = ConfigContainer.call(this, config);
var details = PLANT_DETAILS[type];
var scale = 1 + randomFloatInRange(-PLANT_SCALE_VARIANCE, PLANT_SCALE_VARIANCE);
;
self.update = update;
self.uproot = uproot;
self.action = action;
self.harvest = harvest;
self.updateStage = updateStage;
self.refreshGraphics = refreshGraphics;
self.refreshCountdown = refreshCountdown;
self.type = type;
self.fruit = details.fruit;
self.fruitCount = 0;
self.potted = potted;
self.graphics;
self.growthStage = growthStage;
self.growthStages = details.stages;
self.growthTime = details.growthTime;
self.growthVariance = details.growthVariance;
self.growthCountdown = 0;
self.skipFinalGrowth = false;
self.fruitOffsetMin = 0;
self.fruitOffsetMax = 0;
self.fruitRange = 0;
self.harvest;
self.strong = false; // Whether the plant can prevent weeds overrunning it
self.actionable = false; // Whether passive actions can be performed on this plant
self.uprootable = false; // Whether the plant can be manually uprooted (with the uproot button)
;
function update() {
if (--self.growthCountdown <= 0 && self.growthStage < self.growthStages) {
self.updateStage(self.growthStage + 1);
}
}
function uproot(overrun) {
if (overrun) {
if (!self.strong) {
stats.cropsOverrun++;
self.harvest();
return true;
}
} else if (self.uprootable) {
self.harvest();
return true;
}
return false;
}
function action() {
return self.actionable;
}
function harvest(quantity) {
var max = Math.min(self.fruitCount, quantity || Infinity);
for (var i = 0; i < max; i++) {
self.fruitCount--;
var fruitOffset = randomFloatInRange(self.fruitOffsetMin, self.fruitOffsetMax);
var rotation = self.parent.rotation;
var fruitX = self.parent.x + Math.cos(rotation + MATH_HALF_PI) * (self.y - fruitOffset);
var fruitY = self.parent.y + Math.sin(rotation + MATH_HALF_PI) * (self.y - fruitOffset);
if (self.fruitRange > 0) {
var rangeRotation = Math.random() * MATH_2_PI;
var rangeDistance = Math.random() * self.fruitRange;
fruitX += Math.cos(rangeRotation) * rangeDistance;
fruitY += Math.sin(rangeRotation) * rangeDistance;
}
new Fruit(self.fruit, parentPlanet, {
x: fruitX,
y: fruitY,
rotation: rotation
});
}
}
function updateStage(newStage) {
if (newStage !== self.growthStages || !self.skipFinalGrowth) {
self.growthStage = newStage;
self.refreshGraphics();
}
if (self.growthStage !== self.growthStages) {
self.refreshCountdown();
}
}
function refreshGraphics() {
if (self.graphics) {
self.graphics.destroy();
}
self.graphics = self.createAsset(self.type + self.growthStage, {
anchorX: 0.5,
anchorY: 1,
scaleX: scale * Math.random() < 0.5 ? 1 : -1,
scaleY: scale
});
}
function refreshCountdown() {
var baseTime = self.growthTime + Math.random() * self.growthVariance;
self.growthCountdown = Math.floor(PLANT_GROWTH_FACTOR * baseTime);
}
;
return self;
});
var PlantWeeds = Plant.expand(function (growthStage, potted, parentPlanet, config) {
var self = Plant.call(this, 'plantWeeds', growthStage, potted, parentPlanet, config);
var baseUpdate = self.update;
var baseHarvest = self.harvest;
var baseUpdateStage = self.updateStage;
var spreadCountdown = 0;
;
self.update = update;
self.action = action;
self.harvest = harvest;
self.updateStage = updateStage;
self.strong = true;
self.actionable = !potted;
self.uprootable = potted;
;
function update() {
baseUpdate();
;
if (self.growthStage === self.growthStages && --spreadCountdown <= 0) {
var node = self.parent;
var nextPlant = node.next.plant;
var prevPlant = node.prev.plant;
var nextSpreadable = !nextPlant || !nextPlant.strong;
var prevSpreadable = !prevPlant || !prevPlant.strong;
if (nextSpreadable || prevSpreadable) {
var targetNode;
if (nextSpreadable && prevSpreadable) {
targetNode = Math.random() < 0.5 ? node.next : node.prev;
} else {
targetNode = nextSpreadable ? node.next : node.prev;
}
targetNode.addPlant(self.type, 1, true);
}
refreshCountdown();
}
}
function action() {
if (self.actionable) {
self.harvest();
self.parent.removePlant();
return true;
}
return false;
}
function harvest(quantity) {
baseHarvest(quantity);
;
stats.weedsPulled++;
}
function updateStage(newStage) {
baseUpdateStage(newStage);
;
if (newStage === self.growthStages) {
self.fruitCount = 2;
self.fruitOffsetMin = 85;
self.fruitOffsetMax = 95;
} else if (newStage === self.growthStages - 1) {
self.fruitCount = 1;
self.fruitOffsetMin = 35;
self.fruitOffsetMax = 45;
}
}
function refreshCountdown() {
var baseTime = WEEDS_SPREAD_TIME + Math.random() * WEEDS_SPREAD_VARIANCE;
spreadCountdown = Math.floor(PLANT_GROWTH_FACTOR * baseTime);
}
;
self.updateStage(growthStage);
});
var PlantStalk = Plant.expand(function (growthStage, potted, parentPlanet, config) {
var self = Plant.call(this, 'plantStalk', growthStage, potted, parentPlanet, config);
var baseUpdateStage = self.updateStage;
var yieldStage = 5;
var yieldPerStage = 3;
;
self.updateStage = updateStage;
self.skipFinalGrowth = true;
self.fruitOffsetMin = 200;
self.fruitOffsetMax = 300;
self.fruitRange = 30;
;
function updateStage(newStage) {
baseUpdateStage(newStage);
;
if (newStage >= yieldStage) {
self.fruitCount += yieldPerStage;
}
if (newStage === self.growthStages) {
self.harvest();
self.parent.removePlant();
}
}
;
self.updateStage(growthStage);
return self;
});
var PlantFlower = Plant.expand(function (growthStage, potted, parentPlanet, config) {
var self = Plant.call(this, 'plantFlower', growthStage, potted, parentPlanet, config);
var baseUpdateStage = self.updateStage;
var yieldStage = 4;
var fruitYFactor = 0.75;
var fruitCount = 4;
;
self.action = action;
self.updateStage = updateStage;
self.fruitRange = 50;
;
function action() {
if (self.actionable) {
self.harvest();
self.parent.removePlant();
return true;
}
return false;
}
function updateStage(newStage) {
baseUpdateStage(newStage);
;
if (newStage === self.growthStages) {
// Dead plant/flower
self.fruitCount = Math.round(Math.random());
self.graphics.tint = PLANT_DEAD_COLOUR;
self.actionable = false;
self.uprootable = true;
} else if (newStage >= yieldStage) {
// Harvestable plant/flower
self.fruitCount = fruitCount + Math.round(Math.random());
self.actionable = true;
self.fruitOffsetMin = self.graphics.height * fruitYFactor;
self.fruitOffsetMax = self.graphics.height * fruitYFactor;
}
}
;
self.updateStage(growthStage);
return self;
});
var PlantEyeball = Plant.expand(function (growthStage, potted, parentPlanet, config) {
var self = Plant.call(this, 'plantEyeball', growthStage, potted, parentPlanet, config);
var baseUpdateStage = self.updateStage;
var decorAmount = 3;
var decorScale = 0.5;
var fruitChance = 0.33;
var fruitChances = 20;
;
self.updateStage = updateStage;
self.skipFinalGrowth = true;
self.fruitOffsetMin = 80;
self.fruitOffsetMax = 140;
self.fruitRange = 20;
;
function updateStage(newStage) {
baseUpdateStage(newStage);
;
if (newStage === self.growthStages) {
if (fruitChances-- > 0) {
if (Math.random() < fruitChance) {
self.harvest(self.fruitCount = 1);
}
} else {
self.harvest(self.fruitCount = decorAmount);
self.parent.removePlant();
}
} else if (newStage === self.growthStages - 1) {
createVisuals();
}
}
function createVisuals() {
for (var i = 0; i < decorAmount; i++) {
var decorOffset = randomFloatInRange(self.fruitOffsetMin, self.fruitOffsetMax);
var rangeRotation = Math.random() * MATH_2_PI;
var rangeDistance = Math.random() * self.fruitRange;
var decorX = self.x + Math.cos(rangeRotation) * rangeDistance;
var decorY = self.y - decorOffset + Math.sin(rangeRotation) * rangeDistance;
self.attachAsset(self.fruit, {
x: decorX,
y: decorY,
scaleX: decorScale,
scaleY: decorScale
});
}
}
;
self.updateStage(growthStage);
return self;
});
var PlantDiamond = Plant.expand(function (growthStage, potted, parentPlanet, config) {
var self = Plant.call(this, 'plantDiamond', growthStage, potted, parentPlanet, config);
var baseUpdateStage = self.updateStage;
;
self.action = action;
self.updateStage = updateStage;
self.strong = true;
self.fruitOffsetMin = 123;
self.fruitOffsetMax = 123;
;
function action() {
if (self.actionable) {
self.harvest();
self.parent.removePlant();
return true;
}
return false;
}
function updateStage(newStage) {
baseUpdateStage(newStage);
;
if (newStage === self.growthStages) {
self.fruitCount = 1;
self.actionable = true;
}
}
;
self.updateStage(growthStage);
return self;
});
var PlantBush = Plant.expand(function (growthStage, potted, parentPlanet, config) {
var self = Plant.call(this, 'plantBush', growthStage, potted, parentPlanet, config);
var baseUpdateStage = self.updateStage;
var baseHarvest = self.harvest;
var maxFruitCount = 3;
var fruitRemaining = 6;
var fruitScale = 0.8;
var fruitYFactor = 0.5;
var fruitAssets = [];
;
self.updateStage = updateStage;
self.harvest = harvest;
self.action = action;
self.skipFinalGrowth = true;
self.fruitOffsetMin = 45;
self.fruitOffsetMax = 45;
self.fruitRange = 30;
;
function updateStage(newStage) {
baseUpdateStage(newStage);
;
if (newStage === self.growthStages) {
self.strong = true;
if (!self.skipFinalGrowth) {
self.graphics.tint = PLANT_DEAD_COLOUR;
self.uprootable = true;
self.actionable = false;
self.harvest();
}
if (fruitRemaining-- <= 0) {
self.skipFinalGrowth = false;
} else if (self.fruitCount < maxFruitCount) {
self.fruitCount++;
self.actionable = true;
updateVisibleFruit();
}
}
}
function harvest(quantity) {
baseHarvest(quantity);
;
self.actionable = self.fruitCount > 0;
updateVisibleFruit();
}
function action() {
if (self.actionable && self.fruitCount > 0) {
self.harvest(1);
return true;
}
return false;
}
function updateVisibleFruit() {
var assetSlots = [];
var emptySlots = [];
for (var i = 0; i < maxFruitCount; i++) {
(fruitAssets[i] ? assetSlots : emptySlots).push(i);
}
var change = self.fruitCount - assetSlots.length;
while (change !== 0) {
if (change < 0) {
// Remove asset
var index = randomIntInRange(0, assetSlots.length);
var slot = assetSlots[index];
fruitAssets[slot].destroy();
fruitAssets[slot] = undefined;
assetSlots.splice(index, 1);
change++;
} else if (change > 0) {
// Make new asset
var index = randomIntInRange(0, emptySlots.length);
var slot = emptySlots[index];
var offset = slot - (maxFruitCount / 2 - 0.5);
var fruitX = offset * self.fruitRange;
var fruitY = -randomFloatInRange(self.fruitOffsetMin, self.fruitOffsetMax) - Math.cos(offset * MATH_HALF_PI) * self.fruitRange * fruitYFactor;
fruitAssets[slot] = self.attachAsset(self.fruit, {
x: fruitX,
y: fruitY,
anchorX: 0.5,
anchorY: 0.5,
scaleX: fruitScale,
scaleY: fruitScale,
rotation: -MATH_QUARTER_PI
});
emptySlots.splice(index, 1);
change--;
}
}
}
;
self.updateStage(growthStage);
return self;
});
var Planet = ConfigContainer.expand(function (planetName, config) {
var self = ConfigContainer.call(this, config);
var details = PLANET_DETAILS[planetName];
var perimeter = MATH_2_PI * details.radius;
var numPlots = Math.floor(perimeter / (PLOT_SIZE + PLOT_GAP));
var numNodes = numPlots * 4;
var nodes = [];
var fruitList = [];
var background = self.addChild(new Container());
var planetScale = 2 * details.radius / 1000;
var planetGraphics = self.createAsset(planetName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: planetScale,
scaleY: planetScale
});
;
self.update = update;
self.name = planetName;
self.barren = !!details.barren; // No plant life
self.coarse = !!details.coarse; // No weed nodes (still starts + grows + spreads)
self.nodes = nodes;
self.background = background;
self.fruitList = fruitList;
self.spin = details.spin;
self.radius = details.radius;
;
function update(ticks) {
self.rotation += self.spin;
updateList(nodes);
updateList(fruitList, planet !== self);
}
function createNodes() {
if (!self.barren) {
var defaultPlant = 'plantWeeds';
var maxPlantStage = PLANT_DETAILS[defaultPlant].stages;
for (var i = 0; i < numNodes; i++) {
var angle = i / numNodes * MATH_2_PI;
var nodeX = self.radius * Math.cos(angle);
var nodeY = self.radius * Math.sin(angle);
var rotation = angle + MATH_HALF_PI;
var farmNode = i !== 0 && i % 4 === 0;
var nodeType = farmNode ? FarmNode : i % 2 === 0 && !self.coarse ? WeedsNode : PlantNode;
var node = self.addChild(new nodeType({
x: nodeX,
y: nodeY,
rotation: rotation
}));
if (farmNode) {
node.addPlant(defaultPlant, maxPlantStage);
} else if (Math.random() < WEEDS_SPAWN_CHANCE) {
node.addPlant(defaultPlant, 1 + Math.floor(Math.random() * maxPlantStage));
}
if (i !== 0) {
node.prev = nodes[i - 1];
node.prev.next = node;
if (i === numNodes - 1) {
node.next = nodes[0];
node.next.prev = node;
}
}
nodes.push(node);
}
}
}
;
createNodes();
return self;
});
var PlanetRed = Planet.expand(function (config) {
var self = Planet.call(this, 'planetRed', config);
});
var PlanetOmni = Planet.expand(function (config) {
var self = Planet.call(this, 'planetOmni', config);
});
var PlanetGrey = Planet.expand(function (config) {
var self = Planet.call(this, 'planetGrey', config);
});
var PlanetGold = Planet.expand(function (config) {
var self = Planet.call(this, 'planetGold', config);
});
var PlanetBlue = Planet.expand(function (config) {
var self = Planet.call(this, 'planetBlue', config);
});
var NavigationInterface = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var buttons = {};
var baseOffset = -NAVIGATION.length / 2 + 0.5;
var firstPlanet = NAVIGATION[0];
for (var i = 0; i < NAVIGATION.length; i++) {
var planet = NAVIGATION[i];
var buttonX = (baseOffset + i) * 200;
buttons[planet] = self.addChild(new NavigationButton(planet, buttonCallback, {
x: buttonX,
y: 0
}));
}
;
self.update = update;
self.setDestination = setDestination;
;
function update() {
crosshair.update();
for (var i = 0; i < NAVIGATION.length; i++) {
buttons[NAVIGATION[i]].update();
}
}
function buttonCallback(planet) {
var button = buttons[planet];
if (button.unlocked) {
setDestination(planet);
} else if (money >= button.cost) {
moneyDisplay.setText(money -= button.cost);
button.unlock();
}
}
function setDestination(destPlanet) {
if (player) {
destinationPlanet = destPlanet;
buttons[destPlanet].addChild(crosshair);
resetTasks();
if (destinationPlanet !== currentPlanet) {
taskLaunch = true;
targetAngle = 0;
}
}
}
;
buttonCallback(firstPlanet);
buttons[firstPlanet].addChild(crosshair);
});
var NavigationButton = ConfigContainer.expand(function (planetName, callback, config) {
var self = ConfigContainer.call(this, config);
var details = PLANET_DETAILS[planetName];
var buttonGraphics = self.attachAsset('navigationButton', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFAAAA
});
var planetGraphics = self.createAsset(planetName, {
anchorX: 0.5,
anchorY: 0.5,
width: buttonGraphics.width * 0.4,
height: buttonGraphics.width * 0.4
});
var currencyText = self.addChild(new SymbolText(details.cost, 'currencySymbol', {
anchorX: .5,
anchorY: .5,
tint: true
}));
;
self.update = update;
self.unlock = unlock;
self.cost = details.cost;
self.planet = planetName;
self.unlocked = !details.cost;
self.on('down', function () {
callback(planetName);
});
;
function update() {
planetGraphics.rotation += details.spin;
}
function unlock() {
self.unlocked = true;
buttonGraphics.tint = 0xFFFFFF;
currencyText.destroy();
}
;
if (self.unlocked) {
unlock();
}
});
var InventorySlot = ConfigContainer.expand(function (index, config) {
var self = ConfigContainer.call(this, config);
var itemDisplay;
var frame = self.attachAsset('inventoryFrame', {
anchorX: .5,
anchorY: .5
});
var quantityText = self.addChild(new BorderedText('', {
size: 30,
anchorX: .5,
anchorY: .5,
y: INVENTORY_SLOT_SIZE / 2.2
}));
frame.width = INVENTORY_SLOT_SIZE;
frame.height = INVENTORY_SLOT_SIZE;
;
self.setItem = setItem;
self.adjustQuantity = adjustQuantity;
self.index = index;
self.item = null;
self.quantity = 0;
;
function adjustQuantity(amount) {
self.quantity += amount;
quantityText.setText(self.quantity);
}
function setItem(itemName) {
self.item = itemName;
if (itemDisplay) {
itemDisplay.destroy();
}
itemDisplay = self.attachAsset(itemName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: SCALE_FRUIT_INVENTORY,
scaleY: SCALE_FRUIT_INVENTORY
});
}
;
self.on('down', function () {
self.parent.selectSlot(self.index);
});
});
var Inventory = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var slots = [];
var itemIndices = {};
var selectedIndex = 0;
var widthOffset = INVENTORY_SLOT_SIZE * INVENTORY_COLS * (config.anchorX || 0);
var heightOffset = INVENTORY_SLOT_SIZE * INVENTORY_ROWS * (config.anchorY || 0);
for (var row = 0; row < INVENTORY_ROWS; row++) {
for (var col = 0; col < INVENTORY_COLS; col++) {
var index = row * INVENTORY_COLS + col;
slots.push(self.addChild(new InventorySlot(index, {
x: (col + .5) * INVENTORY_SLOT_SIZE - widthOffset,
y: (row + .5) * INVENTORY_SLOT_SIZE - heightOffset
})));
}
}
var selector = new InventorySelector();
var warningText = self.addChild(new BorderedText('', {
y: INVENTORY_SLOT_SIZE * INVENTORY_ROWS / 2 + 20,
anchorX: 0.5,
anchorY: 0,
size: TEXT_SIZE_SMALL
}));
warningText.visible = false;
;
self.adjustItem = adjustItem;
self.selectSlot = selectSlot;
self.getQuantity = getQuantity;
self.getSelection = getSelection;
self.refreshAllowed = refreshAllowed;
self.selector = selector;
self.allowed = false;
;
function selectSlot(index) {
slots[index].addChild(selector);
selectedIndex = index;
refreshAllowed();
resetTasks();
}
function adjustItem(item, quantity) {
var slotIndex = itemIndices[item];
var newItem = false;
if (slotIndex === undefined) {
for (slotIndex = 0; slotIndex < slots.length; slotIndex++) {
if (!slots[slotIndex].item) {
break;
}
}
slots[slotIndex].setItem(item);
newItem = true;
}
itemIndices[item] = slotIndex;
slots[slotIndex].adjustQuantity(quantity);
if (newItem && slotIndex === selectedIndex) {
slots[slotIndex].addChild(selector);
refreshAllowed();
}
}
function getSelection() {
return slots[selectedIndex].item;
}
function getQuantity(item) {
var slotIndex = itemIndices[item];
return slotIndex === undefined ? 0 : slots[slotIndex].quantity;
}
function refreshAllowed() {
var selection = getSelection();
var allowed = true;
if (selection) {
var itemDetails = FRUIT_DETAILS[selection];
var allowedTypes = PLANET_DETAILS[planet.name].plantTypes;
allowed = allowedTypes.includes(itemDetails.type);
if (!allowed) {
var warning = planet.barren ? GROW_WARNING_BARREN : !itemDetails.type || !itemDetails.plant ? GROW_WARNING_DIAMOND : GROW_WARNING_GENERAL;
warningText.setText(warning);
}
}
self.allowed = allowed;
selector.toggleAllowed(allowed);
warningText.visible = !allowed;
}
;
selectSlot(0);
adjustItem('fruitWeeds', 0);
});
var Fruit = ConfigContainer.expand(function (fruitName, parentPlanet, config) {
var self = ConfigContainer.call(this, config);
var falling = true;
var fruitGraphics = self.attachAsset(fruitName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: SCALE_FRUIT_DROP * (Math.random() < 0.5 ? 1 : -1),
scaleY: SCALE_FRUIT_DROP
});
;
self.fruitName = fruitName;
self.update = update;
;
function update(idle) {
// Check if fruit is within collection range
if (player && !idle) {
var dx = self.x - player.x;
var dy = self.y - player.y;
var sqrDistance = dx * dx + dy * dy;
if (sqrDistance < PLAYER_ACTION_SQRDIST) {
inventory.adjustItem(fruitName, 1);
stats.cropsHarvested++;
var popupEffect = popupMap[fruitName];
if (!popupEffect) {
var rotation = player.rotation;
var offset = parentPlanet.radius + POPUP_OFFSET;
popupEffect = new PopupEffect(fruitName, {
x: Math.cos(rotation - MATH_HALF_PI) * offset,
y: Math.sin(rotation - MATH_HALF_PI) * offset,
rotation: rotation
});
} else {
popupEffect.increment();
}
return true;
}
}
// Handle fruit falling towards the planet
if (falling) {
var angle = self.rotation + MATH_HALF_PI;
self.x += Math.cos(angle) * FRUIT_FALL_SPEED;
self.y += Math.sin(angle) * FRUIT_FALL_SPEED;
if (self.x * self.x + self.y * self.y <= parentPlanet.radius * parentPlanet.radius) {
falling = false;
}
}
}
;
parentPlanet.fruitList.push(parentPlanet.addChild(self));
return self;
});
var Explosion = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var baseScale = config.scale;
var frameCount = 0;
var explosionGraphics = self.attachAsset('explosion', {
anchorX: 0.25,
anchorY: 0.5,
scaleX: baseScale,
scaleY: baseScale,
alpha: 0.5,
rotation: Math.atan2(self.y - planet.y, self.x - planet.x)
});
;
self.update = update;
;
function update() {
frameCount++;
explosionGraphics.alpha -= 0.5 / EXPLOSION_FRAMES;
explosionGraphics.scale.set(baseScale * (1 + frameCount / EXPLOSION_FRAMES));
if (frameCount >= EXPLOSION_FRAMES) {
self.destroy();
return true;
}
}
;
effectsList.push(self);
});
/**
* var config = {
* x : Number || 0, // See: ConfigContainer
* y : Number || 0, // See: ConfigContainer
* rotation : Number || 0, // See: ConfigContainer
* anchorX : Number || 0,
* anchorY : Number || 1,
* size : Number || TEXT_DEFAULT_SIZE,
* font : String || TEXT_DEFAULT_FONT,
* fill : String || TEXT_DEFAULT_FILL,
* border : String || TEXT_DEFAULT_BORDER,
* }
**/
var BorderedText = ConfigContainer.expand(function (text, config) {
var self = ConfigContainer.call(this, config);
config = config || {};
var anchorX = config.anchorX !== undefined ? config.anchorX : 0;
var anchorY = config.anchorY !== undefined ? config.anchorY : 1;
var size = config.size !== undefined ? config.size : TEXT_DEFAULT_SIZE;
var font = config.font !== undefined ? config.font : TEXT_DEFAULT_FONT;
var textFill = config.fill !== undefined ? config.fill : TEXT_DEFAULT_FILL;
var borderFill = config.border !== undefined ? config.border : TEXT_DEFAULT_BORDER;
var mainAsset;
var textAssets = [];
;
self.setText = setText;
self.setFill = setFill;
;
function setText(newText) {
for (var i = 0; i < textAssets.length; i++) {
textAssets[i].setText(newText);
}
}
function setFill(newFill) {
textFill = newFill;
mainAsset.fill = newFill;
}
function buildTextAssets(newText) {
for (var i = 0; i < TEXT_OFFSETS.length; i++) {
var main = i === TEXT_OFFSETS.length - 1;
var textAsset = textAssets[i];
if (textAsset) {
textAsset.destroy();
}
textAsset = self.addChild(new Text2(newText, {
fill: main ? textFill : borderFill,
font: font,
size: size
}));
textAsset.anchor = {
x: anchorX,
y: anchorY
}; // NOTE: Cannot be set in config
textAsset.x = TEXT_OFFSETS[i][0] * TEXT_BORDER_WEIGHT; // NOTE: Cannot be set in config
textAsset.y = TEXT_OFFSETS[i][1] * TEXT_BORDER_WEIGHT; // NOTE: Cannot be set in config
textAssets[i] = textAsset;
}
mainAsset = textAssets[TEXT_OFFSETS.length - 1];
}
;
buildTextAssets(text);
return self;
});
/**
* var config = {
* x : Number || 0, // See: ConfigContainer
* y : Number || 0, // See: ConfigContainer
* rotation : Number || 0, // See: ConfigContainer
* anchorX : Number || .5,
* anchorY : Number || .5,
* scale : Number || undefined,
* scaleX : Number || scale,
* scaleY : Number || scale,
* width : Number || undefined, // Auto-calculated if left undefined and height is set
* height : Number || undefined, // Auto-calculated if left undefined and width is set
* tint : String || 0xFFFFFF, // Not tinted by default
* border : String || TEXT_DEFAULT_BORDER,
* }
**/
var BorderedSymbol = ConfigContainer.expand(function (symbol, config) {
var self = ConfigContainer.call(this, config);
config = config || {};
var width = config.width !== undefined ? config.width : undefined;
var height = config.height !== undefined ? config.height : undefined;
var scale = config.scale !== undefined ? config.scale : undefined;
var scaleX = config.scaleX !== undefined ? config.scaleX : scale;
var scaleY = config.scaleY !== undefined ? config.scaleX : scale;
var anchorX = config.anchorX !== undefined ? config.anchorX : .5;
var anchorY = config.anchorY !== undefined ? config.anchorY : .5;
var symbolTint = config.tint !== undefined ? config.tint : 0xFFFFFF;
var borderTint = config.border !== undefined ? config.border : TEXT_DEFAULT_BORDER;
var mainSymbol;
var symbolAssets = [];
;
self.setSymbol = buildSymbolAssets;
self.setTint = setTint;
;
function setTint(newTint) {
// NOTE: Tinting is currently broken (cannot use string)
// mainSymbol.tint = newTint;
// symbolConfig.tint = newTint;
}
function buildSymbolAssets(newSymbol) {
for (var i = 0; i < TEXT_OFFSETS.length; i++) {
var main = i === TEXT_OFFSETS.length - 1;
var symbolAsset = symbolAssets[i];
if (symbolAsset) {
symbolAsset.destroy();
}
symbolAsset = self.attachAsset(newSymbol, {
// tint: main ? symbolTint : borderTint,
tint: main ? 0xFFFFFF : 0x000000,
// NOTE: Tinting is currently broken (cannot use string)
x: TEXT_OFFSETS[i][0] * TEXT_BORDER_WEIGHT,
y: TEXT_OFFSETS[i][1] * TEXT_BORDER_WEIGHT,
anchorX: anchorX,
anchorY: anchorY
});
if (width !== undefined && height === undefined) {
height = symbolAsset.height * (width / symbolAsset.width);
} else if (height !== undefined && width === undefined) {
width = symbolAsset.width * (height / symbolAsset.height);
}
symbolAsset.width = width;
symbolAsset.height = height;
if (scaleX !== undefined && scaleY !== undefined) {
symbolAsset.scale.set(scaleX, scaleY);
}
symbolAssets[i] = symbolAsset;
}
mainSymbol = symbolAssets[TEXT_OFFSETS.length - 1];
}
;
buildSymbolAssets(symbol);
return self;
});
var BasicButton = ConfigContainer.expand(function (imageName, callback, config) {
var self = ConfigContainer.call(this, config);
config = config || {};
var anchorX = config.anchorX !== undefined ? config.anchorX : 0.5;
var anchorY = config.anchorY !== undefined ? config.anchorY : 0.5;
var scale = config.scale !== undefined ? config.scale : 1;
var scaleX = config.scaleX !== undefined ? config.scaleX : scale;
var scaleY = config.scaleY !== undefined ? config.scaleY : scale;
var buttonBackground = self.attachAsset('buttonBackground', {
anchorX: anchorX,
anchorY: anchorY
});
var imageAsset;
if (imageName) {
imageAsset = self.attachAsset(imageName, {
x: buttonBackground.x - buttonBackground.width * (anchorX - 0.5),
y: buttonBackground.y - buttonBackground.height * (anchorY - 0.5),
anchorX: 0.5,
anchorY: 0.5
});
}
;
self.updateImage = updateImage;
self.imageName = imageName;
self.imageAsset = imageAsset;
self.buttonBackground = buttonBackground;
;
function updateImage(newImageName) {
if (newImageName !== self.imageName) {
self.imageName = newImageName;
if (imageAsset) {
imageAsset.destroy();
}
if (newImageName) {
imageAsset = self.attachAsset(newImageName, {
x: buttonBackground.x - buttonBackground.width * (anchorX - 0.5),
y: buttonBackground.y - buttonBackground.height * (anchorY - 0.5),
anchorX: 0.5,
anchorY: 0.5
});
}
}
}
;
buttonBackground.on('down', callback);
return self;
});
var UprootButton = BasicButton.expand(function (plantName, callback, config) {
var self = BasicButton.call(this, 'cross', callback, config);
;
self.buttonBackground.y = PLOT_BUTTON_OFFSET;
self.imageAsset.y = PLOT_BUTTON_OFFSET;
;
return self;
});
var PlanterButton = BasicButton.expand(function (plantName, callback, config) {
var self = BasicButton.call(this, plantName, callback, config);
var arrow = self.attachAsset('arrow', {
y: PLOT_BUTTON_OFFSET / 2,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.4,
scaleY: 1.4
});
;
self.buttonBackground.y = PLOT_BUTTON_OFFSET;
self.imageAsset.y = PLOT_BUTTON_OFFSET;
;
return self;
});
var AutoWeeder = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
// Custom AutoWeeder logic here
self.update = function () {
// AutoWeeder update logic
};
return self;
});
var Asteroid = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var randomScale = ASTEROID_SCALE_MIN + Math.random() * ASTEROID_SCALE_VAR;
var asteroidGraphics = self.attachAsset('asteroid', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: randomScale,
scaleY: randomScale,
rotation: Math.random() * MATH_2_PI
});
var side = self.x < 0;
var targetY = ASTEROID_TARGET_OFFSET + Math.random() * (GAME_HEIGHT - 2 * ASTEROID_TARGET_OFFSET);
var targetX = side ? GAME_WIDTH + ASTEROID_MARGIN : -ASTEROID_MARGIN;
var rotationSpeed = (Math.random() - 0.5) * 2 * ASTEROID_ROTATION_MAX;
var angle = Math.atan2(targetY - self.y, targetX - self.x);
var velocityX = ASTEROID_SPEED * Math.cos(angle);
var velocityY = ASTEROID_SPEED * Math.sin(angle);
;
self.update = update;
;
function update() {
self.rotation += rotationSpeed;
self.x += velocityX;
self.y += velocityY;
var dx = self.x - planet.x;
var dy = self.y - planet.y;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared <= planet.radius * planet.radius) {
if (Math.random() < ASTEROID_DROP_CHANCE) {
var angle = Math.atan2(self.y - planet.y, self.x - planet.x) - planet.rotation;
var fruitX = Math.cos(angle) * planet.radius;
var fruitY = Math.sin(angle) * planet.radius;
var fruit = planet.addChild(new Fruit('fruitDiamondDust', planet, {
rotation: angle + MATH_HALF_PI,
x: fruitX,
y: fruitY
}));
}
var explosion = game.addChild(new Explosion({
x: self.x,
y: self.y,
scale: randomScale
}));
stats.asteroidImpacts++;
return true;
}
return side ? self.x > targetX : self.x < targetX;
}
});
var Crosshair = Container.expand(function () {
var self = Container.call(this);
var counter = 0;
var arrows = [];
var arrowOffsets = [{
x: 0,
y: -1
}, {
x: 1,
y: 0
}, {
x: 0,
y: 1
}, {
x: -1,
y: 0
}];
for (var i = 0; i < arrowOffsets.length; i++) {
var arrow = self.attachAsset('chevron', {
anchorX: 0.5,
anchorY: 1
});
arrow.rotation = MATH_HALF_PI * i;
arrows.push(arrow);
self.addChild(arrow);
}
;
self.rotation = MATH_QUARTER_PI;
self.update = update;
;
function update() {
if (currentPlanet !== destinationPlanet) {
counter++;
var distance = CROSSHAIR_DIST + CROSSHAIR_VARIANCE * Math.sin(counter / CROSSHAIR_PERIOD);
setDistance(distance);
} else if (counter > 0) {
counter = 0;
setDistance(CROSSHAIR_DIST);
}
}
function setDistance(distance) {
for (var i = 0; i < arrows.length; i++) {
arrows[i].x = arrowOffsets[i].x * distance;
arrows[i].y = arrowOffsets[i].y * distance;
}
}
;
setDistance(CROSSHAIR_DIST);
});
var InventorySelector = Container.expand(function () {
var self = Container.call(this);
var selector = self.attachAsset('inventorySelector', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF3333
});
var crossAsset = self.attachAsset('cross', {
anchorX: 0.5,
anchorY: 0.5
});
;
self.toggleAllowed = toggleAllowed;
;
function toggleAllowed(bool) {
crossAsset.alpha = bool ? 0 : 1;
}
});
var WinningMessage = Container.expand(function (x, y, completionTicks) {
var self = Container.call(this);
var seconds = Math.floor(completionTicks / 60);
var minutes = Math.floor(seconds / 60);
var hours = Math.floor(minutes / 60);
seconds = seconds % 60;
minutes = minutes % 60;
var winningTime = (hours > 0 ? hours + 'h ' : '') + (minutes > 0 ? minutes + 'm ' : '') + seconds + 's';
var messageStatTitles = ['Distance Walked', 'Credits Earned', 'Items Collected', 'Crops Planted', 'Crops Overrun', 'Weeds Pulled', 'Trades Accepted', 'Trades Declined', 'Rocket Launches', 'Asteroid Impacts'];
var messageStatKeys = ['distanceRun', 'creditsEarned', 'cropsHarvested', 'cropsPlanted', 'cropsOverrun', 'weedsPulled', 'salesDone', 'salesRejected', 'rocketLaunches', 'asteroidImpacts'];
var timeText = self.addChild(new BorderedText('You reached the Gold Planet in ' + winningTime, {
anchorX: .5,
anchorY: 1,
y: -TEXT_WINNING_OFFSET
}));
self.addChild(new BorderedText('Congratulations, you won!', {
anchorX: .5,
anchorY: 1,
size: TEXT_SIZE_LARGE,
y: timeText.y - timeText.height - 10
}));
self.addChild(new BorderedText(messageStatTitles.map(mapTitle).join('\n'), {
anchorX: 1,
anchorY: 0,
size: TEXT_SIZE_SMALL,
y: TEXT_WINNING_OFFSET
}));
self.addChild(new BorderedText(messageStatKeys.map(mapKey).join('\n'), {
anchorX: 0,
anchorY: 0,
size: TEXT_SIZE_SMALL,
y: TEXT_WINNING_OFFSET
}));
;
self.x = x;
self.y = y;
;
function mapTitle(title) {
return title + ' ';
}
function mapKey(key) {
var stat = winningStats[key];
var extra = stats[key] - stat;
switch (key) {
case 'distanceRun':
return ': ' + Math.round(stat * PLAYER_DISTANCE_SCALE) + 'm' + (extra ? ' [+' + Math.round(extra * PLAYER_DISTANCE_SCALE) + 'm]' : '');
default:
return ': ' + stat + (extra ? ' [+' + extra + ']' : '');
}
}
});
var FlameParticle = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var flameGraphics = self.attachAsset('flame', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
var lifetime = ROCKET_FLAME_LIFETIME;
;
self.update = update;
;
function update() {
var remaining = lifetime / ROCKET_FLAME_LIFETIME;
self.y -= ROCKET_FLAME_SPEED;
flameGraphics.alpha = 0.5 * remaining;
flameGraphics.scale.set(remaining);
return --lifetime <= 0;
}
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000 // Init game with black background
});
/****
* Game Code
****/
;
//==============================================================================
// Global constants & settings
//==============================================================================
;
// Math constants / pre-calculations
var MATH_2_PI = Math.PI * 2;
var MATH_HALF_PI = Math.PI / 2;
var MATH_QUARTER_PI = Math.PI / 4;
var MATH_HALF_ROOT_3 = Math.sqrt(3) / 2; // Required by: TEXT_OFFSETS, BorderedText, BorderedSymbol, SymbolText
var MATH_APPROX_ZERO = 0.0000001;
;
// Text settings
var TEXT_OFFSETS = [[0, 1], [MATH_HALF_ROOT_3, 0.5], [MATH_HALF_ROOT_3, -0.5], [0, -1], [-MATH_HALF_ROOT_3, -0.5], [-MATH_HALF_ROOT_3, 0.5], [0, 0]]; // Required by: BorderedText, BorderedSymbol, SymbolText
var TEXT_BORDER_WEIGHT = 4; // Required by: BorderedText, BorderedSymbol, SymbolText
var TEXT_DEFAULT_BORDER = '#000000'; // Required by: BorderedText, BorderedSymbol, SymbolText
var TEXT_DEFAULT_FILL = '#FFFFFF'; // Required by: BorderedText, SymbolText
var TEXT_DEFAULT_FONT = 'Arial'; // Required by: BorderedText, SymbolText
var TEXT_DEFAULT_SIZE = 50; // Required by: BorderedText, SymbolText
var TEXT_DEFAULT_MARGIN = 0; // Required by: SymbolText
;
// Game constants
var GAME_TESTING = false;
var GAME_TICKS = 60;
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var GAME_SPEED = 0.1;
;
// Rocket constants
var ROCKET_DIST_REVERSE = 300;
var ROCKET_DIST_LEAVE = 2500;
var ROCKET_SPEED_BASE = 1.2;
var ROCKET_SPEED_DIV = 5;
var ROCKET_SPEED_DELAY = 10;
var ROCKET_SPEED_REVERSE = 2;
var ROCKET_FLAME_BREDTH = 30;
var ROCKET_FLAME_LIFETIME = 30;
var ROCKET_FLAME_SPEED = 5;
;
// Farm constants
var PLOT_SIZE = 80;
var PLOT_GAP = 120;
var PLOT_NODE_OFFSET = 20;
var PLOT_FARM_OFFSET = -30;
var PLOT_ALPHA_STEP = 1 / GAME_TICKS;
var PLOT_BUTTON_OFFSET = -220;
;
// Interface settings
var SCALE_FRUIT_DROP = 0.8;
var SCALE_FRUIT_INVENTORY = 0.6;
var SCALE_FRUIT_TRADE = 0.65;
var SCALE_FRUIT_PLANTER = 0.25;
var TEXT_WINNING_OFFSET = 300;
var TEXT_SIZE_SMALL = 35;
var TEXT_SIZE_LARGE = 75;
var CROSSHAIR_DIST = 40;
var CROSSHAIR_VARIANCE = 10;
var CROSSHAIR_PERIOD = 1.25 * GAME_TICKS / MATH_2_PI;
var POPUP_OFFSET = 100;
var POPUP_DURATION = 1.5 * GAME_TICKS;
var POPUP_RECYCLE = Math.floor(POPUP_DURATION * 0.5);
var NAVIGATION_ROTATE_SPEED = 0.01;
var NAVIGATION_ROTATE_TARGET = MATH_HALF_PI;
;
// Inventory settings
var INVENTORY_ROWS = 1;
var INVENTORY_COLS = 7;
var INVENTORY_ICON_SCALE = 0.55;
var INVENTORY_SLOT_SIZE = 115;
;
// Asteroid settings
var ASTEROID_SPAWN_CHANCE = 0.001;
var ASTEROID_DROP_CHANCE = 0.1; // 10% chance
var ASTEROID_ROTATION_MAX = 0.02;
var ASTEROID_SPEED = 5;
var ASTEROID_MARGIN = 100;
var ASTEROID_SCALE_MIN = 0.75;
var ASTEROID_SCALE_VAR = 0.5;
var ASTEROID_TARGET_OFFSET = 500;
var EXPLOSION_FRAMES = 15; // Number of frames for the explosion animation to last
;
// Player settings
var PLAYER_SPEED = 5;
var PLAYER_SPAWN_DELAY = 500;
var PLAYER_ACTION_INTERVAL = Math.floor(GAME_TICKS / 4);
var PLAYER_ACTION_DIST = 80;
var PLAYER_ACTION_SQRDIST = PLAYER_ACTION_DIST * PLAYER_ACTION_DIST;
var PLAYER_BUFFER_DIST = 400;
var PLAYER_BUFFER_SQRDIST = PLAYER_BUFFER_DIST * PLAYER_BUFFER_DIST;
var PLAYER_START_ANGLE = Math.PI / 16;
var PLAYER_DISTANCE_SCALE = 0.01;
;
// Trader settings
var TRADER_TIME_INITIAL = 10 * GAME_TICKS;
var TRADER_TIME_MIN = 1 * GAME_TICKS;
var TRADER_TIME_MAX = 30 * GAME_TICKS;
var TRADER_COST_VARIANCE = 0.1;
var TRADER_INVENTORY_MIN = 0.25;
var TRADER_INVENTORY_MAX = 1.5;
var TRADER_MINIMUM_BUY = 5;
var TRADER_FRUIT_OFFER_MIN = 1;
var TRADER_FRUIT_OFFER_MAX = 3;
var TRADER_EXCHANGE_MULTIPLIER = 5;
var TRADER_FRAME_OFFSET_X = 0.1;
var TRADER_FRAME_OFFSET_Y = -0.56;
var TRADER_DETAIL_MARGIN = 20;
var TRADER_SHIP_SPACING = 50;
var TRADER_MOVE_INTERVAL = 20;
var TRADER_MOVE_ROTATION = 0.1;
var TRADER_MOVE_ANCHOR = 0.1;
var TRADER_SPAWN_TIME = 2 * GAME_TICKS;
var TRADER_SPAWN_RADIUS = 1000;
var TRADER_AUTOWEEDER_CHANCE = 0.5;
var TRADER_AUTOWEEDER_TRADE = {
buyName: 'autoweeder',
buyAmount: 1,
sellName: 'credits',
sellAmount: 125
};
;
// Plant settings
var WEEDS_SPAWN_CHANCE = 0.6;
var WEEDS_SPAWN_TIME = 5 * 60 * GAME_TICKS;
var WEEDS_SPAWN_VARIANCE = 2 * 60 * GAME_TICKS;
var WEEDS_SPREAD_TIME = 1 * 60 * GAME_TICKS;
var WEEDS_SPREAD_VARIANCE = 20 * GAME_TICKS;
var GROW_WARNING_BARREN = 'Nothing grows on this planet';
var GROW_WARNING_GENERAL = 'This cannot grow on this planet';
var GROW_WARNING_DIAMOND = 'This cannot be planted, only sold';
var FRUIT_FALL_SPEED = 3;
var PLANT_SCALE_VARIANCE = 0.1;
var PLANT_GROWTH_FACTOR = 0.2;
var PLANT_DEAD_COLOUR = 0xA3784B;
var PLANT_DETAILS = {
plantWeeds: {
blueprint: PlantWeeds,
stages: 4,
fruit: 'fruitWeeds',
growthTime: 60 * GAME_TICKS,
growthVariance: 10 * GAME_TICKS
},
plantBush: {
blueprint: PlantBush,
stages: 6,
fruit: 'fruitBush',
growthTime: 40 * GAME_TICKS,
growthVariance: 10 * GAME_TICKS
},
plantStalk: {
blueprint: PlantStalk,
stages: 7,
fruit: 'fruitStalk',
growthTime: 50 * GAME_TICKS,
growthVariance: 15 * GAME_TICKS
},
plantEyeball: {
blueprint: PlantEyeball,
stages: 5,
fruit: 'fruitEyeball',
growthTime: 35 * GAME_TICKS,
growthVariance: 20 * GAME_TICKS
},
plantFlower: {
blueprint: PlantFlower,
stages: 7,
fruit: 'fruitFlower',
growthTime: 60 * GAME_TICKS,
growthVariance: 30 * GAME_TICKS
},
plantDiamond: {
blueprint: PlantDiamond,
stages: 4,
fruit: 'fruitDiamond',
growthTime: 100 * GAME_TICKS,
growthVariance: 30 * GAME_TICKS
}
};
var FRUIT_DETAILS = {
fruitWeeds: {
value: 1,
type: 'green',
plant: 'plantWeeds'
},
fruitBush: {
value: 5,
type: 'green',
plant: 'plantBush'
},
fruitStalk: {
value: 5,
type: 'red',
plant: 'plantStalk'
},
fruitEyeball: {
value: 10,
type: 'red',
plant: 'plantEyeball'
},
fruitFlower: {
value: 15,
type: 'blue',
plant: 'plantFlower'
},
fruitDiamondDust: {
value: 0,
type: 'omni',
plant: 'plantDiamond'
},
fruitDiamond: {
value: 250,
minSell: 1
}
};
;
// Planet & navigation settings
var NAVIGATION = ['planetGrey', 'planetRed', 'planetBlue', 'planetOmni', 'planetGold'];
var PLANET_LAST = NAVIGATION[NAVIGATION.length - 1];
var PLANET_DETAILS = {
planetGrey: {
cost: 0,
radius: 250,
spin: 0.001,
blueprint: PlanetGrey,
plantTypes: ['green'],
buy: ['credits', 'credits', 'fruitBush', 'fruitStalk'],
sell: ['credits', 'fruitWeeds', 'fruitBush']
},
planetRed: {
cost: 75,
radius: 300,
spin: -0.0002,
blueprint: PlanetRed,
plantTypes: ['green', 'red'],
buy: ['credits', 'credits', 'fruitStalk', 'fruitEyeball'],
sell: ['credits', 'fruitWeeds', 'fruitBush', 'fruitStalk', 'fruitEyeball']
},
planetBlue: {
cost: 250,
radius: 250,
spin: 0.001,
blueprint: PlanetBlue,
plantTypes: ['green', 'blue'],
buy: ['credits', 'credits', 'fruitFlower'],
sell: ['credits', 'fruitWeeds', 'fruitBush', 'fruitFlower']
},
planetOmni: {
cost: 1000,
radius: 350,
spin: -0.0005,
coarse: true,
blueprint: PlanetOmni,
plantTypes: ['green', 'blue', 'red', 'omni'],
buy: ['credits'],
sell: ['fruitStalk', 'fruitEyeball', 'fruitFlower', 'fruitDiamond', 'fruitDiamond']
},
planetGold: {
cost: 3250,
radius: 150,
spin: -0.002,
barren: true,
blueprint: PlanetGold,
plantTypes: [],
buy: [],
sell: []
}
};
;
//==============================================================================
// Global variables
//==============================================================================
;
var money = 25;
var planetMap = {};
var asteroidList = [];
var effectsList = [];
var popupMap = {};
var stats = {
creditsEarned: 0,
cropsPlanted: 0,
cropsHarvested: 0,
cropsOverrun: 0,
weedsPulled: 0,
salesDone: 0,
salesRejected: 0,
distanceRun: 0,
rocketLaunches: 0,
asteroidImpacts: 0
};
var winningStats = {};
var winningTick = -1;
var winningTime = '';
var winningMessage;
var player;
var traders = [];
var traderCountdown = TRADER_TIME_INITIAL;
var nextTrade = {
buyName: 'fruitBush',
buyAmount: 2,
sellName: 'fruitWeeds',
sellAmount: 9 + Math.round(2 * Math.random())
};
var skipRetarget = false;
var retargetAngle;
var targetAngle = PLAYER_START_ANGLE;
var taskLaunch = false;
var taskPlanter = false;
var taskUproot = false;
var currentPlanet = NAVIGATION[0];
var destinationPlanet = NAVIGATION[0];
var background = game.addChild(new Background());
var midground = game.addChild(new Container());
var foreground = game.addChild(new Container());
var planet = midground.addChild(new PlanetGrey({
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2,
rotation: Math.random() * MATH_2_PI
}));
var ship = planet.background.addChild(new Ship({
x: planet.radius + ROCKET_DIST_REVERSE,
y: 0
}));
var inventory = LK.gui.top.addChild(new Inventory({
y: INVENTORY_SLOT_SIZE / 2 + 10,
anchorX: .5,
anchorY: .5
}));
var moneyDisplay = LK.gui.top.addChild(new SymbolText(money, 'currencySymbol', {
x: inventory.width / 2 + 25,
y: inventory.y,
tint: true,
anchorX: 0,
anchorY: .5
}));
var crosshair = new Crosshair();
var navigation = LK.gui.bottom.addChild(new NavigationInterface({
x: 0,
y: -100
}));
planetMap[planet.name] = planet;
// Adjustments made purely for testing purposes
if (GAME_TESTING) {
money = 50000;
PLANT_GROWTH_FACTOR = 0.1;
inventory.adjustItem('fruitWeeds', 10);
inventory.adjustItem('fruitBush', 10);
inventory.adjustItem('fruitStalk', 10);
inventory.adjustItem('fruitEyeball', 10);
inventory.adjustItem('fruitFlower', 10);
inventory.adjustItem('fruitDiamondDust', 10);
}
;
//==============================================================================
// Gameplay events
//==============================================================================
;
LK.on('tick', function () {
if (skipRetarget) {
skipRetarget = false;
} else if (retargetAngle !== undefined) {
targetAngle = retargetAngle;
}
retargetAngle = undefined;
if (player) {
player.update();
}
ship.update();
navigation.update();
updateList(effectsList);
for (var key in planetMap) {
planetMap[key].update();
}
for (var i = 0; i < 4; i++) {
var trader = traders[i];
if (trader) {
trader.update();
}
}
if (planet.name !== PLANET_LAST) {
updateList(asteroidList);
trySpawnAsteroid();
trySpawnTrader();
}
});
;
game.on('down', function (obj) {
if (player) {
var clickPosition = obj.event.getLocalPosition(game);
var dx = clickPosition.x - planet.x;
var dy = clickPosition.y - planet.y;
var range = planet.radius + PLAYER_BUFFER_DIST;
if (dx * dx + dy * dy <= range * range) {
resetTasks();
retargetAngle = Math.atan2(dy, dx) - planet.rotation;
}
}
});
;
//==============================================================================
// Global functions
//==============================================================================
;
function updateList(list, idle) {
for (var i = list.length - 1; i >= 0; i--) {
var item = list[i];
if (item.update && item.update(idle)) {
item.destroy();
list.splice(i, 1);
}
}
}
;
function trySpawnAsteroid() {
if (Math.random() < ASTEROID_SPAWN_CHANCE) {
var side = Math.random() < 0.5;
asteroidList.push(midground.addChild(new Asteroid({
x: side ? -ASTEROID_MARGIN : GAME_WIDTH + ASTEROID_MARGIN,
y: Math.random() * GAME_HEIGHT,
vx: side ? ASTEROID_SPEED : -ASTEROID_SPEED
})));
}
}
;
function trySpawnTrader() {
if (--traderCountdown <= 0) {
traderCountdown = randomInt(TRADER_TIME_MIN, TRADER_TIME_MAX);
var available = [];
for (var i = 0; i < 4; i++) {
if (!traders[i]) {
available.push(i);
}
}
if (available.length > 0) {
var spawnIndex = available[Math.floor(Math.random() * available.length)];
traders[spawnIndex] = background.addChild(new Trader({
index: spawnIndex,
x: GAME_WIDTH / 4 * (spawnIndex % 2 === 0 ? 1 : 3),
y: GAME_HEIGHT / 32 * (spawnIndex < 2 ? 9 : 27)
}, nextTrade));
nextTrade = undefined;
}
}
}
;
function transitionPlanets() {
LK.effects.flashScreen(0x000000, 500);
currentPlanet = destinationPlanet;
planet.parent.removeChild(planet);
planet = planetMap[currentPlanet];
stats.rocketLaunches++;
asteroidList.forEach(function (asteroid) {
asteroid.destroy();
});
asteroidList = [];
for (var i = 0; i < 4; i++) {
var trader = traders[i];
if (trader) {
trader.handleTradeCallback(false);
trader.destroy();
}
}
traders = [];
if (!planet) {
var planetClass = PLANET_DETAILS[currentPlanet].blueprint;
planet = planetMap[currentPlanet] = new planetClass({
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2
});
}
if (currentPlanet === NAVIGATION[NAVIGATION.length - 1]) {
if (winningTick < 0) {
saveStats();
winningTick = LK.ticks;
}
winningMessage = LK.gui.center.addChild(new WinningMessage(0, -100, winningTick));
} else if (winningMessage) {
winningMessage.destroy();
winningMessage = undefined;
}
midground.addChild(planet);
planet.background.addChild(ship);
ship.x = planet.radius + ROCKET_DIST_REVERSE;
resetTasks();
targetAngle = PLAYER_START_ANGLE;
background.refresh();
inventory.refreshAllowed();
}
;
function resetTasks() {
if (taskLaunch) {
taskLaunch = false;
navigation.setDestination(currentPlanet);
}
taskUproot = false;
taskPlanter = false;
if (player) {
targetAngle = player.angle;
}
}
;
function saveStats() {
for (var key in stats) {
winningStats[key] = stats[key];
}
}
;
function mod(x, base) {
return (x % base + base) % base;
}
;
// TODO: Remove
function randomInt(min, max) {
return min === max ? min : Math.floor(min + Math.random() * (max - min));
}
function randomIntInRange(min, max) {
return min === max ? min : Math.floor(min + Math.random() * (max - min));
}
function randomFloatInRange(min, max) {
return min === max ? min : min + Math.random() * (max - min);
}
;
function approxZero(value) {
return value > -MATH_APPROX_ZERO && value < MATH_APPROX_ZERO;
}
pixel art of a tiny planet. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a planet. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of an alien currency symbol. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a planet made of gold ore. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
plain black background with stars. 2d repeating Texture.
pixel art of a asteroid. Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a cute alien farmer, side view. Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a rocky explosion.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art flame particle. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a large white, empty, rectangular, speech bubble. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a red chevron. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Pixel art of yellow grapes. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.