/****
* 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) {
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', {
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 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;
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 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, 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;
self.fruitOffsetMin = 85;
self.fruitOffsetMax = 95;
;
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 = 1;
}
}
function refreshCountdown() {
var baseTime = WEEDS_SPREAD_TIME + Math.random() * WEEDS_SPREAD_VARIANCE;
spreadCountdown = Math.floor(PLANT_GROWTH_FACTOR * baseTime);
}
;
self.updateStage(growthStage);
});
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 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 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 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 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 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 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 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 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 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);
});
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 = 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.setDestination = setDestination;
;
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 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 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 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: 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 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 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', 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 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 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.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 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() {
// 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 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 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;
});
/****
* 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);
;
// 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 = 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_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;
;
// 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 planet';
var GROW_WARNING_DIAMOND = 'This cannot be planted, only sold';
var FRUIT_FALL_SPEED = 3;
var PLANT_SCALE_VARIANCE = 0.05;
var PLANT_GROWTH_FACTOR = 0.25;
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: 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: 80 * 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: 150,
radius: 300,
spin: -0.0002,
blueprint: PlanetRed,
plantTypes: ['green', 'red'],
buy: ['credits', 'credits', 'fruitStalk', 'fruitEyeball'],
sell: ['credits', 'fruitWeeds', 'fruitBush', 'fruitStalk', 'fruitEyeball']
},
planetBlue: {
cost: 500,
radius: 250,
spin: 0.001,
blueprint: PlanetBlue,
plantTypes: ['green', 'blue'],
buy: ['credits', 'credits', 'fruitFlower'],
sell: ['credits', 'fruitWeeds', 'fruitBush', 'fruitFlower']
},
planetOmni: {
cost: 2000,
radius: 350,
spin: -0.0005,
coarse: true,
blueprint: PlanetOmni,
plantTypes: ['green', 'blue', 'red', 'omni'],
buy: ['credits'],
sell: ['fruitStalk', 'fruitEyeball', 'fruitFlower', 'fruitDiamond', 'fruitDiamond']
},
planetGold: {
cost: 8500,
radius: 150,
spin: -0.002,
barren: true,
blueprint: PlanetGold,
plantTypes: [],
buy: [],
sell: []
}
};
;
//==============================================================================
// Global variables
//==============================================================================
;
var money = 20;
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: 1,
sellName: 'fruitWeeds',
sellAmount: 5
};
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(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 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();
crosshair.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.