User prompt
make it so that predators have 2 states wandering and hungry in the wandering state they ignore prey and in hungry they will look for prey and after eating the prey they stay in a wandering state for 1 min
User prompt
make it so they cant exit the map and there is a boarders
User prompt
make it so you can move organisims
User prompt
make it so that if a predator chases a herbivore for too long they will get tired and stop for 10 seconds
User prompt
make it so that the herbivores and carnivores randomly move around
User prompt
make it so herbivores run from carnivores
User prompt
make it so that the carnivores and herbivores is slower
User prompt
more left
User prompt
MORE LEFT
User prompt
more left
User prompt
more left
User prompt
more left
User prompt
Its offscreen move the GUI more lwft
User prompt
make the GUI centered on top
Code edit (1 edits merged)
Please save this source code
User prompt
Eco Sandbox: Drag & Place Food Chain
Initial prompt
make a simple green map where players can drag and place plants, herbivores, and carnivores!
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Carnivore class var Carnivore = Container.expand(function () { var self = Container.call(this); var carnAsset = self.attachAsset('carnivore', { anchorX: 0.5, anchorY: 0.5 }); self.target = null; self.speed = 1.5 + Math.random() * 1.2; self.eaten = false; self._chaseTime = 0; // How long we've been chasing a herbivore self._tired = false; // Are we tired? self._tiredTimer = 0; // How long left to rest // --- State management for wandering/hungry --- self.state = "wandering"; // "wandering" or "hungry" self._eatCooldown = 0; // frames left in wandering after eating self.update = function () { if (self.eaten) return; // Handle tired state if (self._tired) { self._tiredTimer--; if (self._tiredTimer <= 0) { self._tired = false; self._chaseTime = 0; } // While tired, don't chase or wander, just stand still return; } // Handle eat cooldown (wandering after eating) if (self._eatCooldown > 0) { self._eatCooldown--; if (self._eatCooldown === 0) { self.state = "hungry"; } } // State: wandering (ignore prey) if (self.state === "wandering") { self.target = null; // Don't chase // --- Random wandering when not chasing herbivores --- if (!self._wanderTimer || self._wanderTimer <= 0) { var angle = Math.random() * Math.PI * 2; self._wanderAngle = angle; self._wanderTimer = 60 + Math.floor(Math.random() * 60); } if (typeof self._wanderAngle === "number") { self.x += Math.cos(self._wanderAngle) * self.speed * 0.5; self.y += Math.sin(self._wanderAngle) * self.speed * 0.5; self._wanderTimer--; // Keep inside map bounds (strictly) var minX = 124 + 60; var maxX = 124 + 1800 - 60; var minY = paletteY + 320 + 60; var maxY = paletteY + 320 + 2000 - 60; if (self.x < minX) self.x = minX; if (self.x > maxX) self.x = maxX; if (self.y < minY) self.y = minY; if (self.y > maxY) self.y = maxY; } self._chaseTime = 0; // Not chasing, reset chase timer return; } // State: hungry (look for prey) if (self.state === "hungry") { // Find nearest herbivore if not already targeting if (!self.target || self.target.eaten) { var minDist = 999999, closest = null; for (var i = 0; i < herbivores.length; i++) { var h = herbivores[i]; if (h.eaten) continue; var dx = h.x - self.x, dy = h.y - self.y; var dist = dx * dx + dy * dy; if (dist < minDist) { minDist = dist; closest = h; } } self.target = closest; } // --- Random wandering when not chasing herbivores --- if ((!self.target || self.target.eaten) && (!self._wanderTimer || self._wanderTimer <= 0)) { var angle = Math.random() * Math.PI * 2; self._wanderAngle = angle; self._wanderTimer = 60 + Math.floor(Math.random() * 60); } if (!self.target || self.target.eaten) { if (typeof self._wanderAngle === "number") { self.x += Math.cos(self._wanderAngle) * self.speed * 0.5; self.y += Math.sin(self._wanderAngle) * self.speed * 0.5; self._wanderTimer--; var minX = 124 + 60; var maxX = 124 + 1800 - 60; var minY = paletteY + 320 + 60; var maxY = paletteY + 320 + 2000 - 60; if (self.x < minX) self.x = minX; if (self.x > maxX) self.x = maxX; if (self.y < minY) self.y = minY; if (self.y > maxY) self.y = maxY; } self._chaseTime = 0; // Not chasing, reset chase timer } // Move toward target herbivore if (self.target && !self.target.eaten) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 1) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } // Track how long we've been chasing self._chaseTime++; // If we've been chasing for more than 6 seconds (360 frames), get tired if (self._chaseTime > 360) { self._tired = true; self._tiredTimer = 600; // 10 seconds at 60fps self._chaseTime = 0; return; } // Eat herbivore if close enough if (dist < 60 && !self.target.eaten) { self.target.die(); self.target = null; self._chaseTime = 0; // Reset chase timer after eating // Animate carnivore "eating" tween(self, { scaleX: 1.2, scaleY: 1.2 }, { duration: 120, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.easeIn }); } }); // After eating, enter wandering state for 1 minute (3600 frames) self.state = "wandering"; self._eatCooldown = 3600; } } } // end hungry state }; // Carnivores are not eaten in this MVP return self; }); // Herbivore class var Herbivore = Container.expand(function () { var self = Container.call(this); var herbAsset = self.attachAsset('herbivore', { anchorX: 0.5, anchorY: 0.5 }); self.target = null; self.speed = 1.2 + Math.random() * 1.0; self.hungry = true; self.eaten = false; self.update = function () { if (self.eaten) return; // Find nearest plant if not already targeting if (!self.target || self.target.eaten) { var minDist = 999999, closest = null; for (var i = 0; i < plants.length; i++) { var p = plants[i]; if (p.eaten) continue; var dx = p.x - self.x, dy = p.y - self.y; var dist = dx * dx + dy * dy; if (dist < minDist) { minDist = dist; closest = p; } } self.target = closest; } // Check for nearest carnivore var nearestCarn = null; var minCarnDist = 999999; for (var ci = 0; ci < carnivores.length; ci++) { var carn = carnivores[ci]; if (carn.eaten) continue; var cdx = carn.x - self.x; var cdy = carn.y - self.y; var cdist = cdx * cdx + cdy * cdy; if (cdist < minCarnDist) { minCarnDist = cdist; nearestCarn = carn; } } var carnivoreTooClose = false; var carnDist = 0; var runAwayDX = 0, runAwayDY = 0; if (nearestCarn) { carnDist = Math.sqrt((nearestCarn.x - self.x) * (nearestCarn.x - self.x) + (nearestCarn.y - self.y) * (nearestCarn.y - self.y)); if (carnDist < 300) { // If carnivore is within 300px, run away carnivoreTooClose = true; runAwayDX = self.x - nearestCarn.x; runAwayDY = self.y - nearestCarn.y; } } if (carnivoreTooClose && carnDist > 1) { // Run away from carnivore self.x += runAwayDX / carnDist * (self.speed * 1.5); self.y += runAwayDY / carnDist * (self.speed * 1.5); } else { // --- Random wandering when not chasing plant --- if ((!self.target || self.target.eaten) && (!self._wanderTimer || self._wanderTimer <= 0)) { // Pick a new random direction every 60-120 frames var angle = Math.random() * Math.PI * 2; self._wanderAngle = angle; self._wanderTimer = 60 + Math.floor(Math.random() * 60); } if (!self.target || self.target.eaten) { // Wander in the chosen direction if (typeof self._wanderAngle === "number") { self.x += Math.cos(self._wanderAngle) * self.speed * 0.5; self.y += Math.sin(self._wanderAngle) * self.speed * 0.5; self._wanderTimer--; // Keep inside map bounds (strictly) var minX = 124 + 60; var maxX = 124 + 1800 - 60; var minY = paletteY + 320 + 60; var maxY = paletteY + 320 + 2000 - 60; if (self.x < minX) self.x = minX; if (self.x > maxX) self.x = maxX; if (self.y < minY) self.y = minY; if (self.y > maxY) self.y = maxY; } } // Move toward target plant if (self.target && !self.target.eaten) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 1) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } // Eat plant if close enough if (dist < 60 && !self.target.eaten) { self.target.die(); self.target = null; // Animate herbivore "eating" tween(self, { scaleX: 1.2, scaleY: 1.2 }, { duration: 120, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.easeIn }); } }); } } } }; // Called when eaten by carnivore self.die = function (_onFinish) { if (self.eaten) return; self.eaten = true; tween(self, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { self.destroy(); if (_onFinish) _onFinish(); } }); }; return self; }); // Plant class var Plant = Container.expand(function () { var self = Container.call(this); var plantAsset = self.attachAsset('plant', { anchorX: 0.5, anchorY: 0.5 }); // Plants are static, but we can animate them when eaten self.eaten = false; self.die = function (_onFinish2) { if (self.eaten) return; self.eaten = true; tween(self, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { self.destroy(); if (_onFinish2) _onFinish2(); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x3cb371 // Greenish background for the map }); /**** * Game Code ****/ // --- Global arrays for organisms --- // --- Asset Initialization --- var plants = []; var herbivores = []; var carnivores = []; // --- Palette UI --- var paletteY = 180; // y position for palette var paletteSpacing = 260; var paletteItems = []; // Shift the palette even further left so it is not offscreen var paletteTotalWidth = paletteSpacing * 2; var paletteXStart = 2048 / 2 - paletteTotalWidth / 2 - 1300; // Create palette items for drag-and-drop function createPaletteItem(type, x, y) { var assetId, color, label; if (type === 'plant') { assetId = 'plant'; color = 0x6adf60; label = 'Plant'; } else if (type === 'herbivore') { assetId = 'herbivore'; color = 0xf7e26b; label = 'Herbivore'; } else { assetId = 'carnivore'; color = 0xe74c3c; label = 'Carnivore'; } var node = new Container(); var icon = node.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); node.x = x; node.y = y; node.type = type; // Add label var txt = new Text2(label, { size: 60, fill: "#fff" }); txt.anchor.set(0.5, 0); txt.y = 60; node.addChild(txt); // Add to GUI overlay (so it stays on top) LK.gui.top.addChild(node); paletteItems.push(node); return node; } // Place palette items (avoid top left 100x100) var plantPalette = createPaletteItem('plant', paletteXStart, paletteY); var herbPalette = createPaletteItem('herbivore', paletteXStart + paletteSpacing, paletteY); var carnPalette = createPaletteItem('carnivore', paletteXStart + paletteSpacing * 2, paletteY); // --- Drag and Drop Logic --- var dragging = null; // {type, node, paletteRef} var dragOffsetX = 0, dragOffsetY = 0; // Helper: create organism at x,y function createOrganism(type, x, y) { var obj; if (type === 'plant') { obj = new Plant(); plants.push(obj); } else if (type === 'herbivore') { obj = new Herbivore(); herbivores.push(obj); } else { obj = new Carnivore(); carnivores.push(obj); } obj.x = x; obj.y = y; obj.scaleX = 1; obj.scaleY = 1; obj.alpha = 1; game.addChild(obj); return obj; } // Helper: check if point is inside a palette item function isInPalette(x, y, node) { var bounds = { x: node.x - 60, y: node.y - 60, w: 120, h: 120 }; return x >= bounds.x && x <= bounds.x + bounds.w && y >= bounds.y && y <= bounds.y + bounds.h; } // --- Event Handlers --- // Down: start drag if on palette game.down = function (x, y, obj) { // Check if touch/click is on a palette item for (var i = 0; i < paletteItems.length; i++) { var p = paletteItems[i]; // Convert GUI coordinates to game coordinates var guiPos = LK.gui.top.toLocal({ x: x, y: y }, game); if (isInPalette(guiPos.x, guiPos.y, p)) { // Start dragging a new organism dragging = { type: p.type, node: createOrganism(p.type, x, y), paletteRef: p }; dragOffsetX = 0; dragOffsetY = 0; // Make it semi-transparent while dragging dragging.node.alpha = 0.7; return; } } // If not on palette, check if on an organism to move it // Check if touch/click is on an organism to move it var found = false; var allOrganisms = plants.concat(herbivores, carnivores); for (var i = allOrganisms.length - 1; i >= 0; i--) { var org = allOrganisms[i]; // Only allow moving if not eaten and touch is within 60px radius of center var dx = x - org.x; var dy = y - org.y; var dist = Math.sqrt(dx * dx + dy * dy); if (!org.eaten && dist < 60) { dragging = { type: plants.indexOf(org) !== -1 ? 'plant' : herbivores.indexOf(org) !== -1 ? 'herbivore' : 'carnivore', node: org, paletteRef: null }; dragOffsetX = org.x - x; dragOffsetY = org.y - y; org.alpha = 0.7; found = true; break; } } if (!found) { dragging = null; } }; // Move: update drag position game.move = function (x, y, obj) { if (dragging && dragging.node) { var minX = 124 + 60; var maxX = 124 + 1800 - 60; var minY = paletteY + 320 + 60; var maxY = paletteY + 320 + 2000 - 60; var newX = x + dragOffsetX; var newY = y + dragOffsetY; // Clamp to map area if (newX < minX) newX = minX; if (newX > maxX) newX = maxX; if (newY < minY) newY = minY; if (newY > maxY) newY = maxY; dragging.node.x = newX; dragging.node.y = newY; } }; // Up: drop organism if dragging game.up = function (x, y, obj) { if (dragging && dragging.node) { // If dropped inside palette area, destroy (cancel) var guiPos = LK.gui.top.toLocal({ x: x, y: y }, game); var cancel = false; for (var i = 0; i < paletteItems.length; i++) { if (isInPalette(guiPos.x, guiPos.y, paletteItems[i])) { cancel = true; break; } } if (cancel) { // Remove organism dragging.node.destroy(); if (dragging.type === 'plant') plants.pop();else if (dragging.type === 'herbivore') herbivores.pop();else carnivores.pop(); } else { // Place organism: snap alpha to 1 dragging.node.alpha = 1; } dragging = null; } }; // --- Main Update Loop --- game.update = function () { // Update all herbivores for (var i = herbivores.length - 1; i >= 0; i--) { var h = herbivores[i]; if (h.eaten) { herbivores.splice(i, 1); continue; } if (h.update) h.update(); } // Update all carnivores for (var i = carnivores.length - 1; i >= 0; i--) { var c = carnivores[i]; if (c.eaten) { carnivores.splice(i, 1); continue; } if (c.update) c.update(); } // Clean up dead plants for (var i = plants.length - 1; i >= 0; i--) { var p = plants[i]; if (p.eaten) { plants.splice(i, 1); } } }; // --- Instructions Text --- var infoTxt = new Text2("Drag organisms from the palette to the map below.\nHerbivores eat plants. Carnivores eat herbivores.", { size: 60, fill: 0xFFFFFF }); infoTxt.anchor.set(0.5, 0); LK.gui.top.addChild(infoTxt); infoTxt.x = 2048 / 2; infoTxt.y = paletteY + 160; // --- Map Area Border (for visual feedback) --- var mapBorder = LK.getAsset('plant', { anchorX: 0, anchorY: 0, width: 1800, height: 2000, color: 0x2e8b57 }); mapBorder.alpha = 0.12; game.addChild(mapBorder); mapBorder.x = 124; mapBorder.y = paletteY + 320; // --- End of Game Code ---
===================================================================
--- original.js
+++ change.js
@@ -18,8 +18,11 @@
self.eaten = false;
self._chaseTime = 0; // How long we've been chasing a herbivore
self._tired = false; // Are we tired?
self._tiredTimer = 0; // How long left to rest
+ // --- State management for wandering/hungry ---
+ self.state = "wandering"; // "wandering" or "hungry"
+ self._eatCooldown = 0; // frames left in wandering after eating
self.update = function () {
if (self.eaten) return;
// Handle tired state
if (self._tired) {
@@ -30,34 +33,24 @@
}
// While tired, don't chase or wander, just stand still
return;
}
- // Find nearest herbivore if not already targeting
- if (!self.target || self.target.eaten) {
- var minDist = 999999,
- closest = null;
- for (var i = 0; i < herbivores.length; i++) {
- var h = herbivores[i];
- if (h.eaten) continue;
- var dx = h.x - self.x,
- dy = h.y - self.y;
- var dist = dx * dx + dy * dy;
- if (dist < minDist) {
- minDist = dist;
- closest = h;
- }
+ // Handle eat cooldown (wandering after eating)
+ if (self._eatCooldown > 0) {
+ self._eatCooldown--;
+ if (self._eatCooldown === 0) {
+ self.state = "hungry";
}
- self.target = closest;
}
- // --- Random wandering when not chasing herbivores ---
- if ((!self.target || self.target.eaten) && (!self._wanderTimer || self._wanderTimer <= 0)) {
- // Pick a new random direction every 60-120 frames
- var angle = Math.random() * Math.PI * 2;
- self._wanderAngle = angle;
- self._wanderTimer = 60 + Math.floor(Math.random() * 60);
- }
- if (!self.target || self.target.eaten) {
- // Wander in the chosen direction
+ // State: wandering (ignore prey)
+ if (self.state === "wandering") {
+ self.target = null; // Don't chase
+ // --- Random wandering when not chasing herbivores ---
+ if (!self._wanderTimer || self._wanderTimer <= 0) {
+ var angle = Math.random() * Math.PI * 2;
+ self._wanderAngle = angle;
+ self._wanderTimer = 60 + Math.floor(Math.random() * 60);
+ }
if (typeof self._wanderAngle === "number") {
self.x += Math.cos(self._wanderAngle) * self.speed * 0.5;
self.y += Math.sin(self._wanderAngle) * self.speed * 0.5;
self._wanderTimer--;
@@ -71,51 +64,97 @@
if (self.y < minY) self.y = minY;
if (self.y > maxY) self.y = maxY;
}
self._chaseTime = 0; // Not chasing, reset chase timer
+ return;
}
- // Move toward target herbivore
- if (self.target && !self.target.eaten) {
- var dx = self.target.x - self.x;
- var dy = self.target.y - self.y;
- var dist = Math.sqrt(dx * dx + dy * dy);
- if (dist > 1) {
- self.x += dx / dist * self.speed;
- self.y += dy / dist * self.speed;
+ // State: hungry (look for prey)
+ if (self.state === "hungry") {
+ // Find nearest herbivore if not already targeting
+ if (!self.target || self.target.eaten) {
+ var minDist = 999999,
+ closest = null;
+ for (var i = 0; i < herbivores.length; i++) {
+ var h = herbivores[i];
+ if (h.eaten) continue;
+ var dx = h.x - self.x,
+ dy = h.y - self.y;
+ var dist = dx * dx + dy * dy;
+ if (dist < minDist) {
+ minDist = dist;
+ closest = h;
+ }
+ }
+ self.target = closest;
}
- // Track how long we've been chasing
- self._chaseTime++;
- // If we've been chasing for more than 6 seconds (360 frames), get tired
- if (self._chaseTime > 360) {
- self._tired = true;
- self._tiredTimer = 600; // 10 seconds at 60fps
- self._chaseTime = 0;
- return;
+ // --- Random wandering when not chasing herbivores ---
+ if ((!self.target || self.target.eaten) && (!self._wanderTimer || self._wanderTimer <= 0)) {
+ var angle = Math.random() * Math.PI * 2;
+ self._wanderAngle = angle;
+ self._wanderTimer = 60 + Math.floor(Math.random() * 60);
}
- // Eat herbivore if close enough
- if (dist < 60 && !self.target.eaten) {
- self.target.die();
- self.target = null;
- self._chaseTime = 0; // Reset chase timer after eating
- // Animate carnivore "eating"
- tween(self, {
- scaleX: 1.2,
- scaleY: 1.2
- }, {
- duration: 120,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- tween(self, {
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 120,
- easing: tween.easeIn
- });
- }
- });
+ if (!self.target || self.target.eaten) {
+ if (typeof self._wanderAngle === "number") {
+ self.x += Math.cos(self._wanderAngle) * self.speed * 0.5;
+ self.y += Math.sin(self._wanderAngle) * self.speed * 0.5;
+ self._wanderTimer--;
+ var minX = 124 + 60;
+ var maxX = 124 + 1800 - 60;
+ var minY = paletteY + 320 + 60;
+ var maxY = paletteY + 320 + 2000 - 60;
+ if (self.x < minX) self.x = minX;
+ if (self.x > maxX) self.x = maxX;
+ if (self.y < minY) self.y = minY;
+ if (self.y > maxY) self.y = maxY;
+ }
+ self._chaseTime = 0; // Not chasing, reset chase timer
}
- }
+ // Move toward target herbivore
+ if (self.target && !self.target.eaten) {
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 1) {
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ }
+ // Track how long we've been chasing
+ self._chaseTime++;
+ // If we've been chasing for more than 6 seconds (360 frames), get tired
+ if (self._chaseTime > 360) {
+ self._tired = true;
+ self._tiredTimer = 600; // 10 seconds at 60fps
+ self._chaseTime = 0;
+ return;
+ }
+ // Eat herbivore if close enough
+ if (dist < 60 && !self.target.eaten) {
+ self.target.die();
+ self.target = null;
+ self._chaseTime = 0; // Reset chase timer after eating
+ // Animate carnivore "eating"
+ tween(self, {
+ scaleX: 1.2,
+ scaleY: 1.2
+ }, {
+ duration: 120,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ tween(self, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 120,
+ easing: tween.easeIn
+ });
+ }
+ });
+ // After eating, enter wandering state for 1 minute (3600 frames)
+ self.state = "wandering";
+ self._eatCooldown = 3600;
+ }
+ }
+ } // end hungry state
};
// Carnivores are not eaten in this MVP
return self;
});
tentacled red circle with a mouth and no eyes. In-Game asset. 2d. High contrast. No shadows. Very simple
A plant. In-Game asset. 2d. High contrast. No shadows. Very simple
Brown Seed. In-Game asset. 2d. High contrast. No shadows. Very simple
A small orange circle with spider legs 1 cute eye and no mouth. In-Game asset. 2d. High contrast. No shadows. Very very simple
Make a red egg with dark red spots. In-Game asset. 2d. High contrast. No shadows
Purple mushroom. In-Game asset. 2d. High contrast. No shadows. Very simple
A green shiny orb with a black circle. In-Game asset. 2d. High contrast. No shadows. Very simple
make a cyan lichen. In-Game asset. 2d. High contrast. No shadows. very simple
Just the color blue filling the entire space. In-Game asset. 2d. High contrast. No shadows
A brown circle with bug like antennas and bug legs with bug jaws and no eyes. In-Game asset. 2d. High contrast. No shadows. Very simple
A venus flytrap with one mouth and is looking up side view. In-Game asset. 2d. High contrast. No shadows VERY SIMPLE
Short brown worm. In-Game asset. 2d. High contrast. No shadows. Very simple
Pile of dirt. In-Game asset. 2d. High contrast. No shadows. Very simple
A yellow circle with 3 eyes no mouth and the eyes are in a triangle orientation. In-Game asset. 2d. High contrast. No shadows
Shrub. In-Game asset. 2d. High contrast. No shadows. Very simple