const imgOffset = -32;
const speed = 4;
const catMessage = document.getElementById("cat-message");
const wrapper = document.getElementById("cat-wrapper");
let mouse = {
x: 0,
y: 0
};
let sitActionTimeout;
let sleepActionTimeout;
let frameInterval;
let cat = {
img: document.getElementById("cat"),
frame: false,
backgroundX: 1,
backgroundY: 1,
setBackground: function() {
let x = cat.backgroundX;
let y = cat.backgroundY;
if (arguments.length) {
cat.backgroundX = x = arguments[0];
cat.backgroundY = y = arguments[1];
}
// Abusing JS math with bool types
cat.img.style.backgroundPositionX = px((x + cat.frame) * imgOffset);
cat.img.style.backgroundPositionY = px(y * imgOffset);
},
getOffset: function() {
return {
x: (cat.img.offsetLeft - imgOffset / 2) - mouse.x,
y: (cat.img.offsetTop - imgOffset) - mouse.y
};
},
run: function() {
let offset = cat.getOffset();
let distance = findDistance(offset.x, offset.y);
let differenceThreshold = distance * 0.5;
if (Math.abs(offset.x) - Math.abs(offset.y) > differenceThreshold) {
// horizontal
if (offset.x > 0) { // left
cat.setBackground(0, 3);
} else { // right
cat.setBackground(2, 3);
}
} else if (Math.abs(offset.y) - Math.abs(offset.x) > differenceThreshold) {
// vertical
if (offset.y > 0) { // up
cat.setBackground(0, 2);
} else { // down
cat.setBackground(2, 2);
}
} else if (offset.x > 0) {
// diagonal left
if (offset.y > 0) { // diagonal up
cat.setBackground(0, 4);
} else { // diagonal down
cat.setBackground(0, 5);
}
} else {
// diagonal right
if (offset.y > 0) { // diagonal up
cat.setBackground(2, 4);
} else { // diagonal down
cat.setBackground(2, 5);
}
}
},
alert: function() {
stopFrameInterval();
cat.setBackground(3, 1);
},
isAlert: function() {
return cat.backgroundX === 3 && cat.backgroundY === 1;
},
sit: function() {
stopFrameInterval();
cat.setBackground(0, 0);
sitActionTimeout = setTimeout(function() {
if (Math.random() > 0.5) {
cat.lick();
} else {
cat.scratch();
}
}, Math.floor(Math.random() * 10) * 500 + 1500);
if (!sleepActionTimeout) {
sleepActionTimeout = setTimeout(function() {
clearTimeout(sitActionTimeout);
cat.sleep();
}, 20000);
}
},
lick: function() {
cat.setBackground(0, 0);
startFrameInterval(500);
sitActionTimeout = setTimeout(function() {
stopFrameInterval();
cat.sit();
}, 2000);
},
scratch: function() {
cat.setBackground(2, 0);
startFrameInterval(500);
sitActionTimeout = setTimeout(function() {
stopFrameInterval();
cat.sit();
}, 4000);
},
sleep: function() {
stopFrameInterval();
cat.setBackground(0, 1);
sleepActionTimeout = setTimeout(function() {
sleepActionTimeout = undefined;
cat.setBackground(1, 1);
startFrameInterval(1000);
}, 1000);
},
isSleep: function() {
return (cat.backgroundX === 1 || cat.backgroundX === 2) && cat.backgroundY === 1;
}
};
function startFrameInterval(interval) {
if (frameInterval) {
clearInterval(frameInterval);
}
frameInterval = setInterval(function() {
cat.frame = !cat.frame;
cat.setBackground();
}, interval);
}
function stopFrameInterval() {
cat.frame = false;
clearInterval(frameInterval);
frameInterval = undefined;
}
// Cat initializes in sleep mode, so just start the animation
startFrameInterval(1000);
window.addEventListener("mousemove", function(event) {
mouse.x = event.x - wrapper.offsetLeft;
mouse.y = event.y - wrapper.offsetTop;
});
function startMoveCatEvent() {
cat.img.removeEventListener("click", startMoveCatEvent);
cat.img.style.cursor = "default";
catMessage.style.opacity = 0;
// catMessage.innerHTML = "Click here to<br>put me back.";
// catMessage.style.cursor = "pointer";
// catMessage.addEventListener("click", messageClickEvent);
window.addEventListener("mousemove", mouseMoveEvent);
startMoveCat();
}
function messageClickEvent() {
window.removeEventListener("mousemove", mouseMoveEvent);
stopCat = true;
cat.img.style.left = "42px";
cat.img.style.top = "37px";
clearTimeout(sitActionTimeout);
clearTimeout(sleepActionTimeout);
sleepActionTimeout = undefined;
cat.sleep();
cat.img.style.cursor = "pointer";
catMessage.innerHTML = "Hey there, click<br>me to wake me up!";
catMessage.style.cursor = "default";
catMessage.removeEventListener("click", messageClickEvent);
cat.img.addEventListener("click", startMoveCatEvent);
}
function mouseMoveEvent() {
if (!cat.isAlert()) {
cat.run();
}
if (stopCat) {
stopCat = false;
startMoveCat();
}
}
cat.img.addEventListener("click", startMoveCatEvent);
function startMoveCat() {
nextFrameTime = Date.now();
clearTimeout(sitActionTimeout);
clearTimeout(sleepActionTimeout);
sleepActionTimeout = undefined;
let wasSleep = cat.isSleep();
if (wasSleep) {
cat.alert();
}
setTimeout(function() {
cat.run();
startFrameInterval(250);
moveCat();
}, wasSleep ? 1000 : 100);
}
const fpsInterval = 1000 / 30;
let stopCat = false;
let currFrameTime;
let nextFrameTime;
function moveCat() {
if (stopCat) {
return;
}
requestAnimationFrame(moveCat);
currFrameTime = Date.now();
let elapsedTime = currFrameTime - nextFrameTime;
if (elapsedTime > fpsInterval) {
nextFrameTime = currFrameTime - (elapsedTime % fpsInterval);
let offset = cat.getOffset();
let distance = findDistance(offset.x, offset.y);
if (distance > 0) {
let sumOffset = Math.abs(offset.x) + Math.abs(offset.y);
let delta = -Math.min(speed, distance);
let xRatio = offset.x / sumOffset;
let yRatio = offset.y / sumOffset;
// Cat runs slower diagonally bc pixel math is bullshit
let diagonalScale = Math.max(1.5 / (Math.abs(Math.abs(xRatio) - Math.abs(yRatio)) + 1), 1);
cat.img.style.left = px(cat.img.offsetLeft + (diagonalScale * delta * xRatio));
cat.img.style.top = px(cat.img.offsetTop + (diagonalScale * delta * yRatio));
} else {
stopCat = true;
cat.sit();
}
}
}
function findDistance(dx, dy) {
return Math.sqrt(dx * dx + dy * dy);
}
function px(val) {
return `${val}px`;
}