//============================================================================= // RPGVault_Minimap.js //============================================================================= /*: * @target MZ * @plugindesc v1.2.0 Zeigt eine Minimap mit Spieler, Events und Ausgängen auf der Karte. * @author GR-FoxDie * @url https://rpg-vault.de * * @help * ============================================================================ * RPGVault_Minimap * ============================================================================ * Zeigt in einer Ecke der Kartenansicht eine Minimap an. Begehbare Felder, * der Spieler, Events und Ausgänge (Events mit "Transfer Player") werden * farbig dargestellt. Ist die Karte größer als die Minimap, scrollt die * Minimap automatisch mit dem Spieler mit. * * Die Karte kann als verkleinerte ORIGINAL-Grafik (Tileset der Karte) oder * als einfarbiges Schema (Boden/Wand) dargestellt werden. Bei der Original- * Darstellung werden die Tile-Ebenen der Karte (ohne Schatten, Regionen und * Parallax-Hintergrund) mit Feldgröße 6-8 Pixel am besten lesbar. * * Dateiname: RPGVault_Minimap.js (nicht umbenennen!) * * ---------------------------------------------------------------------------- * NOTETAGS * ---------------------------------------------------------------------------- * Karten-Notiz: * Auf dieser Karte wird keine Minimap angezeigt. * * Event-Notiz: * Dieses Event wird auf der Minimap nicht angezeigt. * * ---------------------------------------------------------------------------- * PLUGIN-BEFEHLE * ---------------------------------------------------------------------------- * Minimap anzeigen / Minimap verbergen / Minimap umschalten / * Minimap neu zeichnen (z.B. nach Änderung der Karte per Script). * * ---------------------------------------------------------------------------- * TASTE * ---------------------------------------------------------------------------- * Optional kann ein Buchstabe (A-Z) festgelegt werden, mit dem der Spieler * die Minimap ein- und ausblenden kann (Standard: M). * * ---------------------------------------------------------------------------- * LIZENZ / LICENSE * ---------------------------------------------------------------------------- * - Erlaubt: Nutzung in NICHT-kommerziellen UND kommerziellen Projekten. * - Erlaubt: Anpassungen für das eigene Projekt. * - VERBOTEN: Repost, Weitergabe oder Upload dieser Datei (auch verändert) * auf anderen Seiten, Foren, Cloud-Diensten oder in Asset-Paketen. * - VERBOTEN: Entfernen oder Verändern der Header-Hinweise, Copyright- * Angaben und Download-Verweise im Code (auch bei angepassten Versionen). * - Einziger erlaubter Download-Ort: https://rpg-vault.de * - Credits: erwünscht, aber nicht verpflichtend. * - Nutzung auf eigene Gefahr, keine Garantie. * * EN: Free for non-commercial and commercial use. Do NOT repost or * redistribute. Header notices, copyright statements and download * references in the code must NOT be removed or altered, even in * modified versions. Download is only allowed at https://rpg-vault.de * * @command show * @text Minimap anzeigen * @desc Blendet die Minimap ein. * * @command hide * @text Minimap verbergen * @desc Blendet die Minimap aus. * * @command toggle * @text Minimap umschalten * @desc Schaltet die Minimap zwischen sichtbar und verborgen um. * * @command refresh * @text Minimap neu zeichnen * @desc Zeichnet die Minimap der aktuellen Karte komplett neu. * * @param position * @text Position * @type select * @option Oben rechts * @value topRight * @option Oben links * @value topLeft * @option Unten rechts * @value bottomRight * @option Unten links * @value bottomLeft * @default topRight * * @param marginX * @text Abstand horizontal * @type number * @min 0 * @default 8 * * @param marginY * @text Abstand vertikal * @type number * @min 0 * @default 8 * * @param width * @text Breite (Pixel) * @type number * @min 40 * @default 180 * * @param height * @text Höhe (Pixel) * @type number * @min 40 * @default 140 * * @param mapMode * @text Kartendarstellung * @desc Original = echte Karten-Grafik (Tileset) verkleinert. Schema = einfarbig (Boden/Wand). * @type select * @option Original-Karte (Tileset-Grafik) * @value original * @option Schema (Boden/Wand einfarbig) * @value schema * @default original * * @param tileSize * @text Feldgröße (Pixel) * @desc Größe eines Karten-Feldes auf der Minimap (1-16). Für die Original-Karte sind 6-8 empfehlenswert. * @type number * @min 1 * @max 16 * @default 6 * * @param dotSize * @text Punktgröße (Pixel) * @desc Größe der Punkte für Spieler, Events und Ausgänge. * @type number * @min 1 * @max 16 * @default 4 * * @param opacity * @text Deckkraft * @type number * @min 0 * @max 255 * @default 210 * * @param backgroundOpacity * @text Deckkraft: Hintergrund + Rahmen * @desc 0 = unsichtbar, 255 = voll deckend. Spieler/Events bleiben davon unberührt. * @type number * @min 0 * @max 255 * @default 255 * * @param mapOpacity * @text Deckkraft: Kartenfläche * @desc Deckkraft der Boden-/Wandfarben. Niedrige Werte = Spielkarte scheint durch. * @type number * @min 0 * @max 255 * @default 255 * * @param switchId * @text Schalter (optional) * @desc Minimap wird nur angezeigt, wenn dieser Schalter AN ist. 0 = immer. * @type switch * @default 0 * * @param toggleKey * @text Umschalt-Taste * @desc Ein Buchstabe A-Z. Leer lassen = keine Taste. * @type string * @default M * * @param showEvents * @text Events anzeigen * @type boolean * @default true * * @param showTransfers * @text Ausgänge anzeigen * @desc Events mit "Transfer Player" in einer eigenen Farbe anzeigen. * @type boolean * @default true * * @param hideOnMessage * @text Bei Nachrichten ausblenden * @type boolean * @default true * * @param colorFloor * @text Farbe: Boden * @desc CSS-Farbe, z.B. #8fa3b8 oder rgba(255,255,255,0.6) * @type string * @default #8fa3b8 * * @param colorWall * @text Farbe: Wand / Unbegehbar * @type string * @default #2b2f3a * * @param colorBackground * @text Farbe: Hintergrund * @type string * @default #15171d * * @param colorBorder * @text Farbe: Rahmen * @type string * @default #e8e8e8 * * @param colorPlayer * @text Farbe: Spieler * @type string * @default #ffd400 * * @param colorEvent * @text Farbe: Events * @type string * @default #ff5a5a * * @param colorTransfer * @text Farbe: Ausgänge * @type string * @default #4cd964 */ (() => { "use strict"; const PLUGIN_NAME = "RPGVault_Minimap"; const params = PluginManager.parameters(PLUGIN_NAME); const clamp = (v, min, max) => Math.max(min, Math.min(max, v)); const CFG = { position: String(params.position || "topRight"), marginX: Number(params.marginX || 8), marginY: Number(params.marginY || 8), width: Number(params.width || 180), height: Number(params.height || 140), mapMode: String(params.mapMode || "original"), tileSize: clamp(Number(params.tileSize || 6), 1, 16), dotSize: clamp(Number(params.dotSize || 4), 1, 16), opacity: clamp(Number(params.opacity || 210), 0, 255), backgroundOpacity: clamp(Number(params.backgroundOpacity === undefined || params.backgroundOpacity === "" ? 255 : params.backgroundOpacity), 0, 255), mapOpacity: clamp(Number(params.mapOpacity === undefined || params.mapOpacity === "" ? 255 : params.mapOpacity), 0, 255), switchId: Number(params.switchId || 0), toggleKey: String(params.toggleKey || "").trim().toUpperCase().charAt(0), showEvents: params.showEvents !== "false", showTransfers: params.showTransfers !== "false", hideOnMessage: params.hideOnMessage !== "false", colorFloor: String(params.colorFloor || "#8fa3b8"), colorWall: String(params.colorWall || "#2b2f3a"), colorBackground: String(params.colorBackground || "#15171d"), colorBorder: String(params.colorBorder || "#e8e8e8"), colorPlayer: String(params.colorPlayer || "#ffd400"), colorEvent: String(params.colorEvent || "#ff5a5a"), colorTransfer: String(params.colorTransfer || "#4cd964") }; const BORDER = 2; const FADE_SPEED = 16; // Taste registrieren if (CFG.toggleKey >= "A" && CFG.toggleKey <= "Z") { Input.keyMapper[CFG.toggleKey.charCodeAt(0)] = "rpgvaultMinimap"; } //------------------------------------------------------------------------- // Plugin-Befehle //------------------------------------------------------------------------- PluginManager.registerCommand(PLUGIN_NAME, "show", () => { $gameSystem._minimapVisible = true; }); PluginManager.registerCommand(PLUGIN_NAME, "hide", () => { $gameSystem._minimapVisible = false; }); PluginManager.registerCommand(PLUGIN_NAME, "toggle", () => { $gameSystem._minimapVisible = $gameSystem._minimapVisible === false; }); PluginManager.registerCommand(PLUGIN_NAME, "refresh", () => { const scene = SceneManager._scene; if (scene && scene._rpgvaultMinimap) { scene._rpgvaultMinimap.refresh(); } }); //------------------------------------------------------------------------- // Game_System //------------------------------------------------------------------------- const _Game_System_initialize = Game_System.prototype.initialize; Game_System.prototype.initialize = function() { _Game_System_initialize.call(this); this._minimapVisible = true; }; //------------------------------------------------------------------------- // Sprite_Minimap //------------------------------------------------------------------------- class Sprite_Minimap extends Sprite { constructor() { super(); this._frameX = -1; this._frameY = -1; this._transferCache = new WeakMap(); this._background = new Sprite(); this._mapSprite = new Sprite(); this._dynSprite = new Sprite(); this.addChild(this._background); this.addChild(this._mapSprite); this.addChild(this._dynSprite); this.opacity = 0; this.refresh(); } isMapEnabled() { return !($dataMap && $dataMap.meta && $dataMap.meta.NoMinimap); } shouldShow() { if (!this.isMapEnabled()) return false; if ($gameSystem._minimapVisible === false) return false; if (CFG.switchId > 0 && !$gameSwitches.value(CFG.switchId)) return false; if (CFG.hideOnMessage && $gameMessage.isBusy()) return false; return true; } refresh() { const ts = CFG.tileSize; const mapW = $gameMap.width() * ts; const mapH = $gameMap.height() * ts; this._dispW = Math.min(CFG.width, mapW); this._dispH = Math.min(CFG.height, mapH); this._frameX = -1; this._frameY = -1; // Hintergrund + Rahmen const bw = this._dispW + BORDER * 2; const bh = this._dispH + BORDER * 2; const bg = new Bitmap(bw, bh); bg.fillRect(0, 0, bw, bh, CFG.colorBorder); bg.fillRect(BORDER, BORDER, this._dispW, this._dispH, CFG.colorBackground); this._background.bitmap = bg; this._background.opacity = CFG.backgroundOpacity; // Statische Karte if (CFG.mapMode === "original") { // Leere Fläche, bis die Tileset-Bilder geladen sind this._mapSprite.bitmap = new Bitmap(mapW, mapH); this._tilesetBitmaps = this.loadTilesetBitmaps(); this._originalPending = true; } else { this._originalPending = false; this._mapSprite.bitmap = this.buildMapBitmap(); } this._mapSprite.opacity = CFG.mapOpacity; this._mapSprite.x = BORDER; this._mapSprite.y = BORDER; // Dynamische Ebene (Spieler, Events) this._dynSprite.bitmap = new Bitmap(this._dispW, this._dispH); this._dynSprite.x = BORDER; this._dynSprite.y = BORDER; this.updatePosition(bw, bh); } buildMapBitmap() { const ts = CFG.tileSize; const w = $gameMap.width(); const h = $gameMap.height(); const bmp = new Bitmap(w * ts, h * ts); bmp.fillRect(0, 0, w * ts, h * ts, CFG.colorWall); const dirs = [2, 4, 6, 8]; for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { if (dirs.some(d => $gameMap.isPassable(x, y, d))) { bmp.fillRect(x * ts, y * ts, ts, ts, CFG.colorFloor); } } } return bmp; } loadTilesetBitmaps() { const tileset = $gameMap.tileset(); const names = tileset ? tileset.tilesetNames : []; return names.map(name => (name ? ImageManager.loadTileset(name) : null)); } updateOriginal() { if (!this._originalPending) return; const ready = this._tilesetBitmaps.every(b => !b || b.isReady() || b.isError()); if (!ready) return; this._originalPending = false; this._mapSprite.bitmap = this.buildOriginalBitmap(); this._frameX = -1; this._frameY = -1; } buildOriginalBitmap() { const ts = CFG.tileSize; const w = $gameMap.width(); const h = $gameMap.height(); const tw = $gameMap.tileWidth(); const th = $gameMap.tileHeight(); const bmp = new Bitmap(w * ts, h * ts); const data = $dataMap.data; for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { for (let z = 0; z < 4; z++) { const id = data[(z * h + y) * w + x] || 0; if (id > 0 && id < Tilemap.TILE_ID_MAX) { this.drawOriginalTile(bmp, id, x * ts, y * ts, ts, tw, th); } } } } return bmp; } drawOriginalTile(bmp, id, dx, dy, ts, tw, th) { if (Tilemap.isAutotile(id)) { this.drawOriginalAutotile(bmp, id, dx, dy, ts, tw, th); return; } const setNumber = Tilemap.isTileA5(id) ? 4 : 5 + Math.floor(id / 256); const src = this._tilesetBitmaps[setNumber]; if (!src || !src.isReady()) return; const sx = ((Math.floor(id / 128) % 2) * 8 + (id % 8)) * tw; const sy = (Math.floor((id % 256) / 8) % 16) * th; bmp.blt(src, sx, sy, tw, th, dx, dy, ts, ts); } drawOriginalAutotile(bmp, id, dx, dy, ts, tw, th) { let table = Tilemap.FLOOR_AUTOTILE_TABLE; const kind = Tilemap.getAutotileKind(id); const shape = Tilemap.getAutotileShape(id); const tx = kind % 8; const ty = Math.floor(kind / 8); let bx = 0; let by = 0; let setNumber = 0; if (Tilemap.isTileA1(id)) { setNumber = 0; if (kind === 0) { bx = 0; by = 0; } else if (kind === 1) { bx = 0; by = 3; } else if (kind === 2) { bx = 6; by = 0; } else if (kind === 3) { bx = 6; by = 3; } else { bx = Math.floor(tx / 4) * 8; by = ty * 6 + (Math.floor(tx / 2) % 2) * 3; if (kind % 2 === 1) { bx += 6; table = Tilemap.WATERFALL_AUTOTILE_TABLE; } } } else if (Tilemap.isTileA2(id)) { setNumber = 1; bx = tx * 2; by = (ty - 2) * 3; } else if (Tilemap.isTileA3(id)) { setNumber = 2; bx = tx * 2; by = (ty - 6) * 2; table = Tilemap.WALL_AUTOTILE_TABLE; } else if (Tilemap.isTileA4(id)) { setNumber = 3; bx = tx * 2; by = Math.floor((ty - 10) * 2.5 + (ty % 2 === 1 ? 0.5 : 0)); if (ty % 2 === 1) table = Tilemap.WALL_AUTOTILE_TABLE; } const src = this._tilesetBitmaps[setNumber]; if (!src || !src.isReady()) return; const quarter = table[shape]; if (!quarter) return; const qw = tw / 2; const qh = th / 2; const q = ts / 2; for (let i = 0; i < 4; i++) { const sx = (bx * 2 + quarter[i][0]) * qw; const sy = (by * 2 + quarter[i][1]) * qh; const ox = (i % 2) * q; const oy = Math.floor(i / 2) * q; bmp.blt(src, sx, sy, qw, qh, dx + ox, dy + oy, q, q); } } updatePosition(bw, bh) { const bx = Graphics.boxWidth; const by = Graphics.boxHeight; const left = CFG.position === "topLeft" || CFG.position === "bottomLeft"; const top = CFG.position === "topLeft" || CFG.position === "topRight"; this.x = left ? CFG.marginX : bx - bw - CFG.marginX; this.y = top ? CFG.marginY : by - bh - CFG.marginY; } update() { super.update(); this.updateOriginal(); this.updateFade(); if (this.opacity > 0) { this.updateView(); this.drawDynamic(); } } updateFade() { if (this.shouldShow()) { this.opacity = Math.min(CFG.opacity, this.opacity + FADE_SPEED); } else { this.opacity = Math.max(0, this.opacity - FADE_SPEED); } this.visible = this.opacity > 0; } updateView() { const ts = CFG.tileSize; const mapW = $gameMap.width() * ts; const mapH = $gameMap.height() * ts; const px = ($gamePlayer._realX + 0.5) * ts; const py = ($gamePlayer._realY + 0.5) * ts; this._viewX = Math.round(clamp(px - this._dispW / 2, 0, Math.max(0, mapW - this._dispW))); this._viewY = Math.round(clamp(py - this._dispH / 2, 0, Math.max(0, mapH - this._dispH))); if (this._viewX !== this._frameX || this._viewY !== this._frameY) { this._mapSprite.setFrame(this._viewX, this._viewY, this._dispW, this._dispH); this._frameX = this._viewX; this._frameY = this._viewY; } } isTransferEvent(event) { const page = event.page(); if (!page) return false; let result = this._transferCache.get(page.list); if (result === undefined) { result = page.list.some(cmd => cmd.code === 201); this._transferCache.set(page.list, result); } return result; } drawDot(bmp, realX, realY, color) { const ts = CFG.tileSize; const d = CFG.dotSize; const x = Math.round((realX + 0.5) * ts - this._viewX - d / 2); const y = Math.round((realY + 0.5) * ts - this._viewY - d / 2); if (x + d < 0 || y + d < 0 || x > this._dispW || y > this._dispH) return; bmp.fillRect(x, y, d, d, color); } drawDynamic() { const bmp = this._dynSprite.bitmap; bmp.clear(); if (CFG.showEvents || CFG.showTransfers) { for (const event of $gameMap.events()) { if (event._erased || !event.page()) continue; const data = event.event(); if (data && data.meta && data.meta.MinimapHide) continue; if (CFG.showTransfers && this.isTransferEvent(event)) { this.drawDot(bmp, event._realX, event._realY, CFG.colorTransfer); continue; } if (!CFG.showEvents) continue; if (event.isTransparent()) continue; if (!event.characterName() && event.tileId() <= 0) continue; this.drawDot(bmp, event._realX, event._realY, CFG.colorEvent); } } if (!$gamePlayer.isTransparent()) { this.drawDot(bmp, $gamePlayer._realX, $gamePlayer._realY, CFG.colorPlayer); } } } //------------------------------------------------------------------------- // Scene_Map //------------------------------------------------------------------------- const _Scene_Map_createDisplayObjects = Scene_Map.prototype.createDisplayObjects; Scene_Map.prototype.createDisplayObjects = function() { _Scene_Map_createDisplayObjects.call(this); this.createRpgvaultMinimap(); }; Scene_Map.prototype.createRpgvaultMinimap = function() { this._rpgvaultMinimap = new Sprite_Minimap(); // Unter dem Fensterlayer einfügen, damit Nachrichten darüber liegen const index = this.children.indexOf(this._windowLayer); this.addChildAt(this._rpgvaultMinimap, index >= 0 ? index : this.children.length); }; const _Scene_Map_update = Scene_Map.prototype.update; Scene_Map.prototype.update = function() { _Scene_Map_update.call(this); if (CFG.toggleKey && Input.isTriggered("rpgvaultMinimap") && !$gameMessage.isBusy() && !$gamePlayer.isMoveRouteForcing()) { $gameSystem._minimapVisible = $gameSystem._minimapVisible === false; } }; })();