MAJOR UPDATE: DrugEconomySystem (marijuana slow-mo/chill + mushroom hallucinations), CourierSystem (hearts/material rewards), updated CHARACTER_DESIGN_GUIDELINES to EXTREMIST standard (mandatory colored hair/massive plugs), added ROADMAP sections for City Evolution (Vape Factory@50pop), Drug Effects, Courier Missions. All systems integrated with quest/reward mechanics.
This commit is contained in:
@@ -1,35 +1,45 @@
|
|||||||
# 🎨 CHARACTER DESIGN GUIDELINES - Mrtva Dolina
|
# 🎨 CHARACTER DESIGN GUIDELINES - Mrtva Dolina
|
||||||
**Style:** Post-Apocalyptic Punk (Style 32 Dark-Chibi Noir)
|
**Style:** Post-Apocalyptic EXTREMIST Punk (Style 32 Dark-Chibi Noir)
|
||||||
**Last Updated:** 2026-01-05 14:04 CET
|
**Last Updated:** 2026-01-05 14:19 CET
|
||||||
|
**Standard:** EXTREMIST - Maximum punk aesthetic
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔥 **ZAŠČITNI ZNAK - Punk Aesthetic**
|
## 🔥 **ZAŠČITNI ZNAK - EXTREMIST Punk Aesthetic**
|
||||||
|
|
||||||
**POMEMBNO:** Ne delaj preveč normalnih ljudi! Post-apokalipsa = punk survival look.
|
**KRITIČNO:** NI normalnih ljudi! VSI morajo biti extreme punk survivors!
|
||||||
|
|
||||||
### **Core Features (Mix & Match)**
|
### **MANDATORY Features (All NPCs)**
|
||||||
NPC-ji naj imajo **raznolike kombinacije** teh elementov, ne vsi vse:
|
|
||||||
|
|
||||||
#### **Pirsinge** 🔩
|
#### **Pirsinge** 🔩 - **ZAŽELENI PRI VSEH**
|
||||||
- Nos ring
|
- Nos ring (very common)
|
||||||
- Lip ring
|
- Lip ring
|
||||||
- Eyebrow piercing
|
- Eyebrow piercing
|
||||||
- Multiple ear piercings
|
- Multiple ear piercings
|
||||||
- **Ne rabijo vsi!** En NPC lahko ima samo 1 piercing
|
- **Minimum:** Vsak NPC naj ima vsaj 2+ piercings
|
||||||
|
|
||||||
#### **Ear Gauges** 👂
|
#### **Ear Plugs/Gauges** 👂 - **VARIABLE MASSIVE SIZES**
|
||||||
- Razširjena ušesa (stretched ear lobes)
|
- Razširjena ušesa (stretched ear lobes) - **ZAŽELENI**
|
||||||
- Različne velikosti (majhni do veliki)
|
- **Velikost variira:**
|
||||||
- Opcijsko - ni mandatory
|
- Small plugs (5mm)
|
||||||
|
- Medium plugs (10-15mm)
|
||||||
|
- **MASSIVE plugs (20-30mm+)** ← Lahko tudi ekstremni!
|
||||||
|
- **Stil:** Metal rings, wooden plugs, decorative tunnels
|
||||||
|
|
||||||
|
#### **LASJE - OBVEZNO POBARVANI** 💈
|
||||||
|
**NOVA PRAVILA:**
|
||||||
|
- **ČE NIMA DREADOV → LASE OBVEZNO POBARVAJ!**
|
||||||
|
- **Barve:** Neon zelena, pink, modra, purple, orange, red
|
||||||
|
- **NO natural colors** (črna/rjava dovoljena SAMO z dreads)
|
||||||
|
- **Kombinacije:** Rainbow, two-tone, highlights
|
||||||
|
|
||||||
#### **Dreadlokse** 🌿
|
#### **Dreadlokse** 🌿
|
||||||
- **VARIACIJE:**
|
- **VARIACIJE:**
|
||||||
- Full head dreads (kot Kai, Gronk)
|
- Full head dreads (kot Kai, Gronk)
|
||||||
- **Partial dreads:** Ubrita glava z 6 dredloksi odzadi
|
- **Partial dreads:** Ubrita glava z 6 dredloksi odzadi
|
||||||
- **Colored dreads:** Pink, green, purple, gray
|
- **Colored dreads:** Pink, green, purple, gray (MANDATORY COLOR)
|
||||||
- Kratki vs. dolgi
|
- Kratki vs. dolgi
|
||||||
- **Ne rabijo vsi!** Lahko ima NPC samo normalne lase
|
- **ALI ima dreads ALI pobarvane lase (ne plain)**
|
||||||
|
|
||||||
#### **Tatuji** 🖋️
|
#### **Tatuji** 🖋️
|
||||||
- Neck tattoos
|
- Neck tattoos
|
||||||
|
|||||||
@@ -279,6 +279,58 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🏙️ **CITY EVOLUTION & POPULATION MILESTONES**
|
||||||
|
|
||||||
|
| Milestone | Population | Unlock | Priority | Status |
|
||||||
|
|-----------|------------|--------|----------|--------|
|
||||||
|
| **Election System** | 5 NPCs | Volitve za Župana, Social Order | ⭐⭐⭐⭐⭐ | ✅ Implemented |
|
||||||
|
| **Zombie Economy** | 10 NPCs | Worker Zombies, Sanitation | ⭐⭐⭐⭐ | ✅ Implemented |
|
||||||
|
| **Vape Factory** | **50 NPCs** | First major industrial building | ⭐⭐⭐⭐ | 🔴 Planned (Faza 3) |
|
||||||
|
| **Police Force** | 75 NPCs | Drug trade becomes illegal/risky | ⭐⭐⭐⭐ | 🔴 Planned |
|
||||||
|
| **Full Infrastructure** | 100 NPCs | All systems unlocked | ⭐⭐⭐⭐⭐ | 🔴 Future |
|
||||||
|
|
||||||
|
### **Vape Factory (Faza 3 - Industrija)**
|
||||||
|
- **Unlock:** 50 residents in Capital City
|
||||||
|
- **Requirements:** Rare chemicals from ruins (Tehnik processing)
|
||||||
|
- **Features:** Custom liquid crafting, mod building
|
||||||
|
- **Workforce:** Delovni Zombiji + Tehnik supervision
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌿 **SOCIAL & ECONOMY SYSTEMS**
|
||||||
|
|
||||||
|
### **Drug Effects & Illegal Trade**
|
||||||
|
|
||||||
|
| System | Features | Priority | Status |
|
||||||
|
|--------|----------|----------|--------|
|
||||||
|
| **Marijuana Effects** | Slow-mo, chill music, energy regen +100%, -15% walk speed | ⭐⭐⭐⭐ | ✅ Implemented |
|
||||||
|
| **Mushroom Hallucinations** | Psychedelic shader, moving objects, ghost visions, reality warps | ⭐⭐⭐⭐ | ✅ Implemented |
|
||||||
|
| **Black Market Trade** | Free trade until police established (30% bust risk after) | ⭐⭐⭐⭐ | ✅ Implemented |
|
||||||
|
| **Crop Systems** | Cannabis & magic mushroom farming | ⭐⭐⭐⭐ | 🟡 In progress |
|
||||||
|
|
||||||
|
**Psychedelic Visual Effects:**
|
||||||
|
- **Marijuana:** Subtle blur, light green tint, chill lo-fi music
|
||||||
|
- **Mushrooms:** Color cycling, object movement, ghost NPCs, camera distortion
|
||||||
|
|
||||||
|
### **Courier Mission System**
|
||||||
|
|
||||||
|
| Feature | Details | Priority | Status |
|
||||||
|
|---------|---------|----------|--------|
|
||||||
|
| **Delivery Quests** | Random NPC requests for materials (wheat, water, glass, steel) | ⭐⭐⭐⭐ | ✅ Implemented |
|
||||||
|
| **Hearts Reward** | Social Status increases (+5% per heart) | ⭐⭐⭐⭐ | ✅ Implemented |
|
||||||
|
| **Material Reward** | Random loot from pools (common/uncommon/rare) | ⭐⭐⭐⭐ | ✅ Implemented |
|
||||||
|
| **Social Benefits** | Shop discounts, priority quests, VIP access | ⭐⭐⭐ | ✅ Implemented |
|
||||||
|
| **Quest Expiration** | 10-minute timer per delivery | ⭐⭐⭐ | ✅ Implemented |
|
||||||
|
|
||||||
|
**Social Status Tiers:**
|
||||||
|
- 20%: 5% shop discount
|
||||||
|
- 40%: Priority quests
|
||||||
|
- 60%: VIP area access
|
||||||
|
- 80%: +10% rare gift chance
|
||||||
|
- 100%: Maximum respect
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 📝 NOTES
|
## 📝 NOTES
|
||||||
|
|
||||||
**Design Philosophy:**
|
**Design Philosophy:**
|
||||||
|
|||||||
337
src/systems/CourierSystem.js
Normal file
337
src/systems/CourierSystem.js
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
/**
|
||||||
|
* COURIER & DELIVERY SYSTEM
|
||||||
|
* Mrtva Dolina - Side-Quest Material Deliveries
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Random delivery quests from NPCs
|
||||||
|
* - Rewards: Hearts (social status) OR random materials
|
||||||
|
* - Material types: wheat, water, glass, steel, wood, plastic
|
||||||
|
* - Reputation system
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class CourierSystem {
|
||||||
|
constructor(scene) {
|
||||||
|
this.scene = scene;
|
||||||
|
|
||||||
|
// Social status (hearts)
|
||||||
|
this.socialStatus = 0; // 0-100
|
||||||
|
this.hearts = 0; // Visual hearts earned
|
||||||
|
|
||||||
|
// Active courier quests
|
||||||
|
this.activeDeliveries = [];
|
||||||
|
|
||||||
|
// NPC delivery requests database
|
||||||
|
this.deliveryTemplates = [
|
||||||
|
// PEK
|
||||||
|
{ npc: 'pek', item: 'wheat', quantity: [5, 10], reward: { type: 'hearts', amount: 2 }, message: 'Rabim pšenico za kruh!' },
|
||||||
|
{ npc: 'pek', item: 'water', quantity: [3, 5], reward: { type: 'material', pool: ['wood', 'stone'] }, message: 'Dej mi vodo, pa ti dam material!' },
|
||||||
|
|
||||||
|
// ŠIVILJA
|
||||||
|
{ npc: 'sivilja', item: 'cloth', quantity: [10, 20], reward: { type: 'hearts', amount: 3 }, message: 'Material rabim za šivanje, prosim!' },
|
||||||
|
{
|
||||||
|
npc: 'sivilja', item: 'water', quantity: [2, 4], reward: { type: 'material', pool: ['cloth', 'leather'] }, message: 'Vodo za barv
|
||||||
|
|
||||||
|
anje potrebujem!' },
|
||||||
|
|
||||||
|
// TEHNIK
|
||||||
|
{ npc: 'tehnik', item: 'glass', quantity: [3, 7], reward: { type: 'hearts', amount: 4 }, message: 'Steklo za elektroniko!' },
|
||||||
|
{ npc: 'tehnik', item: 'steel', quantity: [5, 10], reward: { type: 'material', pool: ['circuit_board', 'wire'] }, message: 'Jeklo za stroje!' },
|
||||||
|
|
||||||
|
// IVAN KOVAČ
|
||||||
|
{ npc: 'ivan_kovac', item: 'steel', quantity: [8, 15], reward: { type: 'hearts', amount: 3 }, message: 'Potrebujem jeklo za kovačijo!' },
|
||||||
|
{ npc: 'ivan_kovac', item: 'wood', quantity: [10, 20], reward: { type: 'material', pool: ['steel', 'iron_ore'] }, message: 'Les za oglje!' },
|
||||||
|
|
||||||
|
// KUSTOS
|
||||||
|
{ npc: 'kustos', item: 'glass', quantity: [2, 5], reward: { type: 'hearts', amount: 2 }, message: 'Steklo za razstavne omare!' },
|
||||||
|
{ npc: 'kustos', item: 'rare_artifact', quantity: [1, 1], reward: { type: 'material', pool: ['ancient_relic', 'museum_piece'] }, message: 'Artefakt za muzejsko zbirko!' },
|
||||||
|
|
||||||
|
// SMETAR
|
||||||
|
{ npc: 'glavni_smetar', item: 'plastic', quantity: [15, 30], reward: { type: 'hearts', amount: 1 }, message: 'Plastiko moram zbrat iz ruševin!' },
|
||||||
|
{ npc: 'glavni_smetar', item: 'wood', quantity: [5, 10], reward: { type: 'material', pool: ['broom', 'cleaning_supplies'] }, message: 'Les za nove metle!' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Material loot pools
|
||||||
|
this.materialPools = {
|
||||||
|
common: ['wood', 'stone', 'plastic', 'water'],
|
||||||
|
uncommon: ['steel', 'glass', 'cloth', 'leather'],
|
||||||
|
rare: ['circuit_board', 'wire', 'ancient_relic', 'museum_piece']
|
||||||
|
};
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// Start quest generation
|
||||||
|
this.startQuestGeneration();
|
||||||
|
|
||||||
|
// Listen for delivery completions
|
||||||
|
this.scene.events.on('courier:delivery_complete', this.onDeliveryComplete, this);
|
||||||
|
|
||||||
|
console.log('✅ CourierSystem initialized - Side-quests active');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QUEST GENERATION
|
||||||
|
*/
|
||||||
|
startQuestGeneration() {
|
||||||
|
// Generate random delivery quest every 2-5 minutes
|
||||||
|
const generateQuest = () => {
|
||||||
|
if (this.activeDeliveries.length < 5) { // Max 5 active
|
||||||
|
this.generateDeliveryQuest();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule next generation
|
||||||
|
const nextTime = Phaser.Math.Between(120000, 300000); // 2-5 minutes
|
||||||
|
setTimeout(generateQuest, nextTime);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start initial quest immediately
|
||||||
|
generateQuest();
|
||||||
|
}
|
||||||
|
|
||||||
|
generateDeliveryQuest() {
|
||||||
|
const template = Phaser.Utils.Array.GetRandom(this.deliveryTemplates);
|
||||||
|
|
||||||
|
const quest = {
|
||||||
|
id: `delivery_${Date.now()}`,
|
||||||
|
npc: template.npc,
|
||||||
|
item: template.item,
|
||||||
|
quantity: Phaser.Math.Between(template.quantity[0], template.quantity[1]),
|
||||||
|
reward: template.reward,
|
||||||
|
message: template.message,
|
||||||
|
timeCreated: Date.now(),
|
||||||
|
expiresIn: 600000, // 10 minutes
|
||||||
|
completed: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.activeDeliveries.push(quest);
|
||||||
|
|
||||||
|
// Notify player
|
||||||
|
this.scene.events.emit('show-notification', {
|
||||||
|
title: '📦 Nova Dostava',
|
||||||
|
message: `${template.npc}: ${template.message}`,
|
||||||
|
icon: '📬',
|
||||||
|
duration: 5000,
|
||||||
|
color: '#FFD700'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show on NPC
|
||||||
|
this.scene.events.emit('npc:show_quest_marker', template.npc);
|
||||||
|
|
||||||
|
console.log(`📦 New delivery quest: ${template.npc} needs ${quest.quantity}x ${quest.item}`);
|
||||||
|
|
||||||
|
return quest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACCEPT DELIVERY QUEST
|
||||||
|
*/
|
||||||
|
acceptQuest(questId) {
|
||||||
|
const quest = this.activeDeliveries.find(q => q.id === questId);
|
||||||
|
if (!quest) return false;
|
||||||
|
|
||||||
|
quest.accepted = true;
|
||||||
|
|
||||||
|
// Add to player's active quest log
|
||||||
|
if (this.scene.questSystem) {
|
||||||
|
this.scene.questSystem.startQuest(questId);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✅ Accepted delivery quest: ${questId}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELIVER ITEMS
|
||||||
|
*/
|
||||||
|
deliverItems(questId) {
|
||||||
|
const quest = this.activeDeliveries.find(q => q.id === questId);
|
||||||
|
if (!quest || quest.completed) return false;
|
||||||
|
|
||||||
|
// Check if player has items
|
||||||
|
if (!this.scene.inventorySystem.hasItem(quest.item, quest.quantity)) {
|
||||||
|
this.scene.events.emit('show-notification', {
|
||||||
|
title: '❌ Ni dovolj',
|
||||||
|
message: `Rabiš še ${quest.quantity}x ${quest.item}!`,
|
||||||
|
icon: '📦',
|
||||||
|
duration: 3000,
|
||||||
|
color: '#FF4444'
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove items from inventory
|
||||||
|
this.scene.inventorySystem.removeItem(quest.item, quest.quantity);
|
||||||
|
|
||||||
|
// Mark as complete
|
||||||
|
quest.completed = true;
|
||||||
|
|
||||||
|
// Award reward
|
||||||
|
this.awardReward(quest.reward);
|
||||||
|
|
||||||
|
// Remove from active
|
||||||
|
this.activeDeliveries = this.activeDeliveries.filter(q => q.id !== questId);
|
||||||
|
|
||||||
|
// Notify completion
|
||||||
|
this.scene.events.emit('show-notification', {
|
||||||
|
title: '✅ Dostava Končana',
|
||||||
|
message: `${quest.npc} je zadovoljen!`,
|
||||||
|
icon: '🎉',
|
||||||
|
duration: 4000,
|
||||||
|
color: '#00FF00'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove quest marker
|
||||||
|
this.scene.events.emit('npc:remove_quest_marker', quest.npc);
|
||||||
|
|
||||||
|
console.log(`✅ Delivery completed: ${questId}`);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AWARD REWARD
|
||||||
|
*/
|
||||||
|
awardReward(reward) {
|
||||||
|
if (reward.type === 'hearts') {
|
||||||
|
this.addHearts(reward.amount);
|
||||||
|
} else if (reward.type === 'material') {
|
||||||
|
this.awardRandomMaterial(reward.pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addHearts(amount) {
|
||||||
|
this.hearts += amount;
|
||||||
|
this.socialStatus = Math.min(100, this.socialStatus + (amount * 5));
|
||||||
|
|
||||||
|
// Visual heart animation
|
||||||
|
for (let i = 0; i < amount; i++) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.spawnHeart();
|
||||||
|
}, i * 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show status update
|
||||||
|
this.scene.events.emit('show-floating-text', {
|
||||||
|
x: this.scene.player.x,
|
||||||
|
y: this.scene.player.y - 50,
|
||||||
|
text: `+${amount} ❤️`,
|
||||||
|
color: '#FF69B4',
|
||||||
|
fontSize: '32px'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`❤️ +${amount} hearts (Total: ${this.hearts}, Status: ${this.socialStatus}%)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnHeart() {
|
||||||
|
const heart = this.scene.add.sprite(
|
||||||
|
this.scene.player.x + Phaser.Math.Between(-30, 30),
|
||||||
|
this.scene.player.y - 50,
|
||||||
|
'heart_icon'
|
||||||
|
);
|
||||||
|
heart.setScale(0);
|
||||||
|
heart.setDepth(50);
|
||||||
|
|
||||||
|
// Animate heart
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: heart,
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
y: heart.y - 100,
|
||||||
|
alpha: 0,
|
||||||
|
duration: 1500,
|
||||||
|
ease: 'Cubic.easeOut',
|
||||||
|
onComplete: () => heart.destroy()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
awardRandomMaterial(pool) {
|
||||||
|
// Random material from pool
|
||||||
|
const material = Phaser.Utils.Array.GetRandom(pool);
|
||||||
|
const quantity = Phaser.Math.Between(1, 5);
|
||||||
|
|
||||||
|
if (this.scene.inventorySystem) {
|
||||||
|
this.scene.inventorySystem.addItem(material, quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show notification
|
||||||
|
this.scene.events.emit('show-notification', {
|
||||||
|
title: '🎁 Material',
|
||||||
|
message: `Prejel si: ${quantity}x ${material}`,
|
||||||
|
icon: '🔧',
|
||||||
|
duration: 4000,
|
||||||
|
color: '#FFD700'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`🎁 Awarded random material: ${quantity}x ${material}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QUEST EXPIRATION
|
||||||
|
*/
|
||||||
|
update(delta) {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Check for expired quests
|
||||||
|
this.activeDeliveries.forEach(quest => {
|
||||||
|
if (!quest.completed && (now - quest.timeCreated) > quest.expiresIn) {
|
||||||
|
// Quest expired
|
||||||
|
this.expireQuest(quest.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
expireQuest(questId) {
|
||||||
|
const quest = this.activeDeliveries.find(q => q.id === questId);
|
||||||
|
if (!quest) return;
|
||||||
|
|
||||||
|
this.activeDeliveries = this.activeDeliveries.filter(q => q.id !== questId);
|
||||||
|
|
||||||
|
// Notify player
|
||||||
|
this.scene.events.emit('show-notification', {
|
||||||
|
title: '⏰ Quest Expired',
|
||||||
|
message: `${quest.npc} je našel drugega kurirja.`,
|
||||||
|
icon: '😞',
|
||||||
|
duration: 3000,
|
||||||
|
color: '#999999'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove marker
|
||||||
|
this.scene.events.emit('npc:remove_quest_marker', quest.npc);
|
||||||
|
|
||||||
|
console.log(`⏰ Quest expired: ${questId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET SOCIAL STATUS BENEFITS
|
||||||
|
*/
|
||||||
|
getSocialBenefits() {
|
||||||
|
const benefits = [];
|
||||||
|
|
||||||
|
if (this.socialStatus >= 20) benefits.push('5% discount at shops');
|
||||||
|
if (this.socialStatus >= 40) benefits.push('Priority quests from NPCs');
|
||||||
|
if (this.socialStatus >= 60) benefits.push('Access to VIP areas');
|
||||||
|
if (this.socialStatus >= 80) benefits.push('Rare gift chance +10%');
|
||||||
|
if (this.socialStatus >= 100) benefits.push('Maximum respect - All NPCs love you!');
|
||||||
|
|
||||||
|
return benefits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET UI DATA
|
||||||
|
*/
|
||||||
|
getActiveQuests() {
|
||||||
|
return this.activeDeliveries.filter(q => !q.completed).map(q => ({
|
||||||
|
id: q.id,
|
||||||
|
npc: q.npc,
|
||||||
|
item: q.item,
|
||||||
|
quantity: q.quantity,
|
||||||
|
reward: q.reward,
|
||||||
|
timeRemaining: q.expiresIn - (Date.now() - q.timeCreated)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.activeDeliveries = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
476
src/systems/DrugEconomySystem.js
Normal file
476
src/systems/DrugEconomySystem.js
Normal file
@@ -0,0 +1,476 @@
|
|||||||
|
/**
|
||||||
|
* DRUG & NARCO-ECONOMY SYSTEM
|
||||||
|
* Mrtva Dolina - Psychedelic Effects & Illegal Trade
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Marijuana effects (slow-mo, chill, energy regen)
|
||||||
|
* - Mushroom hallucinations (color distortion, ghosts)
|
||||||
|
* - Free trade (no police until Mayor establishes it)
|
||||||
|
* - Drug economy & black market
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class DrugEconomySystem {
|
||||||
|
constructor(scene) {
|
||||||
|
this.scene = scene;
|
||||||
|
|
||||||
|
// Drug effects tracking
|
||||||
|
this.activeEffects = {
|
||||||
|
marijuana: false,
|
||||||
|
mushrooms: false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Duration timers
|
||||||
|
this.effectTimers = {};
|
||||||
|
|
||||||
|
// Police state
|
||||||
|
this.policeEstablished = false;
|
||||||
|
|
||||||
|
// Black market prices
|
||||||
|
this.prices = {
|
||||||
|
marijuana: { buy: 50, sell: 80 },
|
||||||
|
mushrooms: { buy: 100, sell: 150 },
|
||||||
|
marijuana_edible: { buy: 75, sell: 120 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Effects configuration
|
||||||
|
this.effectConfig = {
|
||||||
|
marijuana: {
|
||||||
|
duration: 180000, // 3 minutes
|
||||||
|
timeScale: 0.7, // 70% speed (slower)
|
||||||
|
energyRegenBonus: 2.0, // 2x regen
|
||||||
|
walkSpeedMultiplier: 0.85, // 15% slower walk
|
||||||
|
musicFilter: 'chill', // Music becomes chill
|
||||||
|
visualFilter: 'subtle_blur'
|
||||||
|
},
|
||||||
|
mushrooms: {
|
||||||
|
duration: 300000, // 5 minutes
|
||||||
|
colorShift: true,
|
||||||
|
hallucinationChance: 0.3, // 30% chance per second
|
||||||
|
objectMovement: true,
|
||||||
|
ghostVisions: true,
|
||||||
|
psychedelicShader: 'trippy_colors'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// Listen for consumption events
|
||||||
|
this.scene.events.on('player:consume_drug', this.onDrugConsumed, this);
|
||||||
|
this.scene.events.on('police:established', this.onPoliceEstablished, this);
|
||||||
|
|
||||||
|
console.log('✅ DrugEconomySystem initialized - Free trade active');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CONSUME DRUG
|
||||||
|
*/
|
||||||
|
onDrugConsumed(drugType, method = 'smoke') {
|
||||||
|
if (drugType === 'marijuana') {
|
||||||
|
this.consumeMarijuana(method);
|
||||||
|
} else if (drugType === 'mushrooms') {
|
||||||
|
this.consumeMushrooms();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MARIJUANA EFFECTS
|
||||||
|
*/
|
||||||
|
consumeMarijuana(method) {
|
||||||
|
if (this.activeEffects.marijuana) {
|
||||||
|
console.log('🌿 Already high on marijuana');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeEffects.marijuana = true;
|
||||||
|
const config = this.effectConfig.marijuana;
|
||||||
|
|
||||||
|
console.log(`🌿 Marijuana consumed (${method}) - Chill mode activated`);
|
||||||
|
|
||||||
|
// Visual effects
|
||||||
|
this.applyMarijuanaVisuals(config);
|
||||||
|
|
||||||
|
// Gameplay effects
|
||||||
|
this.applyMarijuanaGameplay(config);
|
||||||
|
|
||||||
|
// Audio effects
|
||||||
|
this.applyMarijuanaAudio(config);
|
||||||
|
|
||||||
|
// Show notification
|
||||||
|
this.scene.events.emit('show-notification', {
|
||||||
|
title: '🌿 Chill Mode',
|
||||||
|
message: 'Čas se upočasni... vse je bolj mirno...',
|
||||||
|
icon: '😌',
|
||||||
|
duration: 5000,
|
||||||
|
color: '#90EE90'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set duration timer
|
||||||
|
this.effectTimers.marijuana = setTimeout(() => {
|
||||||
|
this.endMarijuanaEffects();
|
||||||
|
}, config.duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMarijuanaVisuals(config) {
|
||||||
|
// Subtle blur filter
|
||||||
|
const camera = this.scene.cameras.main;
|
||||||
|
|
||||||
|
// Add slight vignette and blur
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: camera,
|
||||||
|
scrollX: camera.scrollX + 2,
|
||||||
|
scrollY: camera.scrollY + 2,
|
||||||
|
duration: 2000,
|
||||||
|
yoyo: true,
|
||||||
|
repeat: -1,
|
||||||
|
ease: 'Sine.easeInOut'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subtle color tint (light green)
|
||||||
|
camera.setTint(0xE8F5E9); // Very light green tint
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMarijuanaGameplay(config) {
|
||||||
|
// Slow down time
|
||||||
|
this.scene.time.timeScale = config.timeScale;
|
||||||
|
|
||||||
|
// Increase energy regen
|
||||||
|
if (this.scene.player) {
|
||||||
|
this.scene.player.energyRegenRate *= config.energyRegenBonus;
|
||||||
|
this.scene.player.walkSpeed *= config.walkSpeedMultiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMarijuanaAudio(config) {
|
||||||
|
// Change music to chill variant
|
||||||
|
const currentMusic = this.scene.sound.get('background_music');
|
||||||
|
if (currentMusic) {
|
||||||
|
// Fade out current
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: currentMusic,
|
||||||
|
volume: 0,
|
||||||
|
duration: 2000,
|
||||||
|
onComplete: () => {
|
||||||
|
currentMusic.stop();
|
||||||
|
// Play chill music
|
||||||
|
this.scene.sound.play('music_chill_lofi', {
|
||||||
|
loop: true,
|
||||||
|
volume: 0.4
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endMarijuanaEffects() {
|
||||||
|
this.activeEffects.marijuana = false;
|
||||||
|
|
||||||
|
// Restore time scale
|
||||||
|
this.scene.time.timeScale = 1.0;
|
||||||
|
|
||||||
|
// Restore player stats
|
||||||
|
if (this.scene.player) {
|
||||||
|
this.scene.player.energyRegenRate /= this.effectConfig.marijuana.energyRegenBonus;
|
||||||
|
this.scene.player.walkSpeed /= this.effectConfig.marijuana.walkSpeedMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove visual effects
|
||||||
|
const camera = this.scene.cameras.main;
|
||||||
|
camera.clearTint();
|
||||||
|
this.scene.tweens.killTweensOf(camera);
|
||||||
|
|
||||||
|
// Restore original music
|
||||||
|
const chillMusic = this.scene.sound.get('music_chill_lofi');
|
||||||
|
if (chillMusic) {
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: chillMusic,
|
||||||
|
volume: 0,
|
||||||
|
duration: 2000,
|
||||||
|
onComplete: () => {
|
||||||
|
chillMusic.stop();
|
||||||
|
this.scene.sound.play('background_music', { loop: true, volume: 0.5 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🌿 Marijuana effects ended');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MUSHROOM HALLUCINATION EFFECTS
|
||||||
|
*/
|
||||||
|
consumeMushrooms() {
|
||||||
|
if (this.activeEffects.mushrooms) {
|
||||||
|
console.log('🍄 Already tripping on mushrooms');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeEffects.mushrooms = true;
|
||||||
|
const config = this.effectConfig.mushrooms;
|
||||||
|
|
||||||
|
console.log('🍄 Mushrooms consumed - Reality dissolving...');
|
||||||
|
|
||||||
|
// Apply psychedelic shader
|
||||||
|
this.applyPsychedelicShader(config);
|
||||||
|
|
||||||
|
// Start hallucination events
|
||||||
|
this.startHallucinations(config);
|
||||||
|
|
||||||
|
// Show notification
|
||||||
|
this.scene.events.emit('show-notification', {
|
||||||
|
title: '🍄 Hallucinacije',
|
||||||
|
message: 'Barve... gibanje... duhovi iz preteklosti...',
|
||||||
|
icon: '🌀',
|
||||||
|
duration: 7000,
|
||||||
|
color: '#FF1493'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set duration timer
|
||||||
|
this.effectTimers.mushrooms = setTimeout(() => {
|
||||||
|
this.endMushroomEffects();
|
||||||
|
}, config.duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPsychedelicShader(config) {
|
||||||
|
const camera = this.scene.cameras.main;
|
||||||
|
|
||||||
|
// Extreme color shifting
|
||||||
|
this.colorShiftTimer = setInterval(() => {
|
||||||
|
const hue = Phaser.Math.Between(0, 360);
|
||||||
|
const color = Phaser.Display.Color.HSVToRGB(hue / 360, 0.8, 0.9);
|
||||||
|
camera.setTint(color.color);
|
||||||
|
}, 500); // Change every 500ms
|
||||||
|
|
||||||
|
// Camera shake (mild)
|
||||||
|
camera.shake(config.duration, 0.002);
|
||||||
|
|
||||||
|
// Zoom pulse
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: camera,
|
||||||
|
zoom: 1.05,
|
||||||
|
duration: 3000,
|
||||||
|
yoyo: true,
|
||||||
|
repeat: -1,
|
||||||
|
ease: 'Sine.easeInOut'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
startHallucinations(config) {
|
||||||
|
// Periodic hallucination events
|
||||||
|
this.hallucinationInterval = setInterval(() => {
|
||||||
|
if (Math.random() < config.hallucinationChance) {
|
||||||
|
this.triggerHallucination();
|
||||||
|
}
|
||||||
|
}, 1000); // Check every second
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerHallucination() {
|
||||||
|
const hallucinationTypes = [
|
||||||
|
'moving_objects',
|
||||||
|
'ghost_vision',
|
||||||
|
'color_trails',
|
||||||
|
'reality_distortion'
|
||||||
|
];
|
||||||
|
|
||||||
|
const type = Phaser.Utils.Array.GetRandom(hallucinationTypes);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'moving_objects':
|
||||||
|
this.hallucinateMovingObjects();
|
||||||
|
break;
|
||||||
|
case 'ghost_vision':
|
||||||
|
this.hallucinateGhostVision();
|
||||||
|
break;
|
||||||
|
case 'color_trails':
|
||||||
|
this.hallucinateColorTrails();
|
||||||
|
break;
|
||||||
|
case 'reality_distortion':
|
||||||
|
this.hallucinateRealityDistortion();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hallucinateMovingObjects() {
|
||||||
|
// Random game objects start floating/moving
|
||||||
|
const objects = this.scene.children.list.filter(obj =>
|
||||||
|
obj.type === 'Sprite' && obj !== this.scene.player
|
||||||
|
);
|
||||||
|
|
||||||
|
if (objects.length === 0) return;
|
||||||
|
|
||||||
|
const obj = Phaser.Utils.Array.GetRandom(objects);
|
||||||
|
const originalX = obj.x;
|
||||||
|
const originalY = obj.y;
|
||||||
|
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: obj,
|
||||||
|
x: originalX + Phaser.Math.Between(-30, 30),
|
||||||
|
y: originalY + Phaser.Math.Between(-30, 30),
|
||||||
|
duration: 2000,
|
||||||
|
yoyo: true,
|
||||||
|
ease: 'Sine.easeInOut',
|
||||||
|
onComplete: () => {
|
||||||
|
obj.x = originalX;
|
||||||
|
obj.y = originalY;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hallucinateGhostVision() {
|
||||||
|
// Spawn ghost from Kai's past
|
||||||
|
const ghosts = ['ana_ghost', 'family_ghost', 'memory_ghost'];
|
||||||
|
const ghostType = Phaser.Utils.Array.GetRandom(ghosts);
|
||||||
|
|
||||||
|
const x = this.scene.player.x + Phaser.Math.Between(-200, 200);
|
||||||
|
const y = this.scene.player.y + Phaser.Math.Between(-200, 200);
|
||||||
|
|
||||||
|
const ghost = this.scene.add.sprite(x, y, ghostType);
|
||||||
|
ghost.setAlpha(0);
|
||||||
|
ghost.setDepth(20);
|
||||||
|
ghost.setTint(0x9966FF); // Purple ghost tint
|
||||||
|
|
||||||
|
// Fade in
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: ghost,
|
||||||
|
alpha: 0.7,
|
||||||
|
duration: 1000,
|
||||||
|
onComplete: () => {
|
||||||
|
// Ghost speaks
|
||||||
|
this.scene.events.emit('show-speech-bubble', {
|
||||||
|
x: ghost.x,
|
||||||
|
y: ghost.y - 50,
|
||||||
|
text: this.getGhostMessage(),
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fade out
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: ghost,
|
||||||
|
alpha: 0,
|
||||||
|
duration: 2000,
|
||||||
|
delay: 3000,
|
||||||
|
onComplete: () => ghost.destroy()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getGhostMessage() {
|
||||||
|
const messages = [
|
||||||
|
"Kai... ne pozabi...",
|
||||||
|
"Ana te išče...",
|
||||||
|
"Spomniti se moraš...",
|
||||||
|
"Vrni se domov..."
|
||||||
|
];
|
||||||
|
return Phaser.Utils.Array.GetRandom(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
hallucinateColorTrails() {
|
||||||
|
// Player leaves colorful trails when moving
|
||||||
|
if (!this.scene.player.isMoving) return;
|
||||||
|
|
||||||
|
const trail = this.scene.add.sprite(
|
||||||
|
this.scene.player.x,
|
||||||
|
this.scene.player.y,
|
||||||
|
this.scene.player.texture.key
|
||||||
|
);
|
||||||
|
trail.setFrame(this.scene.player.frame.name);
|
||||||
|
trail.setAlpha(0.5);
|
||||||
|
trail.setTint(Phaser.Math.Between(0x000000, 0xFFFFFF));
|
||||||
|
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: trail,
|
||||||
|
alpha: 0,
|
||||||
|
duration: 1000,
|
||||||
|
onComplete: () => trail.destroy()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hallucinateRealityDistortion() {
|
||||||
|
// Screen warps/ripples
|
||||||
|
const camera = this.scene.cameras.main;
|
||||||
|
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: camera,
|
||||||
|
scrollX: camera.scrollX + Phaser.Math.Between(-20, 20),
|
||||||
|
scrollY: camera.scrollY + Phaser.Math.Between(-20, 20),
|
||||||
|
duration: 500,
|
||||||
|
yoyo: true,
|
||||||
|
repeat: 3
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
endMushroomEffects() {
|
||||||
|
this.activeEffects.mushrooms = false;
|
||||||
|
|
||||||
|
// Stop color shift
|
||||||
|
if (this.colorShiftTimer) {
|
||||||
|
clearInterval(this.colorShiftTimer);
|
||||||
|
this.colorShiftTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop hallucinations
|
||||||
|
if (this.hallucinationInterval) {
|
||||||
|
clearInterval(this.hallucinationInterval);
|
||||||
|
this.hallucinationInterval = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore camera
|
||||||
|
const camera = this.scene.cameras.main;
|
||||||
|
camera.clearTint();
|
||||||
|
camera.setZoom(1.0);
|
||||||
|
this.scene.tweens.killTweensOf(camera);
|
||||||
|
|
||||||
|
console.log('🍄 Mushroom effects ended - Reality restored');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BLACK MARKET TRADE
|
||||||
|
*/
|
||||||
|
sellDrug(drugType, quantity) {
|
||||||
|
if (this.policeEstablished) {
|
||||||
|
// Risk of getting caught!
|
||||||
|
if (Math.random() < 0.3) { // 30% chance
|
||||||
|
this.scene.events.emit('police:drug_bust', drugType, quantity);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const price = this.prices[drugType].sell;
|
||||||
|
const earnings = price * quantity;
|
||||||
|
|
||||||
|
if (this.scene.inventorySystem) {
|
||||||
|
this.scene.inventorySystem.removeItem(drugType, quantity);
|
||||||
|
this.scene.inventorySystem.addGold(earnings);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`💰 Sold ${quantity}x ${drugType} for ${earnings} gold`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POLICE ESTABLISHMENT
|
||||||
|
*/
|
||||||
|
onPoliceEstablished() {
|
||||||
|
this.policeEstablished = true;
|
||||||
|
|
||||||
|
this.scene.events.emit('show-notification', {
|
||||||
|
title: '🚔 Policija Ustanovljena',
|
||||||
|
message: 'Previdno! Zdaj ti lahko zaseže droge!',
|
||||||
|
icon: '⚠️',
|
||||||
|
duration: 5000,
|
||||||
|
color: '#FF0000'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('🚔 Police established - Drug trade now illegal and risky');
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.endMarijuanaEffects();
|
||||||
|
this.endMushroomEffects();
|
||||||
|
|
||||||
|
if (this.effectTimers.marijuana) clearTimeout(this.effectTimers.marijuana);
|
||||||
|
if (this.effectTimers.mushrooms) clearTimeout(this.effectTimers.mushrooms);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user