- Сообщений: 4
- Спасибо получено: 0
Плагин на изменения боя. Боевая сетка (Внимание сырой)
2 ч. 11 мин. назад #132782
от Ryterator
Плагин создал мне deepseek Если вы против нейросетей как инструмента помощи в творчестве, просто не используйте этот плагин
.
Пример того, что он делает.
Для установки смените окончание\расшерение файла с txt на Js.
Я не считаю нейросеть вредной в творчестве, но я сторонник общедоступности того, что было ею сделано, так как она учится и работает на открытом контенте сообщества.
Плагин предоставляется «как есть». Можете проверить его самостоятельно перед установкой. Код представлен ниже.
Пример того, что он делает.
Для установки смените окончание\расшерение файла с txt на Js.
Я не считаю нейросеть вредной в творчестве, но я сторонник общедоступности того, что было ею сделано, так как она учится и работает на открытом контенте сообщества.
Плагин предоставляется «как есть». Можете проверить его самостоятельно перед установкой. Код представлен ниже.
Code:
/*:
* @target MZ
* @plugindesc v5.7 Formation Grid: строгая сетка + SV/idle спрайты врагов + SV-UI построения
* @author AI
*
* @help
* ============================================================
* Battle Formation Grid v5.7
* ============================================================
* Сетка: бойцы внутри линии стоят СТОЛБИКОМ СНИЗУ ВВЕРХ,
* линии (вертикальные полосы) идут СЛЕВА НАПРАВО.
* Линия 0 — фронт (слева, ближе к противнику).
*
* --- АВТО-ГЕНЕРАЦИЯ ЯЧЕЕК ---
* Количество ячеек = partySize * partyLines.
* Меняешь partySize / partyLines в параметрах плагина —
* ячейки, спрайты и подписи автоматически перестраиваются.
*
* --- UI ПОСТРОЕНИЯ ---
* Все цифры UI — прямые, в LAYOUT ниже:
* grid.originX / originY — позиция слота 0 (линия 0, низ столбика)
* grid.stepX / stepY — шаг между линиями / бойцами в линии
* grid.cellW / cellH — размер ячейки слота
* Формула расчёта позиции слота — только в Scene_FormationGrid.slotPos.
*
* --- ТЕГИ СКИЛЛОВ / ПРЕДМЕТОВ (в Note) ---
* <melee> <ranged> <pierceLines:N>
* <aoeLine:N> <aoeColumn:N> <aoeCross:N>
* <coverBack> (state)
*
* --- ТЕГИ ВРАГОВ (в Note врага) ---
* <svBattler:filename> img/sv_enemies/filename.png (9×6)
* <charSheet:filename> img/characters/filename.png (12×8)
* <idleSheet:filename:cols> img/pictures/filename.png (полоса)
* <idleSheet:filename:cols:row:rows> с указанием строки/всего строк
* ============================================================
*
* @command openFormation
* @text Открыть построение
*
* @param attackIsMelee
* @text Базовая «Атака» = ближняя
* @type boolean
* @default true
*
* @param enemySvMirror
* @text Enemy: зеркалить SV-мобов
* @type boolean
* @default true
*
* @param partySize
* @text Party: бойцов в линии
* @type number
* @min 1
* @max 6
* @default 3
*
* @param partyLines
* @text Party: линий
* @type number
* @min 1
* @max 6
* @default 3
*
* @param partyOriginX
* @text Party: Origin X
* @type number
* @default 540
*
* @param partyOriginY
* @text Party: Origin Y
* @type number
* @default 540
*
* @param partyColSpacing
* @text Party: шаг бойцов в линии
* @type number
* @default 80
*
* @param partyRowSpacing
* @text Party: шаг между линиями
* @type number
* @default 100
*
* @param partyMirror
* @text Party: Mirror
* @type boolean
* @default false
*
* @param partyFaceLeft
* @text Party: смотрят влево
* @type boolean
* @default true
*
* @param enemySize
* @text Enemy: бойцов в линии
* @type number
* @min 1
* @max 6
* @default 3
*
* @param enemyLines
* @text Enemy: линий
* @type number
* @min 1
* @max 6
* @default 3
*
* @param enemyOriginX
* @text Enemy: Origin X
* @type number
* @default 260
*
* @param enemyOriginY
* @text Enemy: Origin Y
* @type number
* @default 540
*
* @param enemyColSpacing
* @text Enemy: шаг бойцов в линии
* @type number
* @default 80
*
* @param enemyRowSpacing
* @text Enemy: шаг между линиями
* @type number
* @default 100
*
* @param enemyMirror
* @text Enemy: Mirror
* @type boolean
* @default true
*
* @param enemyAnimSpeed
* @text Enemy: скорость idle-анимации
* @type number
* @min 4
* @max 120
* @default 24
*/
(() => {
"use strict";
const pluginName = "BattleFormationGrid";
const raw = PluginManager.parameters(pluginName);
const clamp = (v, a, b) => isNaN(v) ? a : Math.max(a, Math.min(b, v));
const P = {
attackIsMelee: String(raw.attackIsMelee) !== "false",
partySize: clamp(Number(raw.partySize || 3), 1, 6),
partyLines: clamp(Number(raw.partyLines || 3), 1, 6),
partyOriginX: Number(raw.partyOriginX || 540),
partyOriginY: Number(raw.partyOriginY || 540),
partyColSpacing: Number(raw.partyColSpacing || 80),
partyRowSpacing: Number(raw.partyRowSpacing || 100),
partyMirror: String(raw.partyMirror) === "true",
partyFaceLeft: String(raw.partyFaceLeft) !== "false",
enemySize: clamp(Number(raw.enemySize || 3), 1, 6),
enemyLines: clamp(Number(raw.enemyLines || 3), 1, 6),
enemyOriginX: Number(raw.enemyOriginX || 260),
enemyOriginY: Number(raw.enemyOriginY || 540),
enemyColSpacing: Number(raw.enemyColSpacing || 80),
enemyRowSpacing: Number(raw.enemyRowSpacing || 100),
enemyMirror: String(raw.enemyMirror) !== "false",
enemySvMirror: String(raw.enemySvMirror) !== "True",
enemyAnimSpeed: clamp(Number(raw.enemyAnimSpeed || 24), 4, 120)
};
// ============================================================
// UI СЦЕНЫ ПОСТРОЕНИЯ
// Только прямые значения: X, Y, ширина, высота, шаги.
// Количество ячеек = partySize * partyLines (автоматически).
// ============================================================
const LAYOUT = {
// --- Сетка слотов ---
grid: {
originX: 560, // X центра ячейки слота 0 (линия 0, позиция 0)
originY: 500, // Y НИЗА ячейки слота 0 (ноги спрайта)
stepX: 100, // шаг между линиями по X (вправо)
stepY: 90, // шаг между бойцами в линии по Y (вверх)
cellW: 100, // ширина ячейки
cellH: 90, // высота ячейки
innerPadding: 2 // внутр. отступ рамки ячейки
},
// --- Надпись "ФРОНТ" (вертикально, побуквенно) ---
frontLabel: {
x: 460,
y: 260,
charW: 30,
charH: 26,
fontSize: 22
},
// --- Подписи линий ("Первая линия" и т.д.) ---
lineLabel: {
offsetY: 36,
w: 140,
h: 20,
fontSize: 16
},
// --- Плашка "имя + HP" над спрайтом ---
infoLabel: {
offsetY: 60,
w: 180,
h: 46,
nameFontSize: 17,
hpFontSize: 12,
hpBarH: 10,
hpBarMargin: 10,
hpBarBottomGap: 10
},
// --- Нижнее окно подсказки ---
helpWindow: { x: 40, y: 600, w: 1200, h: 70 },
// --- Левая панель "Выбор линии" ---
linePanel: { x: 40, y: 50, w: 220, h: 450, rowH: 38 },
// --- Курсор выбора слота ---
cursor: { extra: 8, thickness: 3 }
};
// ============================================================
// УТИЛИТЫ СЛОТОВ
// ============================================================
function actorSlot(actor) {
if (!actor) return -1;
const map = $gameParty._formationSlots;
if (map) {
const key = String(actor.actorId());
if (Object.prototype.hasOwnProperty.call(map, key)) {
return Number(map[key]);
}
}
return $gameParty.allMembers().indexOf(actor);
}
function battlerSlot(b) {
if (!b) return -1;
if (b.isActor && b.isActor()) return actorSlot(b);
return b.index();
}
// ============================================================
// ГЕОМЕТРИЯ ДЛЯ БОЯ
// ============================================================
function gridPos(index, size, lines, ox, oy, cs, rs, mirror) {
const member = index % size;
const line = Math.floor(index / size);
const dir = mirror ? -1 : 1;
return {
x: Math.round(ox + line * rs * dir),
y: Math.round(oy - member * cs)
};
}
function getLineOfBattler(b) {
if (!b) return -1;
const s = battlerSlot(b);
if (s < 0) return -1;
const size = b.isActor() ? P.partySize : P.enemySize;
return Math.floor(s / size);
}
function getFrontLivingLineOfUnit(unit) {
if (!unit) return -1;
let min = Infinity;
for (const m of unit.aliveMembers()) {
const l = getLineOfBattler(m);
if (l >= 0 && l < min) min = l;
}
return min === Infinity ? -1 : min;
}
function unitLayout(unit) {
const isPartyUnit = !!unit._actors;
return {
size: isPartyUnit ? P.partySize : P.enemySize,
lines: isPartyUnit ? P.partyLines : P.enemyLines
};
}
function cellsOfUnit(unit) {
const { size } = unitLayout(unit);
const cells = [];
for (const m of unit.aliveMembers()) {
const s = battlerSlot(m);
if (s < 0) continue;
cells.push({
member: m,
memberIdx: s % size,
line: Math.floor(s / size)
});
}
return cells;
}
function itemNote(action) { return (action.item() && action.item().note) || ""; }
function actionIsMelee(action) {
const note = itemNote(action);
if (/<ranged>/i.test(note)) return false;
if (/<melee>/i.test(note)) return true;
return P.attackIsMelee && action.isAttack();
}
function parsePierce(action) {
const m = /<pierceLines:(\d+)>/i.exec(itemNote(action));
return m ? clamp(Number(m[1]), 1, 6) : 1;
}
function parseAoeShapes(action) {
const note = itemNote(action);
const shapes = [];
const grab = (re, key) => {
const m = re.exec(note);
if (!m) return;
const n = m[1] ? clamp(Number(m[1]), 1, 36) : 36;
shapes.push({ key, n });
};
grab(/<aoeLine:(\d+)>/i, "line");
grab(/<aoeColumn:(\d+)>/i, "column");
grab(/<aoeCross:(\d+)>/i, "cross");
if (/<aoeLine>(?!\d)/i.test(note) && !/<aoeLine:\d+>/i.test(note))
shapes.push({ key: "line", n: 36 });
if (/<aoeColumn>(?!\d)/i.test(note) && !/<aoeColumn:\d+>/i.test(note))
shapes.push({ key: "column", n: 36 });
if (/<aoeCross>(?!\d)/i.test(note) && !/<aoeCross:\d+>/i.test(note))
shapes.push({ key: "cross", n: 36 });
return shapes;
}
// ============================================================
// ПОЗИЦИИ СПРАЙТОВ В БОЮ
// ============================================================
Sprite_Actor.prototype.setActorHome = function(index) {
const battler = this._actor || this._battler;
const slot = (battler && battler.isActor && battler.isActor())
? actorSlot(battler)
: index;
const s = slot >= 0 ? slot : index;
const p = gridPos(
s,
P.partySize, P.partyLines,
P.partyOriginX, P.partyOriginY,
P.partyColSpacing, P.partyRowSpacing,
P.partyMirror
);
this.setHome(p.x, p.y);
};
Game_Enemy.prototype.screenX = function() {
return gridPos(this.index(),
P.enemySize, P.enemyLines,
P.enemyOriginX, P.enemyOriginY,
P.enemyColSpacing, P.enemyRowSpacing,
P.enemyMirror).x;
};
Game_Enemy.prototype.screenY = function() {
return gridPos(this.index(),
P.enemySize, P.enemyLines,
P.enemyOriginX, P.enemyOriginY,
P.enemyColSpacing, P.enemyRowSpacing,
P.enemyMirror).y;
};
Game_Actor.prototype.screenX = function() {
const s = actorSlot(this);
return gridPos(
s >= 0 ? s : 0,
P.partySize, P.partyLines,
P.partyOriginX, P.partyOriginY,
P.partyColSpacing, P.partyRowSpacing,
P.partyMirror
).x;
};
Game_Actor.prototype.screenY = function() {
const s = actorSlot(this);
return gridPos(
s >= 0 ? s : 0,
P.partySize, P.partyLines,
P.partyOriginX, P.partyOriginY,
P.partyColSpacing, P.partyRowSpacing,
P.partyMirror
).y;
};
// ============================================================
// ФИЛЬТР ПО ПЕРЕДНЕЙ ЛИНИИ
// ============================================================
function filterByFrontLine(action, targets) {
if (!targets || !targets.length) return targets;
if (!actionIsMelee(action)) return targets;
if (parsePierce(action) > 1) return targets;
const unit = action.isForOpponent()
? action.opponentsUnit()
: action.friendsUnit();
const front = getFrontLivingLineOfUnit(unit);
if (front < 0) return targets;
const filtered = targets.filter(t => getLineOfBattler(t) === front);
return filtered.length ? filtered : targets;
}
const _targetsForOpponents = Game_Action.prototype.targetsForOpponents;
Game_Action.prototype.targetsForOpponents = function() {
return filterByFrontLine(this, _targetsForOpponents.call(this));
};
const _targetsForFriends = Game_Action.prototype.targetsForFriends;
Game_Action.prototype.targetsForFriends = function() {
return filterByFrontLine(this, _targetsForFriends.call(this));
};
// ============================================================
// РАСШИРЕНИЕ ЦЕЛЕЙ: pierceLines + AoE
// ============================================================
function expandAoe(action, targets) {
if (!targets.length) return targets;
const note = itemNote(action);
const pierce = parsePierce(action);
const shapes = parseAoeShapes(action);
const hasPierce = pierce > 1 && /<pierceLines:\d+>/i.test(note);
if (!hasPierce && !shapes.length) return targets;
const unit = action.isForOpponent()
? action.opponentsUnit()
: action.friendsUnit();
const cells = cellsOfUnit(unit);
if (!cells.length) return targets;
const result = new Set(targets);
if (hasPierce) {
const lines = [...new Set(cells.map(c => c.line))].sort((a, b) => a - b);
const activeLines = lines.slice(0, pierce);
for (const c of cells) {
if (activeLines.includes(c.line)) result.add(c.member);
}
}
for (const t of targets) {
const tcell = cells.find(c => c.member === t);
if (!tcell) continue;
for (const shape of shapes) {
if (shape.key === "line") {
const group = cells.filter(c => c.line === tcell.line);
group.sort((a, b) =>
Math.abs(a.memberIdx - tcell.memberIdx) -
Math.abs(b.memberIdx - tcell.memberIdx));
group.slice(0, shape.n).forEach(c => result.add(c.member));
} else if (shape.key === "column") {
const group = cells.filter(c => c.memberIdx === tcell.memberIdx);
group.sort((a, b) => a.line - b.line);
group.slice(0, shape.n).forEach(c => result.add(c.member));
} else if (shape.key === "cross") {
const cross = cells.filter(c =>
c.line === tcell.line || c.memberIdx === tcell.memberIdx);
cross.sort((a, b) => {
const da = Math.abs(a.memberIdx - tcell.memberIdx) +
Math.abs(a.line - tcell.line);
const db = Math.abs(b.memberIdx - tcell.memberIdx) +
Math.abs(b.line - tcell.line);
return da - db;
});
cross.slice(0, shape.n).forEach(c => result.add(c.member));
}
}
}
return [...result];
}
const _makeTargets = Game_Action.prototype.makeTargets;
Game_Action.prototype.makeTargets = function() {
let targets = _makeTargets.call(this);
if (this._forcing || !this.subject()) return targets;
if (!targets.length) return targets;
return expandAoe(this, targets);
};
// ============================================================
// ПРИКРЫТИЕ ТЫЛА
// ============================================================
function findCoverGuard(target) {
if (!target || !target.isActor()) return null;
if (getLineOfBattler(target) <= 0) return null;
for (const m of $gameParty.allMembers()) {
if (m === target) continue;
if (m.hp <= 0) continue;
if (getLineOfBattler(m) !== 0) continue;
if (m.states().some(s => s && /<coverBack>/i.test(s.note || ""))) return m;
}
return null;
}
const _executeHpDamage = Game_Action.prototype.executeHpDamage;
Game_Action.prototype.executeHpDamage = function(target, value) {
const guard = findCoverGuard(target);
if (guard && guard.hp > 0) { _executeHpDamage.call(this, guard, value); return; }
_executeHpDamage.call(this, target, value);
};
// ============================================================
// СПРАЙТ АКТЁРА (SV-баттлер)
// ============================================================
function Sprite_FormationActor() { this.initialize(...arguments); }
Sprite_FormationActor.prototype = Object.create(Sprite.prototype);
Sprite_FormationActor.prototype.constructor = Sprite_FormationActor;
Sprite_FormationActor.prototype.initialize = function(actor, faceLeft) {
Sprite.prototype.initialize.call(this);
this._actor = actor || null;
this._faceLeft = !!faceLeft;
this.anchor.x = 0.5;
this.anchor.y = 1.0;
this.z = 10;
this.loadBitmap();
};
Sprite_FormationActor.prototype.setActor = function(actor) {
if (this._actor === actor) return;
this._actor = actor || null;
this.loadBitmap();
};
Sprite_FormationActor.prototype.loadBitmap = function() {
if (!this._actor || !this._actor.battlerName()) {
this.bitmap = null; this.visible = false; return;
}
this.visible = true;
this.bitmap = ImageManager.loadSvActor(this._actor.battlerName());
this.scale.x = this._faceLeft ? 1 : -1;
this.bitmap.addLoadListener(() => this.updateFrame());
this.updateFrame();
};
Sprite_FormationActor.prototype.updateFrame = function() {
if (!this._actor || !this.bitmap || !this.bitmap.width) return;
const pw = Math.floor(this.bitmap.width / 9);
const ph = Math.floor(this.bitmap.height / 6);
if (!pw || !ph) return;
this.setFrame(0, 1 * ph, pw, ph);
};
// ============================================================
// СПРАЙТ «ИМЯ + HP»
// ============================================================
function Sprite_SlotInfo() { this.initialize(...arguments); }
Sprite_SlotInfo.prototype = Object.create(Sprite.prototype);
Sprite_SlotInfo.prototype.constructor = Sprite_SlotInfo;
Sprite_SlotInfo.prototype.initialize = function() {
Sprite.prototype.initialize.call(this);
this._actor = null;
const L = LAYOUT.infoLabel;
this._bitmap = new Bitmap(L.w, L.h);
this.bitmap = this._bitmap;
this.anchor.x = 0.5;
this.anchor.y = 1.0;
this.z = 15;
this.visible = false;
};
Sprite_SlotInfo.prototype.setActor = function(actor) {
this._actor = actor || null;
this.visible = !!actor;
this.refresh();
};
Sprite_SlotInfo.prototype.refresh = function() {
const b = this._bitmap; b.clear();
if (!this._actor) return;
const L = LAYOUT.infoLabel;
const w = b.width;
b.fontSize = L.nameFontSize;
b.outlineColor = "rgba(0,0,0,0.9)"; b.outlineWidth = 4;
b.textColor = "#ffffff";
b.drawText(this._actor.name(), 0, 0, w, 20, "center");
const hp = Math.max(0, this._actor.hp);
const mhp = Math.max(1, this._actor.mhp);
const ratio = Math.max(0, Math.min(1, hp / mhp));
const margin = L.hpBarMargin;
const bw = w - margin * 2;
const bh = L.hpBarH;
const bx = margin;
const by = L.h - bh - L.hpBarBottomGap;
b.fillRect(bx - 1, by - 1, bw + 2, bh + 2, "rgba(0,0,0,0.85)");
b.fillRect(bx, by, bw, bh, "rgba(40,40,40,0.9)");
const color = ratio > 0.5 ? "#3ad13a" : ratio > 0.25 ? "#e0c020" : "#e03030";
b.fillRect(bx, by, Math.floor(bw * ratio), bh, color);
b.fontSize = L.hpFontSize;
b.outlineColor = "rgba(0,0,0,0.95)"; b.outlineWidth = 3;
b.textColor = "#ffffff";
b.drawText(`${hp}/${mhp}`, 0, by - 4, w, 16, "center");
};
// ============================================================
// ЛЕВАЯ ПАНЕЛЬ «ВЫБОР ЛИНИИ»
// ============================================================
function Window_FormationLines() { this.initialize(...arguments); }
Window_FormationLines.prototype = Object.create(Window_Base.prototype);
Window_FormationLines.prototype.constructor = Window_FormationLines;
Window_FormationLines.prototype.initialize = function(rect) {
Window_Base.prototype.initialize.call(this, rect);
this._currentLine = 0;
this.refresh();
};
Window_FormationLines.prototype.setCurrentLine = function(line) {
if (this._currentLine === line) return;
this._currentLine = line;
this.refresh();
};
Window_FormationLines.prototype.refresh = function() {
this.contents.clear();
const names = ["Первая линия", "Вторая", "Третья",
"Четвёртая", "Пятая", "Шестая"];
const lh = LAYOUT.linePanel.rowH;
for (let i = 0; i < P.partyLines; i++) {
const label = i === 0 ? "Фронт" : (names[i] || `Линия ${i + 1}`);
this.changeTextColor(i === this._currentLine ? "#ffe66a" : "#ffffff");
this.contents.outlineColor = "rgba(0,0,0,0.9)";
this.contents.outlineWidth = 4;
this.drawText(label, 0, i * lh, this.contents.width, lh, "left");
}
};
// ============================================================
// СЦЕНА ПОСТРОЕНИЯ
// ============================================================
function Scene_FormationGrid() { this.initialize(...arguments); }
Scene_FormationGrid.prototype = Object.create(Scene_MenuBase.prototype);
Scene_FormationGrid.prototype.constructor = Scene_FormationGrid;
Scene_FormationGrid.prototype.create = function() {
Scene_MenuBase.prototype.create.call(this);
// === АВТО: количество ячеек = partySize * partyLines ===
this._slotCount = P.partySize * P.partyLines;
this._cursorSlot = 0;
this._pickedSlot = -1;
this._slots = this.buildInitialSlots();
this.createGridLayer();
this.createLabelsLayer();
this.createActorSprites();
this.createInfoSprites();
this.createCursorSprite();
this.createHelpWindow();
this.createLineWindow();
this.refreshActors();
this.updateCursorPosition();
};
Scene_FormationGrid.prototype.buildInitialSlots = function() {
const slots = new Array(this._slotCount).fill(null);
const map = $gameParty._formationSlots || {};
const members = $gameParty.members();
for (const a of members) {
const key = String(a.actorId());
if (Object.prototype.hasOwnProperty.call(map, key)) {
const s = Number(map[key]);
if (s >= 0 && s < this._slotCount && !slots[s]) {
slots[s] = a;
}
}
}
let free = 0;
for (const a of members) {
if (slots.indexOf(a) >= 0) continue;
while (free < this._slotCount && slots[free]) free++;
if (free >= this._slotCount) break;
slots[free++] = a;
}
return slots;
};
// ============================================================
// ЕДИНСТВЕННАЯ ФОРМУЛА В UI
// Всё остальное — прямые значения из LAYOUT.grid.
// Количество ячеек берётся из partySize/partyLines автоматически.
// ============================================================
Scene_FormationGrid.prototype.slotPos = function(slot) {
const G = LAYOUT.grid;
const member = slot % P.partySize; // позиция в линии (0 = низ)
const line = Math.floor(slot / P.partySize); // номер линии
const dir = P.partyMirror ? -1 : 1;
return {
x: G.originX + line * G.stepX * dir,
y: G.originY - member * G.stepY
};
};
Scene_FormationGrid.prototype.helpWindowRect = function() {
const H = LAYOUT.helpWindow;
return new Rectangle(H.x, H.y, H.w, H.h);
};
Scene_FormationGrid.prototype.createLineWindow = function() {
const L = LAYOUT.linePanel;
const rect = new Rectangle(L.x, L.y, L.w, L.h);
this._lineWindow = new Window_FormationLines(rect);
this.addChild(this._lineWindow);
};
Scene_FormationGrid.prototype.createGridLayer = function() {
const G = LAYOUT.grid;
const ip = G.innerPadding;
const cw = G.cellW;
const ch = G.cellH;
// Холст достаточно большой, чтобы ВСЕ ячейки поместились,
// даже если они выходят за Graphics.boxWidth.
let maxX = Graphics.boxWidth;
let maxY = Graphics.boxHeight;
for (let i = 0; i < this._slotCount; i++) {
const p = this.slotPos(i);
maxX = Math.max(maxX, p.x + cw);
maxY = Math.max(maxY, p.y + ch);
}
const bmp = new Bitmap(maxX, maxY);
// Все ячейки
bmp.paintOpacity = 60;
for (let i = 0; i < this._slotCount; i++) {
const p = this.slotPos(i);
bmp.fillRect(
Math.round(p.x - cw / 2 + ip),
Math.round(p.y - ch + ip),
cw - ip * 2, ch - ip * 2, "white"
);
}
// Подсветка фронтовой линии
bmp.paintOpacity = 30;
for (let m = 0; m < P.partySize; m++) {
const p = this.slotPos(m);
bmp.fillRect(
Math.round(p.x - cw / 2 + ip),
Math.round(p.y - ch + ip),
cw - ip * 2, ch - ip * 2, "#ffe66a"
);
}
this._gridSprite = new Sprite(bmp);
this._gridSprite.z = 1;
this.addChild(this._gridSprite);
};
Scene_FormationGrid.prototype.createLabelsLayer = function() {
const F = LAYOUT.frontLabel;
const LL = LAYOUT.lineLabel;
const G = LAYOUT.grid;
// === Считаем нужный размер холста заранее ===
// Чтобы вместить и ячейки, и подписи (которые выходят
// за левый/правый край ячейки на LL.w/2).
let maxX = Graphics.boxWidth;
let maxY = Graphics.boxHeight;
for (let i = 0; i < this._slotCount; i++) {
const p = this.slotPos(i);
maxX = Math.max(maxX, p.x + G.cellW / 2 + LL.w / 2 + 20);
maxY = Math.max(maxY, p.y + G.cellH + 20);
}
// плюс место под надпись "ФРОНТ" и подписи сверху
maxX = Math.max(maxX, F.x + F.charW + 20);
maxY = Math.max(maxY, F.y + F.charH * 6 + 20);
const bmp = new Bitmap(maxX, maxY);
bmp.outlineColor = "rgba(0,0,0,0.9)";
bmp.outlineWidth = 4;
// --- Надпись "ФРОНТ" побуквенно ---
const text = "ФРОНТ";
bmp.fontSize = F.fontSize;
bmp.textColor = "#ffe66a";
for (let i = 0; i < text.length; i++) {
bmp.drawText(text[i], F.x, F.y + i * F.charH,
F.charW, F.charH, "center");
}
// --- Подписи линий ---
bmp.fontSize = LL.fontSize;
const names = ["Первая линия", "Вторая", "Третья",
"Четвёртая", "Пятая", "Шестая"];
for (let line = 0; line < P.partyLines; line++) {
const topSlot = line * P.partySize + (P.partySize - 1);
const p = this.slotPos(topSlot);
const label = names[line] || `Линия ${line + 1}`;
bmp.textColor = line === 0 ? "#ffe66a" : "#9ad1ff";
bmp.drawText(label,
p.x - LL.w / 2,
p.y - G.cellH - LL.offsetY,
LL.w, LL.h, "center");
}
this._labelsSprite = new Sprite(bmp);
this._labelsSprite.z = 5;
this.addChild(this._labelsSprite);
};
Scene_FormationGrid.prototype.createActorSprites = function() {
this._actorSprites = [];
for (let i = 0; i < this._slotCount; i++) {
const spr = new Sprite_FormationActor(null, P.partyFaceLeft);
const p = this.slotPos(i);
spr.x = p.x; spr.y = p.y; spr.visible = false;
this.addChild(spr);
this._actorSprites.push(spr);
}
};
Scene_FormationGrid.prototype.createInfoSprites = function() {
this._infoSprites = [];
for (let i = 0; i < this._slotCount; i++) {
const info = new Sprite_SlotInfo();
const p = this.slotPos(i);
info.x = p.x;
info.y = p.y - LAYOUT.infoLabel.offsetY;
this.addChild(info);
this._infoSprites.push(info);
}
};
Scene_FormationGrid.prototype.refreshActors = function() {
for (let i = 0; i < this._slotCount; i++) {
const spr = this._actorSprites[i];
const info = this._infoSprites[i];
const actor = this._slots[i];
const picked = (i === this._pickedSlot);
if (actor) {
spr.setActor(actor); spr.visible = true;
spr.opacity = picked ? 160 : 255;
info.setActor(actor);
info.opacity = picked ? 140 : 255;
} else {
spr.visible = false;
info.visible = false;
}
}
};
Scene_FormationGrid.prototype.createCursorSprite = function() {
const C = LAYOUT.cursor;
const G = LAYOUT.grid;
const cw = G.cellW + C.extra;
const ch = G.cellH + C.extra;
const th = C.thickness;
const bmp = new Bitmap(cw, ch);
bmp.fillRect(0, 0, cw, th, "rgba(255,230,90,0.95)");
bmp.fillRect(0, ch - th, cw, th, "rgba(255,230,90,0.95)");
bmp.fillRect(0, 0, th, ch, "rgba(255,230,90,0.95)");
bmp.fillRect(cw - th, 0, th, ch, "rgba(255,230,90,0.95)");
this._cursorSprite = new Sprite(bmp);
this._cursorSprite.anchor.x = 0.5;
this._cursorSprite.anchor.y = 0.5;
this._cursorSprite.z = 20;
this.addChild(this._cursorSprite);
};
Scene_FormationGrid.prototype.updateCursorPosition = function() {
const p = this.slotPos(this._cursorSlot);
const G = LAYOUT.grid;
this._cursorSprite.x = p.x;
this._cursorSprite.y = p.y - G.cellH / 2;
if (this._lineWindow) {
const line = Math.floor(this._cursorSlot / P.partySize);
this._lineWindow.setCurrentLine(line);
}
};
Scene_FormationGrid.prototype.createHelpWindow = function() {
const rect = this.helpWindowRect();
this._helpWindow = new Window_Help(rect);
this._helpWindow.setText(this.defaultHelpText());
this.addChild(this._helpWindow);
};
Scene_FormationGrid.prototype.defaultHelpText = function() {
return "Стрелки: перемещение по сетке OK: взять/поставить (обмен) Esc: выход";
};
Scene_FormationGrid.prototype.update = function() {
Scene_MenuBase.prototype.update.call(this);
if (this._cursorSprite) this.processInput();
};
Scene_FormationGrid.prototype.processInput = function() {
if (Input.isRepeated("up")) this.moveCursor(0, -1);
if (Input.isRepeated("down")) this.moveCursor(0, 1);
if (Input.isRepeated("left")) this.moveCursor(-1, 0);
if (Input.isRepeated("right")) this.moveCursor( 1, 0);
if (Input.isTriggered("ok")) this.onOk();
if (Input.isTriggered("cancel")) this.onCancel();
};
Scene_FormationGrid.prototype.moveCursor = function(dx, dy) {
let line = Math.floor(this._cursorSlot / P.partySize);
let member = this._cursorSlot % P.partySize;
const lineDelta = P.partyMirror ? -dx : dx;
line = (line + lineDelta + P.partyLines) % P.partyLines;
member = (member - dy + P.partySize) % P.partySize;
this._cursorSlot = line * P.partySize + member;
this.updateCursorPosition();
SoundManager.playCursor();
};
Scene_FormationGrid.prototype.onOk = function() {
if (this._pickedSlot < 0) {
if (this._slots[this._cursorSlot]) {
this._pickedSlot = this._cursorSlot;
SoundManager.playOk();
this.refreshActors();
this._helpWindow.setText("OK: поставить / обменять Esc: отмена");
}
} else {
const a = this._pickedSlot, b = this._cursorSlot;
const tmp = this._slots[a];
this._slots[a] = this._slots[b];
this._slots[b] = tmp;
this._pickedSlot = -1;
SoundManager.playOk();
this.refreshActors();
this._helpWindow.setText(this.defaultHelpText());
}
};
Scene_FormationGrid.prototype.onCancel = function() {
if (this._pickedSlot >= 0) {
this._pickedSlot = -1;
this.refreshActors();
this._helpWindow.setText(this.defaultHelpText());
SoundManager.playCancel();
} else {
this.commitFormation();
SoundManager.playCancel();
this.popScene();
}
};
Scene_FormationGrid.prototype.commitFormation = function() {
const map = $gameParty._formationSlots || ($gameParty._formationSlots = {});
for (const k in map) delete map[k];
const ids = [];
for (let i = 0; i < this._slotCount; i++) {
const a = this._slots[i];
if (a) {
map[String(a.actorId())] = i;
ids.push(a.actorId());
}
}
for (const a of $gameParty.allMembers()) {
const key = String(a.actorId());
if (!Object.prototype.hasOwnProperty.call(map, key)) {
ids.push(a.actorId());
}
}
$gameParty._actors = ids;
};
// ============================================================
// МЕНЮ + ПЛАГИН-КОМАНДА
// ============================================================
Scene_Menu.prototype.commandFormation = function() {
if ($gameParty.size() >= 1) SceneManager.push(Scene_FormationGrid);
};
Window_MenuCommand.prototype.addFormationCommand = function() {
if (this.needsCommand("formation")) {
this.addCommand(TextManager.formation, "formation", $gameParty.size() >= 1);
}
};
PluginManager.registerCommand(pluginName, "openFormation", () => {
if (SceneManager._scene instanceof Scene_Battle) return;
SceneManager.push(Scene_FormationGrid);
});
// ============================================================
// SV / АНИМИРОВАННЫЕ СПРАЙТЫ ВРАГОВ
// ============================================================
function parseEnemySpriteNote(note) {
const sve = /<svEnemy:\s*([^\s>]+)\s*>/i.exec(note)
|| /<svBattler:\s*([^\s>]+)\s*>/i.exec(note);
if (sve) return { mode: "svenemy", name: sve[1] };
const ch = /<charSheet:\s*([^\s>]+)\s*>/i.exec(note);
if (ch) return { mode: "char", name: ch[1] };
const idl = /<idleSheet:\s*([^\s>:]+)\s*:\s*(\d+)(?:\s*:\s*(\d+)\s*:\s*(\d+))?\s*>/i.exec(note);
if (idl) {
return {
mode: "idle",
name: idl[1],
cols: Math.max(1, Number(idl[2])),
row: idl[3] != null ? Number(idl[3]) : 0,
rows: idl[4] != null ? Math.max(1, Number(idl[4])) : 1
};
}
return null;
}
const _Sprite_Enemy_initMembers = Sprite_Enemy.prototype.initMembers;
Sprite_Enemy.prototype.initMembers = function() {
_Sprite_Enemy_initMembers.call(this);
this._enemySpriteMode = null;
this._enemySpriteKey = null;
this._animFrame = 0;
this._animTimer = 0;
this._idleCols = 1;
this._idleRow = 0;
this._idleRows = 1;
};
const _Sprite_Enemy_updateBitmap = Sprite_Enemy.prototype.updateBitmap;
Sprite_Enemy.prototype.updateBitmap = function() {
const enemy = this._enemy;
if (!enemy) { return; }
const note = (enemy.enemy() && enemy.enemy().note) || "";
const cfg = parseEnemySpriteNote(note);
if (!cfg) {
if (this._enemySpriteMode !== null) {
this._enemySpriteMode = null;
this._enemySpriteKey = null;
this._battlerName = "";
this._battlerHue = -1;
}
_Sprite_Enemy_updateBitmap.call(this);
return;
}
const key = cfg.mode + ":" + cfg.name + ":" +
(cfg.cols || 0) + ":" + (cfg.row || 0) + ":" + (cfg.rows || 0);
if (this._enemySpriteKey === key) return;
this._enemySpriteKey = key;
this._enemySpriteMode = cfg.mode;
this._idleCols = cfg.cols || 1;
this._idleRow = cfg.row || 0;
this._idleRows = cfg.rows || 1;
this._animFrame = 0;
this._animTimer = 0;
this.scale.x = (cfg.mode === "svenemy" && P.enemySvMirror) ? -1 : 1;
let bmp;
if (cfg.mode === "svenemy") bmp = ImageManager.loadSvEnemy(cfg.name);
else if (cfg.mode === "char") bmp = ImageManager.loadCharacter(cfg.name);
else bmp = ImageManager.loadPicture(cfg.name);
this.bitmap = bmp;
bmp.addLoadListener(() => {
this._bitmapLoaded = true;
this.updateFrame();
});
};
const _Sprite_Enemy_updateFrame = Sprite_Enemy.prototype.updateFrame;
Sprite_Enemy.prototype.updateFrame = function() {
if (!this._enemySpriteMode || !this.bitmap) {
_Sprite_Enemy_updateFrame.call(this);
return;
}
const bw = this.bitmap.width;
const bh = this.bitmap.height;
if (!bw || !bh) return;
if (this._enemySpriteMode === "svenemy") {
const fw = Math.floor(bw / 9);
const fh = Math.floor(bh / 6);
const f = this._animFrame % 3;
this.setFrame(f * fw, 0, fw, fh);
} else if (this._enemySpriteMode === "char") {
const fw = Math.floor(bw / 12);
const fh = Math.floor(bh / 8);
const f = this._animFrame % 3;
this.setFrame(f * fw, 4 * fh, fw, fh);
} else if (this._enemySpriteMode === "idle") {
const cols = this._idleCols;
const rows = this._idleRows;
const fw = Math.floor(bw / cols);
const fh = Math.floor(bh / rows);
const f = this._animFrame % cols;
this.setFrame(f * fw, this._idleRow * fh, fw, fh);
}
};
const _Sprite_Enemy_update = Sprite_Enemy.prototype.update;
Sprite_Enemy.prototype.update = function() {
_Sprite_Enemy_update.call(this);
if (this._enemySpriteMode) {
this._animTimer++;
if (this._animTimer >= P.enemyAnimSpeed) {
this._animTimer = 0;
this._animFrame = (this._animFrame + 1) % 360;
}
}
};
// ============================================================
// ЭКСПОРТ
// ============================================================
window.BattleFormationGrid = {
config: P,
layout: LAYOUT,
scene: Scene_FormationGrid
};
})();
!Инструкция по устранению неполадок!
Следуйте по пунктам:
1) Проверьте настройки плагина, примечания к навыкам и т.д. Всё, что вы делали.
2) Если выше не помогло, откройте VS code потом Deepseek киньте ему файл и скажите свою проблему. Патч или другие решения в файл просите скинуть целым файлом он даст вам блок кода для вставки от начала и до конца.
3) Если всё остальное не помогло мой Discord: [color=oklab(0.278878 0.00249207 -0.00875986)]1212_3949 [/color]Отвечаю где то в течение недели пишите сразу с причины обращения ЛС открыт.
1) Проверьте настройки плагина, примечания к навыкам и т.д. Всё, что вы делали.
2) Если выше не помогло, откройте VS code потом Deepseek киньте ему файл и скажите свою проблему. Патч или другие решения в файл просите скинуть целым файлом он даст вам блок кода для вставки от начала и до конца.
3) Если всё остальное не помогло мой Discord: [color=oklab(0.278878 0.00249207 -0.00875986)]1212_3949 [/color]Отвечаю где то в течение недели пишите сразу с причины обращения ЛС открыт.
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
2 ч. 3 мин. назад #132783
от Ryterator
Ryterator ответил в теме Плагин на изменения боя. Боевая сетка (Внимание сырой)
Если я не так выложил можно сразу не банить первый раз выкладываю в остальном чаще смотрел..
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
15 мин. назад #132784
от Dassy
Dassy ответил в теме Плагин на изменения боя. Боевая сетка (Внимание сырой)
Ну.. дипсик точно ничего плохого не делает, кроме передачи всего и вся партии )
Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.
Время создания страницы: 0.107 секунд
