hmm
This commit is contained in:
parent
e8f1d1db1e
commit
54aff02658
27 changed files with 1700 additions and 1034 deletions
214
js/structures.js
Normal file
214
js/structures.js
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
"use strict";
|
||||
|
||||
(function (global) {
|
||||
function createStructureId(prefix = "structure") {
|
||||
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;
|
||||
}
|
||||
|
||||
function clonePlain(value) {
|
||||
return JSON.parse(JSON.stringify(value || {}));
|
||||
}
|
||||
|
||||
class TarinaiStructure {
|
||||
constructor(definition, owner, x, y, world) {
|
||||
this.id = createStructureId("structure");
|
||||
this.type = definition.type;
|
||||
this.label = definition.label;
|
||||
this.isStructure = true;
|
||||
this.ownerId = owner?.id || "";
|
||||
this.hp = definition.maxHp || 1;
|
||||
this.maxHp = definition.maxHp || this.hp;
|
||||
this.attachment = definition.type === "plushie" ? 30 : 20;
|
||||
this.usedCount = 0;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.r = definition.type === "plushie" ? 13 : 24;
|
||||
this.amount = this.hp;
|
||||
this.seed = Math.random() * 1000;
|
||||
this.roles = clonePlain(definition.roles);
|
||||
this.needEffects = clonePlain(definition.needEffects);
|
||||
this.carriedById = definition.type === "plushie" ? this.ownerId : "";
|
||||
this.onHead = definition.type === "plushie";
|
||||
this.dead = false;
|
||||
this.world = world || null;
|
||||
}
|
||||
|
||||
owner(worldRef = this.world) {
|
||||
return (worldRef?.tarinai || []).find(t => t && !t.dead && t.id === this.ownerId) || null;
|
||||
}
|
||||
|
||||
update(dt, worldRef = this.world) {
|
||||
this.world = worldRef || this.world;
|
||||
if (this.dead || this.hp <= 0) {
|
||||
this.amount = 0;
|
||||
return;
|
||||
}
|
||||
if (this.type === "plushie") {
|
||||
const owner = this.owner(worldRef);
|
||||
if (!owner) {
|
||||
this.damage(1, worldRef, null);
|
||||
return;
|
||||
}
|
||||
this.x = owner.x;
|
||||
this.y = owner.y - Math.max(18, (owner.radius || 24) * 0.78);
|
||||
if (owner.needs) owner.needs.fulfill = Math.max(0, (owner.needs.fulfill || 0) - dt * 2.8);
|
||||
}
|
||||
this.amount = Math.max(0.001, this.hp);
|
||||
}
|
||||
|
||||
use(user, worldRef = this.world) {
|
||||
const definition = global.StructureRegistry?.get?.(this.type);
|
||||
return definition?.onUse?.(user, this, worldRef) || false;
|
||||
}
|
||||
|
||||
damage(amount = 1, worldRef = this.world, breaker = null) {
|
||||
if (this.dead) return false;
|
||||
this.hp = Math.max(0, (this.hp || 0) - Math.max(0, amount || 0));
|
||||
this.amount = this.hp;
|
||||
if (this.hp > 0) return false;
|
||||
this.dead = true;
|
||||
this.amount = 0;
|
||||
global.StructureRegistry?.get?.(this.type)?.onDestroyed?.(this, worldRef, breaker);
|
||||
return true;
|
||||
}
|
||||
|
||||
draw(ctx, time = 0, lighting = {}) {
|
||||
if (this.dead) return;
|
||||
ctx.save();
|
||||
ctx.translate(this.x, this.y);
|
||||
const night = (lighting?.light || 1) < 0.42;
|
||||
if (this.type === "grass_bed") {
|
||||
ctx.fillStyle = night ? "rgba(108,129,77,0.88)" : "rgba(134,174,83,0.92)";
|
||||
ctx.strokeStyle = night ? "rgba(55,73,49,0.72)" : "rgba(72,105,50,0.72)";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(0, 4, this.r * 1.55, this.r * 0.62, -0.08, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
ctx.strokeStyle = night ? "rgba(185,203,142,0.35)" : "rgba(231,229,139,0.55)";
|
||||
for (let i = 0; i < 12; i++) {
|
||||
const a = -0.8 + i * 0.145;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-this.r * 1.05 + i * this.r * 0.19, this.r * 0.38);
|
||||
ctx.quadraticCurveTo(Math.sin(time + i) * 4, -this.r * 0.12, Math.cos(a) * this.r * 0.92, -this.r * 0.42 + Math.sin(i) * 4);
|
||||
ctx.stroke();
|
||||
}
|
||||
} else if (this.type === "plushie") {
|
||||
const img = typeof getRenderableImage === "function" ? getRenderableImage("smile", "smile") : global.images?.get?.("smile");
|
||||
ctx.rotate(Math.sin(time * 2 + this.seed) * 0.05);
|
||||
if (img) {
|
||||
const w = this.r * 2.2;
|
||||
const metrics = typeof getImageMetrics === "function" ? getImageMetrics("smile") : null;
|
||||
const h = w * (metrics?.ratio || 0.92);
|
||||
ctx.drawImage(img, -w * 0.5, -h * 0.58, w, h);
|
||||
} else {
|
||||
ctx.fillStyle = "#f7dfc0";
|
||||
ctx.strokeStyle = "rgba(86,62,48,0.70)";
|
||||
ctx.lineWidth = 1.7;
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, this.r, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
ctx.fillStyle = "rgba(52,43,38,0.88)";
|
||||
ctx.beginPath(); ctx.arc(-this.r * 0.28, -this.r * 0.08, 1.5, 0, Math.PI * 2); ctx.fill();
|
||||
ctx.beginPath(); ctx.arc(this.r * 0.28, -this.r * 0.08, 1.5, 0, Math.PI * 2); ctx.fill();
|
||||
}
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
const StructureRegistry = {
|
||||
defs: {},
|
||||
register(definition) { this.defs[definition.type] = definition; return definition; },
|
||||
get(type) { return this.defs[type] || null; },
|
||||
create(type, owner, x, y, world) {
|
||||
const definition = this.get(type);
|
||||
if (!definition) return null;
|
||||
return definition.create ? definition.create(owner, x, y, world) : new TarinaiStructure(definition, owner, x, y, world);
|
||||
},
|
||||
};
|
||||
|
||||
function applyStructureNeedShock(owner, safetyGain, fulfillGain, breaker = null) {
|
||||
if (!owner || owner.dead) return;
|
||||
if (typeof global.applyNeedShock === "function") global.applyNeedShock(owner, { safety: safetyGain || 0, fulfill: fulfillGain || 0 }, breaker);
|
||||
else {
|
||||
owner.needShock = owner.needShock || {};
|
||||
owner.needShock.safety = Math.max(owner.needShock.safety || 0, safetyGain || 0);
|
||||
owner.needShock.fulfill = Math.max(owner.needShock.fulfill || 0, fulfillGain || 0);
|
||||
owner.lastNeedShockBreaker = breaker || null;
|
||||
}
|
||||
if (typeof global.triggerNeedShockReaction === "function") {
|
||||
const reason = breaker ? "\u81ea\u5206\u306e\u3082\u306e\u304c\u58ca\u308c\u3066\u30d1\u30cb\u30c3\u30af\u306b\u306a\u3063\u3066\u3044\u308b" : "\u81ea\u5206\u306e\u3082\u306e\u304c\u58ca\u308c\u3066\u3044\u308b";
|
||||
global.triggerNeedShockReaction(owner, breaker, reason);
|
||||
}
|
||||
}
|
||||
|
||||
function damageNearbyStructures(worldRef, x, y, radius, amount, breaker = null) {
|
||||
let hit = 0;
|
||||
for (const structure of worldRef?.nearbyItems?.(x, y, radius + 80) || worldRef?.items || []) {
|
||||
if (!structure?.isStructure || structure.dead) continue;
|
||||
const d = distXY(x, y, structure.x, structure.y);
|
||||
const p = clamp(1 - d / Math.max(1, radius + (structure.r || 12)), 0, 1);
|
||||
if (p <= 0) continue;
|
||||
if (structure.damage?.(Math.max(1, amount * (0.35 + p * 0.65)), worldRef, breaker)) hit++;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
StructureRegistry.register({
|
||||
type: "grass_bed",
|
||||
label: "\u8349\u306e\u7c21\u6613\u30d9\u30c3\u30c9",
|
||||
buildNeed: "fulfill",
|
||||
buildTime: 4,
|
||||
maxHp: 100,
|
||||
materials: { grassMaterial: 1 },
|
||||
roles: { sleepPlace: true, ownedStructure: true },
|
||||
needEffects: { food: 0, sleep: 60, health: 5, safety: 25, social: 0, fulfill: 40 },
|
||||
create(owner, x, y, world) { return new TarinaiStructure(this, owner, x, y, world); },
|
||||
onUse(user, structure) {
|
||||
const ownerUse = user?.id && user.id === structure.ownerId;
|
||||
if (ownerUse) structure.attachment = Math.min(100, (structure.attachment || 0) + 2);
|
||||
structure.usedCount = (structure.usedCount || 0) + 1;
|
||||
if (user) {
|
||||
user.needShock = user.needShock || {};
|
||||
user.needShock.sleep = Math.min(0, (user.needShock.sleep || 0) - (ownerUse ? 18 : 8));
|
||||
user.needShock.safety = Math.min(0, (user.needShock.safety || 0) - (ownerUse ? 12 : 4));
|
||||
user.needShock.fulfill = Math.min(0, (user.needShock.fulfill || 0) - (ownerUse ? 18 : 6));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onDestroyed(structure, world, breaker) {
|
||||
applyStructureNeedShock(structure.owner(world), 42, 46, breaker);
|
||||
},
|
||||
});
|
||||
|
||||
StructureRegistry.register({
|
||||
type: "plushie",
|
||||
label: "\u306c\u3044\u3050\u308b\u307f",
|
||||
buildNeed: "fulfill",
|
||||
buildTime: 4,
|
||||
maxHp: 1,
|
||||
materials: { grassMaterial: 1 },
|
||||
roles: { carriedObject: true, ownedStructure: true, fulfillObject: true },
|
||||
needEffects: { food: 0, sleep: 0, health: 0, safety: 5, social: 0, fulfill: 50 },
|
||||
create(owner, x, y, world) { return new TarinaiStructure(this, owner, x, y, world); },
|
||||
onUse(user, structure) {
|
||||
if (user?.id === structure.ownerId) structure.attachment = Math.min(100, (structure.attachment || 0) + 1);
|
||||
structure.usedCount = (structure.usedCount || 0) + 1;
|
||||
if (user?.id === structure.ownerId) {
|
||||
user.needShock = user.needShock || {};
|
||||
user.needShock.fulfill = Math.min(0, (user.needShock.fulfill || 0) - 24);
|
||||
user.needShock.safety = Math.min(0, (user.needShock.safety || 0) - 4);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onDestroyed(structure, world, breaker) {
|
||||
applyStructureNeedShock(structure.owner(world), 55, 64, breaker);
|
||||
},
|
||||
});
|
||||
|
||||
global.TarinaiStructure = TarinaiStructure;
|
||||
global.StructureRegistry = StructureRegistry;
|
||||
global.damageNearbyStructures = damageNearbyStructures;
|
||||
})(typeof window !== "undefined" ? window : globalThis);
|
||||
Loading…
Add table
Add a link
Reference in a new issue