Updated sprites and pet logic
This commit is contained in:
396
index.js
396
index.js
@@ -10,7 +10,11 @@ const DEFAULT_CONFIG = {
|
||||
toyCooldownMs: 20000,
|
||||
successToyChance: 0.35,
|
||||
chaseCursorChance: 0.04,
|
||||
walkSpeed: 0.65,
|
||||
walkSpeed: 0.45,
|
||||
animationSpeed: 0.5,
|
||||
sleepMinDurationMs: 30000,
|
||||
sleepMaxDurationMs: 180000,
|
||||
debugFrames: false,
|
||||
reducedMotion: false,
|
||||
onlyActivePane: true
|
||||
};
|
||||
@@ -36,6 +40,10 @@ function normalizeConfig(input) {
|
||||
next.successToyChance = clamp(Number(next.successToyChance), 0, 1);
|
||||
next.chaseCursorChance = clamp(Number(next.chaseCursorChance), 0, 1);
|
||||
next.walkSpeed = Math.max(Number(next.walkSpeed) || DEFAULT_CONFIG.walkSpeed, 0.2);
|
||||
next.animationSpeed = Math.max(Number(next.animationSpeed) || DEFAULT_CONFIG.animationSpeed, 0.15);
|
||||
next.sleepMinDurationMs = Math.max(Number(next.sleepMinDurationMs) || DEFAULT_CONFIG.sleepMinDurationMs, 1000);
|
||||
next.sleepMaxDurationMs = Math.max(Number(next.sleepMaxDurationMs) || DEFAULT_CONFIG.sleepMaxDurationMs, next.sleepMinDurationMs);
|
||||
next.debugFrames = Boolean(next.debugFrames);
|
||||
next.petTypes = Array.isArray(next.petTypes) && next.petTypes.length ? next.petTypes : DEFAULT_CONFIG.petTypes;
|
||||
next.petTypes = next.petTypes.filter((type) => availablePetTypes.includes(type));
|
||||
if (next.petTypes.length === 0) {
|
||||
@@ -44,20 +52,114 @@ function normalizeConfig(input) {
|
||||
return next;
|
||||
}
|
||||
|
||||
function modeToClip(mode) {
|
||||
function findHyperPetsConfig(value, depth) {
|
||||
if (!value || typeof value !== 'object' || depth > 4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value.hyperPets && typeof value.hyperPets === 'object') {
|
||||
return value.hyperPets;
|
||||
}
|
||||
|
||||
const keys = Object.keys(value);
|
||||
for (let index = 0; index < keys.length; index += 1) {
|
||||
const nested = value[keys[index]];
|
||||
if (nested && typeof nested === 'object') {
|
||||
const found = findHyperPetsConfig(nested, depth + 1);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function refreshRuntimeConfigFromRenderer() {
|
||||
try {
|
||||
if (typeof window === 'undefined') {
|
||||
return runtimeConfig;
|
||||
}
|
||||
|
||||
const candidates = [
|
||||
window.__hyperpetsRuntimeConfig,
|
||||
window.config,
|
||||
window.config && window.config.getConfig ? window.config.getConfig() : null,
|
||||
window.store && window.store.getState ? window.store.getState() : null
|
||||
];
|
||||
|
||||
for (let index = 0; index < candidates.length; index += 1) {
|
||||
const found = findHyperPetsConfig(candidates[index], 0);
|
||||
if (found) {
|
||||
const normalized = normalizeConfig(found);
|
||||
runtimeConfig = normalized;
|
||||
window.__hyperpetsRuntimeConfig = normalized;
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[hyperpets] failed to refresh renderer config', error);
|
||||
}
|
||||
|
||||
return runtimeConfig;
|
||||
}
|
||||
|
||||
function randomIdleClip() {
|
||||
const variants = ['idle0', 'idle1', 'idle2', 'idle3'];
|
||||
return variants[Math.floor(Math.random() * variants.length)];
|
||||
}
|
||||
|
||||
function randomSleepDuration() {
|
||||
const min = runtimeConfig.sleepMinDurationMs;
|
||||
const max = runtimeConfig.sleepMaxDurationMs;
|
||||
if (max <= min) {
|
||||
return min;
|
||||
}
|
||||
return min + Math.round(Math.random() * (max - min));
|
||||
}
|
||||
|
||||
function isIdleClipName(clipName) {
|
||||
return typeof clipName === 'string' && /^idle\d*$/.test(clipName);
|
||||
}
|
||||
|
||||
function getDefaultClipName(petAsset) {
|
||||
if (petAsset.clips.idle0) {
|
||||
return 'idle0';
|
||||
}
|
||||
if (petAsset.clips.idle) {
|
||||
return 'idle';
|
||||
}
|
||||
return Object.keys(petAsset.clips)[0];
|
||||
}
|
||||
|
||||
function getEffectiveClipFps(clip, reducedMotion) {
|
||||
const baseFps = clip.fps * runtimeConfig.animationSpeed;
|
||||
return reducedMotion ? Math.max(baseFps * 0.6, 1) : Math.max(baseFps, 1);
|
||||
}
|
||||
|
||||
function modeToClip(pet) {
|
||||
const mode = pet.mode;
|
||||
const facingRight = pet.facing >= 0;
|
||||
const petAsset = getPetAsset(pet.type);
|
||||
if (mode === 'sleep') {
|
||||
return 'sleep';
|
||||
}
|
||||
if (mode === 'inspect') {
|
||||
return 'inspect';
|
||||
return facingRight ? 'inspectRight' : 'inspectLeft';
|
||||
}
|
||||
if (mode === 'chase-cursor' || mode === 'chase-toy') {
|
||||
return 'chase';
|
||||
return facingRight ? 'walkRight' : 'walkLeft';
|
||||
}
|
||||
if (mode === 'walk') {
|
||||
return 'walk';
|
||||
return facingRight ? 'walkRight' : 'walkLeft';
|
||||
}
|
||||
return 'idle';
|
||||
if (pet.idleClip && petAsset.clips[pet.idleClip]) {
|
||||
return pet.idleClip;
|
||||
}
|
||||
if (isIdleClipName(pet.clip) && petAsset.clips[pet.clip]) {
|
||||
return pet.clip;
|
||||
}
|
||||
return getDefaultClipName(petAsset);
|
||||
}
|
||||
|
||||
function getClipDuration(petType, clipName, reducedMotion) {
|
||||
@@ -67,7 +169,7 @@ function getClipDuration(petType, clipName, reducedMotion) {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
const fps = reducedMotion ? Math.max(clip.fps * 0.6, 1) : clip.fps;
|
||||
const fps = getEffectiveClipFps(clip, reducedMotion);
|
||||
return (clip.frames.length / fps) * 1000;
|
||||
}
|
||||
|
||||
@@ -114,14 +216,21 @@ function resolveAnimationState(pet, desiredClip, now, reducedMotion) {
|
||||
|
||||
function getAnimatedFrame(pet, reducedMotion, now) {
|
||||
const petAsset = getPetAsset(pet.type);
|
||||
const clipName = pet.clip || 'idle';
|
||||
const clip = petAsset.clips[clipName] || petAsset.clips.idle;
|
||||
const fps = reducedMotion ? Math.max(clip.fps * 0.6, 1) : clip.fps;
|
||||
const duration = 1000 / fps;
|
||||
const elapsed = now - (pet.clipStartedAt || now);
|
||||
const rawIndex = clip.frames.length === 1 ? 0 : Math.floor(elapsed / duration);
|
||||
const safeIndex = ((rawIndex % clip.frames.length) + clip.frames.length) % clip.frames.length;
|
||||
return clip.frames[safeIndex] || petAsset.clips.idle.frames[0];
|
||||
const defaultClipName = getDefaultClipName(petAsset);
|
||||
const clipName = pet.clip || defaultClipName;
|
||||
const clip = petAsset.clips[clipName] || petAsset.clips[defaultClipName];
|
||||
const fps = getEffectiveClipFps(clip, reducedMotion);
|
||||
const safeIndex = typeof pet.frameIndex === 'number' && clip.frames[pet.frameIndex]
|
||||
? pet.frameIndex
|
||||
: 0;
|
||||
const frame = clip.frames[safeIndex] || petAsset.clips[defaultClipName].frames[0];
|
||||
return {
|
||||
clipName,
|
||||
fps,
|
||||
frame,
|
||||
frameIndex: safeIndex,
|
||||
frameCount: clip.frames.length
|
||||
};
|
||||
}
|
||||
|
||||
function subscribe(uid, listener) {
|
||||
@@ -201,6 +310,17 @@ function inspectSessionData(uid, data) {
|
||||
|
||||
exports.decorateConfig = (config) => {
|
||||
runtimeConfig = normalizeConfig(config.hyperPets);
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.__hyperpetsRuntimeConfig = runtimeConfig;
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore window access errors during non-renderer evaluation.
|
||||
}
|
||||
console.log('[hyperpets] decorateConfig', {
|
||||
input: config.hyperPets || null,
|
||||
normalized: runtimeConfig
|
||||
});
|
||||
return Object.assign({}, config, {
|
||||
css: `
|
||||
${config.css || ''}
|
||||
@@ -223,8 +343,6 @@ exports.decorateConfig = (config) => {
|
||||
.hyperpets-pet {
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
width: 48px;
|
||||
height: 32px;
|
||||
transform-origin: center bottom;
|
||||
will-change: transform, left;
|
||||
}
|
||||
@@ -254,13 +372,45 @@ exports.decorateConfig = (config) => {
|
||||
overflow: visible;
|
||||
}
|
||||
.hyperpets-frame {
|
||||
width: 48px;
|
||||
height: 32px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
.hyperpets-frame-debug {
|
||||
outline: 1px dashed rgba(80, 220, 255, 0.7);
|
||||
background-color: rgba(80, 220, 255, 0.06);
|
||||
}
|
||||
.hyperpets-frame svg {
|
||||
display: block;
|
||||
width: 48px;
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.hyperpets-debug-label {
|
||||
position: absolute;
|
||||
left: -12px;
|
||||
bottom: 56px;
|
||||
min-width: 150px;
|
||||
padding: 4px 6px;
|
||||
border-radius: 6px;
|
||||
background: rgba(8, 12, 18, 0.86);
|
||||
color: #9ee7ff;
|
||||
font: 10px/1.35 Menlo, Monaco, monospace;
|
||||
white-space: pre;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hyperpets-debug-hud {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
max-width: min(420px, calc(100% - 16px));
|
||||
padding: 6px 8px;
|
||||
border-radius: 8px;
|
||||
background: rgba(8, 12, 18, 0.9);
|
||||
color: #9ee7ff;
|
||||
font: 11px/1.4 Menlo, Monaco, monospace;
|
||||
white-space: pre-wrap;
|
||||
pointer-events: none;
|
||||
z-index: 20;
|
||||
}
|
||||
.hyperpets-z {
|
||||
position: absolute;
|
||||
@@ -297,6 +447,8 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
this._resizeHandler = this.handleResize.bind(this);
|
||||
this._mouseHandler = this.handleMouseMove.bind(this);
|
||||
this._tick = this.tick.bind(this);
|
||||
this._debugFrameHistory = new Map();
|
||||
this._debugLastLoggedToken = new Map();
|
||||
}
|
||||
|
||||
buildInitialState() {
|
||||
@@ -305,6 +457,8 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
for (let index = 0; index < config.petCount; index += 1) {
|
||||
const now = Date.now();
|
||||
const type = config.petTypes[index % config.petTypes.length] || 'cat';
|
||||
const petAsset = getPetAsset(type);
|
||||
const idleClip = petAsset.clips.idle0 ? randomIdleClip() : getDefaultClipName(petAsset);
|
||||
pets.push({
|
||||
id: `pet-${index}`,
|
||||
type,
|
||||
@@ -318,9 +472,15 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
nextDecisionAt: now + 2200 + Math.random() * 1800,
|
||||
nextAttentionAt: now + 2500 + Math.random() * 2500,
|
||||
attentionUntil: 0,
|
||||
pendingMode: null,
|
||||
pendingModeAt: 0,
|
||||
pendingFacing: 1,
|
||||
idleClip,
|
||||
zOffset: Math.round(Math.random() * 6),
|
||||
clip: 'idle',
|
||||
clip: idleClip,
|
||||
clipStartedAt: now,
|
||||
frameIndex: 0,
|
||||
nextFrameAt: now + (1000 / getEffectiveClipFps(petAsset.clips[idleClip], runtimeConfig.reducedMotion)),
|
||||
transitionTo: null
|
||||
});
|
||||
}
|
||||
@@ -337,6 +497,13 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
componentDidMount() {
|
||||
this._mounted = true;
|
||||
this._container = this.props.termRef || null;
|
||||
refreshRuntimeConfigFromRenderer();
|
||||
if (runtimeConfig.debugFrames) {
|
||||
console.log('[hyperpets] HyperPetsLayer mounted', {
|
||||
uid: this.props.uid,
|
||||
runtimeConfig
|
||||
});
|
||||
}
|
||||
this.measure();
|
||||
window.addEventListener('resize', this._resizeHandler);
|
||||
if (this._container) {
|
||||
@@ -405,6 +572,8 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
mode: pet.mode === 'sleep' ? 'idle' : pet.mode,
|
||||
sleepUntil: 0,
|
||||
attentionUntil: 0,
|
||||
pendingMode: null,
|
||||
pendingModeAt: 0,
|
||||
nextAttentionAt: Date.now() + 2200,
|
||||
nextDecisionAt: Date.now() + 1600
|
||||
}))
|
||||
@@ -486,13 +655,29 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
const speedScale = runtimeConfig.reducedMotion ? 0.45 : 1;
|
||||
const travel = next.vx * runtimeConfig.walkSpeed * dt * speedScale;
|
||||
const attentionActive = next.attentionUntil > now;
|
||||
const isMovingMode = next.mode === 'walk' || next.mode === 'chase-toy' || next.mode === 'chase-cursor';
|
||||
|
||||
if (!active) {
|
||||
next.mode = 'sleep';
|
||||
next.sleepUntil = now + 5000;
|
||||
next.sleepUntil = now + randomSleepDuration();
|
||||
next.mood = 'calm';
|
||||
next.targetX = null;
|
||||
next.attentionUntil = 0;
|
||||
next.pendingMode = null;
|
||||
next.pendingModeAt = 0;
|
||||
} else if (next.pendingMode && next.mode === 'idle' && now >= next.pendingModeAt) {
|
||||
next.mode = next.pendingMode;
|
||||
next.pendingMode = null;
|
||||
next.pendingModeAt = 0;
|
||||
next.targetX = null;
|
||||
next.facing = next.pendingFacing || next.facing;
|
||||
if (next.mode === 'sleep') {
|
||||
next.sleepUntil = now + randomSleepDuration();
|
||||
next.attentionUntil = 0;
|
||||
} else if (next.mode === 'inspect') {
|
||||
next.attentionUntil = now + 1600 + Math.random() * 1400;
|
||||
next.nextDecisionAt = now + 1800 + Math.random() * 1200;
|
||||
}
|
||||
} else if (targetToy) {
|
||||
next.targetX = targetToy.x;
|
||||
next.mode = 'chase-toy';
|
||||
@@ -509,30 +694,54 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
next.nextAttentionAt = now + 5000 + Math.random() * 5000;
|
||||
next.nextDecisionAt = now + 1800 + Math.random() * 1200;
|
||||
} else if (shouldNoticeCursor) {
|
||||
next.targetX = cursorTarget;
|
||||
next.mode = 'inspect';
|
||||
next.mood = 'curious';
|
||||
next.attentionUntil = now + 900 + Math.random() * 900;
|
||||
next.nextAttentionAt = now + 4200 + Math.random() * 4200;
|
||||
next.nextDecisionAt = now + 1500 + Math.random() * 900;
|
||||
next.pendingFacing = cursorTarget >= next.x ? 1 : -1;
|
||||
if (isMovingMode) {
|
||||
next.targetX = null;
|
||||
next.mode = 'idle';
|
||||
next.pendingMode = 'inspect';
|
||||
next.pendingModeAt = now + 900;
|
||||
next.attentionUntil = 0;
|
||||
next.nextDecisionAt = now + 900;
|
||||
} else {
|
||||
next.targetX = null;
|
||||
next.mode = 'inspect';
|
||||
next.mood = 'curious';
|
||||
next.facing = next.pendingFacing;
|
||||
next.attentionUntil = now + 900 + Math.random() * 900;
|
||||
next.nextAttentionAt = now + 4200 + Math.random() * 4200;
|
||||
next.nextDecisionAt = now + 1500 + Math.random() * 900;
|
||||
}
|
||||
} else {
|
||||
next.nextAttentionAt = now + 1800 + Math.random() * 2800;
|
||||
}
|
||||
} else if (now >= next.nextDecisionAt) {
|
||||
const roll = Math.random();
|
||||
if (roll < 0.2) {
|
||||
next.mode = 'sleep';
|
||||
next.sleepUntil = now + 3500 + Math.random() * 5000;
|
||||
next.targetX = null;
|
||||
next.attentionUntil = 0;
|
||||
if (isMovingMode) {
|
||||
next.mode = 'idle';
|
||||
next.targetX = null;
|
||||
next.attentionUntil = 0;
|
||||
next.pendingMode = 'sleep';
|
||||
next.pendingModeAt = now + 900;
|
||||
} else {
|
||||
next.mode = 'sleep';
|
||||
next.sleepUntil = now + randomSleepDuration();
|
||||
next.targetX = null;
|
||||
next.attentionUntil = 0;
|
||||
}
|
||||
} else if (roll < 0.78) {
|
||||
next.mode = 'walk';
|
||||
next.targetX = clamp(24 + Math.random() * (width - 48), 16, laneMax);
|
||||
next.attentionUntil = now + 1200 + Math.random() * 1600;
|
||||
next.pendingMode = null;
|
||||
next.pendingModeAt = 0;
|
||||
} else {
|
||||
next.mode = 'idle';
|
||||
next.targetX = null;
|
||||
next.attentionUntil = 0;
|
||||
next.pendingMode = null;
|
||||
next.pendingModeAt = 0;
|
||||
next.idleClip = randomIdleClip();
|
||||
}
|
||||
next.mood = 'calm';
|
||||
next.nextDecisionAt = now + 3200 + Math.random() * 3400;
|
||||
@@ -546,14 +755,20 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
next.mode = 'idle';
|
||||
next.nextDecisionAt = now + 1600 + Math.random() * 1600;
|
||||
next.nextAttentionAt = now + 2000 + Math.random() * 2000;
|
||||
next.pendingMode = null;
|
||||
next.pendingModeAt = 0;
|
||||
next.idleClip = randomIdleClip();
|
||||
}
|
||||
|
||||
if (typeof next.targetX === 'number') {
|
||||
if (typeof next.targetX === 'number' && (next.mode === 'walk' || next.mode === 'chase-toy' || next.mode === 'chase-cursor')) {
|
||||
const dx = next.targetX - next.x;
|
||||
if (Math.abs(dx) < 4) {
|
||||
next.targetX = null;
|
||||
next.attentionUntil = 0;
|
||||
next.mode = 'idle';
|
||||
next.pendingMode = null;
|
||||
next.pendingModeAt = 0;
|
||||
next.idleClip = randomIdleClip();
|
||||
} else {
|
||||
next.facing = dx >= 0 ? 1 : -1;
|
||||
next.x = clamp(next.x + Math.sign(dx) * travel, 12, laneMax);
|
||||
@@ -561,6 +776,8 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
next.mode = 'walk';
|
||||
}
|
||||
}
|
||||
} else if (typeof next.targetX === 'number') {
|
||||
next.targetX = null;
|
||||
} else if (next.mode === 'walk') {
|
||||
next.mode = 'idle';
|
||||
}
|
||||
@@ -569,12 +786,31 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
next.facing *= -1;
|
||||
}
|
||||
|
||||
const desiredClip = modeToClip(next.mode);
|
||||
const desiredClip = modeToClip(next);
|
||||
const animationState = resolveAnimationState(next, desiredClip, now, runtimeConfig.reducedMotion);
|
||||
const previousClip = next.clip;
|
||||
next.clip = animationState.clip;
|
||||
next.clipStartedAt = animationState.clipStartedAt;
|
||||
next.transitionTo = animationState.transitionTo;
|
||||
|
||||
const petAsset = getPetAsset(next.type);
|
||||
const defaultClipName = getDefaultClipName(petAsset);
|
||||
const activeClip = petAsset.clips[next.clip] || petAsset.clips[defaultClipName];
|
||||
const frameDuration = 1000 / getEffectiveClipFps(activeClip, runtimeConfig.reducedMotion);
|
||||
|
||||
if (previousClip !== next.clip) {
|
||||
next.frameIndex = 0;
|
||||
next.nextFrameAt = now + frameDuration;
|
||||
} else if (now >= (next.nextFrameAt || 0)) {
|
||||
const lastIndex = Math.max(activeClip.frames.length - 1, 0);
|
||||
const currentIndex = typeof next.frameIndex === 'number' ? next.frameIndex : 0;
|
||||
next.frameIndex = lastIndex === 0 ? 0 : currentIndex + 1;
|
||||
if (next.frameIndex > lastIndex) {
|
||||
next.frameIndex = activeClip.nextClip ? lastIndex : 0;
|
||||
}
|
||||
next.nextFrameAt = now + frameDuration;
|
||||
}
|
||||
|
||||
return next;
|
||||
});
|
||||
|
||||
@@ -598,11 +834,58 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
}
|
||||
|
||||
renderPet(pet) {
|
||||
const frame = getAnimatedFrame(pet, runtimeConfig.reducedMotion, Date.now());
|
||||
const petAsset = getPetAsset(pet.type);
|
||||
const frameInfo = getAnimatedFrame(pet, runtimeConfig.reducedMotion, Date.now());
|
||||
const frame = frameInfo.frame;
|
||||
const desiredFacing = pet.facing >= 0 ? 'right' : 'left';
|
||||
const basisFacing = frame.facingBasis || 'left';
|
||||
const flipX = desiredFacing !== basisFacing;
|
||||
const frameRow = frame.frameY != null ? Math.round(frame.frameY / frame.frameHeight) + 1 : null;
|
||||
const frameCol = frame.frameX != null ? Math.round(frame.frameX / frame.frameWidth) + 1 : null;
|
||||
const debugToken = frameRow != null && frameCol != null
|
||||
? `${frameInfo.clipName}:${frameInfo.frameIndex + 1}/${frameInfo.frameCount}@r${frameRow}c${frameCol}`
|
||||
: `${frameInfo.clipName}:${frameInfo.frameIndex + 1}/${frameInfo.frameCount}@svg`;
|
||||
const existingHistory = this._debugFrameHistory.get(pet.id) || [];
|
||||
const nextHistory = existingHistory.length > 0 && existingHistory[existingHistory.length - 1] === debugToken
|
||||
? existingHistory
|
||||
: existingHistory.concat(debugToken).slice(-5);
|
||||
this._debugFrameHistory.set(pet.id, nextHistory);
|
||||
if (runtimeConfig.debugFrames) {
|
||||
const previousToken = this._debugLastLoggedToken.get(pet.id);
|
||||
if (previousToken !== debugToken) {
|
||||
this._debugLastLoggedToken.set(pet.id, debugToken);
|
||||
console.log(
|
||||
`[hyperpets] ${pet.id} mode=${pet.mode} facing=${desiredFacing} basis=${basisFacing} clip=${frameInfo.clipName} frame=${frameInfo.frameIndex + 1}/${frameInfo.frameCount}` +
|
||||
(frameRow != null && frameCol != null ? ` cell=r${frameRow}c${frameCol}` : ' cell=svg')
|
||||
);
|
||||
}
|
||||
}
|
||||
const className = [
|
||||
'hyperpets-pet',
|
||||
pet.mood === 'excited' ? 'excited' : ''
|
||||
].filter(Boolean).join(' ');
|
||||
const frameNode = frame.svg
|
||||
? React.createElement('div', {
|
||||
className: 'hyperpets-frame',
|
||||
style: {
|
||||
width: `${petAsset.width}px`,
|
||||
height: `${petAsset.height}px`,
|
||||
transform: `scaleX(${flipX ? -1 : 1}) translateZ(0)`
|
||||
},
|
||||
dangerouslySetInnerHTML: { __html: frame.svg }
|
||||
})
|
||||
: React.createElement('div', {
|
||||
className: `hyperpets-frame${runtimeConfig.debugFrames ? ' hyperpets-frame-debug' : ''}`,
|
||||
style: {
|
||||
width: `${petAsset.width}px`,
|
||||
height: `${petAsset.height}px`,
|
||||
transform: `scaleX(${flipX ? -1 : 1}) translateZ(0)`,
|
||||
backgroundImage: `url("${frame.sheetUrl}")`,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundSize: `${(frame.sheetWidth / frame.frameWidth) * petAsset.width}px ${(frame.sheetHeight / frame.frameHeight) * petAsset.height}px`,
|
||||
backgroundPosition: `-${(frame.frameX / frame.frameWidth) * petAsset.width}px -${(frame.frameY / frame.frameHeight) * petAsset.height}px`
|
||||
}
|
||||
});
|
||||
|
||||
return React.createElement(
|
||||
'div',
|
||||
@@ -611,16 +894,25 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
className,
|
||||
style: {
|
||||
left: `${pet.x}px`,
|
||||
transform: `scaleX(${pet.facing}) translateZ(0)`
|
||||
width: `${petAsset.width}px`,
|
||||
height: `${petAsset.height}px`
|
||||
}
|
||||
},
|
||||
React.createElement(
|
||||
'div',
|
||||
{
|
||||
className: 'hyperpets-frame',
|
||||
dangerouslySetInnerHTML: { __html: frame.svg }
|
||||
}
|
||||
),
|
||||
frameNode,
|
||||
runtimeConfig.debugFrames
|
||||
? React.createElement(
|
||||
'div',
|
||||
{ className: 'hyperpets-debug-label' },
|
||||
[
|
||||
`${frameInfo.clipName} ${frameInfo.frameIndex + 1}/${frameInfo.frameCount}`,
|
||||
`face ${desiredFacing} basis ${basisFacing}`,
|
||||
frameRow != null && frameCol != null
|
||||
? `cell r${frameRow} c${frameCol}`
|
||||
: 'svg-frame',
|
||||
`hist ${nextHistory.join(' | ')}`
|
||||
].join('\n')
|
||||
)
|
||||
: null,
|
||||
pet.mode === 'sleep'
|
||||
? React.createElement('div', {
|
||||
className: 'hyperpets-z',
|
||||
@@ -631,13 +923,29 @@ exports.decorateTerm = (Term, { React }) => {
|
||||
}
|
||||
|
||||
render() {
|
||||
refreshRuntimeConfigFromRenderer();
|
||||
if (!runtimeConfig.enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const debugHud = runtimeConfig.debugFrames
|
||||
? React.createElement(
|
||||
'div',
|
||||
{ className: 'hyperpets-debug-hud' },
|
||||
this.state.pets.map((pet) => {
|
||||
const history = this._debugFrameHistory.get(pet.id) || [];
|
||||
return [
|
||||
`${pet.id} mode=${pet.mode} facing=${pet.facing >= 0 ? 'right' : 'left'}`,
|
||||
history.length ? `hist ${history.join(' | ')}` : 'hist (waiting for frames)'
|
||||
].join('\n');
|
||||
}).join('\n\n')
|
||||
)
|
||||
: null;
|
||||
|
||||
return React.createElement(
|
||||
'div',
|
||||
{ className: 'hyperpets-layer' },
|
||||
debugHud,
|
||||
React.createElement('div', { className: 'hyperpets-floor' }),
|
||||
this.state.toys.map((toy) => this.renderToy(toy)),
|
||||
this.state.pets.map((pet) => this.renderPet(pet))
|
||||
|
||||
Reference in New Issue
Block a user