phase 12 koncxana
This commit is contained in:
20
TASKS.md
20
TASKS.md
@@ -216,18 +216,18 @@ Implementacija jedrnih mehanik iz novega koncepta "Krvava Žetev".
|
|||||||
|
|
||||||
## 🧬 Phase 12: Exploration & Legacy (Endgame)
|
## 🧬 Phase 12: Exploration & Legacy (Endgame)
|
||||||
- [ ] **Livestock System**
|
- [ ] **Livestock System**
|
||||||
- [ ] Hlev za živali.
|
- [x] Hlev za živali.
|
||||||
- [ ] Loot Tables: Normalno vs. Mutirano (Mleko vs. Svetleče Mleko).
|
- [x] Loot Tables: Normalno vs. Mutirano (Mleko vs. Svetleče Mleko).
|
||||||
- [ ] **Ocean System**
|
- [ ] **Ocean System**
|
||||||
- [ ] Potapljanje (animacija, kisik bar).
|
- [x] Potapljanje (animacija, kisik bar).
|
||||||
- [ ] Čoln (Vehicle controller).
|
- [x] Čoln (Vehicle controller).
|
||||||
- [ ] Generacija Otokov (Island Nodes).
|
- [x] Generacija Otokov (Island Nodes).
|
||||||
- [ ] **Legacy System (Generacije)**
|
- [x] **Legacy System (Generacije)**
|
||||||
- [ ] Age Counter (Leta/Letni časi).
|
- [x] Age Counter (Leta/Letni časi).
|
||||||
- [ ] Marriage Logic + Child Spawn.
|
- [x] Marriage Logic + Child Spawn.
|
||||||
- [ ] **Inheritance**: Prenos inventarja/farme na novega lika ob smrti.
|
- [x] **Inheritance**: Prenos inventarja/farme na novega lika ob smrti.
|
||||||
- [ ] **Fractions**
|
- [ ] **Fractions**
|
||||||
- [ ] Reputation System za Mutante (Dobri/Zlobni).
|
- [x] Reputation System za Mutante (Dobri/Zlobni).
|
||||||
|
|
||||||
## 🌡️ Phase 13: Elements & Survival (Hardcore)
|
## 🌡️ Phase 13: Elements & Survival (Hardcore)
|
||||||
- [ ] **Weather System v2.0**
|
- [ ] **Weather System v2.0**
|
||||||
|
|||||||
@@ -98,6 +98,7 @@
|
|||||||
<script src="src/systems/BlueprintSystem.js"></script>
|
<script src="src/systems/BlueprintSystem.js"></script>
|
||||||
<script src="src/systems/CollectionSystem.js"></script>
|
<script src="src/systems/CollectionSystem.js"></script>
|
||||||
<script src="src/systems/HybridSkillSystem.js"></script>
|
<script src="src/systems/HybridSkillSystem.js"></script>
|
||||||
|
<script src="src/systems/OceanSystem.js"></script>
|
||||||
|
|
||||||
<!-- Multiplayer -->
|
<!-- Multiplayer -->
|
||||||
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
|
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ class NPC {
|
|||||||
this.moveSpeed = type.includes('chicken') ? 120 : 60; // Chickens faster than cows
|
this.moveSpeed = type.includes('chicken') ? 120 : 60; // Chickens faster than cows
|
||||||
this.gridMoveTime = type.includes('chicken') ? 250 : 500;
|
this.gridMoveTime = type.includes('chicken') ? 250 : 500;
|
||||||
this.passive = true; // NEW FLAG
|
this.passive = true; // NEW FLAG
|
||||||
|
|
||||||
|
if (type.includes('cow')) {
|
||||||
|
this.milkReady = true; // Starts milkable
|
||||||
|
this.milkCooldown = 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.hp = 20;
|
this.hp = 20;
|
||||||
this.maxHp = 20;
|
this.maxHp = 20;
|
||||||
@@ -244,6 +249,18 @@ class NPC {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3.1 Livestock Production Logic
|
||||||
|
if (this.type.includes('cow') && !this.milkReady) {
|
||||||
|
this.milkCooldown -= delta;
|
||||||
|
if (this.milkCooldown <= 0) {
|
||||||
|
this.milkReady = true;
|
||||||
|
// Optional: Visual indicator (milk particle?)
|
||||||
|
if (this.scene.events) this.scene.events.emit('show-floating-text', {
|
||||||
|
x: this.sprite.x, y: this.sprite.y - 40, text: '!', color: '#FFFFFF'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 3. AI Logic
|
// 3. AI Logic
|
||||||
if (this.type !== 'merchant' && this.state !== 'TAMED' && this.state !== 'FOLLOW' && !this.passive) {
|
if (this.type !== 'merchant' && this.state !== 'TAMED' && this.state !== 'FOLLOW' && !this.passive) {
|
||||||
this.handleAggressiveAI(delta);
|
this.handleAggressiveAI(delta);
|
||||||
|
|||||||
@@ -280,6 +280,8 @@ class GameScene extends Phaser.Scene {
|
|||||||
this.multiplayerSystem = new MultiplayerSystem(this);
|
this.multiplayerSystem = new MultiplayerSystem(this);
|
||||||
this.worldEventSystem = new WorldEventSystem(this);
|
this.worldEventSystem = new WorldEventSystem(this);
|
||||||
this.hybridSkillSystem = new HybridSkillSystem(this);
|
this.hybridSkillSystem = new HybridSkillSystem(this);
|
||||||
|
this.oceanSystem = new OceanSystem(this);
|
||||||
|
this.legacySystem = new LegacySystem(this);
|
||||||
|
|
||||||
// Initialize Sound Manager
|
// Initialize Sound Manager
|
||||||
console.log('🎵 Initializing Sound Manager...');
|
console.log('🎵 Initializing Sound Manager...');
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ class UIScene extends Phaser.Scene {
|
|||||||
// this.createDebugInfo();
|
// this.createDebugInfo();
|
||||||
this.createSettingsButton();
|
this.createSettingsButton();
|
||||||
|
|
||||||
|
// Listeners for Age
|
||||||
|
if (this.gameScene) {
|
||||||
|
this.gameScene.events.on('update-age-ui', (data) => this.updateAge(data.gen, data.age));
|
||||||
|
}
|
||||||
|
|
||||||
// Resize event
|
// Resize event
|
||||||
this.scale.on('resize', this.resize, this);
|
this.scale.on('resize', this.resize, this);
|
||||||
|
|
||||||
@@ -58,6 +63,10 @@ class UIScene extends Phaser.Scene {
|
|||||||
this.craftingRecipes = [
|
this.craftingRecipes = [
|
||||||
{ id: 'axe', name: 'Stone Axe', req: { 'wood': 3, 'stone': 3 }, output: 1, type: 'tool', desc: 'Used for chopping trees.' },
|
{ id: 'axe', name: 'Stone Axe', req: { 'wood': 3, 'stone': 3 }, output: 1, type: 'tool', desc: 'Used for chopping trees.' },
|
||||||
{ id: 'pickaxe', name: 'Stone Pickaxe', req: { 'wood': 3, 'stone': 3 }, output: 1, type: 'tool', desc: 'Used for mining rocks.' },
|
{ id: 'pickaxe', name: 'Stone Pickaxe', req: { 'wood': 3, 'stone': 3 }, output: 1, type: 'tool', desc: 'Used for mining rocks.' },
|
||||||
|
{ id: 'bucket', name: 'Iron Bucket', req: { 'iron_bar': 2 }, output: 1, type: 'tool', desc: 'Used for milking cows.' },
|
||||||
|
{ id: 'stable', name: 'Stable', req: { 'wood': 40, 'stone': 20 }, output: 1, type: 'building', desc: 'Shelter for animals.', buildingId: 'stable' },
|
||||||
|
{ id: 'animal_feed', name: 'Animal Feed', req: { 'wheat': 2 }, output: 1, type: 'item', desc: 'Food for livestock.' },
|
||||||
|
{ id: 'boat', name: 'Wood Boat', req: { 'wood': 25 }, output: 1, type: 'vehicle', desc: 'Travel on water.' },
|
||||||
{ id: 'hoe', name: 'Stone Hoe', req: { 'wood': 2, 'stone': 2 }, output: 1, type: 'tool', desc: 'Prepares soil for planting.' },
|
{ id: 'hoe', name: 'Stone Hoe', req: { 'wood': 2, 'stone': 2 }, output: 1, type: 'tool', desc: 'Prepares soil for planting.' },
|
||||||
{ id: 'sword', name: 'Stone Sword', req: { 'wood': 5, 'stone': 2 }, output: 1, type: 'weapon', desc: 'Deals damage to zombies.' },
|
{ id: 'sword', name: 'Stone Sword', req: { 'wood': 5, 'stone': 2 }, output: 1, type: 'weapon', desc: 'Deals damage to zombies.' },
|
||||||
{ id: 'fence', name: 'Wood Fence', req: { 'wood': 2 }, output: 1, type: 'building', desc: 'Simple barrier.' },
|
{ id: 'fence', name: 'Wood Fence', req: { 'wood': 2 }, output: 1, type: 'building', desc: 'Simple barrier.' },
|
||||||
@@ -66,6 +75,16 @@ class UIScene extends Phaser.Scene {
|
|||||||
{ id: 'mint', name: 'Mint', req: { 'stone': 50, 'iron_bar': 5 }, output: 1, type: 'machine', desc: 'Mints coins from bars.' },
|
{ id: 'mint', name: 'Mint', req: { 'stone': 50, 'iron_bar': 5 }, output: 1, type: 'machine', desc: 'Mints coins from bars.' },
|
||||||
{ id: 'grave', name: 'Grave', req: { 'stone': 10 }, output: 1, type: 'furniture', desc: 'Resting place for zombies.' }
|
{ id: 'grave', name: 'Grave', req: { 'stone': 10 }, output: 1, type: 'furniture', desc: 'Resting place for zombies.' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// OXYGEN EVENTS
|
||||||
|
if (this.gameScene) {
|
||||||
|
this.gameScene.events.on('update-inventory', () => this.updateInventoryUI());
|
||||||
|
this.gameScene.events.on('show-floating-text', (data) => this.showFloatingText(data));
|
||||||
|
this.gameScene.events.on('update-oxygen', (data) => this.updateOxygen(data));
|
||||||
|
this.gameScene.events.on('hide-oxygen', () => this.hideOxygen());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.createOxygenBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... (rest of class) ...
|
// ... (rest of class) ...
|
||||||
@@ -563,6 +582,7 @@ class UIScene extends Phaser.Scene {
|
|||||||
bg.fillStyle(0xDAA520, 0.2); // Goldish bg
|
bg.fillStyle(0xDAA520, 0.2); // Goldish bg
|
||||||
bg.fillRect(x, y, 130, 30);
|
bg.fillRect(x, y, 130, 30);
|
||||||
bg.lineStyle(2, 0xFFD700, 0.8);
|
bg.lineStyle(2, 0xFFD700, 0.8);
|
||||||
|
bg.lineStyle(2, 0xFFD700, 0.8);
|
||||||
bg.strokeRect(x, y, 130, 30);
|
bg.strokeRect(x, y, 130, 30);
|
||||||
|
|
||||||
this.goldBg = bg; // Save ref
|
this.goldBg = bg; // Save ref
|
||||||
@@ -574,6 +594,16 @@ class UIScene extends Phaser.Scene {
|
|||||||
fontStyle: 'bold'
|
fontStyle: 'bold'
|
||||||
});
|
});
|
||||||
this.goldText.setOrigin(0.5, 0.5);
|
this.goldText.setOrigin(0.5, 0.5);
|
||||||
|
|
||||||
|
// GENERATION / AGE UI (Below Gold)
|
||||||
|
if (this.genText) this.genText.destroy();
|
||||||
|
this.genText = this.add.text(x + 65, y + 40, 'Gen: 1 | Age: 18', {
|
||||||
|
fontSize: '12px', color: '#AAAAAA'
|
||||||
|
}).setOrigin(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAge(gen, age) {
|
||||||
|
if (this.genText) this.genText.setText(`Gen: ${gen} | Age: ${age}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGold(amount) {
|
updateGold(amount) {
|
||||||
@@ -1445,4 +1475,43 @@ class UIScene extends Phaser.Scene {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- OXYGEN BAR ---
|
||||||
|
createOxygenBar() {
|
||||||
|
this.oxygenContainer = this.add.container(this.scale.width / 2, this.scale.height - 100);
|
||||||
|
this.oxygenContainer.setDepth(2000);
|
||||||
|
|
||||||
|
// Bg
|
||||||
|
const bg = this.add.rectangle(0, 0, 204, 24, 0x000000);
|
||||||
|
bg.setStrokeStyle(2, 0xffffff);
|
||||||
|
this.oxygenContainer.add(bg);
|
||||||
|
|
||||||
|
// Bar
|
||||||
|
this.oxygenBar = this.add.rectangle(-100, 0, 200, 20, 0x00FFFF);
|
||||||
|
this.oxygenBar.setOrigin(0, 0.5);
|
||||||
|
this.oxygenContainer.add(this.oxygenBar);
|
||||||
|
|
||||||
|
// Text
|
||||||
|
const text = this.add.text(0, -25, 'OXYGEN', {
|
||||||
|
fontSize: '14px', fontStyle: 'bold', fontFamily: 'monospace', color: '#00FFFF'
|
||||||
|
}).setOrigin(0.5);
|
||||||
|
this.oxygenContainer.add(text);
|
||||||
|
|
||||||
|
this.oxygenContainer.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOxygen(data) {
|
||||||
|
if (!this.oxygenContainer) return;
|
||||||
|
this.oxygenContainer.setVisible(true);
|
||||||
|
const percent = Math.max(0, data.current / data.max);
|
||||||
|
this.oxygenBar.width = 200 * percent;
|
||||||
|
|
||||||
|
// Color warning
|
||||||
|
if (percent < 0.3) this.oxygenBar.fillColor = 0xFF0000;
|
||||||
|
else this.oxygenBar.fillColor = 0x00FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
hideOxygen() {
|
||||||
|
if (this.oxygenContainer) this.oxygenContainer.setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ class BuildingSystem {
|
|||||||
wall: { name: 'Stone Wall', cost: { stone: 2 }, w: 1, h: 1 },
|
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
|
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 },
|
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
|
// 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_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_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_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
|
// House Lv2 Texture
|
||||||
TextureGenerator.createStructureSprite(this.scene, 'struct_house_lv2', 'house_lv2');
|
TextureGenerator.createStructureSprite(this.scene, 'struct_house_lv2', 'house_lv2');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,6 +157,12 @@ class InteractionSystem {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3.3.1 BOAT DEPLOY
|
||||||
|
if (activeTool === 'boat' && !isAttack && this.scene.oceanSystem) {
|
||||||
|
this.scene.oceanSystem.useBoat();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 3.4 Check for Vehicles (Scooter)
|
// 3.4 Check for Vehicles (Scooter)
|
||||||
if (!isAttack && this.scene.vehicles) {
|
if (!isAttack && this.scene.vehicles) {
|
||||||
for (const vehicle of 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();
|
if (!isAttack) npc.toggleState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,144 @@
|
|||||||
class LegacySystem {
|
class LegacySystem {
|
||||||
constructor(scene) {
|
constructor(scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.worldAge = 0; // Days passed
|
|
||||||
this.generation = 1;
|
|
||||||
this.currentAge = 18; // Protagonist age
|
|
||||||
|
|
||||||
this.family = {
|
// AGE & GENERATION
|
||||||
partner: null,
|
this.generation = 1;
|
||||||
children: []
|
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
|
update(time, delta) {
|
||||||
advanceDay() {
|
if (!this.scene.weatherSystem) return;
|
||||||
this.worldAge++;
|
|
||||||
// Age Logic
|
const currentDay = this.scene.weatherSystem.dayCount;
|
||||||
if (this.worldAge % 365 === 0) {
|
|
||||||
this.currentAge++;
|
// Calculate Age
|
||||||
console.log('🎂 Birthday! Now age:', this.currentAge);
|
// 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) {
|
// --- REPUTATION ---
|
||||||
this.family.partner = npcId;
|
modifyReputation(fraction, amount) {
|
||||||
console.log('💍 Married to:', npcId);
|
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.scene.player) {
|
||||||
if (this.family.children.length < 2) {
|
this.scene.events.emit('show-floating-text', {
|
||||||
this.family.children.push({ name: name, age: 0 });
|
x: this.scene.player.sprite.x,
|
||||||
console.log('👶 New Child:', name);
|
y: this.scene.player.sprite.y - 60,
|
||||||
|
text: `Reputation (${fraction}): ${amount > 0 ? '+' : ''}${amount}`,
|
||||||
|
color: amount > 0 ? '#00FF00' : '#FF0000'
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dieAndInherit(heirIndex) {
|
// --- MARRIAGE ---
|
||||||
console.log('⚰️ Character Died. Passing legacy...');
|
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.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
181
src/systems/OceanSystem.js
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -277,7 +277,12 @@ class TerrainSystem {
|
|||||||
if (x < 3 || x >= this.width - 3 || y < 3 || y >= this.height - 3) {
|
if (x < 3 || x >= this.width - 3 || y < 3 || y >= this.height - 3) {
|
||||||
terrainType = this.terrainTypes.GRASS_FULL;
|
terrainType = this.terrainTypes.GRASS_FULL;
|
||||||
} else {
|
} 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.1) terrainType = this.terrainTypes.SAND;
|
||||||
else if (elevation > 0.2) terrainType = this.terrainTypes.GRASS_FULL;
|
else if (elevation > 0.2) terrainType = this.terrainTypes.GRASS_FULL;
|
||||||
else if (elevation > 0.3) terrainType = this.terrainTypes.GRASS_TOP;
|
else if (elevation > 0.3) terrainType = this.terrainTypes.GRASS_TOP;
|
||||||
|
|||||||
@@ -229,6 +229,17 @@ class TextureGenerator {
|
|||||||
ctx.beginPath(); ctx.arc(32, 16, 4, 0, Math.PI * 2); ctx.fill();
|
ctx.beginPath(); ctx.arc(32, 16, 4, 0, Math.PI * 2); ctx.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (name === 'stable') {
|
||||||
|
// Stable
|
||||||
|
ctx.fillStyle = '#8B4513'; // Wood
|
||||||
|
ctx.fillRect(0, 16, 32, 16);
|
||||||
|
ctx.fillStyle = '#A0522D'; // Roof
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, 16); ctx.lineTo(16, 0); ctx.lineTo(32, 16);
|
||||||
|
ctx.fill();
|
||||||
|
// Door
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
ctx.fillRect(10, 20, 12, 12);
|
||||||
} else {
|
} else {
|
||||||
// Generic box for others
|
// Generic box for others
|
||||||
ctx.fillStyle = '#8B4513';
|
ctx.fillStyle = '#8B4513';
|
||||||
@@ -629,6 +640,62 @@ class TextureGenerator {
|
|||||||
drawBlock(ctx, 10, 8, '#696969');
|
drawBlock(ctx, 10, 8, '#696969');
|
||||||
c.refresh();
|
c.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 6. BUCKET (Empty)
|
||||||
|
{
|
||||||
|
const { c, ctx } = refresh('item_bucket');
|
||||||
|
// Body
|
||||||
|
drawBlock(ctx, 12, 14, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 14, 14, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 16, 14, '#C0C0C0');
|
||||||
|
|
||||||
|
drawBlock(ctx, 11, 12, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 17, 12, '#C0C0C0');
|
||||||
|
|
||||||
|
drawBlock(ctx, 10, 10, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 18, 10, '#C0C0C0');
|
||||||
|
|
||||||
|
// Handle
|
||||||
|
ctx.fillStyle = '#696969';
|
||||||
|
ctx.fillRect(10, 6, 12, 2);
|
||||||
|
|
||||||
|
c.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. BUCKET_MILK
|
||||||
|
{
|
||||||
|
const { c, ctx } = refresh('item_bucket_milk');
|
||||||
|
// Body (Same as bucket)
|
||||||
|
drawBlock(ctx, 12, 14, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 14, 14, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 16, 14, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 11, 12, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 17, 12, '#C0C0C0');
|
||||||
|
|
||||||
|
// Milk Content
|
||||||
|
ctx.fillStyle = '#FFFFFF';
|
||||||
|
ctx.fillRect(12, 10, 8, 4);
|
||||||
|
|
||||||
|
c.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. BUCKET_MILK_GLOWING
|
||||||
|
{
|
||||||
|
const { c, ctx } = refresh('item_bucket_milk_glowing');
|
||||||
|
// Body
|
||||||
|
drawBlock(ctx, 12, 14, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 14, 14, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 16, 14, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 11, 12, '#C0C0C0');
|
||||||
|
drawBlock(ctx, 17, 12, '#C0C0C0');
|
||||||
|
|
||||||
|
// Glowing Milk Content
|
||||||
|
ctx.fillStyle = '#00FF00';
|
||||||
|
ctx.fillRect(12, 10, 8, 4);
|
||||||
|
|
||||||
|
c.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static createItemSprites(scene) {
|
static createItemSprites(scene) {
|
||||||
@@ -643,7 +710,10 @@ class TextureGenerator {
|
|||||||
{ name: 'item_bone', color: '#F5F5DC' }, // Beige
|
{ name: 'item_bone', color: '#F5F5DC' }, // Beige
|
||||||
{ name: 'item_scrap', color: '#B87333' }, // Copper/Bronze (kovinski kos)
|
{ name: 'item_scrap', color: '#B87333' }, // Copper/Bronze (kovinski kos)
|
||||||
{ name: 'item_chip', color: '#00CED1' }, // DarkTurquoise (elektronski chip)
|
{ name: 'item_chip', color: '#00CED1' }, // DarkTurquoise (elektronski chip)
|
||||||
{ name: 'artefact_old', color: '#8B4513' } // Ancient Pot (Brown)
|
{ name: 'artefact_old', color: '#8B4513' }, // Ancient Pot (Brown)
|
||||||
|
{ name: 'milk', color: '#FFFFFF' }, // White
|
||||||
|
{ name: 'glowing_milk', color: '#00FF00' }, // Green
|
||||||
|
{ name: 'animal_feed', color: '#F4A460' } // Sandy Brown (Feed)
|
||||||
];
|
];
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const it = typeof item === 'string' ? item : item.name;
|
const it = typeof item === 'string' ? item : item.name;
|
||||||
@@ -778,6 +848,7 @@ class TextureGenerator {
|
|||||||
TextureGenerator.createToolSprites(this.scene);
|
TextureGenerator.createToolSprites(this.scene);
|
||||||
TextureGenerator.createItemSprites(this.scene);
|
TextureGenerator.createItemSprites(this.scene);
|
||||||
TextureGenerator.createScooterSprite(this.scene, 'scooter', false);
|
TextureGenerator.createScooterSprite(this.scene, 'scooter', false);
|
||||||
|
TextureGenerator.createBoatSprite(this.scene, 'boat');
|
||||||
TextureGenerator.createScooterSprite(this.scene, 'scooter_broken', true);
|
TextureGenerator.createScooterSprite(this.scene, 'scooter_broken', true);
|
||||||
TextureGenerator.createAnimatedWaterSprite(this.scene);
|
TextureGenerator.createAnimatedWaterSprite(this.scene);
|
||||||
TextureGenerator.createPathStoneSprite(this.scene, 'path_stone');
|
TextureGenerator.createPathStoneSprite(this.scene, 'path_stone');
|
||||||
@@ -954,6 +1025,34 @@ class TextureGenerator {
|
|||||||
canvas.refresh();
|
canvas.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static createBoatSprite(scene, key = 'boat') {
|
||||||
|
if (scene.textures.exists(key)) return;
|
||||||
|
const canvas = scene.textures.createCanvas(key, 48, 24);
|
||||||
|
const ctx = canvas.getContext();
|
||||||
|
ctx.clearRect(0, 0, 48, 24);
|
||||||
|
|
||||||
|
// Hull (Wood)
|
||||||
|
ctx.fillStyle = '#8B4513';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, 0);
|
||||||
|
ctx.lineTo(48, 0);
|
||||||
|
ctx.lineTo(40, 24);
|
||||||
|
ctx.lineTo(8, 24);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Rim
|
||||||
|
ctx.strokeStyle = '#603010';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeRect(2, 2, 44, 20);
|
||||||
|
|
||||||
|
// Seat
|
||||||
|
ctx.fillStyle = '#603010';
|
||||||
|
ctx.fillRect(16, 8, 16, 4);
|
||||||
|
|
||||||
|
canvas.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
static createChickenSprite(scene, key, activeMutation) {
|
static createChickenSprite(scene, key, activeMutation) {
|
||||||
if (scene.textures.exists(key)) return;
|
if (scene.textures.exists(key)) return;
|
||||||
const canvas = scene.textures.createCanvas(key, 24, 24);
|
const canvas = scene.textures.createCanvas(key, 24, 24);
|
||||||
|
|||||||
Reference in New Issue
Block a user