Code edit (1 edits merged)
Please save this source code
User prompt
Create a new UprootButton class that has the same logic as the PlanterButton
User prompt
Duplicate the PlanterButton as UprootButton and PlanterButton classes
Code edit (9 edits merged)
Please save this source code
User prompt
Please remove PLOT_EXTRA_LINGER
Code edit (4 edits merged)
Please save this source code
User prompt
When collecting a fruit, create a PopupEffect (adding it to the effectsList) above the player
User prompt
create a PopupEffect class inheriting from the ConfigContainer class and taking in a fruitName and config params. The class contains a SymbolText element which slowly increases it's y value and disappears after 1 second
Code edit (1 edits merged)
Please save this source code
User prompt
after performing the player collection check, fruit should fall towards the planet at a fixed rate, stopping when it reaches the planet's radius distance
User prompt
fruit should fall towards the planet at a fixed rate
Code edit (9 edits merged)
Please save this source code
User prompt
In the planterButtonCallback function, instead of setting the targetPoint to 0, set targetPoint to the angle between the planet and the self's positions
Code edit (1 edits merged)
Please save this source code
User prompt
When a fruit is collected, increment the cropsHarvested stat
User prompt
When the trader buttoncallback is called, increment the salesRejected stat if the accepted param is false, otherwise increment the salesDone stat if it was fully completed
User prompt
When an asteroid impacts the planet, increase the stats.asteroidImpacts value by 1
Code edit (10 edits merged)
Please save this source code
User prompt
Fix Bug: 'TypeError: trader.handleTradeCallback is not a function' in or related to this line: 'trader.handleTradeCallback(false);' Line Number: 1946
Code edit (4 edits merged)
Please save this source code
User prompt
Add an LK.timeout of 500ms before creating the player in the ship
Code edit (1 edits merged)
Please save this source code
Code edit (9 edits merged)
Please save this source code
User prompt
The TraderDialogue's setTint should flash the dialogueBackground object the tint colour for 500ms
Code edit (9 edits merged)
Please save this source code
/****
* Classes
****/
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 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) {
console.log(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 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 Player = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var actionCountdown = PLAYER_ACTION_INTERVAL;
var playerGraphics = self.attachAsset('player', {
rotation: MATH_HALF_PI,
anchorX: 0.5,
anchorY: 0.75
});
;
self.update = update;
;
function update() {
var currentAngle = Math.atan2(self.y, self.x);
var angleDifference = targetAngle - currentAngle;
angleDifference = mod(angleDifference + Math.PI, MATH_2_PI) - Math.PI;
if (angleDifference < -MATH_APPROX_ZERO || angleDifference > MATH_APPROX_ZERO) {
var direction = angleDifference / Math.abs(angleDifference);
var angleStep = PLAYER_SPEED / planet.radius;
var newAngle = currentAngle + direction * Math.min(Math.abs(angleDifference), angleStep);
self.x = Math.cos(newAngle) * planet.radius;
self.y = Math.sin(newAngle) * planet.radius;
playerGraphics.scale.x = direction < 0 ? -1 : 1;
playerGraphics.rotation = newAngle + MATH_HALF_PI;
actionCountdown = 0; // Perform action immediately after stopping
} else if (--actionCountdown <= 0) {
actionCountdown = PLAYER_ACTION_INTERVAL;
performAction(currentAngle);
}
}
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 Ship = Container.expand(function (x, y) {
var self = Container.call(this);
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.x = x;
self.y = y;
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;
player = planet.addChild(new Player({
rotation: ship.rotation,
x: ship.x,
y: ship.y
}));
}
} 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;
if (direction > 0) {
player.destroy();
player = undefined;
}
}
}
});
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;
}
});
var Plant = ConfigContainer.expand(function (type, growStage, config) {
var self = ConfigContainer.call(this, config);
var details = PLANT_DETAILS[type];
;
self.update = update;
self.harvest = harvest;
self.overrun = overrun;
self.updateStage = updateStage;
self.refreshGraphics = refreshGraphics;
self.refreshCountdown = refreshCountdown;
self.type = type;
self.fruit = details.fruit;
self.growStage = growStage;
self.growStages = details.stages;
self.growthTime = details.growthTime;
self.growthVariance = details.growthVariance;
self.growthCountdown = 0;
self.strong = false;
self.harvestable = growStage === self.growStages;
;
function update() {
if (--self.growthCountdown <= 0 && self.growStage < self.growStages) {
self.updateStage(self.growStage + 1);
}
}
function harvest() {
if (self.harvestable) {
if (self.growStage === self.growStages) {
new Fruit(self.fruit, {
x: self.parent.x,
y: self.parent.y,
rotation: self.parent.rotation
});
}
self.parent.plant = undefined;
self.destroy();
return true;
}
}
function overrun() {
if (!self.strong) {
self.destroy();
return true;
}
}
function updateStage(newStage) {
self.growStage = newStage;
self.refreshGraphics();
if (self.growStage === self.growStages) {
self.harvestable = true;
} else {
self.refreshCountdown();
}
}
function refreshGraphics() {
if (self.graphics) {
self.graphics.destroy();
}
self.graphics = self.createAsset(self.type + (self.growStage - 1), {
anchorX: 0.5,
anchorY: 1,
scaleX: Math.random() < 0.5 ? 1 : -1
});
}
function refreshCountdown() {
var baseTime = self.growthTime + Math.random() * self.growthVariance;
self.growthCountdown = Math.floor(PLANT_GROWTH_FACTOR * baseTime);
}
;
refreshCountdown();
refreshGraphics();
return self;
});
var PlantWeeds = Plant.expand(function (growthStage, config) {
var self = Plant.call(this, 'plantWeeds', growthStage, config);
var baseUpdate = self.update;
var spreadCountdown = 0;
;
self.harvestable = true;
self.update = update;
;
function update() {
baseUpdate();
;
if (self.growStage === self.growStages && --spreadCountdown <= 0) {
var node = self.parent;
var nextPlant = node.next.plant;
var prevPlant = node.prev.plant;
var nextSpreadable = !nextPlant || nextPlant.type !== self.type && !nextPlant.strong;
var prevSpreadable = !prevPlant || prevPlant.type !== self.type && !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('plantWeeds', 1);
}
refreshCountdown();
}
}
function refreshCountdown() {
var baseTime = WEEDS_SPREAD_TIME + Math.random() * WEEDS_SPREAD_VARIANCE;
spreadCountdown = Math.floor(PLANT_GROWTH_FACTOR * baseTime);
}
;
refreshCountdown();
});
var PlantBush = Plant.expand(function (growthStage, config) {
var self = Plant.call(this, 'plantBush', growthStage, config);
var baseUpdateStage = self.updateStage;
var fruitCount = 0;
var maxFruitCount = 3;
;
self.updateStage = updateStage;
self.harvest = harvest;
;
function updateStage(newStage) {
if (newStage === self.growStages) {
self.refreshCountdown();
if (fruitCount < maxFruitCount) {
fruitCount++;
self.harvestable = true;
}
} else {
baseUpdateStage(newStage);
}
}
function addFruit() {}
function harvest() {
if (self.harvestable && fruitCount > 0) {
return true;
}
}
});
var PlantStalk = Plant.expand(function (growthStage, config) {
var self = Plant.call(this, 'plantStalk', growthStage, config);
});
var PlantEyeball = Plant.expand(function (growthStage, config) {
var self = Plant.call(this, 'plantEyeball', growthStage, config);
});
var PlantFlower = Plant.expand(function (growthStage, config) {
var self = Plant.call(this, 'plantFlower', growthStage, config);
});
var PlantDiamond = Plant.expand(function (growthStage, config) {
var self = Plant.call(this, 'plantDiamond', growthStage, config);
;
self.strong = true;
});
var PlantNode = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
;
self.update = update;
self.addPlant = addPlant;
self.tryAction = tryAction;
self.offset = PLOT_NODE_OFFSET;
self.plant;
self.farm;
self.prev;
self.next;
;
function update() {
if (self.plant) {
self.plant.update();
}
}
function addPlant(plantName, stage) {
if (!self.plant || self.plant.overrun()) {
var plantBlueprint = PLANT_DETAILS[plantName].blueprint;
self.plant = self.addChild(new plantBlueprint(stage, {
y: self.offset
}));
}
}
function tryAction() {
if (self.plant && self.plant.harvestable && self.plant.harvest()) {
return true;
}
}
;
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);
}
}
function addPlant(plantName, stage) {
baseAddPlant(plantName, stage);
refreshCountdown();
}
function tryAction() {
if (baseTryAction()) {
refreshCountdown();
return true;
}
}
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 baseOffset = self.offset;
var baseUpdate = self.update;
var baseAddPlant = self.addPlant;
var baseTryAction = self.tryAction;
var targetAlpha = 0;
var planterButton;
var planterLinger = 0;
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;
self.offset = PLOT_FARM_OFFSET;
self.potted = false;
;
function update() {
baseUpdate();
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;
}
if (planterLinger > 0) {
planterLinger--;
} else if (planterButton) {
planterButton.destroy();
planterButton = undefined;
}
}
function addPlant(plantName, stage, potted) {
baseAddPlant(plantName, stage);
if (!potted && plantName === 'plantWeeds') {
self.plant.y = baseOffset;
targetAlpha = 0;
} else {
targetAlpha = 1;
}
self.potted = potted;
}
function tryAction() {
if (baseTryAction()) {
targetAlpha = 1;
return true;
} else if (self.alpha === 1 && !self.plant && inventory.allowed) {
var fruitName = inventory.getSelection();
var quantity = inventory.getQuantity(fruitName);
if (quantity > 0) {
if (planterButton) {
planterButton.updateImage(fruitName);
} else {
planterButton = self.addChild(new PlanterButton(fruitName, function () {
self.tryPlantFruit(inventory.getSelection());
skipRetarget = true;
}, {
scale: SCALE_FRUIT_PLANTER
}));
}
planterLinger = PLAYER_ACTION_INTERVAL + PLOT_EXTRA_LINGER;
} else {
planterLinger = 0;
}
} else {
planterLinger = 0;
}
}
function tryPlantFruit(fruitName) {
if (!self.plant) {
var plantName = FRUIT_DETAILS[fruitName].plant;
self.addPlant(plantName, 1, true);
inventory.adjustItem(fruitName, -1);
if (plantName === 'plantWeeds') {
self.plant.harvestable = false;
}
}
}
});
var Planet = ConfigContainer.expand(function (planetName, config, barren) {
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 = !!barren;
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);
}
function createNodes() {
if (!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 ? 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 PlanetGrey = Planet.expand(function (config) {
var self = Planet.call(this, 'planetGrey', config);
});
var PlanetRed = Planet.expand(function (config) {
var self = Planet.call(this, 'planetRed', config);
});
var PlanetBlue = Planet.expand(function (config) {
var self = Planet.call(this, 'planetBlue', config);
});
var PlanetOmni = Planet.expand(function (config) {
var self = Planet.call(this, 'planetOmni', config);
});
var PlanetGold = Planet.expand(function (config) {
var self = Planet.call(this, 'planetGold', config, true);
});
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 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.unlock = unlock;
self.cost = details.cost;
self.planet = planetName;
self.unlocked = !details.cost;
self.on('down', function () {
callback(planetName);
});
;
function unlock() {
self.unlocked = true;
buttonGraphics.tint = 0xFFFFFF;
currencyText.destroy();
}
;
if (self.unlocked) {
unlock();
}
});
var NavigationInterface = Container.expand(function (x, y) {
var self = Container.call(this);
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
}));
}
buttons[firstPlanet].addChild(crosshair);
self.x = x;
self.y = y;
;
function buttonCallback(planet) {
var button = buttons[planet];
if (button.unlocked) {
setDestination(planet);
} else if (money >= button.cost) {
moneyDisplay.setText(money -= button.cost);
button.unlock();
}
}
self.setDestination = setDestination;
;
function setDestination(planet) {
if (destinationPlanet !== planet && ship.direction === 0) {
destinationPlanet = planet;
buttons[planet].addChild(crosshair);
ship.launch(1);
}
}
buttonCallback(firstPlanet);
});
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 = ['Credits', 'Harvests', 'Weeds', 'Trades'];
var messageStatKeys = ['creditsEarned', 'cropsHarvested', 'weedsPulled', 'salesDone'];
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 win!', {
anchorX: .5,
anchorY: 1,
size: 80,
y: timeText.y - timeText.height - 10
}));
self.addChild(new BorderedText(messageStatTitles.map(mapTitle).join('\n'), {
anchorX: 1,
anchorY: 0,
y: TEXT_WINNING_OFFSET
}));
self.addChild(new BorderedText(messageStatKeys.map(mapKey).join('\n'), {
anchorX: 0,
anchorY: 0,
y: TEXT_WINNING_OFFSET
}));
;
self.x = x;
self.y = y;
;
function mapTitle(title) {
return '• ' + title + ' ';
}
function mapKey(key) {
var stat = winningStats[key];
var extra = stat - stats[key];
return ': ' + stat + (extra ? ' (+ ' + extra + ')' : '');
}
});
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();
}
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 : GROW_WARNING_GENERAL;
warningText.setText(warning);
}
}
self.allowed = allowed;
selector.toggleAllowed(allowed);
warningText.visible = !allowed;
}
;
selectSlot(0);
refreshAllowed();
adjustItem('fruitWeeds', 0);
});
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 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 PlanterButton = BasicButton.expand(function (plantName, callback, config) {
var self = BasicButton.call(this, plantName, callback, config);
var arrow = self.attachAsset('arrow', {
y: PLANT_BUTTON_OFFSET / 2,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.4,
scaleY: 1.4
});
;
self.buttonBackground.y = PLANT_BUTTON_OFFSET;
self.imageAsset.y = PLANT_BUTTON_OFFSET;
;
return self;
});
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 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', {
rotation: angle + MATH_HALF_PI,
x: fruitX,
y: fruitY
}));
}
var explosion = game.addChild(new Explosion({
x: self.x,
y: self.y,
scale: randomScale
}));
return true;
}
return side ? self.x > targetX : self.x < targetX;
}
});
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 Fruit = ConfigContainer.expand(function (fruitName, config) {
var self = ConfigContainer.call(this, config);
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() {
if (player) {
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);
return true;
}
}
}
;
planet.fruitList.push(planet.addChild(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;
;
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 = self.addChild(new TraderDialogue(handleTradeCallback, trade, {
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();
}
}
if (traderDialogue) {
traderDialogue.setTint(checkValue(trade.sellName, trade.sellAmount) ? TEXT_DEFAULT_FILL : '#FF0000');
}
}
function handleTradeCallback(accepted) {
var closeDialogue = false;
skipRetarget = true;
if (accepted) {
if (checkValue(trade.sellName, trade.sellAmount)) {
makeAdjustment(trade.sellName, -trade.sellAmount);
makeAdjustment(trade.buyName, trade.buyAmount);
closeDialogue = true;
}
} else {
closeDialogue = true;
}
if (closeDialogue) {
direction = -1;
traderDialogue.destroy();
}
}
function generateTrade() {
// 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 sellAmount, buyAmount;
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 value = valueFactor * FRUIT_DETAILS[sellName].value;
var quantity = Math.max(1, inventory.getQuantity(sellName));
sellAmount = randomInt(Math.max(1, 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) {
if (name === 'credits') {
moneyDisplay.setText(money += amount);
} else {
inventory.adjustItem(name, amount);
}
}
;
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) {
tradeSell.setFill(tint);
}
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
}));
});
/****
* 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_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_EXTRA_LINGER = Math.floor(GAME_TICKS / 2);
;
// Interface settings
var SCALE_FRUIT_DROP = 0.8;
var SCALE_FRUIT_INVENTORY = 0.5;
var SCALE_FRUIT_TRADE = 0.65;
var SCALE_FRUIT_PLANTER = 0.4;
var TEXT_WINNING_OFFSET = 300;
var TEXT_SIZE_SMALL = 35;
var CROSSHAIR_DIST = 40;
var CROSSHAIR_VARIANCE = 10;
var CROSSHAIR_PERIOD = 1.25 * GAME_TICKS / MATH_2_PI;
;
// Inventory settings
var INVENTORY_ROWS = 1;
var INVENTORY_COLS = 8;
var INVENTORY_ICON_SCALE = 0.5;
var INVENTORY_SLOT_SIZE = 100;
;
// 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_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;
;
// Trader settings
var TRADER_TIME_INITIAL = 15 * GAME_TICKS;
var TRADER_TIME_MIN = 10 * GAME_TICKS;
var TRADER_TIME_MAX = 50 * GAME_TICKS;
var TRADER_COST_VARIANCE = 0.1;
var TRADER_INVENTORY_MIN = 0.25;
var TRADER_INVENTORY_MAX = 1.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;
;
// Plant settings
var WEEDS_SPAWN_CHANCE = 0.3;
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 plant';
var PLANT_GROWTH_FACTOR = 0.1;
var PLANT_BUTTON_OFFSET = -220;
var PLANT_FRUIT_SCALE = 0.6;
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: 60 * GAME_TICKS,
growthVariance: 30 * GAME_TICKS
},
plantStalk: {
blueprint: PlantStalk,
stages: 7,
fruit: 'fruitStalk',
growthTime: 60 * GAME_TICKS,
growthVariance: 20 * GAME_TICKS
},
plantEyeball: {
blueprint: PlantEyeball,
stages: 5,
fruit: 'fruitEyeball',
growthTime: 40 * GAME_TICKS,
growthVariance: 10 * GAME_TICKS
},
plantFlower: {
blueprint: PlantFlower,
stages: 4,
fruit: 'fruitFlower',
growthTime: 40 * GAME_TICKS,
growthVariance: 10 * GAME_TICKS
},
plantDiamond: {
blueprint: PlantDiamond,
stages: 4,
fruit: 'fruitDiamond',
growthTime: 80 * 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: 'plantBean'
},
fruitEyeball: {
value: 15,
type: 'red',
plant: 'plantEyeball'
},
fruitFlower: {
value: 25,
type: 'blue',
plant: 'plantFlower'
},
fruitDiamondDust: {
value: 0,
type: 'omni',
plant: 'plantDiamond'
},
fruitDiamond: {
value: 250,
type: 'none',
plant: 'none'
}
};
;
// 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: 100,
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: 800,
radius: 350,
spin: -0.0005,
blueprint: PlanetOmni,
plantTypes: ['green', 'blue', 'red', 'omni'],
buy: ['credits'],
sell: ['fruitStalk', 'fruitEyeball', 'fruitFlower', 'fruitDiamond', 'fruitDiamond']
},
planetGold: {
cost: 3000,
radius: 150,
spin: -0.002,
blueprint: PlanetGold,
plantTypes: [],
buy: [],
sell: []
}
};
;
//==============================================================================
// Global variables
//==============================================================================
;
var money = 10000;
var planetMap = {};
var asteroidList = [];
var stats = {
creditsEarned: 0,
cropsHarvested: 0,
cropsOverrun: 0,
weedsPulled: 0,
salesDone: 0,
salesRejected: 0
};
var winningStats = {};
var winningTick = -1;
var winningTime = '';
var winningMessage;
var effectsList = [];
var player;
var traders = [];
var traderCountdown = TRADER_TIME_INITIAL;
var nextTrade = {
buyName: 'fruitBush',
buyAmount: 1,
sellName: 'fruitWeeds',
sellAmount: 5
};
var skipRetarget = false;
var retargetAngle;
var targetAngle = PLAYER_START_ANGLE;
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 = background.addChild(new PlanetGrey({
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2,
rotation: Math.random() * MATH_2_PI
}));
var ship = planet.background.addChild(new Ship(planet.radius + ROCKET_DIST_REVERSE, 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 navigation = LK.gui.bottom.addChild(new NavigationInterface(0, -100));
var crosshair = new Crosshair();
planetMap[planet.name] = planet;
;
//==============================================================================
// Gameplay events
//==============================================================================
;
LK.on('tick', function () {
if (skipRetarget) {
skipRetarget = false;
} else if (retargetAngle !== undefined) {
targetAngle = retargetAngle;
}
retargetAngle = undefined;
if (player) {
player.update();
}
ship.update();
planet.update();
crosshair.update();
updateList(effectsList);
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 sqrDistance = dx * dx + dy * dy;
var range = planet.radius + PLAYER_BUFFER_DIST;
if (sqrDistance <= range * range) {
retargetAngle = Math.atan2(dy, dx) - planet.rotation;
}
}
});
;
//==============================================================================
// Global functions
//==============================================================================
;
function updateList(list) {
for (var i = list.length - 1; i >= 0; i--) {
var item = list[i];
if (item.update && item.update()) {
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] = foreground.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];
asteroidList.forEach(function (asteroid) {
asteroid.destroy();
});
asteroidList = [];
for (var i = 0; i < 4; i++) {
var trader = traders[i];
if (trader) {
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;
}
background.addChild(planet);
planet.background.addChild(ship);
planet.rotation = Math.random() * MATH_2_PI;
ship.x = planet.radius + ROCKET_DIST_REVERSE;
targetAngle = PLAYER_START_ANGLE;
background.refresh();
inventory.refreshAllowed();
}
;
function mod(x, base) {
return (x % base + base) % base;
}
;
function randomInt(min, max) {
return Math.floor(min + Math.random() * (max - min));
}
;
function saveStats() {
for (var key in stats) {
winningStats[key] = stats[key];
}
}
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.