phase 12 koncxana

This commit is contained in:
2025-12-08 14:16:24 +01:00
parent f3d476e843
commit 81a7895c10
11 changed files with 547 additions and 39 deletions

View File

@@ -9,7 +9,8 @@ class BuildingSystem {
wall: { name: 'Stone Wall', cost: { stone: 2 }, w: 1, h: 1 },
house: { name: 'House', cost: { wood: 20, stone: 20, gold: 50 }, w: 1, h: 1 }, // Visual is bigger but anchor is 1 tile
barn: { name: 'Barn', cost: { wood: 50, stone: 10 }, w: 1, h: 1 },
silo: { name: 'Silo', cost: { wood: 30, stone: 30 }, w: 1, h: 1 }
silo: { name: 'Silo', cost: { wood: 30, stone: 30 }, w: 1, h: 1 },
stable: { name: 'Stable', cost: { wood: 40, stone: 20 }, w: 1, h: 1 }
};
// Textures init
@@ -18,6 +19,7 @@ class BuildingSystem {
if (!this.scene.textures.exists('struct_house')) TextureGenerator.createStructureSprite(this.scene, 'struct_house', 'house');
if (!this.scene.textures.exists('struct_barn')) TextureGenerator.createStructureSprite(this.scene, 'struct_barn', 'barn');
if (this.scene.textures.exists('struct_silo')) TextureGenerator.createStructureSprite(this.scene, 'struct_silo', 'silo');
if (!this.scene.textures.exists('struct_stable')) TextureGenerator.createStructureSprite(this.scene, 'struct_stable', 'stable');
// House Lv2 Texture
TextureGenerator.createStructureSprite(this.scene, 'struct_house_lv2', 'house_lv2');
}

View File

@@ -157,6 +157,12 @@ class InteractionSystem {
return;
}
// 3.3.1 BOAT DEPLOY
if (activeTool === 'boat' && !isAttack && this.scene.oceanSystem) {
this.scene.oceanSystem.useBoat();
return;
}
// 3.4 Check for Vehicles (Scooter)
if (!isAttack && this.scene.vehicles) {
for (const vehicle of this.scene.vehicles) {
@@ -215,6 +221,32 @@ class InteractionSystem {
}
}
// 3.6 Check for Livestock Interaction (Milking)
if (npc.type.includes('cow') && activeTool === 'bucket' && !isAttack) {
if (npc.milkReady) {
npc.milkReady = false;
npc.milkCooldown = 60000; // 60s cooldown
const product = npc.type.includes('mutant') ? 'glowing_milk' : 'milk';
if (invSys) {
invSys.addItem(product, 1);
this.scene.events.emit('show-floating-text', {
x: npc.sprite.x, y: npc.sprite.y - 40, text: `+1 ${product}`, color: '#FFFFFF'
});
console.log(`🥛 Milked ${npc.type}: ${product}`);
// Optional: Replace bucket with empty? No, bucket is tool.
// Maybe replace bucket with bucket_milk if it was a single use item, but let's keep it as tool.
}
} else {
this.scene.events.emit('show-floating-text', {
x: npc.sprite.x, y: npc.sprite.y - 40, text: 'Not ready...', color: '#AAAAAA'
});
}
return;
}
if (!isAttack) npc.toggleState();
return;
}

View File

@@ -1,44 +1,144 @@
class LegacySystem {
constructor(scene) {
this.scene = scene;
this.worldAge = 0; // Days passed
this.generation = 1;
this.currentAge = 18; // Protagonist age
this.family = {
partner: null,
children: []
// AGE & GENERATION
this.generation = 1;
this.birthDay = 1; // Global Day count when current char was born
this.currentAge = 18; // Start at 18
// FAMILY
this.isMarried = false;
this.spouseName = null;
this.childName = null;
this.childAge = 0;
// FRACTIONS / REPUTATION
this.reputation = {
survivors: 0, // Human NPCs
mutants: -50, // Starts hostile
zombies: 0 // Handled by Hybrid Skill, but we can track here too
};
console.log('⏳ LegacySystem: Initialized (Gen ' + this.generation + ')');
// Listen for Day Change logic
this.scene.events.on('update', (time, delta) => this.update(time, delta));
}
// Call daily
advanceDay() {
this.worldAge++;
// Age Logic
if (this.worldAge % 365 === 0) {
this.currentAge++;
console.log('🎂 Birthday! Now age:', this.currentAge);
update(time, delta) {
if (!this.scene.weatherSystem) return;
const currentDay = this.scene.weatherSystem.dayCount;
// Calculate Age
// 1 Year = 28 Days (4 seasons * 7 days).
const daysAlive = Math.max(0, currentDay - this.birthDay);
const yearsAlive = Math.floor(daysAlive / 28);
// Don't spam update
if (this.currentAge !== 18 + yearsAlive) {
this.currentAge = 18 + yearsAlive;
this.scene.events.emit('update-age-ui', { gen: this.generation, age: this.currentAge });
}
// Child Growth
if (this.childName && currentDay % 28 === 0) {
// Child ages up logic could go here
}
}
marry(npcId) {
this.family.partner = npcId;
console.log('💍 Married to:', npcId);
}
// --- REPUTATION ---
modifyReputation(fraction, amount) {
if (this.reputation[fraction] !== undefined) {
this.reputation[fraction] += amount;
// Clamp -100 to 100
this.reputation[fraction] = Phaser.Math.Clamp(this.reputation[fraction], -100, 100);
haveChild(name) {
if (this.family.children.length < 2) {
this.family.children.push({ name: name, age: 0 });
console.log('👶 New Child:', name);
if (this.scene.player) {
this.scene.events.emit('show-floating-text', {
x: this.scene.player.sprite.x,
y: this.scene.player.sprite.y - 60,
text: `Reputation (${fraction}): ${amount > 0 ? '+' : ''}${amount}`,
color: amount > 0 ? '#00FF00' : '#FF0000'
});
}
}
}
dieAndInherit(heirIndex) {
console.log('⚰️ Character Died. Passing legacy...');
// --- MARRIAGE ---
propose(npc) {
if (this.isMarried) {
this.showText("You are already married!");
return;
}
// Logic: Need Ring and High Rep
const hasRing = this.scene.inventorySystem && this.scene.inventorySystem.hasItem('gold_coin'); // Placeholder: use Gold Coin as "Ring" for now
// Or better: Let's create 'ring' item? For now, Gold Coins (50?).
// Let's require 50 Gold Coins for now? Or specific item?
// Let's assume inventory checks are done before calling this, or simple check here.
const highRep = this.reputation.survivors >= 50;
// Temp logic: Always succeed if rep > 0 for testing?
if (highRep) {
this.isMarried = true;
this.spouseName = npc.name || "Villager";
this.showText(`You married ${this.spouseName}!`);
} else {
this.showText("They refused... (Need 50 Rep)");
}
}
haveChild() {
if (!this.isMarried) return;
if (this.childName) return;
this.childName = "Baby Jr.";
this.childAge = 0;
this.showText("A child is born!");
}
// --- INHERITANCE / DEATH ---
triggerInheritance() {
console.log('💀 TRIGGERING INHERITANCE...');
// 1. Increment Generation
this.generation++;
this.currentAge = 18; // Reset age for heir
// TODO: Transfer inventory and stats
// 2. Reset Player Stats
if (this.scene.weatherSystem) {
this.birthDay = this.scene.weatherSystem.dayCount; // Born today
}
this.currentAge = 18; // Reset age
this.isMarried = false;
this.spouseName = null;
this.childName = null; // Clean slate (child became protagonist)
// 3. Clear transient reputation, but keep a bit?
// Inheritance bonus: Keep 20% of rep?
this.reputation.survivors = Math.floor(this.reputation.survivors * 0.2);
this.reputation.mutants = Math.floor(this.reputation.mutants * 0.2) - 40; // Still suspicious
this.showText(`Step Id: 760
Generation ${this.generation} Begins!`, '#FFD700');
// Heal
if (this.scene.player) {
this.scene.player.hp = this.scene.player.maxHp;
this.scene.player.isDead = false;
this.scene.player.sprite.setTint(0xffffff);
this.scene.player.sprite.setAlpha(1);
}
}
showText(msg, color = '#FFFFFF') {
const player = this.scene.player;
if (player) {
this.scene.events.emit('show-floating-text', {
x: player.sprite.x, y: player.sprite.y - 50, text: msg, color: color
});
}
}
}

181
src/systems/OceanSystem.js Normal file
View File

@@ -0,0 +1,181 @@
class OceanSystem {
constructor(scene) {
this.scene = scene;
this.isDiving = false;
this.oxygen = 100;
this.maxOxygen = 100;
this.oxygenDecayRate = 10; // Oxygen lost per second
this.oxygenRegenRate = 20; // Oxygen gained per second
this.damageTimer = 0;
// Visual Effects
this.overlay = null;
this.bubbleParticles = null;
// Boat
this.currentBoat = null;
this.isBoating = false;
// Init UI Event Listener
this.scene.events.on('update', (time, delta) => {
this.update(time, delta);
this.updateBoatVisuals();
});
}
update(time, delta) {
if (!this.scene.player) return;
const playerPos = this.scene.player.getPosition();
const tile = this.scene.terrainSystem.getTile(playerPos.x, playerPos.y);
if (tile && tile.type && tile.type.name === 'water') {
// Check if boating
if (this.isBoating) {
// No drowning logic if in boat
this.oxygen = this.maxOxygen; // Regaining oxygen/safe
} else {
this.enterWater();
this.handleDiving(delta);
}
} else {
// LAND LOGIC
// If boating on land -> Beaching/Stop boating
if (this.isBoating) {
this.exitBoat();
}
this.exitWater();
this.handleRegen(delta);
}
// Update UI
if (this.isDiving || this.oxygen < this.maxOxygen) {
this.scene.events.emit('update-oxygen', { current: this.oxygen, max: this.maxOxygen });
} else {
this.scene.events.emit('hide-oxygen');
}
}
enterWater() {
if (this.isDiving) return;
this.isDiving = true;
console.log('🌊 Entered Water');
// Visuals
if (!this.overlay) {
this.overlay = this.scene.add.rectangle(0, 0, this.scene.scale.width, this.scene.scale.height, 0x0000FF, 0.3);
this.overlay.setScrollFactor(0);
this.overlay.setDepth(9000); // Above most things, below UI
}
this.overlay.setVisible(true);
// Slow player?
// Slow player?
this.scene.player.moveSpeed = 60; // Slower in water
}
useBoat(boatItem) {
if (this.isBoating) {
this.exitBoat();
return;
}
// Check if on water or near water?
// Actually, let's allow "deploying" boat anywhere, but only moving fast on water?
// OR: Only allow deploying on water tile.
const playerPos = this.scene.player.getPosition();
const tile = this.scene.terrainSystem.getTile(playerPos.x, playerPos.y);
if (tile && tile.type && tile.type.name === 'water') {
this.enterBoat(playerPos.x, playerPos.y);
} else {
this.scene.events.emit('show-floating-text', {
x: this.scene.player.sprite.x, y: this.scene.player.sprite.y - 50, text: 'Must be in water!', color: '#FF0000'
});
}
}
enterBoat() {
this.isBoating = true;
this.isDiving = false;
if (this.overlay) this.overlay.setVisible(false); // No blue overlay on boat
// Visual change
this.scene.player.moveSpeed = 200; // Fast speed!
// Create boat visual under player?
// Simplified: Change player texture or add container
if (!this.currentBoat) {
this.currentBoat = this.scene.add.sprite(0, 0, 'boat');
this.currentBoat.setDepth(this.scene.player.sprite.depth - 1);
}
this.currentBoat.setVisible(true);
this.scene.events.emit('show-floating-text', {
x: this.scene.player.sprite.x, y: this.scene.player.sprite.y - 50, text: 'Deployed Boat!', color: '#00FFFF'
});
}
exitBoat() {
this.isBoating = false;
if (this.currentBoat) this.currentBoat.setVisible(false);
// Return to normal or water speed?
// Logic in update loop will handle re-entering water state if still on water
this.scene.player.moveSpeed = 150;
}
exitWater() {
if (!this.isDiving) return;
this.isDiving = false;
console.log('🏖️ Exited Water');
if (this.overlay) this.overlay.setVisible(false);
// Restore speed
this.scene.player.moveSpeed = 150; // Normal speed
}
handleDiving(delta) {
this.oxygen -= (this.oxygenDecayRate * delta) / 1000;
if (this.oxygen <= 0) {
this.oxygen = 0;
this.damageTimer += delta;
if (this.damageTimer > 1000) {
this.damageTimer = 0;
this.scene.player.takeDamage(5);
this.scene.events.emit('show-floating-text', {
x: this.scene.player.sprite.x,
y: this.scene.player.sprite.y - 50,
text: 'Drowning!',
color: '#FF0000'
});
}
}
}
handleRegen(delta) {
if (this.oxygen < this.maxOxygen) {
this.oxygen += (this.oxygenRegenRate * delta) / 1000;
if (this.oxygen > this.maxOxygen) this.oxygen = this.maxOxygen;
}
updateBoatVisuals() {
if (this.isBoating && this.currentBoat && this.scene.player) {
this.currentBoat.x = this.scene.player.sprite.x;
this.currentBoat.y = this.scene.player.sprite.y + 10; // Slightly below
this.currentBoat.setDepth(this.scene.player.sprite.depth - 1);
// Flip based on movement?
// Access input vector or previous pos?
// Simplified:
if (this.scene.input.keyboard.checkDown(this.scene.input.keyboard.addKey('A'), 100)) this.currentBoat.setFlipX(false);
if (this.scene.input.keyboard.checkDown(this.scene.input.keyboard.addKey('D'), 100)) this.currentBoat.setFlipX(true);
}
}
}
}

View File

@@ -277,7 +277,12 @@ class TerrainSystem {
if (x < 3 || x >= this.width - 3 || y < 3 || y >= this.height - 3) {
terrainType = this.terrainTypes.GRASS_FULL;
} else {
if (elevation < -0.6) terrainType = this.terrainTypes.WATER;
if (elevation < -0.6) terrainType = this.terrainTypes.WATER; // Deep Ocean
else if (elevation < -0.4 && Math.random() < 0.2) {
// ISOLATED ISLANDS (Nodes)
// If we are in deep water but get random bump -> Island
terrainType = this.terrainTypes.SAND;
}
else if (elevation > 0.1) terrainType = this.terrainTypes.SAND;
else if (elevation > 0.2) terrainType = this.terrainTypes.GRASS_FULL;
else if (elevation > 0.3) terrainType = this.terrainTypes.GRASS_TOP;