1.1.0 — weather, treats, hunger, drift

This commit is contained in:
2026-08-18 13:45:25 -04:00
parent c0a634cab5
commit 820b30468d
7 changed files with 296 additions and 25 deletions

View File

@@ -1,5 +1,20 @@
# Changelog # Changelog
## 1.1.0
He gets a life.
- **Weather**: `denWeather('rain'|'snow'|'clear')`. Rain sends him to shelter under
the nearest POI, where he sulks until it passes. Snow delights him and makes his
prints linger 3x longer.
- **Treats**: `denTreat(x, y)` or the `treat` shell command. He sprints for it, eats
it, and it counts toward trust. Hunger builds over a session and he'll say so.
- **Scroll reaction**: he's carried by the page when you scroll, scrambles back to
your cursor, and arrives winded.
- **Personality drift**: idle mutterings change based on hunger, and on whether a
returning visitor has ever actually touched him.
- Trust is now pets + treats throughout (gift threshold, resting behaviour, emotes).
## 1.0.0 ## 1.0.0
First release. He exists. First release. He exists.

View File

@@ -35,13 +35,28 @@ committing a crime. Emote bubbles float off him — `❗` when he notices you,
canid walks before lying down, and a bed fades in with a breathing lump under canid walks before lying down, and a bed fades in with a breathing lump under
the blanket. Any keypress, click, scroll, or mouse twitch wakes him — groggily. the blanket. Any keypress, click, scroll, or mouse twitch wakes him — groggily.
**Memory.** Visit count, lifetime pets, and his name persist in `localStorage`. **Memory.** Visit count, lifetime pets, treats, and his name persist in
Return after a week and he says so. Pet him ten times and he decides you're `localStorage`. Return after a week and he says so. Reach ten points of trust
friends; at twenty-five he starts lingering near your cursor instead of (pets + treats) and he decides you're friends; at twenty-five he lingers near
wandering off, and his stealing turns into gift-giving. your cursor instead of wandering off, and his stealing turns into gift-giving.
**Drift.** What he mutters while wandering depends on how you've treated him. Feed
him and he's content. Visit repeatedly without ever touching him and he starts
asking whether anyone's there.
**A shell.** A `>_ den` tab sits in the corner of every page. Open it and you can **A shell.** A `>_ den` tab sits in the corner of every page. Open it and you can
`come`, `sit`, `pet`, `fetch`, `sleep`, `where`, `status`, or `name` him. `come`, `sit`, `pet`, `treat`, `fetch`, `rain`, `snow`, `sleep`, `where`,
`status`, or `name` him.
**Weather.** `denWeather('rain')` and he bolts for the nearest POI to wait it out,
sulking under cover until it stops. `denWeather('snow')` and he loses his mind —
a burst of speed, and his prints linger three times as long in it.
**Treats.** Drop one and he sprints for it. Treats build trust the same way pets
do, and he gets visibly hungry if he hasn't been fed in a while.
**Scrolling.** He's standing on the page, not floating above it — scroll and he
gets carried along, has to scramble back, and arrives out of breath.
**Presence.** Optional. With `den.php` on the server he'll notice how many other **Presence.** Optional. With `den.php` on the server he'll notice how many other
people are on the site and mention that he can smell them. people are on the site and mention that he can smell them.
@@ -119,6 +134,8 @@ denEvent('alarm', { text: 'YIP?! (INTRUDER)' });
denEvent('mischief'); denEvent('mischief');
denEvent('sleepy'); denEvent('sleepy');
denEvent('zoomies'); denEvent('zoomies');
denEvent('rain'); // also 'snow', 'clear'
denEvent('treat', { x: 400, y: 300 }); // or { el: '#bowl' }
``` ```
`celebrate` is a happy spin with a heart burst. `investigate` sends him on an `celebrate` is a happy spin with a heart burst. `investigate` sends him on an
@@ -132,6 +149,8 @@ denSay('yip!!'); // speech bubble at his current position
denYip(); // the synthesized squeak denYip(); // the synthesized squeak
denSetName('crumb'); // returns the sanitized name, or null denSetName('crumb'); // returns the sanitized name, or null
denGetName(); denGetName();
denWeather('snow'); // 'rain' | 'snow' | 'clear'
denTreat(x, y); // drop a treat at a point
denOthers; // number, current presence count denOthers; // number, current presence count
``` ```
@@ -180,9 +199,9 @@ npm install -g terser javascript-obfuscator
| file | size | notes | | file | size | notes |
| --- | --- | --- | | --- | --- | --- |
| `src/den.js` | 33 KB | readable source, the one you edit | | `src/den.js` | 42 KB | readable source, the one you edit |
| `build/den.min.js` | 20 KB | minified; recommended | | `build/den.min.js` | 25 KB | minified; recommended |
| `build/den.obf.js` | 88 KB | string-encoded and control-flow flattened | | `build/den.obf.js` | 112 KB | string-encoded and control-flow flattened |
The obfuscated build hides all his dialogue and secrets from casual reading, at The obfuscated build hides all his dialogue and secrets from casual reading, at
4x the file size. Nothing stops a determined reader — this is spoiler protection, 4x the file size. Nothing stops a determined reader — this is spoiler protection,

3
build/den.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -80,6 +80,7 @@
<p><b>Type <code>yip</code></b> — anywhere on the page. He answers.</p> <p><b>Type <code>yip</code></b> — anywhere on the page. He answers.</p>
<p><b>Open the <code>&gt;_ den</code> tab</b> in the corner, then try <code>come</code>, <code>fetch</code>, <code>status</code>, or <code>name crumb</code>.</p> <p><b>Open the <code>&gt;_ den</code> tab</b> in the corner, then try <code>come</code>, <code>fetch</code>, <code>status</code>, or <code>name crumb</code>.</p>
<p><b>Press</b> <kbd></kbd><kbd></kbd><kbd></kbd><kbd></kbd><kbd></kbd><kbd></kbd><kbd></kbd><kbd></kbd><kbd>B</kbd><kbd>A</kbd> — or just type <code>zoomies</code>.</p> <p><b>Press</b> <kbd></kbd><kbd></kbd><kbd></kbd><kbd></kbd><kbd></kbd><kbd></kbd><kbd></kbd><kbd></kbd><kbd>B</kbd><kbd>A</kbd> — or just type <code>zoomies</code>.</p>
<p><b>Scroll fast</b> — he gets carried off with the page and has to sprint back.</p>
<p><b>Wait ten minutes</b> without touching anything. He goes to bed.</p> <p><b>Wait ten minutes</b> without touching anything. He goes to bed.</p>
</div> </div>
@@ -106,6 +107,13 @@
<button onclick="denEvent('mischief')">mischief</button> <button onclick="denEvent('mischief')">mischief</button>
<button onclick="denEvent('sleepy')">sleepy</button> <button onclick="denEvent('sleepy')">sleepy</button>
</p> </p>
<p style="text-align:center;">
<button onclick="denEvent('treat')">drop a treat</button>
<button onclick="denWeather('rain')">rain</button>
<button onclick="denWeather('snow')">snow</button>
<button onclick="denWeather('clear')">clear skies</button>
</p>
<p class="hint" style="text-align:center;">Rain sends him under the nearest panel to sulk. Snow does the opposite.</p>
<p id="thing" class="hint" style="text-align:center;">↑ "investigate" sends him to this line, specifically.</p> <p id="thing" class="hint" style="text-align:center;">↑ "investigate" sends him to this line, specifically.</p>
</div> </div>

View File

@@ -1,6 +1,6 @@
{ {
"name": "den.js", "name": "den.js",
"version": "1.0.0", "version": "1.1.0",
"description": "An invisible kobold lives on your website.", "description": "An invisible kobold lives on your website.",
"main": "src/den.js", "main": "src/den.js",
"files": ["src/", "build/", "README.md", "LICENSE"], "files": ["src/", "build/", "README.md", "LICENSE"],

View File

@@ -23,7 +23,14 @@
'.denterm .dout{height:170px;overflow-y:auto;white-space:pre-wrap;word-break:break-word;color:#c9b8ff;}' + '.denterm .dout{height:170px;overflow-y:auto;white-space:pre-wrap;word-break:break-word;color:#c9b8ff;}' +
'.denterm .dline{display:flex;gap:6px;margin-top:5px;align-items:center;}' + '.denterm .dline{display:flex;gap:6px;margin-top:5px;align-items:center;}' +
'.denterm .dprompt{color:#ff6ec7;white-space:nowrap;}' + '.denterm .dprompt{color:#ff6ec7;white-space:nowrap;}' +
'.denterm input{flex:1;background:transparent;border:none;outline:none;color:#a8ffe0;font-family:inherit;font-size:12px;}'; '.denterm input{flex:1;background:transparent;border:none;outline:none;color:#a8ffe0;font-family:inherit;font-size:12px;}' +
'.denwx{position:fixed;top:0;left:0;right:0;bottom:0;pointer-events:none;z-index:9996;overflow:hidden;}' +
'.denwx i{position:absolute;top:-24px;display:block;animation-name:denfall;animation-timing-function:linear;animation-fill-mode:forwards;}' +
'.denwx i.r{width:2px;height:15px;background:linear-gradient(180deg,rgba(79,195,255,0),rgba(79,195,255,.85));border-radius:1px;}' +
'.denwx i.s{color:#fff;font-size:12px;line-height:1;}' +
'@keyframes denfall{from{transform:translateY(0);}to{transform:translateY(104vh);}}' +
'.dentreat{position:fixed;z-index:9997;pointer-events:none;font-size:19px;line-height:1;animation:denbob 1.3s ease-in-out infinite;filter:drop-shadow(0 0 4px #ffb3de);}' +
'@keyframes denbob{0%,100%{transform:translateY(0);}50%{transform:translateY(-5px);}}';
document.head.appendChild(st); document.head.appendChild(st);
var actx = null; var actx = null;
@@ -82,17 +89,25 @@
var sleepPrepUntil = 0, wakeUntil = 0, suppressBumpUntil = 0; var sleepPrepUntil = 0, wakeUntil = 0, suppressBumpUntil = 0;
var asleep = false, bedEl = null, nextSleepZ = 0; var asleep = false, bedEl = null, nextSleepZ = 0;
var speedMult = 1; var speedMult = 1;
var PAW_LIFE = 1300;
var weather = 'clear', wxTimer = null, wxLayer = null;
var shelterPt = null, nextSulk = 0;
var treatEl = null, treatPt = null;
var fedAt = performance.now();
var lastScrollY = window.scrollY || window.pageYOffset || 0;
var windedUntil = 0;
var poiSay = null, lastPoi = null; var poiSay = null, lastPoi = null;
var errandTarget = null, errandSay = null, errandRun = false, errandDeadline = 0; var errandTarget = null, errandSay = null, errandRun = false, errandDeadline = 0;
var mis = null, lastMischief = 0; var mis = null, lastMischief = 0;
var dout = null, dinp = null, dpr = null; var dout = null, dinp = null, dpr = null;
var mem = { v: 0, pets: 0, name: null, last: 0, id: null }; var mem = { v: 0, pets: 0, treats: 0, name: null, last: 0, id: null };
try { try {
var m0 = JSON.parse(localStorage.getItem('denmem')); var m0 = JSON.parse(localStorage.getItem('denmem'));
if (m0 && typeof m0 === 'object') { if (m0 && typeof m0 === 'object') {
mem.v = m0.v | 0; mem.v = m0.v | 0;
mem.pets = m0.pets | 0; mem.pets = m0.pets | 0;
mem.treats = m0.treats | 0;
mem.name = (typeof m0.name === 'string' && m0.name) ? m0.name.slice(0, 16) : null; mem.name = (typeof m0.name === 'string' && m0.name) ? m0.name.slice(0, 16) : null;
mem.last = m0.last || 0; mem.last = m0.last || 0;
if (typeof m0.id === 'string' && /^[a-f0-9]{16}$/.test(m0.id)) mem.id = m0.id; if (typeof m0.id === 'string' && /^[a-f0-9]{16}$/.test(m0.id)) mem.id = m0.id;
@@ -117,6 +132,9 @@
} }
saveMem(); saveMem();
function trust(){ return mem.pets + mem.treats; }
function hungry(){ return performance.now() - fedAt > 240000; }
function greeting(){ function greeting(){
var away = prevLast ? (Date.now() - prevLast) : 0; var away = prevLast ? (Date.now() - prevLast) : 0;
if (mem.v <= 1) return '*yip* (hello)'; if (mem.v <= 1) return '*yip* (hello)';
@@ -230,6 +248,27 @@
wakeUntil = now + 900; wakeUntil = now + 900;
} }
} }
window.addEventListener('scroll', function(){
var y = window.scrollY || window.pageYOffset || 0;
var delta = y - lastScrollY;
lastScrollY = y;
if (asleep || sleepPrepUntil || !delta) return;
pos.y -= delta;
var pushed = false;
if (pos.y < 18) { pos.y = 18; pushed = true; }
else if (pos.y > window.innerHeight - 18) { pos.y = window.innerHeight - 18; pushed = true; }
if (pushed && Math.abs(delta) > 45) windedUntil = performance.now() + 2500;
if (treatEl && treatPt) {
treatPt.y -= delta;
treatEl.style.top = (treatPt.y - 10) + 'px';
if (treatPt.y < 0 || treatPt.y > window.innerHeight) {
treatEl.remove(); treatEl = null; treatPt = null;
if (mode === 'treat') { mode = 'wander'; target = pickWander(); }
}
}
if (shelterPt) shelterPt.y -= delta;
}, { passive: true });
document.addEventListener('keydown', bump); document.addEventListener('keydown', bump);
document.addEventListener('scroll', bump, { passive: true }); document.addEventListener('scroll', bump, { passive: true });
document.addEventListener('touchstart', bump, { passive: true }); document.addEventListener('touchstart', bump, { passive: true });
@@ -237,7 +276,8 @@
document.addEventListener('mousemove', function(e){ document.addEventListener('mousemove', function(e){
target.x = e.clientX; target.y = e.clientY; target.x = e.clientX; target.y = e.clientY;
bump(); bump();
if (mode === 'errand' || mode === 'mischief') return; if (mode === 'errand' || mode === 'mischief' || mode === 'treat') return;
if (mode === 'shelter' && weather === 'rain') return;
var now = performance.now(); var now = performance.now();
if (now < petUntil || now < wakeUntil) return; if (now < petUntil || now < wakeUntil) return;
if (mode !== 'chase') emote('\u2757'); if (mode !== 'chase') emote('\u2757');
@@ -309,7 +349,7 @@
phase: 'togo', phase: 'togo',
pt: clampPt(r.left + r.width / 2, r.top + r.height / 2), pt: clampPt(r.left + r.width / 2, r.top + r.height / 2),
deadline: now + 18000, deadline: now + 18000,
gift: mem.pets >= 25 gift: trust() >= 25
}; };
lastMischief = now; lastMischief = now;
mode = 'mischief'; mode = 'mischief';
@@ -337,6 +377,84 @@
mis.phase = 'carry'; mis.phase = 'carry';
} }
function spawnWx(){
if (!wxLayer) return;
var rain = weather === 'rain';
var p = document.createElement('i');
p.className = rain ? 'r' : 's';
if (!rain) p.textContent = '\u2744';
p.style.left = (Math.random() * 100) + 'vw';
var dur = rain ? (0.55 + Math.random() * 0.35) : (3.2 + Math.random() * 3.4);
p.style.animationDuration = dur + 's';
p.style.opacity = rain ? (0.3 + Math.random() * 0.45) : (0.5 + Math.random() * 0.5);
wxLayer.appendChild(p);
setTimeout(function(){ p.remove(); }, dur * 1000 + 300);
}
function setWeather(kind){
if (kind !== 'rain' && kind !== 'snow') kind = 'clear';
if (kind === weather) return;
weather = kind;
if (wxTimer) { clearInterval(wxTimer); wxTimer = null; }
if (wxLayer) { var old = wxLayer; wxLayer = null; old.remove(); }
PAW_LIFE = (kind === 'snow') ? 4200 : 1300;
if (kind === 'clear') {
shelterPt = null;
if (mode === 'shelter') { mode = 'wander'; target = pickWander(); sayText('yip!! (it stopped)'); }
return;
}
wxLayer = document.createElement('div');
wxLayer.className = 'denwx';
document.body.appendChild(wxLayer);
wxTimer = setInterval(spawnWx, kind === 'rain' ? 85 : 240);
if (asleep) bump();
if (kind === 'rain') {
sayText('yip?! (wet)');
emote('\u2614', true);
seekShelter();
} else {
sayText('YIP!! (snow!!)');
emote('\u2744', true);
speedMult = 1.6;
setTimeout(function(){ if (speedMult === 1.6) speedMult = 1; }, 6000);
}
}
window.denWeather = setWeather;
function seekShelter(){
var ps = pois();
if (!ps.length) { shelterPt = null; return false; }
var best = null, bd = Infinity;
for (var i = 0; i < ps.length; i++) {
var r = ps[i].r;
var cx = r.left + r.width * 0.5;
var cy = r.top + Math.min(r.height * 0.5, 45);
var dd = Math.hypot(cx - pos.x, cy - pos.y);
if (dd < bd) { bd = dd; best = { x: cx, y: cy }; }
}
if (!best) return false;
shelterPt = clampPt(best.x, best.y);
nextSulk = performance.now() + 2200;
mode = 'shelter';
return true;
}
function dropTreat(x, y){
if (treatEl) { treatEl.remove(); treatEl = null; }
treatPt = clampPt(x, y);
treatEl = document.createElement('div');
treatEl.className = 'dentreat';
treatEl.textContent = '\uD83C\uDF56';
treatEl.style.left = (treatPt.x - 10) + 'px';
treatEl.style.top = (treatPt.y - 10) + 'px';
document.body.appendChild(treatEl);
if (asleep) bump();
if (mis) returnItem();
emote('\u2757', true);
mode = 'treat';
}
window.denTreat = dropTreat;
function clampPt(x, y){ function clampPt(x, y){
return { return {
x: Math.min(Math.max(x, 20), window.innerWidth - 20), x: Math.min(Math.max(x, 20), window.innerWidth - 20),
@@ -365,6 +483,16 @@
startMischief(true); startMischief(true);
return; return;
} }
if (type === 'rain' || type === 'snow' || type === 'clear') {
setWeather(type);
return;
}
if (type === 'treat') {
var tp = resolvePoint(opts);
if (!tp) tp = clampPt(pos.x + (Math.random() * 220 - 110), pos.y + (Math.random() * 160 - 80));
dropTreat(tp.x, tp.y);
return;
}
if (type === 'celebrate') { if (type === 'celebrate') {
petUntil = now + 1100; petUntil = now + 1100;
mode = 'rest'; mode = 'rest';
@@ -453,6 +581,13 @@
return d; return d;
} }
function driftLine(){
if (hungry() && Math.random() < 0.55) return pick(['yip (hungry)', '(food?)', 'yip? (snacks)']);
if (mem.v >= 3 && trust() === 0 && Math.random() < 0.4) return pick(['yip...', '(is anyone there)', 'yip? (hello?)']);
if (trust() >= 25 && Math.random() < 0.4) return pick(['yip (comfy)', 'uwu', 'yip yip (good day)']);
return null;
}
function pickWander(){ function pickWander(){
poiSay = null; poiSay = null;
if (Math.random() < 0.45) { if (Math.random() < 0.45) {
@@ -492,7 +627,7 @@
p.querySelector('svg').style.transform = 'rotate(' + deg + 'deg) scaleX(' + side + ')'; p.querySelector('svg').style.transform = 'rotate(' + deg + 'deg) scaleX(' + side + ')';
p.querySelector('svg').style.display = 'block'; p.querySelector('svg').style.display = 'block';
document.body.appendChild(p); document.body.appendChild(p);
setTimeout(function(){ p.remove(); }, 1300); setTimeout(function(){ p.remove(); }, PAW_LIFE);
} }
function tick(now){ function tick(now){
@@ -622,6 +757,73 @@
return; return;
} }
if (mode === 'shelter') {
if (weather !== 'rain') {
shelterPt = null;
mode = 'wander';
target = pickWander();
} else if (!shelterPt) {
if (!seekShelter()) { mode = 'wander'; target = pickWander(); }
} else {
var sx = shelterPt.x - pos.x, sy = shelterPt.y - pos.y;
var sd = Math.hypot(sx, sy);
if (sd < 14) {
if (now > nextSulk) {
nextSulk = now + 3200 + Math.random() * 2600;
if (Math.random() < 0.5) emote(pick(['\u2614', '\uD83D\uDCA7']));
else sayText(pick(['yip... (waiting it out)', '(damp)', 'yip (i do not like this)']));
}
} else {
var sw = Math.atan2(sy, sx);
var smx = TURN * dt * 2.2;
heading += Math.max(-smx, Math.min(smx, angDiff(heading, sw)));
var sspeed = (sd > 150 ? RUN : TROT) * speedMult;
var sstep = Math.min(sspeed * dt, sd);
pos.x += Math.cos(heading) * sstep;
pos.y += Math.sin(heading) * sstep;
distSince += sstep;
if (distSince >= (sd > 150 ? STRIDE_RUN : STRIDE_WALK)) { distSince = 0; drop(); }
}
}
requestAnimationFrame(tick);
return;
}
if (mode === 'treat') {
if (!treatPt || !treatEl) {
mode = 'wander';
target = pickWander();
} else {
var tx = treatPt.x - pos.x, ty = treatPt.y - pos.y;
var td = Math.hypot(tx, ty);
if (td < 15) {
treatEl.remove();
treatEl = null;
treatPt = null;
fedAt = now;
mem.treats++;
saveMem();
emote('\uD83D\uDC96', true);
sayText(pick(['*nom nom* yip!!', 'yip!! (thank u)', '(best day)']));
yipSound();
mode = 'rest';
restUntil = now + 2600;
nextZzz = now + 99999;
} else {
var tw = Math.atan2(ty, tx);
var tmx = TURN * dt * 2.4;
heading += Math.max(-tmx, Math.min(tmx, angDiff(heading, tw)));
var tstep = Math.min(RUN * speedMult * dt, td);
pos.x += Math.cos(heading) * tstep;
pos.y += Math.sin(heading) * tstep;
distSince += tstep;
if (distSince >= STRIDE_RUN) { distSince = 0; drop(); }
}
}
requestAnimationFrame(tick);
return;
}
if (mode === 'errand') { if (mode === 'errand') {
if (!errandTarget || now > errandDeadline) { if (!errandTarget || now > errandDeadline) {
errandTarget = null; errandTarget = null;
@@ -661,14 +863,17 @@
if (mode === 'chase') { if (mode === 'chase') {
if (dist < 26) { if (dist < 26) {
mode = 'rest'; mode = 'rest';
restUntil = now + (mem.pets >= 25 ? 4200 : 2500); restUntil = now + (trust() >= 25 ? 4200 : 2500);
nextZzz = now + 1700; nextZzz = now + 1700;
if (Math.random() < 0.5) emote(mem.pets >= 10 ? '\uD83D\uDC95' : '\u2728'); if (now < windedUntil) { windedUntil = 0; sayText('*wheeze* (u went so far)'); }
else if (Math.random() < 0.5) emote(trust() >= 10 ? '\uD83D\uDC95' : '\u2728');
} }
} else if (mode === 'rest') { } else if (mode === 'rest') {
if (now > nextZzz) { emote('\uD83D\uDCA4'); nextZzz = now + 1600; } if (now > nextZzz) { emote('\uD83D\uDCA4'); nextZzz = now + 1600; }
if (hungry() && now > nextSulk) { emote('\uD83C\uDF56'); nextSulk = now + 9000; }
if (now > restUntil) { if (now > restUntil) {
if (!startMischief(false)) { target = pickWander(); mode = 'wander'; } if (weather === 'rain' && seekShelter()) { /* back under cover */ }
else if (!startMischief(false)) { target = pickWander(); mode = 'wander'; }
} }
} else { } else {
if (dist < 14) { if (dist < 14) {
@@ -679,7 +884,9 @@
poiSay = null; poiSay = null;
} else { } else {
restUntil = now + 600 + Math.random() * 1600; restUntil = now + 600 + Math.random() * 1600;
emote(pick(['\uD83D\uDC43','\u2753','\uD83C\uDF38','\u2728','\uD83D\uDC3E'])); var dl = driftLine();
if (dl) sayText(dl);
else emote(pick(['\uD83D\uDC43','\u2753','\uD83C\uDF38','\u2728','\uD83D\uDC3E']));
} }
} }
} }
@@ -727,7 +934,7 @@
if (lc.indexOf('rm ') === 0 || lc === 'rm') { dprint('rm: the hoard is load-bearing. request denied'); return; } if (lc.indexOf('rm ') === 0 || lc === 'rm') { dprint('rm: the hoard is load-bearing. request denied'); return; }
switch (lc) { switch (lc) {
case 'help': case 'help':
dprint('commands: pet, come, sit, fetch, sleep, wake, zoomies, where, status, name <x>, ls, whoami, pwd, neofetch, yip, clear, exit'); dprint('commands: pet, treat, come, sit, fetch, rain, snow, sun, sleep, wake, zoomies, where, status, name <x>, ls, whoami, pwd, neofetch, yip, clear, exit');
break; break;
case 'pet': case 'pet':
case 'pet kobold': case 'pet kobold':
@@ -737,12 +944,34 @@
break; break;
case 'fetch': { case 'fetch': {
if (startMischief(true)) { if (startMischief(true)) {
dprint(mem.pets >= 25 ? 'he goes to find u something' : 'oh no'); dprint(trust() >= 25 ? 'he goes to find u something' : 'oh no');
} else { } else {
dprint(mis ? 'his paws are full' : 'nothing here worth taking. sad'); dprint(mis ? 'his paws are full' : 'nothing here worth taking. sad');
} }
break; break;
} }
case 'treat': {
var tr = term.getBoundingClientRect();
dropTreat(
Math.max(tr.left - 60, 40),
Math.max(tr.top - 50, 40)
);
dprint(hungry() ? 'he was waiting for this' : 'a treat appears. he has noticed');
break;
}
case 'rain':
setWeather('rain');
dprint('it rains. he does not care for it');
break;
case 'snow':
setWeather('snow');
dprint('it snows. he cares for it a great deal');
break;
case 'sun':
case 'clear':
setWeather('clear');
dprint('the sky clears');
break;
case 'come': { case 'come': {
var r = term.getBoundingClientRect(); var r = term.getBoundingClientRect();
target = { target = {
@@ -781,7 +1010,9 @@
dprint('listen for the yip'); dprint('listen for the yip');
break; break;
case 'status': case 'status':
dprint('mode: ' + (asleep ? 'asleep' : mode) + ' | pets: ' + mem.pets + ' | name: ' + (mem.name || '(unnamed)') + ' | visits: ' + mem.v); dprint('mode: ' + (asleep ? 'asleep' : mode) + ' | pets: ' + mem.pets + ' | treats: ' + mem.treats +
' | name: ' + (mem.name || '(unnamed)') + ' | visits: ' + mem.v +
' | weather: ' + weather + (hungry() ? ' | hungry' : ''));
break; break;
case 'ls': case 'ls':
dprint('den/ hoard/ guestbook.txt do_not_open/ retopo_FINAL_v2/'); dprint('den/ hoard/ guestbook.txt do_not_open/ retopo_FINAL_v2/');