feat: temperature system + XP on loot pickup

This commit is contained in:
Mk 2026-05-26 18:10:25 +00:00
parent 94e6f535d0
commit d948b1743d
2 changed files with 82 additions and 5 deletions

84
game.js
View File

@ -390,7 +390,6 @@ function customConfirm(msg, onYes) {
// Give loot to the killer
if (sm.kind === 'chicken') playSound('hurt_chicken');
spawnDrops(sm.x, sm.y, sm.kind);
grantXP(getMobXP(sm.kind));
rebuildHotbar();
}
serverMobs.delete(data.id);
@ -1207,6 +1206,7 @@ function customConfirm(msg, onYes) {
// UI
const hpEl = document.getElementById('hp');
const foodEl = document.getElementById('food');
const tempEl = document.getElementById("temp");
const sxEl = document.getElementById('sx');
const syEl = document.getElementById('sy');
const todEl = document.getElementById('tod');
@ -1256,6 +1256,7 @@ function customConfirm(msg, onYes) {
// Область карты — центрирована на игроке
const pGX = Math.floor(player.x / TILE);
const biome = getCachedBiome(pGX);
const pGY = Math.floor(player.y / TILE);
const viewW = Math.floor(mW / scale);
const viewH = Math.floor(mH / scale);
@ -2586,7 +2587,7 @@ registerProcessor('voice-playback', VoicePlaybackProcessor);
vy: -1 - Math.random() * 2,
item: entry.item,
qty: qty,
age: 0
age: 0, xpValue: getMobXP(kind)
});
}
}
@ -2618,6 +2619,7 @@ registerProcessor('voice-playback', VoicePlaybackProcessor);
if (dx*dx + dy*dy < 30*30) {
if (!inv[d.item]) inv[d.item] = 0;
inv[d.item] += d.qty;
if (d.xpValue) grantXP(Math.ceil(d.xpValue * 0.3));
drops.splice(i, 1);
rebuildHotbar();
}
@ -2707,6 +2709,7 @@ registerProcessor('voice-playback', VoicePlaybackProcessor);
xp: player.xp,
level: player.level
},
temperature: player.temperature,
inventory: inv,
time: worldTime,
isNight: isNightTime,
@ -2789,6 +2792,7 @@ registerProcessor('voice-playback', VoicePlaybackProcessor);
// Миграция версий
if(saveData.version === 1){
player.temperature = saveData.player.temperature !== undefined ? saveData.player.temperature : 15;
migrateV1toV2(saveData);
}
@ -3346,7 +3350,6 @@ registerProcessor('voice-playback', VoicePlaybackProcessor);
if(m.hp<=0){
if(m.kind === 'chicken') playSound('hurt_chicken');
spawnDrops(m.x + m.w/2, m.y + m.h/2, m.kind);
grantXP(getMobXP(m.kind));
// Remove from the correct array
if(m.id !== undefined){
serverMobs.delete(m.id);
@ -4256,6 +4259,66 @@ registerProcessor('voice-playback', VoicePlaybackProcessor);
// голод убывает, но HP не отнимает (как просили)
player.hunger = Math.max(0, player.hunger - dt*0.2); // замедлил в 4 раза
// ========== ТЕМПЕРАТУРА ==========
if (player.temperature === undefined) player.temperature = 15;
const BIOME_TEMP = { tundra: -20, plains: 15, swamp: 20, desert: 45, mountains: 5 };
const UNDERGROUND_TEMP = 15;
const COLD_THRESHOLD = -5;
const HEAT_THRESHOLD = 35;
const HEAT_RADIUS = 5;
const pGX = Math.floor(player.x / TILE);
const biome = getCachedBiome(pGX);
const pGY = Math.floor(player.y / TILE);
const sGY = surfaceGyAt(pGX);
const isUndergroundTemp = (sGY - pGY) > 2;
let targetTemp = BIOME_TEMP[biome] || 15;
if (isUndergroundTemp) targetTemp = UNDERGROUND_TEMP;
if (isNight() && !isUndergroundTemp) targetTemp -= 10;
if ((weatherState.type === 'rain' || weatherState.type === 'storm') && !isUndergroundTemp) targetTemp -= 5;
player.temperature += (targetTemp - player.temperature) * dt * 0.3;
let nearHeat = false, nearCool = false;
for (let dx = -HEAT_RADIUS; dx <= HEAT_RADIUS; dx++) {
for (let dy = -HEAT_RADIUS; dy <= HEAT_RADIUS; dy++) {
const b = getBlock(pGX + dx, pGY + dy);
if (!b || b.dead) continue;
if (b.t === 'campfire' || b.t === 'torch') nearHeat = true;
if (b.t === 'water' && biome === 'desert') nearCool = true;
}
}
if (nearHeat) {
player.temperature += (30 - player.temperature) * dt * 2;
if (player.hp < 100) player.hp = Math.min(100, player.hp + 8 * dt);
}
if (nearCool) {
player.temperature += (15 - player.temperature) * dt * 1.5;
if (player.hp < 100 && player.temperature > HEAT_THRESHOLD) {
player.hp = Math.min(100, player.hp + 8 * dt);
}
}
if (isUndergroundTemp && !nearHeat) {
if (player.hp < 100 && player.temperature >= COLD_THRESHOLD && player.temperature <= HEAT_THRESHOLD) {
player.hp = Math.min(100, player.hp + 2 * dt);
}
}
const aboveBlk = getBlock(pGX, pGY - 1);
const inShade = aboveBlk && BLOCKS[aboveBlk.t] && BLOCKS[aboveBlk.t].solid && aboveBlk.t !== 'glass';
if (inShade && !isUndergroundTemp && !nearCool) {
player.temperature -= 5 * dt;
if (player.temperature > HEAT_THRESHOLD - 2 && player.hp < 100) {
player.hp = Math.min(100, player.hp + 1 * dt);
}
}
if (player.temperature < COLD_THRESHOLD) {
const severity = Math.abs(player.temperature - COLD_THRESHOLD) / 15;
player.hp -= 3 * severity * dt;
if (severity > 0.5) player.vx *= (1 - 0.3 * Math.min(1, severity) * dt);
}
if (player.temperature > HEAT_THRESHOLD) {
const severity = (player.temperature - HEAT_THRESHOLD) / 15;
player.hp -= 2.5 * severity * dt;
if (severity > 0.5) player.hunger -= 0.5 * severity * dt;
}
// Игрок не может двигаться во время сна
if(player.sleeping){
player.vx = 0;
@ -4482,7 +4545,6 @@ registerProcessor('voice-playback', VoicePlaybackProcessor);
}
if(m.hp <= 0){
spawnDrops(m.x + m.w/2, m.y + m.h/2, m.kind);
grantXP(getMobXP(m.kind));
// Remove from the correct array
if(m.id !== undefined){
serverMobs.delete(m.id);
@ -5002,6 +5064,7 @@ registerProcessor('voice-playback', VoicePlaybackProcessor);
const xpInLevel = player.xp - lvXpCur;
const xpNeeded = lvXpNext - lvXpCur;
document.getElementById('xpbar').textContent = xpInLevel + '/' + xpNeeded;
tempEl.textContent = Math.round(player.temperature) + "°C";
worldIdEl.textContent = worldId;
if(isMultiplayer){
document.getElementById('multiplayerStatus').style.display = 'flex';
@ -5024,6 +5087,19 @@ registerProcessor('voice-playback', VoicePlaybackProcessor);
}
// Рисуем дропы
// Температурный оверлей (заморозка/тепловой удар)
if (typeof player.temperature !== 'undefined' && (player.temperature < -5 || player.temperature > 35)) {
const severity = player.temperature < -5
? Math.abs(player.temperature + 5) / 20
: (player.temperature - 35) / 20;
const alpha = Math.min(0.35, severity * 0.15);
if (player.temperature < -5) {
ctx.fillStyle = 'rgba(100,150,255,' + alpha + ')';
} else {
ctx.fillStyle = 'rgba(255,100,50,' + alpha + ')';
}
ctx.fillRect(0, 0, W, H);
}
drawDrops(ctx);
// Пикап дропов
pickupDrops();

View File

@ -17,6 +17,7 @@
<div id="stats">
<div class="row">❤️ <span id="hp">100</span> &nbsp; 🍗 <span id="food">100</span></div>
<div class="row">🫁 <span id="o2">100</span></div>
<div class="row">🌡️ <span id="temp">15°C</span></div>
<div class="row">📍 X:<span id="sx">0</span> Y:<span id="sy">0</span></div>
<div class="row">🕒 <span id="tod">День</span></div>
<div class="row">🌐 <span id="worldId" style="cursor:pointer; text-decoration:underline;" title="Нажмите, чтобы скопировать ссылку">default</span></div>
@ -93,6 +94,6 @@
</div>
</div>
<script src="game.js?v=25"></script>
<script src="game.js?v=26"></script>
</body>
</html>