/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Animal: tries to escape from inside Marvin's belly
var Animal = Container.expand(function () {
	var self = Container.call(this);
	// Pick a random color for each animal
	var colors = [0xffa726, 0x29b6f6, 0xab47bc, 0xff7043, 0x66bb6a, 0xffeb3b];
	var color = colors[Math.floor(Math.random() * colors.length)];
	var animal = self.attachAsset('animal', {
		anchorX: 0.5,
		anchorY: 0.5,
		color: color
	});
	// Escape direction (angle in radians)
	self.angle = Math.random() * Math.PI * 2;
	// Escape speed
	self.speed = 6 + Math.random() * 2;
	// Wobble for fun
	self.wobble = Math.random() * Math.PI * 2;
	// Used for escape state
	self.isEscaping = false;
	self.escapeTimer = 0;
	// Used for stress
	self.lastEscaping = false;
	// Used for tracking if outside
	self.hasEscaped = false;
	// Used for animal "push" animation
	self.pushStrength = 0;
	return self;
});
// SnakeBodySegment: a draggable segment of Marvin's body
var SnakeBodySegment = Container.expand(function () {
	var self = Container.call(this);
	// Attach body asset
	var body = self.attachAsset('snakeBody', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Used for dragging
	self.isDragging = false;
	// Used for physics
	self.vx = 0;
	self.vy = 0;
	// Used for linking to previous/next segments
	self.prev = null;
	self.next = null;
	// Used for stress effect
	self.stressPulse = 0;
	// Used for animal collision
	self.segmentIndex = 0;
	// Drag event handlers
	self.down = function (x, y, obj) {
		self.isDragging = true;
	};
	self.up = function (x, y, obj) {
		self.isDragging = false;
	};
	return self;
});
// SnakeHead: Marvin's head, draggable, slightly larger
var SnakeHead = Container.expand(function () {
	var self = Container.call(this);
	var head = self.attachAsset('snakeHead', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Used for dragging
	self.isDragging = false;
	// Used for physics
	self.vx = 0;
	self.vy = 0;
	// Used for linking to next segment
	self.next = null;
	// Drag event handlers
	self.down = function (x, y, obj) {
		self.isDragging = true;
	};
	self.up = function (x, y, obj) {
		self.isDragging = false;
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x222b1a
});
/**** 
* Game Code
****/ 
// Stress bar fill
// Stress bar background
// Animal (ellipse, random color, will be set per instance)
// Snake head (ellipse, slightly larger, yellow-green)
// Snake body segment (ellipse, green)
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var BELLY_RADIUS = 420; // Radius of Marvin's belly (where animals are contained)
var SNAKE_SEGMENTS = 7; // Number of body segments (not counting head)
var SNAKE_LENGTH = 900; // Total length of snake body (for initial placement)
var ANIMAL_COUNT = 3; // Number of animals inside belly at once
var ANIMAL_PUSH_INTERVAL = 120; // Ticks between animal escape attempts
var ANIMAL_PUSH_DURATION = 60; // How long an animal pushes per attempt
var ANIMAL_PUSH_STRENGTH = 60; // How far animals try to push per attempt
var STRESS_PER_PUSH = 0.12; // How much stress increases per animal push
var STRESS_DECAY = 0.002; // How much stress decays per tick
var STRESS_MAX = 1.0; // Max stress
var STRESS_BAR_WIDTH = 600;
var STRESS_BAR_HEIGHT = 60;
// Game state
var snakeHead;
var snakeSegments = [];
var animals = [];
var stress = 0;
var stressBarBg, stressBarFill, stressBarText;
var score = 0;
var scoreTxt;
var dragNode = null;
var lastMoveX = 0,
	lastMoveY = 0;
// Center of Marvin's belly
var bellyCenterX = GAME_WIDTH / 2;
var bellyCenterY = GAME_HEIGHT / 2 + 200;
// --- Setup Snake ---
// Create snake head
snakeHead = new SnakeHead();
snakeHead.x = bellyCenterX + SNAKE_LENGTH / 2;
snakeHead.y = bellyCenterY;
game.addChild(snakeHead);
// Create snake body segments, arranged in a gentle S-curve
snakeSegments = [];
for (var i = 0; i < SNAKE_SEGMENTS; i++) {
	var seg = new SnakeBodySegment();
	var t = i / (SNAKE_SEGMENTS - 1);
	// S-curve: x = center + cos(t*PI) * amplitude, y = center + sin(t*PI) * amplitude
	var angle = Math.PI * (t - 0.5);
	var amplitude = SNAKE_LENGTH / 2.2;
	seg.x = bellyCenterX + Math.cos(angle) * amplitude;
	seg.y = bellyCenterY + Math.sin(angle) * amplitude * 0.7;
	seg.segmentIndex = i;
	game.addChild(seg);
	snakeSegments.push(seg);
	// Link segments
	if (i > 0) {
		seg.prev = snakeSegments[i - 1];
		snakeSegments[i - 1].next = seg;
	}
}
snakeHead.next = snakeSegments[0];
// --- Setup Animals ---
animals = [];
for (var i = 0; i < ANIMAL_COUNT; i++) {
	var animal = new Animal();
	// Place randomly inside belly
	var angle = Math.random() * Math.PI * 2;
	var r = Math.random() * (BELLY_RADIUS - 80);
	animal.x = bellyCenterX + Math.cos(angle) * r;
	animal.y = bellyCenterY + Math.sin(angle) * r;
	game.addChild(animal);
	animals.push(animal);
}
// --- Setup Stress Bar ---
stressBarBg = LK.getAsset('stressBarBg', {
	anchorX: 0,
	anchorY: 0.5,
	x: (GAME_WIDTH - STRESS_BAR_WIDTH) / 2,
	y: 120
});
LK.gui.top.addChild(stressBarBg);
stressBarFill = LK.getAsset('stressBarFill', {
	anchorX: 0,
	anchorY: 0.5,
	x: (GAME_WIDTH - STRESS_BAR_WIDTH) / 2,
	y: 120
});
LK.gui.top.addChild(stressBarFill);
stressBarText = new Text2('Stress', {
	size: 48,
	fill: "#fff"
});
stressBarText.anchor.set(0.5, 0.5);
stressBarText.x = GAME_WIDTH / 2;
stressBarText.y = 120;
LK.gui.top.addChild(stressBarText);
// --- Setup Score ---
scoreTxt = new Text2('0', {
	size: 120,
	fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.x = GAME_WIDTH / 2;
scoreTxt.y = 200;
// --- Dragging Logic ---
game.down = function (x, y, obj) {
	// Check if touching head or any segment
	var found = null;
	// Manual ellipse hit test for snakeHead
	var headAsset = snakeHead.children[0];
	var hx = snakeHead.x;
	var hy = snakeHead.y;
	var hw = headAsset.width;
	var hh = headAsset.height;
	var dx = x - hx;
	var dy = y - hy;
	if (dx * dx / (hw / 2 * (hw / 2)) + dy * dy / (hh / 2 * (hh / 2)) <= 1) {
		found = snakeHead;
	} else {
		for (var i = 0; i < snakeSegments.length; i++) {
			var seg = snakeSegments[i];
			var segAsset = seg.children[0];
			var sx = seg.x;
			var sy = seg.y;
			var sw = segAsset.width;
			var sh = segAsset.height;
			var sdx = x - sx;
			var sdy = y - sy;
			if (sdx * sdx / (sw / 2 * (sw / 2)) + sdy * sdy / (sh / 2 * (sh / 2)) <= 1) {
				found = seg;
				break;
			}
		}
	}
	if (found) {
		dragNode = found;
		dragNode.isDragging = true;
		lastMoveX = x;
		lastMoveY = y;
	}
};
game.up = function (x, y, obj) {
	if (dragNode) {
		dragNode.isDragging = false;
		dragNode = null;
	}
};
function clamp(val, min, max) {
	return Math.max(min, Math.min(max, val));
}
// --- Move Handler ---
game.move = function (x, y, obj) {
	if (dragNode && dragNode.isDragging) {
		// Move the dragged node to the new position, but clamp inside game area
		dragNode.x = clamp(x, 120, GAME_WIDTH - 120);
		dragNode.y = clamp(y, 220, GAME_HEIGHT - 120);
		lastMoveX = x;
		lastMoveY = y;
	}
};
// --- Game Update ---
game.update = function () {
	// --- Snake Body Physics: segments follow each other like a rope ---
	// Head is controlled by drag, but if not dragging, it relaxes toward center
	if (!snakeHead.isDragging) {
		// Gently return to default position
		var dx = bellyCenterX + SNAKE_LENGTH / 2 - snakeHead.x;
		var dy = bellyCenterY - snakeHead.y;
		snakeHead.x += dx * 0.01;
		snakeHead.y += dy * 0.01;
	}
	// Each segment follows the previous one, with a fixed distance
	var prevX = snakeHead.x;
	var prevY = snakeHead.y;
	var segmentDist = SNAKE_LENGTH / (SNAKE_SEGMENTS + 1);
	for (var i = 0; i < snakeSegments.length; i++) {
		var seg = snakeSegments[i];
		var dx = prevX - seg.x;
		var dy = prevY - seg.y;
		var dist = Math.sqrt(dx * dx + dy * dy);
		if (dist > 1) {
			var pull = (dist - segmentDist) * 0.22;
			seg.x += dx / dist * pull;
			seg.y += dy / dist * pull;
		}
		// Small spring to belly center if not being dragged
		if (!seg.isDragging) {
			var cx = bellyCenterX + Math.cos(Math.PI * (i / (SNAKE_SEGMENTS - 1) - 0.5)) * BELLY_RADIUS * 0.7;
			var cy = bellyCenterY + Math.sin(Math.PI * (i / (SNAKE_SEGMENTS - 1) - 0.5)) * BELLY_RADIUS * 0.3;
			seg.x += (cx - seg.x) * 0.003;
			seg.y += (cy - seg.y) * 0.003;
		}
		prevX = seg.x;
		prevY = seg.y;
	}
	// --- Animals try to escape ---
	for (var i = 0; i < animals.length; i++) {
		var animal = animals[i];
		// If not escaping, randomly decide to push
		if (!animal.isEscaping && LK.ticks % ANIMAL_PUSH_INTERVAL === i * 23 % ANIMAL_PUSH_INTERVAL) {
			animal.isEscaping = true;
			animal.escapeTimer = ANIMAL_PUSH_DURATION;
			animal.angle = Math.random() * Math.PI * 2;
			animal.pushStrength = ANIMAL_PUSH_STRENGTH + Math.random() * 20;
		}
		// If escaping, move outward
		if (animal.isEscaping) {
			animal.escapeTimer--;
			// Wobble for fun
			animal.wobble += 0.2;
			var wobbleR = Math.sin(animal.wobble) * 8;
			var dx = Math.cos(animal.angle) * (animal.pushStrength + wobbleR);
			var dy = Math.sin(animal.angle) * (animal.pushStrength + wobbleR);
			animal.x += dx / ANIMAL_PUSH_DURATION;
			animal.y += dy / ANIMAL_PUSH_DURATION;
			// Increase stress
			stress += STRESS_PER_PUSH / 60;
			if (stress > STRESS_MAX) stress = STRESS_MAX;
			// If timer done, stop escaping
			if (animal.escapeTimer <= 0) {
				animal.isEscaping = false;
			}
		} else {
			// Gently drift back toward belly center
			var dx = bellyCenterX - animal.x;
			var dy = bellyCenterY - animal.y;
			animal.x += dx * 0.01;
			animal.y += dy * 0.01;
		}
		// --- Check if animal is outside belly (escaped) ---
		var distToCenter = Math.sqrt((animal.x - bellyCenterX) * (animal.x - bellyCenterX) + (animal.y - bellyCenterY) * (animal.y - bellyCenterY));
		if (!animal.hasEscaped && distToCenter > BELLY_RADIUS) {
			// Check if any snake segment is close enough to block
			var blocked = false;
			for (var j = 0; j < snakeSegments.length; j++) {
				var seg = snakeSegments[j];
				var dseg = Math.sqrt((animal.x - seg.x) * (animal.x - seg.x) + (animal.y - seg.y) * (animal.y - seg.y));
				if (dseg < 120) {
					blocked = true;
					// Bounce animal back in
					var angle = Math.atan2(animal.y - seg.y, animal.x - seg.x);
					animal.x = seg.x + Math.cos(angle) * 120;
					animal.y = seg.y + Math.sin(angle) * 120;
					// Add stress for close call
					stress += 0.04;
					if (stress > STRESS_MAX) stress = STRESS_MAX;
					// Fun pulse effect
					tween(seg, {
						scaleX: 1.2,
						scaleY: 1.2
					}, {
						duration: 80,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							tween(seg, {
								scaleX: 1,
								scaleY: 1
							}, {
								duration: 120,
								easing: tween.easeIn
							});
						}
					});
					break;
				}
			}
			// Also check head
			var dhead = Math.sqrt((animal.x - snakeHead.x) * (animal.x - snakeHead.x) + (animal.y - snakeHead.y) * (animal.y - snakeHead.y));
			if (!blocked && dhead < 130) {
				blocked = true;
				var angle = Math.atan2(animal.y - snakeHead.y, animal.x - snakeHead.x);
				animal.x = snakeHead.x + Math.cos(angle) * 130;
				animal.y = snakeHead.y + Math.sin(angle) * 130;
				stress += 0.05;
				if (stress > STRESS_MAX) stress = STRESS_MAX;
				tween(snakeHead, {
					scaleX: 1.18,
					scaleY: 1.18
				}, {
					duration: 80,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(snakeHead, {
							scaleX: 1,
							scaleY: 1
						}, {
							duration: 120,
							easing: tween.easeIn
						});
					}
				});
			}
			if (!blocked) {
				// Animal escapes!
				animal.hasEscaped = true;
				// Flash screen, show game over
				LK.effects.flashScreen(0xff0000, 900);
				LK.showGameOver();
				return;
			}
		}
	}
	// --- Stress decay ---
	stress -= STRESS_DECAY;
	if (stress < 0) stress = 0;
	// --- Stress bar update ---
	var fillWidth = Math.floor(STRESS_BAR_WIDTH * clamp(stress, 0, 1));
	stressBarFill.width = fillWidth;
	// --- Score: survive time, +1 per 2 seconds ---
	if (LK.ticks % 120 === 0) {
		score++;
		scoreTxt.setText(score);
	}
	// --- Win condition: survive 60 seconds ---
	if (score >= 30) {
		LK.showYouWin();
		return;
	}
};
// --- End of Game Code --- /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Animal: tries to escape from inside Marvin's belly
var Animal = Container.expand(function () {
	var self = Container.call(this);
	// Pick a random color for each animal
	var colors = [0xffa726, 0x29b6f6, 0xab47bc, 0xff7043, 0x66bb6a, 0xffeb3b];
	var color = colors[Math.floor(Math.random() * colors.length)];
	var animal = self.attachAsset('animal', {
		anchorX: 0.5,
		anchorY: 0.5,
		color: color
	});
	// Escape direction (angle in radians)
	self.angle = Math.random() * Math.PI * 2;
	// Escape speed
	self.speed = 6 + Math.random() * 2;
	// Wobble for fun
	self.wobble = Math.random() * Math.PI * 2;
	// Used for escape state
	self.isEscaping = false;
	self.escapeTimer = 0;
	// Used for stress
	self.lastEscaping = false;
	// Used for tracking if outside
	self.hasEscaped = false;
	// Used for animal "push" animation
	self.pushStrength = 0;
	return self;
});
// SnakeBodySegment: a draggable segment of Marvin's body
var SnakeBodySegment = Container.expand(function () {
	var self = Container.call(this);
	// Attach body asset
	var body = self.attachAsset('snakeBody', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Used for dragging
	self.isDragging = false;
	// Used for physics
	self.vx = 0;
	self.vy = 0;
	// Used for linking to previous/next segments
	self.prev = null;
	self.next = null;
	// Used for stress effect
	self.stressPulse = 0;
	// Used for animal collision
	self.segmentIndex = 0;
	// Drag event handlers
	self.down = function (x, y, obj) {
		self.isDragging = true;
	};
	self.up = function (x, y, obj) {
		self.isDragging = false;
	};
	return self;
});
// SnakeHead: Marvin's head, draggable, slightly larger
var SnakeHead = Container.expand(function () {
	var self = Container.call(this);
	var head = self.attachAsset('snakeHead', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Used for dragging
	self.isDragging = false;
	// Used for physics
	self.vx = 0;
	self.vy = 0;
	// Used for linking to next segment
	self.next = null;
	// Drag event handlers
	self.down = function (x, y, obj) {
		self.isDragging = true;
	};
	self.up = function (x, y, obj) {
		self.isDragging = false;
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x222b1a
});
/**** 
* Game Code
****/ 
// Stress bar fill
// Stress bar background
// Animal (ellipse, random color, will be set per instance)
// Snake head (ellipse, slightly larger, yellow-green)
// Snake body segment (ellipse, green)
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var BELLY_RADIUS = 420; // Radius of Marvin's belly (where animals are contained)
var SNAKE_SEGMENTS = 7; // Number of body segments (not counting head)
var SNAKE_LENGTH = 900; // Total length of snake body (for initial placement)
var ANIMAL_COUNT = 3; // Number of animals inside belly at once
var ANIMAL_PUSH_INTERVAL = 120; // Ticks between animal escape attempts
var ANIMAL_PUSH_DURATION = 60; // How long an animal pushes per attempt
var ANIMAL_PUSH_STRENGTH = 60; // How far animals try to push per attempt
var STRESS_PER_PUSH = 0.12; // How much stress increases per animal push
var STRESS_DECAY = 0.002; // How much stress decays per tick
var STRESS_MAX = 1.0; // Max stress
var STRESS_BAR_WIDTH = 600;
var STRESS_BAR_HEIGHT = 60;
// Game state
var snakeHead;
var snakeSegments = [];
var animals = [];
var stress = 0;
var stressBarBg, stressBarFill, stressBarText;
var score = 0;
var scoreTxt;
var dragNode = null;
var lastMoveX = 0,
	lastMoveY = 0;
// Center of Marvin's belly
var bellyCenterX = GAME_WIDTH / 2;
var bellyCenterY = GAME_HEIGHT / 2 + 200;
// --- Setup Snake ---
// Create snake head
snakeHead = new SnakeHead();
snakeHead.x = bellyCenterX + SNAKE_LENGTH / 2;
snakeHead.y = bellyCenterY;
game.addChild(snakeHead);
// Create snake body segments, arranged in a gentle S-curve
snakeSegments = [];
for (var i = 0; i < SNAKE_SEGMENTS; i++) {
	var seg = new SnakeBodySegment();
	var t = i / (SNAKE_SEGMENTS - 1);
	// S-curve: x = center + cos(t*PI) * amplitude, y = center + sin(t*PI) * amplitude
	var angle = Math.PI * (t - 0.5);
	var amplitude = SNAKE_LENGTH / 2.2;
	seg.x = bellyCenterX + Math.cos(angle) * amplitude;
	seg.y = bellyCenterY + Math.sin(angle) * amplitude * 0.7;
	seg.segmentIndex = i;
	game.addChild(seg);
	snakeSegments.push(seg);
	// Link segments
	if (i > 0) {
		seg.prev = snakeSegments[i - 1];
		snakeSegments[i - 1].next = seg;
	}
}
snakeHead.next = snakeSegments[0];
// --- Setup Animals ---
animals = [];
for (var i = 0; i < ANIMAL_COUNT; i++) {
	var animal = new Animal();
	// Place randomly inside belly
	var angle = Math.random() * Math.PI * 2;
	var r = Math.random() * (BELLY_RADIUS - 80);
	animal.x = bellyCenterX + Math.cos(angle) * r;
	animal.y = bellyCenterY + Math.sin(angle) * r;
	game.addChild(animal);
	animals.push(animal);
}
// --- Setup Stress Bar ---
stressBarBg = LK.getAsset('stressBarBg', {
	anchorX: 0,
	anchorY: 0.5,
	x: (GAME_WIDTH - STRESS_BAR_WIDTH) / 2,
	y: 120
});
LK.gui.top.addChild(stressBarBg);
stressBarFill = LK.getAsset('stressBarFill', {
	anchorX: 0,
	anchorY: 0.5,
	x: (GAME_WIDTH - STRESS_BAR_WIDTH) / 2,
	y: 120
});
LK.gui.top.addChild(stressBarFill);
stressBarText = new Text2('Stress', {
	size: 48,
	fill: "#fff"
});
stressBarText.anchor.set(0.5, 0.5);
stressBarText.x = GAME_WIDTH / 2;
stressBarText.y = 120;
LK.gui.top.addChild(stressBarText);
// --- Setup Score ---
scoreTxt = new Text2('0', {
	size: 120,
	fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.x = GAME_WIDTH / 2;
scoreTxt.y = 200;
// --- Dragging Logic ---
game.down = function (x, y, obj) {
	// Check if touching head or any segment
	var found = null;
	// Manual ellipse hit test for snakeHead
	var headAsset = snakeHead.children[0];
	var hx = snakeHead.x;
	var hy = snakeHead.y;
	var hw = headAsset.width;
	var hh = headAsset.height;
	var dx = x - hx;
	var dy = y - hy;
	if (dx * dx / (hw / 2 * (hw / 2)) + dy * dy / (hh / 2 * (hh / 2)) <= 1) {
		found = snakeHead;
	} else {
		for (var i = 0; i < snakeSegments.length; i++) {
			var seg = snakeSegments[i];
			var segAsset = seg.children[0];
			var sx = seg.x;
			var sy = seg.y;
			var sw = segAsset.width;
			var sh = segAsset.height;
			var sdx = x - sx;
			var sdy = y - sy;
			if (sdx * sdx / (sw / 2 * (sw / 2)) + sdy * sdy / (sh / 2 * (sh / 2)) <= 1) {
				found = seg;
				break;
			}
		}
	}
	if (found) {
		dragNode = found;
		dragNode.isDragging = true;
		lastMoveX = x;
		lastMoveY = y;
	}
};
game.up = function (x, y, obj) {
	if (dragNode) {
		dragNode.isDragging = false;
		dragNode = null;
	}
};
function clamp(val, min, max) {
	return Math.max(min, Math.min(max, val));
}
// --- Move Handler ---
game.move = function (x, y, obj) {
	if (dragNode && dragNode.isDragging) {
		// Move the dragged node to the new position, but clamp inside game area
		dragNode.x = clamp(x, 120, GAME_WIDTH - 120);
		dragNode.y = clamp(y, 220, GAME_HEIGHT - 120);
		lastMoveX = x;
		lastMoveY = y;
	}
};
// --- Game Update ---
game.update = function () {
	// --- Snake Body Physics: segments follow each other like a rope ---
	// Head is controlled by drag, but if not dragging, it relaxes toward center
	if (!snakeHead.isDragging) {
		// Gently return to default position
		var dx = bellyCenterX + SNAKE_LENGTH / 2 - snakeHead.x;
		var dy = bellyCenterY - snakeHead.y;
		snakeHead.x += dx * 0.01;
		snakeHead.y += dy * 0.01;
	}
	// Each segment follows the previous one, with a fixed distance
	var prevX = snakeHead.x;
	var prevY = snakeHead.y;
	var segmentDist = SNAKE_LENGTH / (SNAKE_SEGMENTS + 1);
	for (var i = 0; i < snakeSegments.length; i++) {
		var seg = snakeSegments[i];
		var dx = prevX - seg.x;
		var dy = prevY - seg.y;
		var dist = Math.sqrt(dx * dx + dy * dy);
		if (dist > 1) {
			var pull = (dist - segmentDist) * 0.22;
			seg.x += dx / dist * pull;
			seg.y += dy / dist * pull;
		}
		// Small spring to belly center if not being dragged
		if (!seg.isDragging) {
			var cx = bellyCenterX + Math.cos(Math.PI * (i / (SNAKE_SEGMENTS - 1) - 0.5)) * BELLY_RADIUS * 0.7;
			var cy = bellyCenterY + Math.sin(Math.PI * (i / (SNAKE_SEGMENTS - 1) - 0.5)) * BELLY_RADIUS * 0.3;
			seg.x += (cx - seg.x) * 0.003;
			seg.y += (cy - seg.y) * 0.003;
		}
		prevX = seg.x;
		prevY = seg.y;
	}
	// --- Animals try to escape ---
	for (var i = 0; i < animals.length; i++) {
		var animal = animals[i];
		// If not escaping, randomly decide to push
		if (!animal.isEscaping && LK.ticks % ANIMAL_PUSH_INTERVAL === i * 23 % ANIMAL_PUSH_INTERVAL) {
			animal.isEscaping = true;
			animal.escapeTimer = ANIMAL_PUSH_DURATION;
			animal.angle = Math.random() * Math.PI * 2;
			animal.pushStrength = ANIMAL_PUSH_STRENGTH + Math.random() * 20;
		}
		// If escaping, move outward
		if (animal.isEscaping) {
			animal.escapeTimer--;
			// Wobble for fun
			animal.wobble += 0.2;
			var wobbleR = Math.sin(animal.wobble) * 8;
			var dx = Math.cos(animal.angle) * (animal.pushStrength + wobbleR);
			var dy = Math.sin(animal.angle) * (animal.pushStrength + wobbleR);
			animal.x += dx / ANIMAL_PUSH_DURATION;
			animal.y += dy / ANIMAL_PUSH_DURATION;
			// Increase stress
			stress += STRESS_PER_PUSH / 60;
			if (stress > STRESS_MAX) stress = STRESS_MAX;
			// If timer done, stop escaping
			if (animal.escapeTimer <= 0) {
				animal.isEscaping = false;
			}
		} else {
			// Gently drift back toward belly center
			var dx = bellyCenterX - animal.x;
			var dy = bellyCenterY - animal.y;
			animal.x += dx * 0.01;
			animal.y += dy * 0.01;
		}
		// --- Check if animal is outside belly (escaped) ---
		var distToCenter = Math.sqrt((animal.x - bellyCenterX) * (animal.x - bellyCenterX) + (animal.y - bellyCenterY) * (animal.y - bellyCenterY));
		if (!animal.hasEscaped && distToCenter > BELLY_RADIUS) {
			// Check if any snake segment is close enough to block
			var blocked = false;
			for (var j = 0; j < snakeSegments.length; j++) {
				var seg = snakeSegments[j];
				var dseg = Math.sqrt((animal.x - seg.x) * (animal.x - seg.x) + (animal.y - seg.y) * (animal.y - seg.y));
				if (dseg < 120) {
					blocked = true;
					// Bounce animal back in
					var angle = Math.atan2(animal.y - seg.y, animal.x - seg.x);
					animal.x = seg.x + Math.cos(angle) * 120;
					animal.y = seg.y + Math.sin(angle) * 120;
					// Add stress for close call
					stress += 0.04;
					if (stress > STRESS_MAX) stress = STRESS_MAX;
					// Fun pulse effect
					tween(seg, {
						scaleX: 1.2,
						scaleY: 1.2
					}, {
						duration: 80,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							tween(seg, {
								scaleX: 1,
								scaleY: 1
							}, {
								duration: 120,
								easing: tween.easeIn
							});
						}
					});
					break;
				}
			}
			// Also check head
			var dhead = Math.sqrt((animal.x - snakeHead.x) * (animal.x - snakeHead.x) + (animal.y - snakeHead.y) * (animal.y - snakeHead.y));
			if (!blocked && dhead < 130) {
				blocked = true;
				var angle = Math.atan2(animal.y - snakeHead.y, animal.x - snakeHead.x);
				animal.x = snakeHead.x + Math.cos(angle) * 130;
				animal.y = snakeHead.y + Math.sin(angle) * 130;
				stress += 0.05;
				if (stress > STRESS_MAX) stress = STRESS_MAX;
				tween(snakeHead, {
					scaleX: 1.18,
					scaleY: 1.18
				}, {
					duration: 80,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(snakeHead, {
							scaleX: 1,
							scaleY: 1
						}, {
							duration: 120,
							easing: tween.easeIn
						});
					}
				});
			}
			if (!blocked) {
				// Animal escapes!
				animal.hasEscaped = true;
				// Flash screen, show game over
				LK.effects.flashScreen(0xff0000, 900);
				LK.showGameOver();
				return;
			}
		}
	}
	// --- Stress decay ---
	stress -= STRESS_DECAY;
	if (stress < 0) stress = 0;
	// --- Stress bar update ---
	var fillWidth = Math.floor(STRESS_BAR_WIDTH * clamp(stress, 0, 1));
	stressBarFill.width = fillWidth;
	// --- Score: survive time, +1 per 2 seconds ---
	if (LK.ticks % 120 === 0) {
		score++;
		scoreTxt.setText(score);
	}
	// --- Win condition: survive 60 seconds ---
	if (score >= 30) {
		LK.showYouWin();
		return;
	}
};
// --- End of Game Code ---