hmm
This commit is contained in:
parent
10fbeb2327
commit
c6ba3e38a2
21 changed files with 1565 additions and 898 deletions
234
js/items.js
234
js/items.js
|
|
@ -76,6 +76,12 @@ class Item {
|
|||
this.fuseTimer = 5;
|
||||
this.fuseMax = 5;
|
||||
}
|
||||
if (type === "genkotsu") {
|
||||
this.amount = 100;
|
||||
this.dropMax = 0;
|
||||
this.dropImpactDone = false;
|
||||
this.impactFlash = 0;
|
||||
}
|
||||
if (isServingFoodType(type)) {
|
||||
this.toolSize = "medium";
|
||||
this.foodServingScale = 1;
|
||||
|
|
@ -106,6 +112,11 @@ class Item {
|
|||
else this.amount = 0;
|
||||
}
|
||||
}
|
||||
if (this.type === "genkotsu") {
|
||||
this.impactFlash = Math.max(0, (this.impactFlash || 0) - dt);
|
||||
if (this.dropImpactDone) this.amount -= dt * 115;
|
||||
else this.amount = Math.max(this.amount || 0, 100);
|
||||
}
|
||||
if (this.type === "ball") this.updateBall(dt, worldRef);
|
||||
if (this.type === "zunchi") this.updateZunchiMotion(dt, worldRef);
|
||||
if (this.type === "grass") {
|
||||
|
|
@ -176,8 +187,6 @@ class Item {
|
|||
const speed = Math.hypot(this.vx || 0, this.vy || 0);
|
||||
const searchRadius = Math.max(150, (this.r || 18) + speed * Math.max(dt || 0.016, 0.016) + 120);
|
||||
const items = worldRef.nearbyItems(this.x, this.y, searchRadius) || [];
|
||||
let solidChecks = 0;
|
||||
const maxSolidChecks = CONFIG.maxFenceCollisionChecks ?? 24;
|
||||
for (const it of items) {
|
||||
if (!it || it === this || it.dead) continue;
|
||||
if (it.type === "bed") {
|
||||
|
|
@ -187,8 +196,6 @@ class Item {
|
|||
}
|
||||
const rects = worldRef.solidObstacleRects ? worldRef.solidObstacleRects(it) : [];
|
||||
if (!rects.length) continue;
|
||||
if (solidChecks >= maxSolidChecks) continue;
|
||||
solidChecks += 1;
|
||||
for (const rect of rects) this.resolveBallRectBounce(rect, worldRef, it);
|
||||
}
|
||||
}
|
||||
|
|
@ -467,148 +474,6 @@ class Item {
|
|||
get dead() {
|
||||
return this.amount <= 0;
|
||||
}
|
||||
serialize() {
|
||||
return {
|
||||
type: this.type, x: this.x, y: this.y, amount: this.amount, age: this.age, seed: this.seed,
|
||||
stage: this.stage, stageTimer: this.stageTimer, fertility: this.fertility, freshness: this.freshness,
|
||||
growth: this.growth, health: this.health, seedTimer: this.seedTimer, eatenAmount: this.eatenAmount, fertilityBoost: this.fertilityBoost, lifeSpan: this.lifeSpan, wither: this.wither,
|
||||
comfort: this.comfort, wear: this.wear, crawlOpen: this.crawlOpen, zunchiVariant: this.zunchiVariant, toolSize: this.toolSize, foodServingScale: this.foodServingScale, foodServingsMax: this.foodServingsMax, foodServingsRemaining: this.foodServingsRemaining, blastScale: this.blastScale, fuseTimer: this.fuseTimer, fuseMax: this.fuseMax, dropTimer: this.dropTimer, dropMax: this.dropMax,
|
||||
vx: this.vx, vy: this.vy, prevX: this.prevX, prevY: this.prevY,
|
||||
spin: this.spin, spinVelocity: this.spinVelocity, lastKickedAt: this.lastKickedAt, lastKickerId: this.lastKickerId,
|
||||
lastPokedAt: this.lastPokedAt, pokeCombo: this.pokeCombo, lastPokeAngle: this.lastPokeAngle,
|
||||
antCount: this.antCount, antSpawnTimer: this.antSpawnTimer, antSpawnCooldown: this.antSpawnCooldown, antWorkers: Array.isArray(this.antWorkers) ? this.antWorkers.slice() : undefined, queenSpawnAt: this.queenSpawnAt, workerSprite: this.workerSprite, decayTimer: this.decayTimer
|
||||
};
|
||||
}
|
||||
static from(o) {
|
||||
const type = o?.type || "grass";
|
||||
const it = Object.create(Item.prototype);
|
||||
Item._loadId = (Item._loadId || 0) + 1;
|
||||
it.id = o.id || `loaded-${Item._loadId}`;
|
||||
it.type = type;
|
||||
it.x = o.x;
|
||||
it.y = o.y;
|
||||
it.r = itemRadiusFor(type, 12);
|
||||
it.amount = o.amount ?? itemAmountFor(type, 80);
|
||||
it.age = o.age || 0;
|
||||
it.seed = o.seed || Math.random() * 1000;
|
||||
it.dropTimer = 0;
|
||||
it.dropMax = 0;
|
||||
it.dropImpactDone = false;
|
||||
it.lifecycleTimer = o.lifecycleTimer ?? rand(0, type === "grass" ? 1.15 : 0.55);
|
||||
it.lifecycleInterval = o.lifecycleInterval ?? (type === "grass" ? (1.75 + stableUnit(it.id, "grass-life") * 0.90) : (type === "zunchi" ? (0.95 + stableUnit(it.id, "zunchi-life") * 0.45) : 0));
|
||||
if (type === "grass") {
|
||||
it.growth = 0.25;
|
||||
it.health = 1;
|
||||
it.seedTimer = rand(18, 38);
|
||||
it.eatenAmount = 0;
|
||||
it.fertilityBoost = 0;
|
||||
}
|
||||
if (type === "bed") {
|
||||
it.comfort = 1;
|
||||
it.wear = 0;
|
||||
}
|
||||
if (type === "ball") {
|
||||
it.vx = o.vx || 0;
|
||||
it.vy = o.vy || 0;
|
||||
it.prevX = o.prevX ?? o.x;
|
||||
it.prevY = o.prevY ?? o.y;
|
||||
it.spin = o.spin || 0;
|
||||
it.spinVelocity = o.spinVelocity || 0;
|
||||
it.lastKickedAt = o.lastKickedAt ?? -999;
|
||||
it.lastKickerId = o.lastKickerId || "";
|
||||
it.lastPokedAt = o.lastPokedAt ?? -999;
|
||||
it.pokeCombo = o.pokeCombo || 0;
|
||||
it.lastPokeAngle = o.lastPokeAngle || rand(0, Math.PI * 2);
|
||||
}
|
||||
if (type === "zunchi") {
|
||||
it.stage = "fresh";
|
||||
it.stageTimer = 0;
|
||||
it.fertility = 0;
|
||||
it.freshness = 1;
|
||||
it.zunchiVariant = "zunchi";
|
||||
}
|
||||
if (type === "ant_nest") {
|
||||
it.antCount = Number.isFinite(o.antCount) ? clamp(Math.round(o.antCount), 0, ANT_NEST_MAX_COUNT || 10) : (ANT_NEST_START_COUNT || 6);
|
||||
it.antSpawnTimer = o.antSpawnTimer ?? rand(0.6, 2.2);
|
||||
it.antSpawnCooldown = o.antSpawnCooldown ?? rand(0.8, 2.4);
|
||||
const legacyPool = Array.isArray(o.antWorkers) ? o.antWorkers : (Array.isArray(o.workerHpPool) ? o.workerHpPool : null);
|
||||
it.antWorkers = Array.isArray(legacyPool)
|
||||
? legacyPool.map(v => clamp(Number(v) || ANT_WORKER_HP || 32, 0.1, ANT_WORKER_HP || 32)).slice(0, ANT_NEST_MAX_COUNT || 10)
|
||||
: (typeof buildAntWorkerPool === "function"
|
||||
? buildAntWorkerPool(it.antCount)
|
||||
: Array.from({ length: it.antCount }, () => ANT_WORKER_HP || 32));
|
||||
it.queenSpawnAt = Number.isFinite(o.queenSpawnAt) ? o.queenSpawnAt : -999;
|
||||
}
|
||||
if (type === "ant_corpse") {
|
||||
it.workerSprite = o.workerSprite || "ant_worker";
|
||||
it.decayTimer = o.decayTimer || 0;
|
||||
}
|
||||
if (type === "firecracker") {
|
||||
it.fuseTimer = 5;
|
||||
it.fuseMax = 5;
|
||||
}
|
||||
it.stage = o.stage || it.stage;
|
||||
it.stageTimer = o.stageTimer || 0;
|
||||
it.fertility = o.fertility || 0;
|
||||
it.freshness = o.freshness ?? it.freshness;
|
||||
it.growth = o.growth ?? it.growth;
|
||||
it.health = o.health ?? it.health;
|
||||
it.seedTimer = o.seedTimer ?? it.seedTimer;
|
||||
it.eatenAmount = o.eatenAmount || 0;
|
||||
it.fertilityBoost = o.fertilityBoost || 0;
|
||||
it.lifeSpan = o.lifeSpan ?? it.lifeSpan;
|
||||
it.wither = o.wither ?? it.wither ?? 0;
|
||||
it.comfort = o.comfort ?? it.comfort;
|
||||
it.wear = o.wear ?? it.wear;
|
||||
it.crawlOpen = o.crawlOpen ?? it.crawlOpen;
|
||||
it.vx = o.vx ?? it.vx ?? 0;
|
||||
it.vy = o.vy ?? it.vy ?? 0;
|
||||
it.prevX = o.prevX ?? it.prevX ?? it.x;
|
||||
it.prevY = o.prevY ?? it.prevY ?? it.y;
|
||||
it.spin = o.spin ?? it.spin ?? 0;
|
||||
it.spinVelocity = o.spinVelocity ?? it.spinVelocity ?? 0;
|
||||
it.lastKickedAt = o.lastKickedAt ?? it.lastKickedAt ?? -999;
|
||||
it.lastKickerId = o.lastKickerId || it.lastKickerId || "";
|
||||
it.lastPokedAt = o.lastPokedAt ?? it.lastPokedAt ?? -999;
|
||||
it.pokeCombo = o.pokeCombo ?? it.pokeCombo ?? 0;
|
||||
it.lastPokeAngle = o.lastPokeAngle ?? it.lastPokeAngle ?? rand(0, Math.PI * 2);
|
||||
it.zunchiVariant = o.zunchiVariant || it.zunchiVariant || "zunchi";
|
||||
it.antCount = Number.isFinite(o.antCount) ? clamp(Math.round(o.antCount), 0, ANT_NEST_MAX_COUNT || 10) : it.antCount;
|
||||
it.antSpawnTimer = o.antSpawnTimer ?? it.antSpawnTimer;
|
||||
it.antSpawnCooldown = o.antSpawnCooldown ?? it.antSpawnCooldown;
|
||||
if (type === "ant_nest") {
|
||||
const legacyPool = Array.isArray(o.antWorkers) ? o.antWorkers : (Array.isArray(o.workerHpPool) ? o.workerHpPool : null);
|
||||
if (Array.isArray(legacyPool)) {
|
||||
it.antWorkers = legacyPool.map(v => clamp(Number(v) || 0, 0, ANT_WORKER_HP || 32)).filter(v => v > 0).slice(0, ANT_NEST_MAX_COUNT || 10);
|
||||
} else if (!Array.isArray(it.antWorkers)) {
|
||||
it.antWorkers = typeof buildAntWorkerPool === "function"
|
||||
? buildAntWorkerPool(it.antCount || (ANT_NEST_START_COUNT || 6))
|
||||
: Array.from({ length: it.antCount || (ANT_NEST_START_COUNT || 6) }, () => ANT_WORKER_HP || 32);
|
||||
}
|
||||
it.queenSpawnAt = Number.isFinite(o.queenSpawnAt) ? o.queenSpawnAt : (it.queenSpawnAt ?? -999);
|
||||
}
|
||||
it.workerSprite = o.workerSprite || it.workerSprite;
|
||||
it.decayTimer = o.decayTimer ?? it.decayTimer;
|
||||
it.toolSize = o.toolSize || it.toolSize || "medium";
|
||||
it.foodServingScale = o.foodServingScale || it.foodServingScale || ({ small: 0.68, medium: 1.0, large: 1.48 }[it.toolSize] || 1);
|
||||
if (isServingFoodType(it.type)) {
|
||||
const servingMax = foodServingsForSize(it.toolSize);
|
||||
it.foodServingsMax = Number.isFinite(o.foodServingsMax) ? o.foodServingsMax : servingMax;
|
||||
if (Number.isFinite(o.foodServingsRemaining)) {
|
||||
it.foodServingsRemaining = clamp(o.foodServingsRemaining, 0, it.foodServingsMax);
|
||||
} else {
|
||||
it.foodServingsRemaining = Math.max(1, Math.min(it.foodServingsMax, Math.ceil(it.foodServingsMax * clamp((o.amount ?? it.amount) / 70, 0.05, 1))));
|
||||
}
|
||||
it.amount = it.foodServingsRemaining;
|
||||
}
|
||||
it.blastScale = o.blastScale || it.blastScale || 1;
|
||||
it.fuseTimer = o.fuseTimer ?? it.fuseTimer;
|
||||
it.fuseMax = o.fuseMax ?? it.fuseMax;
|
||||
it.dropTimer = 0;
|
||||
it.dropMax = 0;
|
||||
it.dropImpactDone = true;
|
||||
return it;
|
||||
}
|
||||
draw(ctx, t, lighting = null) {
|
||||
const lightState = lighting || getLightingState(world);
|
||||
const night = clamp(lightState.nightStrength * 1.35, 0, 1);
|
||||
|
|
@ -618,7 +483,8 @@ class Item {
|
|||
const servingRatio = isServingFoodType(this.type) ? ((this.foodServingsRemaining ?? this.amount) / Math.max(1, this.foodServingsMax || foodServingsForSize(this.toolSize || "medium"))) : null;
|
||||
const visibleAlpha = isServingFoodType(this.type) ? clamp(servingRatio ?? 1, 0.24, 1) : clamp(this.amount / 40, 0.24, 1);
|
||||
const dropT = this.dropMax > 0 ? clamp(this.dropTimer / this.dropMax, 0, 1) : 0;
|
||||
const dropY = dropT > 0 ? -170 * Math.pow(dropT, 0.42) : 0;
|
||||
const dropFallHeight = this.type === "genkotsu" ? Math.max(360, this.r * 5.8) : 170;
|
||||
const dropY = dropT > 0 ? -dropFallHeight * dropT : 0;
|
||||
const shadow = projectedShadowParams(lightState, Math.max(0.4, this.r / 18));
|
||||
if (this.type !== "trace" && this.type !== "splat") {
|
||||
const zunchiId = this.type === "zunchi" ? (this.zunchiVariant || "zunchi") : null;
|
||||
|
|
@ -689,13 +555,8 @@ class Item {
|
|||
ctx.ellipse(-this.r * 0.28, -this.r * 0.18, this.r * 0.23, this.r * 0.10, -0.35, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
} else if (fightMochi) {
|
||||
ctx.fillStyle = "#d55632";
|
||||
ctx.font = `${Math.round((this.r || 13) * 1.02)}px sans-serif`;
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillText("\u2726", 0, 1);
|
||||
ctx.strokeStyle = "rgba(168,76,42,0.72)";
|
||||
ctx.lineWidth = 1.4;
|
||||
ctx.strokeStyle = "rgba(168,76,42,0.82)";
|
||||
ctx.lineWidth = 1.8;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-this.r * 0.44, -this.r * 0.42);
|
||||
ctx.lineTo(this.r * 0.44, this.r * 0.42);
|
||||
|
|
@ -725,6 +586,42 @@ class Item {
|
|||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillText("Z", 0, this.r * 1.18);
|
||||
} else if (this.type === "genkotsu") {
|
||||
const img = typeof getRenderableImage === "function" ? getRenderableImage("genkotsu", "genkotsu") : images.get("genkotsu");
|
||||
const impact = clamp(this.impactFlash || 0, 0, 1);
|
||||
ctx.save();
|
||||
ctx.shadowColor = "rgba(54,42,31,0.26)";
|
||||
ctx.shadowBlur = 5 + impact * 8;
|
||||
ctx.shadowOffsetY = 4;
|
||||
const squashY = 1 - impact * 0.08;
|
||||
const stretchX = 1 + impact * 0.05;
|
||||
ctx.scale(stretchX, squashY);
|
||||
if (img) {
|
||||
const metrics = typeof getImageMetrics === "function" ? getImageMetrics("genkotsu") : null;
|
||||
const w = this.r * 2.55;
|
||||
const h = w * (metrics?.ratio || 1.27);
|
||||
ctx.drawImage(img, -w * 0.5, -h * 0.76, w, h);
|
||||
} else {
|
||||
ctx.fillStyle = "rgba(255,255,255,0.94)";
|
||||
ctx.strokeStyle = "rgba(64,64,64,0.78)";
|
||||
ctx.lineWidth = 5;
|
||||
roundedRect(ctx, -this.r * 0.72, -this.r * 1.45, this.r * 1.44, this.r * 1.84, 18);
|
||||
ctx.fill(); ctx.stroke();
|
||||
ctx.fillStyle = "rgba(42,46,52,0.92)";
|
||||
ctx.font = `bold ${Math.round(this.r * 0.45)}px sans-serif`;
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillText("\u6b63", 0, -this.r * 0.74);
|
||||
ctx.fillText("\u7fa9", 0, -this.r * 0.27);
|
||||
}
|
||||
if (impact > 0.01) {
|
||||
ctx.globalAlpha = impact * 0.20;
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(0, -this.r * 0.65, this.r * 0.92, this.r * 0.22, -0.10, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.restore();
|
||||
} else if (this.type === "water_bowl") {
|
||||
ctx.fillStyle = "rgba(171, 120, 76, 0.72)";
|
||||
ctx.strokeStyle = "rgba(94, 69, 48, 0.55)";
|
||||
|
|
@ -812,21 +709,26 @@ class Item {
|
|||
ctx.stroke();
|
||||
}
|
||||
} else if (this.type === "nest_box") {
|
||||
ctx.fillStyle = styleNight ? "#7d6040" : (styleWarm ? "#b98246" : "#9a6b3d");
|
||||
ctx.strokeStyle = styleNight ? "#4a3a2d" : "#5d4028";
|
||||
ctx.lineWidth = 2.4;
|
||||
ctx.fillStyle = styleNight ? "#5f4933" : (styleWarm ? "#89613e" : "#775237");
|
||||
ctx.strokeStyle = styleNight ? "#31261f" : "#3f2e24";
|
||||
ctx.lineWidth = 2.6;
|
||||
roundedRect(ctx, -this.r * 1.28, -this.r * 0.80, this.r * 2.56, this.r * 1.52, 9);
|
||||
ctx.fill(); ctx.stroke();
|
||||
ctx.fillStyle = styleNight ? "#3b2f27" : "#65432a";
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, -this.r * 0.02, this.r * 0.42, 0, Math.PI * 2);
|
||||
ctx.fillStyle = styleNight ? "#6e5439" : "#8a5f3b";
|
||||
roundedRect(ctx, -this.r * 1.12, -this.r * 0.62, this.r * 2.24, this.r * 1.18, 6);
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = styleNight ? "rgba(42,32,25,0.76)" : "rgba(65,45,32,0.72)";
|
||||
ctx.lineWidth = 2.0;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-this.r * 0.95, -this.r * 0.50);
|
||||
ctx.lineTo(this.r * 0.95, -this.r * 0.50);
|
||||
ctx.moveTo(-this.r * 0.95, this.r * 0.48);
|
||||
ctx.lineTo(this.r * 0.95, this.r * 0.48);
|
||||
ctx.stroke();
|
||||
ctx.fillStyle = styleNight ? "#2b211c" : "#3c281f";
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, -this.r * 0.08, this.r * 0.40, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.fillStyle = "rgba(255,230,152,0.38)";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(randSeed(this.seed + i, -this.r * 0.84, this.r * 0.84), randSeed(this.seed + 20 + i, this.r * 0.32, this.r * 0.68), this.r * 0.26, this.r * 0.045, randSeed(this.seed + 40 + i, -0.7, 0.7), 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
} else if (this.type === "ant_nest") {
|
||||
const count = clamp(Math.round(this.antCount ?? ANT_NEST_START_COUNT ?? 6), 0, ANT_NEST_MAX_COUNT ?? 10);
|
||||
ctx.fillStyle = styleNight ? "#5e4634" : (styleWarm ? "#956b42" : "#77583a");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue