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:
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 = [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user