//============================================================================= // MinimapZoom.js //============================================================================= /*: * @plugindesc Adds a minimap/zoom-out feature. Press M to toggle a zoomed-out view of the entire map. * @author Dazed Translations * * @param Toggle Key * @desc The key to toggle minimap (keycode: M = 79) * @default 79 * * @help * Press the M key while on the map to toggle a zoomed-out minimap view. * Shows the entire map scaled to fit the screen. * Character sprites are hidden, and a marker shows your position. * Press M again to return to normal view. */ (function() { 'use strict'; // Plugin parameters const pluginName = 'MinimapZoom'; const parameters = PluginManager.parameters(pluginName); const toggleKeyCode = Number(parameters['Toggle Key'] || 79); // M key // Track minimap state let minimapActive = false; //========================================================================= // Input - Add minimap key //========================================================================= Input.keyMapper[toggleKeyCode] = 'minimap'; //========================================================================= // Scene_Map - Handle minimap toggle //========================================================================= const _Scene_Map_update = Scene_Map.prototype.update; Scene_Map.prototype.update = function() { _Scene_Map_update.call(this); this.updateMinimapToggle(); }; Scene_Map.prototype.updateMinimapToggle = function() { // Only allow toggle when not busy with messages/menus if (Input.isTriggered('minimap') && !$gameMessage.isBusy()) { this.toggleMinimap(); } // Update player marker position and atmospheric effects if minimap is active if (minimapActive) { if (this._minimapPlayerMarker) { this.updateMinimapPlayerMarker(); } this.updateMinimapAtmosphere(); } }; Scene_Map.prototype.updateMinimapAtmosphere = function() { // Subtle flicker effect on the map content to simulate candlelight if (this._minimapContent) { const flicker = 0.92 + Math.sin(Graphics.frameCount * 0.05) * 0.03 + Math.sin(Graphics.frameCount * 0.13) * 0.02; this._minimapContent.opacity = Math.floor(255 * flicker); } }; Scene_Map.prototype.toggleMinimap = function() { minimapActive = !minimapActive; if (minimapActive) { this.showMinimap(); SoundManager.playCursor(); } else { this.hideMinimap(); SoundManager.playCancel(); } }; Scene_Map.prototype.showMinimap = function() { if (!this._spriteset) return; // Hide the normal spriteset this._spriteset.visible = false; // Create minimap overlay this.createMinimapOverlay(); }; Scene_Map.prototype.hideMinimap = function() { if (!this._spriteset) return; // Show the normal spriteset this._spriteset.visible = true; // Remove minimap overlay this.removeMinimapOverlay(); }; Scene_Map.prototype.createMinimapOverlay = function() { if (this._minimapOverlay) return; // Create container for minimap this._minimapOverlay = new Sprite(); this._minimapOverlay.bitmap = new Bitmap(Graphics.width, Graphics.height); // Draw dark atmospheric background with vignette this.drawAtmosphericBackground(); // Get map dimensions const mapWidth = $gameMap.width(); const mapHeight = $gameMap.height(); const tileWidth = $gameMap.tileWidth(); const tileHeight = $gameMap.tileHeight(); // Calculate scale to fit entire map on screen with padding const padding = 70; const availableWidth = Graphics.width - padding * 2; const availableHeight = Graphics.height - padding * 2; const mapPixelWidth = mapWidth * tileWidth; const mapPixelHeight = mapHeight * tileHeight; const scaleX = availableWidth / mapPixelWidth; const scaleY = availableHeight / mapPixelHeight; const scale = Math.min(scaleX, scaleY, 1); // Don't zoom in past 1x const scaledWidth = mapPixelWidth * scale; const scaledHeight = mapPixelHeight * scale; // Center the map const offsetX = (Graphics.width - scaledWidth) / 2; const offsetY = (Graphics.height - scaledHeight) / 2; // Store for marker positioning this._minimapScale = scale; this._minimapOffsetX = offsetX; this._minimapOffsetY = offsetY; this._minimapTileWidth = tileWidth; this._minimapTileHeight = tileHeight; // Create the map snapshot sprite this._minimapContent = new Sprite(); this._minimapContent.x = offsetX; this._minimapContent.y = offsetY; this._minimapContent.scale.x = scale; this._minimapContent.scale.y = scale; // Render the tilemap to a bitmap this.renderMinimapTiles(); // Draw ornate border around map this.drawOrnateBorder(offsetX, offsetY, scaledWidth, scaledHeight); // Add title text with gothic styling this._minimapOverlay.bitmap.fontFace = 'GameFont'; this._minimapOverlay.bitmap.fontSize = 26; this._minimapOverlay.bitmap.textColor = '#d4af37'; // Gold color this._minimapOverlay.bitmap.outlineColor = '#1a0a00'; this._minimapOverlay.bitmap.outlineWidth = 4; this._minimapOverlay.bitmap.drawText('~ Cartograph ~', 0, 12, Graphics.width, 30, 'center'); // Add map name with aged look const mapName = $gameMap.displayName() || ('Unknown Region'); this._minimapOverlay.bitmap.fontSize = 20; this._minimapOverlay.bitmap.textColor = '#c9a959'; this._minimapOverlay.bitmap.drawText('― ' + mapName + ' ―', 0, Graphics.height - 38, Graphics.width, 24, 'center'); // Smaller instruction text this._minimapOverlay.bitmap.fontSize = 14; this._minimapOverlay.bitmap.textColor = '#8b7355'; this._minimapOverlay.bitmap.drawText('Press M to close', 0, Graphics.height - 18, Graphics.width, 20, 'center'); // Draw legend this.drawMinimapLegend(); this.addChild(this._minimapOverlay); this.addChild(this._minimapContent); // Create player marker this.createMinimapPlayerMarker(); }; Scene_Map.prototype.drawAtmosphericBackground = function() { const bitmap = this._minimapOverlay.bitmap; const ctx = bitmap.context; const w = Graphics.width; const h = Graphics.height; // Base dark parchment color ctx.fillStyle = '#1a1410'; ctx.fillRect(0, 0, w, h); // Add subtle parchment texture noise for (let i = 0; i < 8000; i++) { const x = Math.random() * w; const y = Math.random() * h; const alpha = Math.random() * 0.08; const shade = Math.floor(Math.random() * 30) + 20; ctx.fillStyle = `rgba(${shade}, ${shade - 5}, ${shade - 10}, ${alpha})`; ctx.fillRect(x, y, 1, 1); } // Add vignette effect (darker edges) const gradient = ctx.createRadialGradient(w/2, h/2, h * 0.3, w/2, h/2, h * 0.8); gradient.addColorStop(0, 'rgba(0, 0, 0, 0)'); gradient.addColorStop(0.7, 'rgba(0, 0, 0, 0.3)'); gradient.addColorStop(1, 'rgba(0, 0, 0, 0.7)'); ctx.fillStyle = gradient; ctx.fillRect(0, 0, w, h); }; Scene_Map.prototype.drawOrnateBorder = function(x, y, width, height) { const ctx = this._minimapOverlay.bitmap.context; const borderSize = 6; // Outer dark border ctx.strokeStyle = '#0d0805'; ctx.lineWidth = borderSize + 4; ctx.strokeRect(x - borderSize - 2, y - borderSize - 2, width + (borderSize + 2) * 2, height + (borderSize + 2) * 2); // Main gold border ctx.strokeStyle = '#8b7355'; ctx.lineWidth = borderSize; ctx.strokeRect(x - borderSize/2, y - borderSize/2, width + borderSize, height + borderSize); // Inner highlight ctx.strokeStyle = '#c9a959'; ctx.lineWidth = 1; ctx.strokeRect(x - 1, y - 1, width + 2, height + 2); // Corner ornaments const cornerSize = 12; ctx.fillStyle = '#d4af37'; // Top-left corner ctx.beginPath(); ctx.arc(x, y, cornerSize/2, 0, Math.PI * 2); ctx.fill(); // Top-right corner ctx.beginPath(); ctx.arc(x + width, y, cornerSize/2, 0, Math.PI * 2); ctx.fill(); // Bottom-left corner ctx.beginPath(); ctx.arc(x, y + height, cornerSize/2, 0, Math.PI * 2); ctx.fill(); // Bottom-right corner ctx.beginPath(); ctx.arc(x + width, y + height, cornerSize/2, 0, Math.PI * 2); ctx.fill(); // Inner corner dots ctx.fillStyle = '#1a0a00'; const innerDot = 4; ctx.beginPath(); ctx.arc(x, y, innerDot/2, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(x + width, y, innerDot/2, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(x, y + height, innerDot/2, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(x + width, y + height, innerDot/2, 0, Math.PI * 2); ctx.fill(); }; Scene_Map.prototype.drawMinimapLegend = function() { const bitmap = this._minimapOverlay.bitmap; const legendX = 18; const legendY = 50; const dotSize = 10; const lineHeight = 24; const ctx = bitmap.context; bitmap.fontSize = 14; bitmap.textColor = '#c9a959'; bitmap.outlineColor = '#1a0a00'; bitmap.outlineWidth = 3; // Legend title with underline bitmap.drawText('Legend', legendX, legendY, 100, 18, 'left'); ctx.strokeStyle = '#8b7355'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(legendX, legendY + 18); ctx.lineTo(legendX + 55, legendY + 18); ctx.stroke(); bitmap.textColor = '#a89070'; // Player marker ctx.fillStyle = '#cc0000'; ctx.beginPath(); ctx.arc(legendX + dotSize/2, legendY + lineHeight + dotSize/2 + 5, dotSize/2, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#ffcc00'; ctx.lineWidth = 2; ctx.stroke(); bitmap.drawText('You', legendX + dotSize + 10, legendY + lineHeight + 5, 100, 18, 'left'); // NPC marker (warm gold) ctx.fillStyle = '#daa520'; ctx.beginPath(); ctx.arc(legendX + dotSize/2, legendY + lineHeight * 2 + dotSize/2 + 5, dotSize/2, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#2a1a00'; ctx.lineWidth = 1; ctx.stroke(); bitmap.drawText('NPC', legendX + dotSize + 10, legendY + lineHeight * 2 + 5, 100, 18, 'left'); // Locked door/chest marker (teal/mysterious) ctx.fillStyle = '#40e0d0'; ctx.beginPath(); ctx.arc(legendX + dotSize/2, legendY + lineHeight * 3 + dotSize/2 + 5, dotSize/2, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#2a1a00'; ctx.lineWidth = 1; ctx.stroke(); bitmap.drawText('Locked', legendX + dotSize + 10, legendY + lineHeight * 3 + 5, 150, 18, 'left'); // Exit/Transfer marker (eerie green) ctx.fillStyle = '#7cfc00'; ctx.beginPath(); ctx.arc(legendX + dotSize/2, legendY + lineHeight * 4 + dotSize/2 + 5, dotSize/2, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#2a1a00'; ctx.lineWidth = 1; ctx.stroke(); bitmap.drawText('Passage', legendX + dotSize + 10, legendY + lineHeight * 4 + 5, 150, 18, 'left'); }; Scene_Map.prototype.renderMinimapTiles = function() { const mapWidth = $gameMap.width(); const mapHeight = $gameMap.height(); const tileWidth = $gameMap.tileWidth(); const tileHeight = $gameMap.tileHeight(); // Create bitmap for the map const bitmap = new Bitmap(mapWidth * tileWidth, mapHeight * tileHeight); this._minimapContent.bitmap = bitmap; // Get tileset const tileset = $gameMap.tileset(); if (!tileset) return; // Dark fantasy color palette const wallColor = '#1a1410'; // Very dark brown/black for walls const floorColor = '#5a4f45'; // Lighter dusty floor (more contrast) const floorAltColor = '#4d4238'; // Floor variation // Simple tile rendering - draw colored rectangles based on passability for (let y = 0; y < mapHeight; y++) { for (let x = 0; x < mapWidth; x++) { const px = x * tileWidth; const py = y * tileHeight; // Check tile properties - use checkPassage for more accurate detection // checkPassage checks the actual tile bits rather than just directional passability const bit = 0x0f; // All directions const passage = $gameMap.checkPassage(x, y, bit); // Also check standard passability as backup const dirPassable = $gameMap.isPassable(x, y, 2) || $gameMap.isPassable(x, y, 4) || $gameMap.isPassable(x, y, 6) || $gameMap.isPassable(x, y, 8); // Check if any tile at this location is a "void" tile (like pits) // by examining the actual tile IDs const tiles = $gameMap.allTiles(x, y); let hasFloorTile = false; // Look for actual floor/ground tiles // RPG Maker MZ Tileset Layout: // A1 (animated/water): 2048-2815 - water, waterfalls, poison swamps, lava // A2 (ground): 2816-4351 - grass, dirt, stone floors // A3 (buildings): 4352-5887 - roofs and building exteriors // A4 (walls): 5888-8191 - wall tops // A5 (normal tiles): 1536-1663 - simple ground tiles // B-E tiles: start at different offsets for decoration for (const tileId of tiles) { if (tileId === 0) continue; // A5 tiles (simple ground): 1536-1663 if (tileId >= 1536 && tileId < 1664) { hasFloorTile = true; break; } // A1 tiles (water/animated - including poison, lava, swamps): 2048-2815 if (tileId >= 2048 && tileId < 2816) { hasFloorTile = true; break; } // A2 tiles (ground autotiles): 2816-4351 if (tileId >= 2816 && tileId < 4352) { hasFloorTile = true; break; } // B-E tiles can also be walkable floors (tile ID encoding varies) // These are typically in lower ranges and used for details/overlays // If a B-E tile is passable, it should be considered ground if (tileId > 0 && tileId < 1536) { // This is a B-E tile, check if it's passable via flags const flags = $gameMap.tilesetFlags(); const flag = flags[tileId]; if (flag !== undefined && (flag & 0x10) === 0) { // Not star passability (not an overlay) - could be floor if ((flag & 0x0F) !== 0x0F) { // Not fully blocked hasFloorTile = true; break; } } } } // Consider passable only if: passage check passes AND has a floor tile // OR if standard directional passability says it's passable AND has floor const isPassable = (passage || dirPassable) && hasFloorTile; // Get terrain tag for variety const terrainTag = $gameMap.terrainTag(x, y); const regionId = $gameMap.regionId(x, y); // Color based on passability and terrain - dark fantasy palette let color; if (!isPassable) { // Impassable - dark stone walls color = wallColor; // Add slight variation if ((x * 7 + y * 13) % 5 === 0) { color = '#352a24'; } } else if (terrainTag > 0) { // Special terrain - different dungeon features const terrainColors = [ '#3d4a3d', // Mossy '#4a4a3d', // Sandy '#3d3d4a', // Damp stone '#4a3d3d', // Bloodstained '#3d4a4a', // Flooded '#4a3d4a', // Corrupted '#4a4a4a' // Ashen ]; color = terrainColors[(terrainTag - 1) % terrainColors.length]; } else { // Normal passable floor - aged stone color = (x + y) % 2 === 0 ? floorColor : floorAltColor; } // Add noise for texture const noise = ((x * 17 + y * 31) % 10) - 5; color = this.adjustColor(color, noise); bitmap.fillRect(px, py, tileWidth, tileHeight, color); // Draw subtle worn grid lines (like old stone tiles) const ctx = bitmap.context; ctx.strokeStyle = 'rgba(0, 0, 0, 0.15)'; ctx.lineWidth = 1; ctx.strokeRect(px, py, tileWidth, tileHeight); // Add occasional cracks/details on floors if (isPassable && (x * 23 + y * 37) % 20 === 0) { ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)'; ctx.beginPath(); ctx.moveTo(px + 2, py + 2); ctx.lineTo(px + tileWidth - 4, py + tileHeight - 4); ctx.stroke(); } } } // Draw important events as markers for (const event of $gameMap.events()) { if (!event) continue; const ex = event.x * tileWidth + tileWidth / 2; const ey = event.y * tileHeight + tileHeight / 2; const ctx = bitmap.context; const markerSize = Math.min(tileWidth, tileHeight) / 2.5; // Check event types in priority order if (this.isLockedDoorOrChest(event)) { // Draw locked door/chest marker (teal/mysterious) ctx.fillStyle = '#40e0d0'; ctx.shadowColor = '#40e0d0'; ctx.shadowBlur = 4; ctx.beginPath(); ctx.arc(ex, ey, markerSize, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; ctx.strokeStyle = '#1a3a3a'; ctx.lineWidth = 2; ctx.stroke(); } else if (this.isMapTransfer(event)) { // Draw exit/entrance marker (eerie green glow) ctx.fillStyle = '#7cfc00'; ctx.shadowColor = '#7cfc00'; ctx.shadowBlur = 6; ctx.beginPath(); ctx.arc(ex, ey, markerSize, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; ctx.strokeStyle = '#2a4a00'; ctx.lineWidth = 2; ctx.stroke(); } else if (this.isImportantEvent(event)) { // Draw NPC marker (warm gold) ctx.fillStyle = '#daa520'; ctx.shadowColor = '#daa520'; ctx.shadowBlur = 3; ctx.beginPath(); ctx.arc(ex, ey, markerSize, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; ctx.strokeStyle = '#4a3500'; ctx.lineWidth = 2; ctx.stroke(); } } }; // Check if an event is a locked door, chest, or barred gate Scene_Map.prototype.isLockedDoorOrChest = function(event) { if (!event) return false; // Get the event data const eventData = event.event(); if (!eventData) return false; const name = eventData.name; // Check for locked door patterns if (name.includes('鍵扉')) return true; // "Locked door" // Check for chest patterns if (name.includes('宝箱')) return true; // "Treasure chest" // Check for barred door/gate patterns (iron bars) if (name.includes('鉄格子')) return true; // "Iron bars/Barred gate" // Check for barred/latch doors (opens from above/below pattern) if (name.includes('上から') && name.includes('下から')) return true; // "From above... from below" latch doors return false; }; // Check if an event transfers to another map Scene_Map.prototype.isMapTransfer = function(event) { if (!event) return false; // Get the event data const eventData = event.event(); if (!eventData) return false; const name = eventData.name; // Check for transfer event name patterns if (name.includes('移動')) return true; // "Transfer/Move" // Check the current page for Transfer Player command (code 201) const page = event.page(); if (!page) return false; const list = page.list; if (!list) return false; for (const cmd of list) { if (cmd.code === 201) { // Transfer Player command return true; } } return false; }; // Check if an event is a locked door that can be picked (legacy - kept for compatibility) Scene_Map.prototype.isLockedDoor = function(event) { return this.isLockedDoorOrChest(event); }; // Determine if an event is "important" (NPC, not decoration/ambient) Scene_Map.prototype.isImportantEvent = function(event) { if (!event) return false; // Must have a character graphic if (!event.characterName()) return false; // Get the event data const eventData = event.event(); if (!eventData) return false; // Check the current page const page = event.page(); if (!page) return false; // Skip if no trigger (parallel process/autorun without interaction) // Trigger types: 0=Action Button, 1=Player Touch, 2=Event Touch, 3=Autorun, 4=Parallel const trigger = page.trigger; // Only show Action Button (0) and Player Touch (1) - these are interactive NPCs if (trigger !== 0 && trigger !== 1) return false; // Skip common decoration/ambient event names const name = eventData.name.toLowerCase(); const skipPatterns = [ '窓', 'window', // Windows '鳩', 'bird', 'pigeon', // Birds '猫', 'cat', // Cats '犬', 'dog', // Dogs 'light', '灯', 'lamp', // Lights 'flame', '炎', 'fire', // Fire effects 'smoke', '煙', // Smoke 'effect', '効果', // Effects 'particle', // Particles 'ambient', // Ambient 'decor', '装飾', // Decorations 'bg', 'background', // Background elements 'fog', '霧', // Fog 'water', '水', // Water effects 'shadow', '影', // Shadows 'grass', '草', // Grass 'tree', '木', // Trees 'flower', '花', // Flowers 'rock', '岩', // Rocks 'sparkle', 'shine', // Sparkle effects 'animation', 'anim', // Animations ]; for (const pattern of skipPatterns) { if (name.includes(pattern)) return false; } // Skip events with very short command lists (likely just empty or simple triggers) const list = page.list; if (!list || list.length <= 2) return false; // Check if event has meaningful commands (text, choices, transfers, etc.) // Command codes: 101=Show Text, 102=Show Choices, 201=Transfer Player, // 121=Control Switches, 122=Control Variables, 301=Battle Processing const meaningfulCodes = [101, 102, 103, 104, 105, 201, 301, 302, 303, 311, 312, 326]; let hasMeaningfulCommand = false; for (const cmd of list) { if (meaningfulCodes.includes(cmd.code)) { hasMeaningfulCommand = true; break; } } return hasMeaningfulCommand; }; Scene_Map.prototype.isTilePassable = function(x, y) { // First check: is this tile visually a "ceiling/void" tile? // These are often star-passable but should show as walls on minimap const tiles = $gameMap.allTiles(x, y); const flags = $gameMap.tilesetFlags(); for (const tileId of tiles) { if (tileId === 0) continue; // A4 wall-top tiles (2048-2815) - these are wall tops/ceilings // A5 floor tiles start at 1536, A4 starts at 2048 if (tileId >= 2048 && tileId < 2816) { // This is an A4 wall-top tile - treat as impassable for minimap return false; } // Check for tiles in the B-E range that have star passability // Star passable (0x10) tiles that are in upper layers are usually ceilings/walls if (tileId >= 256) { // B-E tiles const flag = flags[tileId]; if (flag !== undefined && (flag & 0x10) !== 0) { // Star passable - this is likely a ceiling tile, treat as wall // Unless it's actually ground level const layerTiles = $gameMap.layeredTiles(x, y); if (layerTiles.length > 0 && layerTiles[0] === tileId) { // This star tile is on the top layer - it's a ceiling/wall return false; } } } } // Check standard directional passability (all 4 directions) const passableDown = $gameMap.isPassable(x, y, 2); const passableLeft = $gameMap.isPassable(x, y, 4); const passableRight = $gameMap.isPassable(x, y, 6); const passableUp = $gameMap.isPassable(x, y, 8); // If any direction is passable, consider it a floor if (passableDown || passableLeft || passableRight || passableUp) { // But also check if there's a blocking event at this location const events = $gameMap.eventsXy(x, y); for (const event of events) { if (event && !event.isThrough() && event.isNormalPriority()) { return false; } } return true; } return false; }; Scene_Map.prototype.adjustColor = function(hex, amount) { // Simple color adjustment let r = parseInt(hex.slice(1, 3), 16); let g = parseInt(hex.slice(3, 5), 16); let b = parseInt(hex.slice(5, 7), 16); r = Math.min(255, Math.max(0, r + amount)); g = Math.min(255, Math.max(0, g + amount)); b = Math.min(255, Math.max(0, b + amount)); return '#' + r.toString(16).padStart(2, '0') + g.toString(16).padStart(2, '0') + b.toString(16).padStart(2, '0'); }; Scene_Map.prototype.createMinimapPlayerMarker = function() { if (this._minimapPlayerMarker) return; // Create player marker sprite - mystical/atmospheric style this._minimapPlayerMarker = new Sprite(); this._minimapPlayerMarker.bitmap = new Bitmap(36, 36); const ctx = this._minimapPlayerMarker.bitmap.context; const cx = 18, cy = 18; // Ethereal outer glow - deep crimson const outerGlow = ctx.createRadialGradient(cx, cy, 0, cx, cy, 16); outerGlow.addColorStop(0, 'rgba(139, 69, 19, 0)'); outerGlow.addColorStop(0.5, 'rgba(139, 69, 19, 0.3)'); outerGlow.addColorStop(1, 'rgba(139, 69, 19, 0)'); ctx.fillStyle = outerGlow; ctx.beginPath(); ctx.arc(cx, cy, 16, 0, Math.PI * 2); ctx.fill(); // Main marker - mystical blood red const mainGrad = ctx.createRadialGradient(cx, cy, 0, cx, cy, 10); mainGrad.addColorStop(0, '#c41e3a'); mainGrad.addColorStop(0.7, '#8b0000'); mainGrad.addColorStop(1, '#4a0000'); ctx.fillStyle = mainGrad; ctx.beginPath(); ctx.arc(cx, cy, 10, 0, Math.PI * 2); ctx.fill(); // Inner glow - warm amber const innerGrad = ctx.createRadialGradient(cx, cy, 0, cx, cy, 6); innerGrad.addColorStop(0, '#ffd700'); innerGrad.addColorStop(0.5, '#daa520'); innerGrad.addColorStop(1, '#b8860b'); ctx.fillStyle = innerGrad; ctx.beginPath(); ctx.arc(cx, cy, 6, 0, Math.PI * 2); ctx.fill(); // Center soul light ctx.fillStyle = '#fff8dc'; ctx.beginPath(); ctx.arc(cx, cy, 2, 0, Math.PI * 2); ctx.fill(); this._minimapPlayerMarker.anchor.x = 0.5; this._minimapPlayerMarker.anchor.y = 0.5; this.addChild(this._minimapPlayerMarker); this.updateMinimapPlayerMarker(); }; Scene_Map.prototype.updateMinimapPlayerMarker = function() { if (!this._minimapPlayerMarker) return; const playerX = $gamePlayer.x; const playerY = $gamePlayer.y; // Calculate marker position const markerX = this._minimapOffsetX + (playerX + 0.5) * this._minimapTileWidth * this._minimapScale; const markerY = this._minimapOffsetY + (playerY + 0.5) * this._minimapTileHeight * this._minimapScale; this._minimapPlayerMarker.x = markerX; this._minimapPlayerMarker.y = markerY; // Pulse effect const pulse = Math.sin(Graphics.frameCount * 0.1) * 0.2 + 1; this._minimapPlayerMarker.scale.x = pulse; this._minimapPlayerMarker.scale.y = pulse; }; Scene_Map.prototype.removeMinimapOverlay = function() { if (this._minimapOverlay) { this.removeChild(this._minimapOverlay); this._minimapOverlay = null; } if (this._minimapContent) { this.removeChild(this._minimapContent); this._minimapContent = null; } if (this._minimapPlayerMarker) { this.removeChild(this._minimapPlayerMarker); this._minimapPlayerMarker = null; } }; //========================================================================= // Handle scene transitions - Reset minimap when leaving map //========================================================================= const _Scene_Map_terminate = Scene_Map.prototype.terminate; Scene_Map.prototype.terminate = function() { if (minimapActive) { this.hideMinimap(); minimapActive = false; } _Scene_Map_terminate.call(this); }; //========================================================================= // Prevent menu/actions while minimap is active //========================================================================= const _Scene_Map_isMenuEnabled = Scene_Map.prototype.isMenuEnabled; Scene_Map.prototype.isMenuEnabled = function() { if (minimapActive) return false; return _Scene_Map_isMenuEnabled.call(this); }; const _Scene_Map_isMapTouchOk = Scene_Map.prototype.isMapTouchOk; Scene_Map.prototype.isMapTouchOk = function() { if (minimapActive) return false; return _Scene_Map_isMapTouchOk.call(this); }; const _Game_Player_canMove = Game_Player.prototype.canMove; Game_Player.prototype.canMove = function() { if (minimapActive) return false; return _Game_Player_canMove.call(this); }; // Public accessor for minimap state Scene_Map.prototype.isMinimapActive = function() { return minimapActive; }; })();