
...
Сообщений 1 страница 4 из 4
ПеревестиПоделиться22026-05-24 11:36:32
[Intervention] Slow network is detected. See https://www.chromestatus.com/feature/5636954674692096 for more details. Fallback font will be used while loading: chrome-extension://njgcanhfjdabfmnlmpmdedalocpafnhl/fonts/mplus-1m-regular.woff
rmmz_managers.js:824 [Intervention] Slow network is detected. See https://www.chromestatus.com/feature/5636954674692096 for more details. Fallback font will be used while loading: chrome-extension://njgcanhfjdabfmnlmpmdedalocpafnhl/fonts/mplus-2p-bold-sub.woff
0.peerjs.com/peerjs/id?ts=17796117758130.4098274064605709&version=1.5.2:1 Failed to load resource: the server responded with a status of 403 ()
peerjs.min.js:7 ERROR PeerJS: Error retrieving ID (Error) Error. Status:403
_print @ peerjs.min.js:7
peerjs.min.js:7 ERROR PeerJS: Aborting!
_print @ peerjs.min.js:7
peerjs.min.js:7 ERROR PeerJS: Error: (Error) Could not get an ID from the server.
_print @ peerjs.min.js:7
NetworkCoop_P2P.js:104 PEER ERROR (Client): Error
at e1.emitError (peerjs.min.js:7)
at e1._abort (peerjs.min.js:7)
at peerjs.min.js:7
(anonymous) @ NetworkCoop_P2P.js:104
DevTools failed to load SourceMap: Could not load content for chrome-extension://njgcanhfjdabfmnlmpmdedalocpafnhl/js/libs/peerjs.min.js.map: System error: net::ERR_FILE_NOT_FOUND
DevTools failed to load SourceMap: Could not load content for chrome-extension://njgcanhfjdabfmnlmpmdedalocpafnhl/js/libs/pixi.js.map: System error: net::ERR_FILE_NOT_FOUND
Поделиться42026-05-24 21:41:33
//=============================================================================
// NetworkCoop_P2P.js - Версия 15.0: Фикс ИИ (State Machine) и Фикс HP
//=============================================================================
(() => {
const NETWORK_EVENT_ID = 1;
const CHAR_LIST = [
{ name: "People2", index: 0, label: "1. Дед (Воин)", playerId: 1, actorId: 1,
vars: { hp: 22, luck: 23, speed: 24, baseStat: 25, weaponId: 26, resource: 27, agility: 35 } },
{ name: "People2", index: 1, label: "2. Кабачок (Маг)", playerId: 1, actorId: 2,
vars: { hp: 29, luck: 30, speed: 31, baseStat: 32, weaponId: 33, resource: 34, agility: 36 } },
{ name: "People2", index: 4, label: "3. Богач (Воин)", playerId: 2, actorId: 3,
vars: { hp: 42, luck: 43, speed: 44, baseStat: 45, weaponId: 46, resource: 47, agility: 37 } },
{ name: "People2", index: 5, label: "4. Монашка (Маг)", playerId: 2, actorId: 4,
vars: { hp: 49, luck: 50, speed: 51, baseStat: 52, weaponId: 53, resource: 54, agility: 38 } }
];
let peer = null;
let conn = null;
let lastSentX = -1;
let lastSentY = -1;
let lastSentDir = -1;
let networkActive = false;
let isHost = false;
let remoteTargetX = 0;
let remoteTargetY = 0;
let lastSentEventPos = {};
let currentTargetId = 0;
let currentTargetVarId = 0;
let currentTargetDeathSwitch = 0;
let autoAttackTimer = 0;
const ATTACK_RANGE = 2;
const BASE_COOLDOWN = 90;
let myStats = null;
let remotePlayerStats = null;
let myActorId = 1;
let targetNameCache = "";
let targetMaxHpCache = 100;
// ИИ ВРАГОВ (State Machine)
// Состояния: 0 = Спокойствие, 1 = Тревога, 2 = Бой
let enemyAI = {};
Input.keyMapper[82] = 'targetKey'; // R
// ==========================================================
// HTML UI
// ==========================================================
let targetFrame = null;
let hpBarInner = null;
let targetNameEl = null;
let targetHpTextEl = null;
let damageNumberEl = null;
let playerFrame = null;
let playerHpBarInner = null;
let playerNameEl = null;
let playerHpTextEl = null;
let playerAvatarEl = null;
function createHTMLUI() {
if (document.getElementById('mmo-target-frame')) return;
// --- ФРЕЙМ ВРАГА ---
targetFrame = document.createElement('div');
targetFrame.id = 'mmo-target-frame';
targetFrame.style.cssText = `position: fixed; top: 20px; left: 50%; transform: translateX(-50%); width: 300px; height: 50px; background: rgba(0, 0, 0, 0.8); border: 2px solid #c9a034; border-radius: 8px; padding: 10px; display: none; z-index: 9999; font-family: Arial, sans-serif; color: white; pointer-events: none;`;
targetNameEl = document.createElement('div');
targetNameEl.style.cssText = `font-size: 16px; font-weight: bold; margin-bottom: 5px; text-align: center; color: #ffcc00;`;
targetFrame.appendChild(targetNameEl);
let hpBarOuter = document.createElement('div');
hpBarOuter.style.cssText = `width: 100%; height: 15px; background: #333; border-radius: 3px; border: 1px solid #000;`;
hpBarInner = document.createElement('div');
hpBarInner.style.cssText = `width: 100%; height: 100%; background: linear-gradient(to bottom, #ff4444, #cc0000); border-radius: 3px; transition: width 0.2s;`;
hpBarOuter.appendChild(hpBarInner);
targetFrame.appendChild(hpBarOuter);
targetHpTextEl = document.createElement('div');
targetHpTextEl.style.cssText = `font-size: 12px; text-align: center; margin-top: 2px;`;
targetFrame.appendChild(targetHpTextEl);
// --- ФРЕЙМ ИГРОКА ---
playerFrame = document.createElement('div');
playerFrame.id = 'mmo-player-frame';
playerFrame.style.cssText = `position: fixed; bottom: 20px; right: 20px; width: 250px; height: 60px; background: rgba(0, 0, 0, 0.8); border: 2px solid #1e3a5f; border-radius: 8px; padding: 10px; display: flex; align-items: center; z-index: 9999; font-family: Arial, sans-serif; color: white; pointer-events: none;`;
playerAvatarEl = document.createElement('div');
playerAvatarEl.style.cssText = `width: 40px; height: 40px; background: #555; border: 2px solid #fff; border-radius: 5px; margin-right: 10px; display: flex; justify-content: center; align-items: center; font-weight: bold;`;
playerFrame.appendChild(playerAvatarEl);
let playerInfoDiv = document.createElement('div');
playerInfoDiv.style.cssText = `flex-grow: 1;`;
playerNameEl = document.createElement('div');
playerNameEl.style.cssText = `font-size: 14px; font-weight: bold; margin-bottom: 5px;`;
playerInfoDiv.appendChild(playerNameEl);
let pHpBarOuter = document.createElement('div');
pHpBarOuter.style.cssText = `width: 100%; height: 10px; background: #333; border-radius: 3px; border: 1px solid #000;`;
playerHpBarInner = document.createElement('div');
playerHpBarInner.style.cssText = `width: 100%; height: 100%; background: linear-gradient(to bottom, #2ecc71, #27ae60); border-radius: 3px; transition: width 0.2s;`;
pHpBarOuter.appendChild(playerHpBarInner);
playerInfoDiv.appendChild(pHpBarOuter);
playerHpTextEl = document.createElement('div');
playerHpTextEl.style.cssText = `font-size: 10px; text-align: right; margin-top: 2px;`;
playerInfoDiv.appendChild(playerHpTextEl);
playerFrame.appendChild(playerInfoDiv);
// --- ВСПЛЫВАЮЩИЙ УРОН ---
damageNumberEl = document.createElement('div');
damageNumberEl.id = 'mmo-damage-number';
damageNumberEl.style.cssText = `position: fixed; top: 35%; left: 50%; transform: translateX(-50%); font-size: 40px; font-weight: bold; color: white; text-shadow: 2px 2px 0 #000; z-index: 10000; pointer-events: none; display: none; opacity: 0; transition: all 0.5s ease-out;`;
document.body.appendChild(targetFrame);
document.body.appendChild(playerFrame);
document.body.appendChild(damageNumberEl);
}
function showDamageNumber(text, isCrit, isDodge) {
if (!damageNumberEl) return;
damageNumberEl.textContent = text;
if (isDodge) { damageNumberEl.style.color = "#ffff00"; damageNumberEl.style.fontSize = "45px"; }
else { damageNumberEl.style.color = isCrit ? "#ffaa00" : "#ffffff"; damageNumberEl.style.fontSize = isCrit ? "55px" : "40px"; }
damageNumberEl.style.transition = 'none'; damageNumberEl.style.display = 'block'; damageNumberEl.style.opacity = 1; damageNumberEl.style.top = '35%';
setTimeout(() => { damageNumberEl.style.transition = 'all 0.5s ease-out'; damageNumberEl.style.opacity = 0; damageNumberEl.style.top = '25%'; }, 50);
}
function updateUI() {
if (!targetFrame) return;
// Враг
if (currentTargetId > 0 && currentTargetDeathSwitch > 0 && !$gameSwitches.value(currentTargetDeathSwitch)) {
targetFrame.style.display = 'block';
let currentHp = $gameVariables.value(currentTargetVarId);
targetNameEl.textContent = targetNameCache; targetHpTextEl.textContent = `${currentHp} / ${targetMaxHpCache}`;
hpBarInner.style.width = Math.max(0, (currentHp / targetMaxHpCache) * 100) + '%';
} else { targetFrame.style.display = 'none'; }
// Игрок (БЕЗ БАЗЫ ДАННЫХ!)
if (myStats && $gameActors && $gameActors.actor(myActorId)) {
let actor = $gameActors.actor(myActorId);
let currentHp = $gameVariables.value(myStats.hp);
playerNameEl.textContent = actor.name();
playerHpTextEl.textContent = `HP: ${currentHp}`; // Только текущее HP из переменной
// Полоска будет просто показывать текущее значение. Так как мы не знаем макс,
// сделаем так: если HP > 20, считаем что максимум = текущее. Иначе 20.
// Позже ты добавишь переменную Max HP и мы сделаем идеально!
let maxHp = Math.max(currentHp, 20);
playerHpBarInner.style.width = Math.max(0, (currentHp / maxHp) * 100) + '%';
playerAvatarEl.textContent = actor.name().charAt(0);
playerAvatarEl.style.backgroundColor = myStats.agility === 35 ? "#c0392b" : "#2980b9";
}
}
// ==========================================================
// ИНИЦИАЛИЗАЦИЯ И СЕТЬ
// ==========================================================
window.InitializeEnemyHP = function(varId, enemyId) {
if ($gameVariables.value(varId) === 0) {
let enemyData = $dataEnemies[enemyId];
if (enemyData) { let maxHp = enemyData.params[0]; $gameVariables.setValue(varId, maxHp); if (isHost && networkActive) NetworkSendVariable(varId, maxHp); }
}
};
window.NetworkSendSwitch = function(switchId, value) { if (networkActive && conn && conn.open) conn.send({ type: 'switch', switchId: switchId, value: value }); };
window.NetworkSendVariable = function(varId, value) { if (networkActive && conn && conn.open) conn.send({ type: 'variable', varId: varId, value: value }); };
function sendFullSync() {
if (!networkActive || !conn || !conn.open || !$gameSwitches || !$gameVariables) return;
let swData = []; let varData = [];
for (let i = 1; i < $gameSwitches._data.length; i++) { if ($gameSwitches._data[i]) swData.push({ id: i, val: true }); }
for (let i = 1; i < $gameVariables._data.length; i++) { if ($gameVariables._data[i] !== 0) varData.push({ id: i, val: $gameVariables._data[i] }); }
conn.send({ type: 'full_sync', switches: swData, variables: varData });
// Добавляем синхронизацию позиций врагов при подключении
if ($gameMap) {
$gameMap.events().forEach(event => {
if (event.event().meta && event.event().meta.enemy) {
conn.send({ type: 'event_move', eventId: event.eventId(), x: event.x, y: event.y, dir: event.direction() });
}
});
}
}
function setupConnection(connection) {
conn = connection;
conn.on('open', function() {
networkActive = true;
if ($gamePlayer) {
conn.send({ type: 'init_graphic', graphicName: $gamePlayer.characterName(), graphicIndex: $gamePlayer.characterIndex() });
conn.send({ type: 'init_stats', stats: myStats });
}
if (isHost) setTimeout(sendFullSync, 1000);
});
conn.on('data', function(data) { // <--- ОШИБКА БЫЛА ЗДЕСЬ: обязательно должно быть (data)
if (!$gameMap) return;
// Движение второго игрока
if (data.type === 'move') { remoteTargetX = data.x; remoteTargetY = data.y; }
// Инициализация графики и статов
if (data.type === 'init_graphic') { const event = $gameMap.event(NETWORK_EVENT_ID); if (event) event.setImage(data.graphicName, data.graphicIndex); }
if (data.type === 'init_stats') { remotePlayerStats = data.stats; }
// Синхронизация переключателей и переменных
if (data.type === 'switch' && $gameSwitches) $gameSwitches.setValue(data.switchId, data.value);
if (data.type === 'variable' && $gameVariables) $gameVariables.setValue(data.varId, data.value);
// Полная синхронизация
if (data.type === 'full_sync') {
if ($gameSwitches) data.switches.forEach(sw => $gameSwitches.setValue(sw.id, sw.val));
if ($gameVariables) data.variables.forEach(vr => $gameVariables.setValue(vr.id, vr.val));
if ($gameMap) $gameMap.refresh();
}
// Анимации атак
if (data.type === 'attack_anim') { let targetEvent = $gameMap.event(data.targetId); if (targetEvent) $gameTemp.requestAnimation([targetEvent], 1); }
if (data.type === 'player_hit_anim') {
// Если хост говорит, что ударили его — на экране клиента анимация на модельке хоста (Event)
// Если хост говорит, что ударили клиента — на экране клиента анимация на $gamePlayer (Игрок)
let target = data.targetNetworkId === "host" ? $gameMap.event(NETWORK_EVENT_ID) : $gamePlayer;
// Проигрываем анимацию удара (если не уворот)
if (target && data.animId > 0) $gameTemp.requestAnimation([target], data.animId);
// Если ударили тебя (клиента), показываем всплывающий урон и звуки
if (data.targetNetworkId === "client") {
if (data.isDodge) {
showDamageNumber("DODGE!", false, true);
AudioManager.playSe({name: "Cancel2", volume: 90, pitch: 150, pan: 0});
} else {
showDamageNumber("-" + data.damage, false, false);
AudioManager.playSe({name: "Damage1", volume: 90, pitch: 100, pan: 0});
}
}
}
// НОВОЕ: Синхронизация движения врагов/событий
if (data.type === 'event_move') {
let event = $gameMap.event(data.eventId);
if (event) {
if (!event.isMoving()) {
let dx = data.x - event.x;
let dy = data.y - event.y;
// Если враг слишком далеко (рассинхрон), телепортируем
if (Math.abs(dx) > 1 || Math.abs(dy) > 1) {
event.setPosition(data.x, data.y);
} else if (dx !== 0 || dy !== 0) {
// Иначе плавно двигаем
event.setDirection(data.dir);
event.moveStraight(data.dir);
} else {
// Если координаты совпадают, просто обновляем направление взгляда
event.setDirection(data.dir);
}
} else if (Math.abs(data.x - event.x) > 2 || Math.abs(data.y - event.y) > 2) {
// Жесткий фикс: если враг застрял в анимации ходьбы, но сильно отстал — телепортируем
event.setPosition(data.x, data.y);
}
}
}
});
conn.on('close', function() { networkActive = false; });
conn.on('error', function(err) { console.error("P2P Ошибка:", err); });
}
const _Scene_Map_start = Scene_Map.prototype.start;
Scene_Map.prototype.start = function() {
_Scene_Map_start.call(this);
createHTMLUI();
if (!peer) {
let promptText = "CHOOSE YOUR CLASS:\n\n";
for (let i = 0; i < CHAR_LIST.length; i++) promptText += CHAR_LIST[i].label + "\n";
promptText += "\n(Type a number)";
const charChoice = prompt(promptText, "1");
let charIndex = parseInt(charChoice) - 1;
if (charIndex < 0 || charIndex >= CHAR_LIST.length) charIndex = 0;
let chosenChar = CHAR_LIST[charIndex];
$gamePlayer.setImage(chosenChar.name, chosenChar.index);
myStats = chosenChar.vars;
myActorId = chosenChar.actorId;
const choice = prompt("CO-OP MULTIPLAYER\n\n1. Type 'host' to CREATE room\n2. Type 'join' to CONNECT", "host");
if (choice === 'host') {
isHost = true;
const roomCode = Math.floor(1000 + Math.random() * 9000);
peer = new Peer('rpg' + roomCode, { secure: true, debug: 1 });
peer.on('open', id => alert("YOUR ROOM CODE:\n\n" + roomCode));
peer.on('connection', conn => { setupConnection(conn); alert("FRIEND CONNECTED!"); });
peer.on('error', err => alert("Error: " + err.type));
} else if (choice === 'join') {
isHost = false;
const inputCode = prompt("ENTER HOST'S 4-DIGIT CODE:");
if (inputCode) {
peer = new Peer({ secure: true, debug: 1 });
peer.on('open', () => { setupConnection(peer.connect('rpg' + inputCode, { reliable: true })); setTimeout(() => alert(networkActive ? "CONNECTED!" : "Failed."), 3000); });
peer.on('error', err => alert("Error: " + err.type));
}
}
}
};
// ==========================================================
// ОБНОВЛЕНИЕ КАРТЫ
// ==========================================================
const _Scene_Map_update = Scene_Map.prototype.update;
Scene_Map.prototype.update = function() {
_Scene_Map_update.call(this);
if (!$gameMap || !$gamePlayer || !myStats) return;
// --- СЕТЬ ДВИЖЕНИЯ ---
if (networkActive && conn && conn.open) {
const x = $gamePlayer.x; const y = $gamePlayer.y; const dir = $gamePlayer.direction();
if (x !== lastSentX || y !== lastSentY || dir !== lastSentDir) {
try { conn.send({ type: 'move', x: x, y: y, dir: dir }); } catch(e) {}
lastSentX = x; lastSentY = y; lastSentDir = dir;
}
const friendEvent = $gameMap.event(NETWORK_EVENT_ID);
if (friendEvent && !friendEvent.isMoving()) {
let dx = remoteTargetX - friendEvent.x; let dy = remoteTargetY - friendEvent.y;
if (Math.abs(dx) > 1 || Math.abs(dy) > 1) friendEvent.setPosition(remoteTargetX, remoteTargetY);
else if (dx !== 0 || dy !== 0) { let dir = Math.abs(dx) > Math.abs(dy) ? (dx > 0 ? 6 : 4) : (dy > 0 ? 2 : 8); friendEvent.setDirection(dir); friendEvent.moveStraight(dir); }
}
}
// --- ТАГРЕТИНГ (R) ---
if (Input.isTriggered('targetKey')) {
AudioManager.playSe({name: "Cursor1", volume: 90, pitch: 100, pan: 0});
let closestDist = 999; let closestId = 0; let closestVar = 0; let closestDeathSw = 0;
$gameMap.events().forEach(event => {
if (event.event().meta && event.event().meta.enemy) {
let varId = parseInt(event.event().meta.enemy);
let deathSw = parseInt(event.event().meta.deathSwitch || 0);
let isDead = deathSw > 0 ? $gameSwitches.value(deathSw) : false;
if ($gameVariables.value(varId) > 0 && !isDead) {
let dist = Math.abs($gamePlayer.x - event.x) + Math.abs($gamePlayer.y - event.y);
if (dist < closestDist) { closestDist = dist; closestId = event.eventId(); closestVar = varId; closestDeathSw = deathSw; }
}
}
});
if (closestId > 0) {
currentTargetId = closestId; currentTargetVarId = closestVar; currentTargetDeathSwitch = closestDeathSw;
let event = $gameMap.event(closestId); targetNameCache = event.event().name;
let enemyDbId = parseInt(event.event().meta.enemyId || 1);
targetMaxHpCache = $dataEnemies[enemyDbId] ? $dataEnemies[enemyDbId].params[0] : 100;
AudioManager.playSe({name: "Decision1", volume: 90, pitch: 150, pan: 0});
} else { currentTargetId = 0; currentTargetVarId = 0; currentTargetDeathSwitch = 0; targetNameCache = ""; AudioManager.playSe({name: "Buzzer1", volume: 90, pitch: 100, pan: 0}); }
}
// --- АВТОАТАКА ИГРОКА ---
if (currentTargetId > 0 && autoAttackTimer <= 0) {
let targetEvent = $gameMap.event(currentTargetId);
let isDead = currentTargetDeathSwitch > 0 ? $gameSwitches.value(currentTargetDeathSwitch) : false;
if (targetEvent && $gameVariables.value(currentTargetVarId) > 0 && !isDead) {
let dist = Math.abs($gamePlayer.x - targetEvent.x) + Math.abs($gamePlayer.y - targetEvent.y);
if (dist <= ATTACK_RANGE) {
if (isHost || !networkActive) {
let baseStatVal = $gameVariables.value(myStats.baseStat);
let weaponIdVal = $gameVariables.value(myStats.weaponId);
let luckVal = $gameVariables.value(myStats.luck);
let weaponDmg = (weaponIdVal > 0 && $dataWeapons[weaponIdVal]) ? $dataWeapons[weaponIdVal].params[3] : 0;
let totalDamage = baseStatVal + weaponDmg;
let isCrit = false;
if (Math.randomInt(100) + 1 <= luckVal) { totalDamage *= 2; isCrit = true; AudioManager.playSe({name: "Crossbow", volume: 90, pitch: 150, pan: 0}); }
totalDamage = Math.max(1, totalDamage);
let newHp = Math.max(0, $gameVariables.value(currentTargetVarId) - totalDamage);
$gameVariables.setValue(currentTargetVarId, newHp);
showDamageNumber("-" + totalDamage, isCrit, false);
if (newHp <= 0 && currentTargetDeathSwitch > 0) { $gameSwitches.setValue(currentTargetDeathSwitch, true); if (networkActive) NetworkSendSwitch(currentTargetDeathSwitch, true); }
if (networkActive) { NetworkSendVariable(currentTargetVarId, newHp); conn.send({ type: 'attack_anim', targetId: currentTargetId }); }
$gameTemp.requestAnimation([targetEvent], 1);
}
autoAttackTimer = Math.max(20, BASE_COOLDOWN - ($gameVariables.value(myStats.speed) * 5));
}
} else { currentTargetId = 0; currentTargetVarId = 0; currentTargetDeathSwitch = 0; }
}
if (autoAttackTimer > 0) autoAttackTimer--;
// --- УМНЫЙ ИИ ВРАГОВ (State Machine) ---
if (isHost || !networkActive) {
const AGGRO_RANGE = 4;
const DEAGGRO_RANGE = 6;
const ENEMY_ATK_RANGE = 1;
const ENEMY_COOLDOWN = 120;
const AGGRO_DELAY = 90;
$gameMap.events().forEach(event => {
if (!event.event().meta || !event.event().meta.enemy) return;
let varId = parseInt(event.event().meta.enemy);
let deathSw = parseInt(event.event().meta.deathSwitch || 0);
if ($gameVariables.value(varId) <= 0 || (deathSw > 0 && $gameSwitches.value(deathSw))) return;
let eId = event.eventId();
if (!enemyAI[eId]) enemyAI[eId] = { state: 0, timer: 0, cooldown: 0 };
let ai = enemyAI[eId];
let p1Dist = Math.abs($gamePlayer.x - event.x) + Math.abs($gamePlayer.y - event.y);
let p2Dist = 999;
if (networkActive && remotePlayerStats) {
let p2Event = $gameMap.event(NETWORK_EVENT_ID);
p2Dist = Math.abs(p2Event.x - event.x) + Math.abs(p2Event.y - event.y);
}
let targetIsP1 = (p1Dist <= p2Dist);
let targetDist = targetIsP1 ? p1Dist : p2Dist;
// Деагро (Сброс состояния)
if (targetDist > DEAGGRO_RANGE) {
ai.state = 0; ai.timer = 0;
return;
}
// Игрок в радиусе агро
if (targetDist <= AGGRO_RANGE) {
// СОСТОЯНИЕ 0: СПОКОЙСТВИЕ -> ТРЕВОГА
if (ai.state === 0) {
ai.state = 1;
ai.timer = AGGRO_DELAY;
AudioManager.playSe({name: "Skill3", volume: 90, pitch: 100, pan: 0}); // Звук 1 раз!
}
// СОСТОЯНИЕ 1: ТРЕВОГА (Ждет)
if (ai.state === 1) {
ai.timer--;
if (ai.timer <= 0) ai.state = 2; // Переходим к бою
return; // Стоим на месте
}
// СОСТОЯНИЕ 2: БОЙ (Гоняется и бьет)
if (ai.state === 2) {
if (ai.cooldown > 0) { ai.cooldown--; return; }
if (targetDist <= ENEMY_ATK_RANGE) {
// АТАКА
let targetAgilityVar = targetIsP1 ? myStats.agility : (remotePlayerStats ? remotePlayerStats.agility : 0);
let targetHpVar = targetIsP1 ? myStats.hp : (remotePlayerStats ? remotePlayerStats.hp : 0);
let targetEntity = targetIsP1 ? $gamePlayer : $gameMap.event(NETWORK_EVENT_ID);
let agility = $gameVariables.value(targetAgilityVar);
if (Math.randomInt(100) + 1 <= agility) {
// УВОРОТ
if (targetIsP1) {
showDamageNumber("DODGE!", false, true);
AudioManager.playSe({name: "Cancel2", volume: 90, pitch: 150, pan: 0});
}
if (networkActive) {
let hitTarget = targetIsP1 ? "host" : "client";
conn.send({ type: 'player_hit_anim', targetNetworkId: hitTarget, animId: 0, isDodge: true });
}
} else {
// ПОПАДАНИЕ
let enemyAtk = parseInt(event.event().meta.atk || 3);
let currentHp = $gameVariables.value(targetHpVar);
let newHp = Math.max(0, currentHp - enemyAtk);
$gameVariables.setValue(targetHpVar, newHp);
$gameTemp.requestAnimation([targetEntity], 1);
if (targetIsP1) {
showDamageNumber("-" + enemyAtk, false, false);
AudioManager.playSe({name: "Damage1", volume: 90, pitch: 100, pan: 0});
}
if (networkActive) {
NetworkSendVariable(targetHpVar, newHp);
let hitTarget = targetIsP1 ? "host" : "client";
conn.send({ type: 'player_hit_anim', targetNetworkId: hitTarget, animId: 1, damage: enemyAtk, isDodge: false });
}
}
ai.cooldown = ENEMY_COOLDOWN;
} else {
// ПРЕСЛЕДОВАНИЕ
if (!event.isMoving()) {
if (targetIsP1) { event.moveTowardPlayer(); }
else {
let p2Event = $gameMap.event(NETWORK_EVENT_ID);
let dx = p2Event.x - event.x; let dy = p2Event.y - event.y;
let dir = Math.abs(dx) > Math.abs(dy) ? (dx > 0 ? 6 : 4) : (dy > 0 ? 2 : 8);
event.setDirection(dir); event.moveStraight(dir);
}
}
}
}
}
});
}
// --- СИНХРОНИЗАЦИЯ ПОЗИЦИЙ ВРАГОВ ---
if (isHost && networkActive && conn && conn.open) {
$gameMap.events().forEach(event => {
// Синхронизируем только врагов (можно убрать проверку meta.enemy, если нужны все NPC)
if (event.event().meta && event.event().meta.enemy) {
let eId = event.eventId();
let eX = event.x;
let eY = event.y;
let eDir = event.direction();
// Отправляем только если позиция или направление изменились
if (!lastSentEventPos[eId] || lastSentEventPos[eId].x !== eX || lastSentEventPos[eId].y !== eY || lastSentEventPos[eId].dir !== eDir) {
try {
conn.send({ type: 'event_move', eventId: eId, x: eX, y: eY, dir: eDir });
} catch(e) {}
lastSentEventPos[eId] = { x: eX, y: eY, dir: eDir };
}
}
});
}
updateUI();
};
})();
