/** * NPC PRIVACY & HOME DECORATION SYSTEM * Part of: Game Systems Expansion * Created: January 4, 2026 * * Features: * - Hobby-based automatic interior generation * - Door lock system (heart-based access) * - NPC home visit mechanics * - Relationship effects from visits * - Privacy violations & consequences */ class NPCPrivacySystem { constructor(game) { this.game = game; this.player = game.player; // Privacy settings this.privacyLevels = { PUBLIC: 0, // Anyone can enter (living room, kitchen) FRIENDLY: 3, // 3+ hearts required PRIVATE: 5, // 5+ hearts required (bedroom) INTIMATE: 8, // 8+ hearts required (special rooms) LOCKED: 10 // 10 hearts or marriage required }; // Visit tracking this.visitHistory = {}; this.lastVisitTimes = {}; // NPC home decorations (generated based on hobby) this.npcHomes = {}; } /** * Generate NPC home interior based on hobby */ generateNPCHome(npcId) { const npc = this.game.npcs.get(npcId); if (!npc) return null; const hobby = npc.hobby; // Base home structure const home = { npcId: npcId, rooms: { living_room: { name: 'Living Room', privacyLevel: this.privacyLevels.PUBLIC, objects: this.generateLivingRoomObjects(hobby) }, kitchen: { name: 'Kitchen', privacyLevel: this.privacyLevels.PUBLIC, objects: this.generateKitchenObjects(hobby) }, bedroom: { name: 'Bedroom', privacyLevel: this.privacyLevels.PRIVATE, objects: this.generateBedroomObjects(hobby) }, hobby_room: { name: this.getHobbyRoomName(hobby), privacyLevel: this.privacyLevels.FRIENDLY, objects: this.generateHobbyRoomObjects(hobby) } }, visitCount: 0, lastVisit: null }; // Store home this.npcHomes[npcId] = home; return home; } /** * Generate living room objects */ generateLivingRoomObjects(hobby) { const base = [ 'interior_table_small', 'interior_bookshelf', 'interior_gothic_lantern' ]; // Hobby-specific additions const hobbyAdditions = { fishing: ['mounted_fish', 'fishing_net'], farming: ['grain_sack', 'tool_rack'], cooking: ['recipe_shelf'], reading: ['bookshelf', 'reading_chair'], music: ['guitar_stand', 'music_sheets'], crafting: ['crafting_workshop'] }; return [...base, ...(hobbyAdditions[hobby] || [])]; } /** * Generate kitchen objects */ generateKitchenObjects(hobby) { const base = [ 'interior_kitchen_stove', 'interior_kitchen_counter' ]; if (hobby === 'cooking') { base.push( 'interior_kitchen_fridge', 'interior_kitchen_sink', 'interior_recipe_shelf' ); } return base; } /** * Generate bedroom objects */ generateBedroomObjects(hobby) { const base = [ 'interior_bed_wooden', 'interior_wardrobe', 'interior_chest_locked' ]; // Personal items based on hobby const hobbyItems = { zombie_worker: ['brain_jar', 'work_uniform'], baker: ['flour_workspace', 'dough_tools'], barber: ['dreadlock_kit', 'styling_tools'], fisherman: ['tackle_box', 'fish_collection'] }; return [...base, ...(hobbyItems[hobby] || [])]; } /** * Generate hobby room objects */ generateHobbyRoomObjects(hobby) { const hobbyRooms = { fishing: [ 'fishing_rod_rack', 'bait_storage', 'fish_tank', 'mounted_trophy_fish' ], zombie_worker: [ 'brain_jar', 'work_bench', 'tool_storage', 'miner_equipment' ], baker: [ 'flour_workspace', 'mixing_bowls', 'bread_storage', 'recipe_collection' ], alchemy: [ 'interior_alchemy_bottle', 'interior_brewing_cauldron', 'interior_chemistry_set', 'interior_potion_shelf' ], crafting: [ 'interior_crafting_workshop', 'interior_tool_rack', 'material_storage', 'blueprint_table' ], reading: [ 'interior_bookshelf', 'reading_chair', 'ancient_manuscripts', 'writing_desk' ] }; return hobbyRooms[hobby] || ['generic_workspace']; } /** * Get hobby room name */ getHobbyRoomName(hobby) { const names = { fishing: 'Fishing Den', zombie_worker: 'Worker\'s Quarters', baker: 'Baking Workshop', alchemy: 'Alchemy Lab', crafting: 'Craft Room', reading: 'Library', music: 'Music Studio', farming: 'Storage Shed' }; return names[hobby] || 'Hobby Room'; } /** * Attempt to enter NPC room */ attemptEntry(npcId, roomId) { const npc = this.game.npcs.get(npcId); if (!npc) { return { success: false, message: 'NPC not found!' }; } // Generate home if doesn't exist if (!this.npcHomes[npcId]) { this.generateNPCHome(npcId); } const home = this.npcHomes[npcId]; const room = home.rooms[roomId]; if (!room) { return { success: false, message: 'Room not found!' }; } // Check privacy level const playerHearts = this.player.getRelationshipHearts(npcId); const requiredHearts = room.privacyLevel; if (playerHearts < requiredHearts) { // Privacy violation! return this.handlePrivacyViolation(npcId, roomId, requiredHearts); } // Allowed entry return this.handleSuccessfulEntry(npcId, roomId); } /** * Handle privacy violation */ handlePrivacyViolation(npcId, roomId, requiredHearts) { const npc = this.game.npcs.get(npcId); // Relationship penalty const penalty = (requiredHearts - this.player.getRelationshipHearts(npcId)) * 20; npc.addRelationshipPoints(-penalty); // NPC reaction const reactions = [ "Hey! That's private!", "What are you doing in my room?!", "Get out! This is MY space!", "I can't believe you just walked in here...", "Privacy, please!" ]; const reaction = Phaser.Utils.Array.GetRandom(reactions); this.game.showDialogue(npc.name, reaction, { mood: 'angry', animation: 'shocked' }); this.game.showMessage( `${npc.name} is upset! -${penalty} relationship points` ); // Player gets kicked out this.game.player.teleportToLocation('outside_' + npcId + '_home'); return { success: false, privacyViolation: true, penalty: penalty, requiredHearts: requiredHearts, currentHearts: this.player.getRelationshipHearts(npcId) }; } /** * Handle successful entry */ handleSuccessfulEntry(npcId, roomId) { const npc = this.game.npcs.get(npcId); const home = this.npcHomes[npcId]; // Track visit if (!this.visitHistory[npcId]) { this.visitHistory[npcId] = []; } const visit = { roomId: roomId, timestamp: this.game.time.currentTime, timeOfDay: this.game.time.getTimeOfDay() }; this.visitHistory[npcId].push(visit); this.lastVisitTimes[npcId] = this.game.time.currentTime; home.visitCount++; home.lastVisit = this.game.time.currentTime; // Relationship effects based on visit this.applyVisitEffects(npcId, roomId, visit); // Enter room scene this.game.scene.start('NPCRoomScene', { npcId: npcId, roomId: roomId, room: home.rooms[roomId] }); return { success: true, room: home.rooms[roomId], npc: npc }; } /** * Apply relationship effects from visit */ applyVisitEffects(npcId, roomId, visit) { const npc = this.game.npcs.get(npcId); const timeOfDay = visit.timeOfDay; // Visit frequency check const recentVisits = this.visitHistory[npcId].filter(v => { const hoursSince = (this.game.time.currentTime - v.timestamp) / 3600; return hoursSince < 24; // Last 24 hours }); if (recentVisits.length > 3) { // Visiting TOO much = annoying npc.addRelationshipPoints(-10); this.game.showMessage( `${npc.name} seems a bit annoyed by frequent visits...` ); return; } // Time of day effects if (timeOfDay === 'night' && roomId === 'bedroom') { // Visiting bedroom at night = awkward npc.addRelationshipPoints(-5); this.game.showDialogue( npc.name, "It's quite late... Is everything okay?", { mood: 'concerned' } ); } else if (timeOfDay === 'morning' && roomId === 'kitchen') { // Morning kitchen visit = breakfast together! npc.addRelationshipPoints(5); this.game.showDialogue( npc.name, "Good morning! Care to join me for breakfast?", { mood: 'happy' } ); } else if (roomId === 'hobby_room') { // Showing interest in hobby = bonus points! npc.addRelationshipPoints(10); this.game.showDialogue( npc.name, "I'm glad you're interested in my hobby!", { mood: 'excited' } ); } } /** * Get visit statistics for NPC */ getVisitStats(npcId) { const visits = this.visitHistory[npcId] || []; // Count visits per room const roomCounts = {}; visits.forEach(visit => { roomCounts[visit.roomId] = (roomCounts[visit.roomId] || 0) + 1; }); // Average visits per day const daysSinceFirstVisit = visits.length > 0 ? (this.game.time.currentTime - visits[0].timestamp) / 86400 : 0; const avgVisitsPerDay = daysSinceFirstVisit > 0 ? visits.length / daysSinceFirstVisit : 0; return { totalVisits: visits.length, roomCounts: roomCounts, avgVisitsPerDay: avgVisitsPerDay, lastVisit: this.lastVisitTimes[npcId], favoriteRoom: Object.keys(roomCounts).reduce((a, b) => roomCounts[a] > roomCounts[b] ? a : b, null) }; } /** * Check if player can access special room */ canAccessSpecialRoom(npcId, roomId) { const npc = this.game.npcs.get(npcId); if (!npc) return false; const home = this.npcHomes[npcId]; if (!home) return false; const room = home.rooms[roomId]; if (!room) return false; const playerHearts = this.player.getRelationshipHearts(npcId); // Special case: marriage allows LOCKED access if (room.privacyLevel === this.privacyLevels.LOCKED) { return this.player.spouse === npcId; } return playerHearts >= room.privacyLevel; } /** * Get NPC home UI data */ getNPCHomeUIData(npcId) { if (!this.npcHomes[npcId]) { this.generateNPCHome(npcId); } const home = this.npcHomes[npcId]; const npc = this.game.npcs.get(npcId); return { npc: npc, home: home, accessibleRooms: Object.keys(home.rooms).filter(roomId => this.canAccessSpecialRoom(npcId, roomId) ), lockedRooms: Object.keys(home.rooms).filter(roomId => !this.canAccessSpecialRoom(npcId, roomId) ), visitStats: this.getVisitStats(npcId) }; } }