1.1.0 — weather, treats, hunger, drift
This commit is contained in:
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,5 +1,20 @@
|
||||
# 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
|
||||
|
||||
First release. He exists.
|
||||
|
||||
35
README.md
35
README.md
@@ -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
|
||||
the blanket. Any keypress, click, scroll, or mouse twitch wakes him — groggily.
|
||||
|
||||
**Memory.** Visit count, lifetime pets, and his name persist in `localStorage`.
|
||||
Return after a week and he says so. Pet him ten times and he decides you're
|
||||
friends; at twenty-five he starts lingering near your cursor instead of
|
||||
wandering off, and his stealing turns into gift-giving.
|
||||
**Memory.** Visit count, lifetime pets, treats, and his name persist in
|
||||
`localStorage`. Return after a week and he says so. Reach ten points of trust
|
||||
(pets + treats) and he decides you're friends; at twenty-five he lingers near
|
||||
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
|
||||
`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
|
||||
people are on the site and mention that he can smell them.
|
||||
@@ -119,6 +134,8 @@ denEvent('alarm', { text: 'YIP?! (INTRUDER)' });
|
||||
denEvent('mischief');
|
||||
denEvent('sleepy');
|
||||
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
|
||||
@@ -132,6 +149,8 @@ denSay('yip!!'); // speech bubble at his current position
|
||||
denYip(); // the synthesized squeak
|
||||
denSetName('crumb'); // returns the sanitized name, or null
|
||||
denGetName();
|
||||
denWeather('snow'); // 'rain' | 'snow' | 'clear'
|
||||
denTreat(x, y); // drop a treat at a point
|
||||
denOthers; // number, current presence count
|
||||
```
|
||||
|
||||
@@ -180,9 +199,9 @@ npm install -g terser javascript-obfuscator
|
||||
|
||||
| file | size | notes |
|
||||
| --- | --- | --- |
|
||||
| `src/den.js` | 33 KB | readable source, the one you edit |
|
||||
| `build/den.min.js` | 20 KB | minified; recommended |
|
||||
| `build/den.obf.js` | 88 KB | string-encoded and control-flow flattened |
|
||||
| `src/den.js` | 42 KB | readable source, the one you edit |
|
||||
| `build/den.min.js` | 25 KB | minified; recommended |
|
||||
| `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
|
||||
4x the file size. Nothing stops a determined reader — this is spoiler protection,
|
||||
|
||||
3
build/den.min.js
vendored
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
@@ -80,6 +80,7 @@
|
||||
<p><b>Type <code>yip</code></b> — anywhere on the page. He answers.</p>
|
||||
<p><b>Open the <code>>_ 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>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>
|
||||
</div>
|
||||
|
||||
@@ -106,6 +107,13 @@
|
||||
<button onclick="denEvent('mischief')">mischief</button>
|
||||
<button onclick="denEvent('sleepy')">sleepy</button>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "den.js",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "An invisible kobold lives on your website.",
|
||||
"main": "src/den.js",
|
||||
"files": ["src/", "build/", "README.md", "LICENSE"],
|
||||
|
||||
255
src/den.js
255
src/den.js
@@ -23,7 +23,14 @@
|
||||
'.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 .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);
|
||||
|
||||
var actx = null;
|
||||
@@ -82,17 +89,25 @@
|
||||
var sleepPrepUntil = 0, wakeUntil = 0, suppressBumpUntil = 0;
|
||||
var asleep = false, bedEl = null, nextSleepZ = 0;
|
||||
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 errandTarget = null, errandSay = null, errandRun = false, errandDeadline = 0;
|
||||
var mis = null, lastMischief = 0;
|
||||
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 {
|
||||
var m0 = JSON.parse(localStorage.getItem('denmem'));
|
||||
if (m0 && typeof m0 === 'object') {
|
||||
mem.v = m0.v | 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.last = m0.last || 0;
|
||||
if (typeof m0.id === 'string' && /^[a-f0-9]{16}$/.test(m0.id)) mem.id = m0.id;
|
||||
@@ -117,6 +132,9 @@
|
||||
}
|
||||
saveMem();
|
||||
|
||||
function trust(){ return mem.pets + mem.treats; }
|
||||
function hungry(){ return performance.now() - fedAt > 240000; }
|
||||
|
||||
function greeting(){
|
||||
var away = prevLast ? (Date.now() - prevLast) : 0;
|
||||
if (mem.v <= 1) return '*yip* (hello)';
|
||||
@@ -230,6 +248,27 @@
|
||||
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('scroll', bump, { passive: true });
|
||||
document.addEventListener('touchstart', bump, { passive: true });
|
||||
@@ -237,7 +276,8 @@
|
||||
document.addEventListener('mousemove', function(e){
|
||||
target.x = e.clientX; target.y = e.clientY;
|
||||
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();
|
||||
if (now < petUntil || now < wakeUntil) return;
|
||||
if (mode !== 'chase') emote('\u2757');
|
||||
@@ -309,7 +349,7 @@
|
||||
phase: 'togo',
|
||||
pt: clampPt(r.left + r.width / 2, r.top + r.height / 2),
|
||||
deadline: now + 18000,
|
||||
gift: mem.pets >= 25
|
||||
gift: trust() >= 25
|
||||
};
|
||||
lastMischief = now;
|
||||
mode = 'mischief';
|
||||
@@ -337,6 +377,84 @@
|
||||
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){
|
||||
return {
|
||||
x: Math.min(Math.max(x, 20), window.innerWidth - 20),
|
||||
@@ -365,6 +483,16 @@
|
||||
startMischief(true);
|
||||
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') {
|
||||
petUntil = now + 1100;
|
||||
mode = 'rest';
|
||||
@@ -453,6 +581,13 @@
|
||||
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(){
|
||||
poiSay = null;
|
||||
if (Math.random() < 0.45) {
|
||||
@@ -492,7 +627,7 @@
|
||||
p.querySelector('svg').style.transform = 'rotate(' + deg + 'deg) scaleX(' + side + ')';
|
||||
p.querySelector('svg').style.display = 'block';
|
||||
document.body.appendChild(p);
|
||||
setTimeout(function(){ p.remove(); }, 1300);
|
||||
setTimeout(function(){ p.remove(); }, PAW_LIFE);
|
||||
}
|
||||
|
||||
function tick(now){
|
||||
@@ -622,6 +757,73 @@
|
||||
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 (!errandTarget || now > errandDeadline) {
|
||||
errandTarget = null;
|
||||
@@ -661,14 +863,17 @@
|
||||
if (mode === 'chase') {
|
||||
if (dist < 26) {
|
||||
mode = 'rest';
|
||||
restUntil = now + (mem.pets >= 25 ? 4200 : 2500);
|
||||
restUntil = now + (trust() >= 25 ? 4200 : 2500);
|
||||
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') {
|
||||
if (now > nextZzz) { emote('\uD83D\uDCA4'); nextZzz = now + 1600; }
|
||||
if (hungry() && now > nextSulk) { emote('\uD83C\uDF56'); nextSulk = now + 9000; }
|
||||
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 {
|
||||
if (dist < 14) {
|
||||
@@ -679,7 +884,9 @@
|
||||
poiSay = null;
|
||||
} else {
|
||||
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; }
|
||||
switch (lc) {
|
||||
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;
|
||||
case 'pet':
|
||||
case 'pet kobold':
|
||||
@@ -737,12 +944,34 @@
|
||||
break;
|
||||
case 'fetch': {
|
||||
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 {
|
||||
dprint(mis ? 'his paws are full' : 'nothing here worth taking. sad');
|
||||
}
|
||||
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': {
|
||||
var r = term.getBoundingClientRect();
|
||||
target = {
|
||||
@@ -781,7 +1010,9 @@
|
||||
dprint('listen for the yip');
|
||||
break;
|
||||
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;
|
||||
case 'ls':
|
||||
dprint('den/ hoard/ guestbook.txt do_not_open/ retopo_FINAL_v2/');
|
||||
|
||||
Reference in New Issue
Block a user