Windows NT KAMIDAKI 10.0 build 19045 (Windows 10) AMD64
Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.3.9
Server IP : 192.168.3.16 & Your IP : 216.73.216.84
Domains :
Cant Read [ /etc/named.conf ]
User : SISTEMA
Terminal
Auto Root
Create File
Create Folder
Localroot Suggester
Backdoor Destroyer
Readme
C: /
Servidor Aion - OLD CLASS /
Delete
Unzip
Name
Size
Permission
Date
Action
BACKUP COMMANDS PLAYER
[ DIR ]
drwxrwxrwx
2025-06-04 14:54
SRC Vortex Aion - Atualizado em 02 Fevereiro as 05AM
[ DIR ]
drwxrwxrwx
2025-06-04 14:54
Tabelas Custom SQL + Emulador da VORTEX AION
[ DIR ]
drwxrwxrwx
2025-06-04 14:54
chat-server
[ DIR ]
drwxrwxrwx
2025-06-03 05:13
game-server
[ DIR ]
drwxrwxrwx
2025-06-04 14:54
login-server
[ DIR ]
drwxrwxrwx
2025-06-03 05:13
Aion Sys Editor.exe
3.32
MB
-rwxrwxrwx
2025-04-17 01:21
Bloqueio portas.bat
1.38
KB
-rwxrwxrwx
2025-05-03 20:12
Iniciar OldClassAion Server.bat
2.08
KB
-rwxrwxrwx
2025-04-30 20:20
OldClass-PathFinding-DinamicSimulator.html
59.46
KB
-rw-rw-rw-
2025-07-24 23:24
ShugoConsole.exe
4.62
MB
-rwxrwxrwx
2025-03-26 00:46
code.mp4
9.57
MB
-rw-rw-rw-
2025-07-31 05:34
prt_txt_structured_editable.py
8.72
KB
-rw-rw-rw-
2025-07-21 19:56
Save
Rename
<!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Pathfinding 3D - Realistic Map Environment</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <style> body { margin: 0; padding: 0; background: #0f1f1f; font-family: 'Arial', sans-serif; overflow: hidden; } #container { position: relative; width: 100vw; height: 100vh; } #controls { position: absolute; top: 20px; left: 20px; background: rgba(0, 20, 20, 0.9); padding: 20px; border-radius: 15px; color: #00ffaa; z-index: 100; max-width: 320px; border: 2px solid #00ffaa; box-shadow: 0 0 20px rgba(0, 255, 170, 0.3); } .control-group { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; font-weight: bold; color: #00ffaa; } select, button { width: 100%; padding: 8px; border: 2px solid #00ffaa; border-radius: 8px; background: rgba(0, 30, 30, 0.8); color: #00ffaa; font-size: 14px; } button { background: linear-gradient(45deg, #00aa66, #00ffaa); color: #000; cursor: pointer; margin-top: 5px; font-weight: bold; transition: all 0.3s ease; } button:hover { background: linear-gradient(45deg, #00ffaa, #00aa66); box-shadow: 0 0 15px rgba(0, 255, 170, 0.5); transform: translateY(-2px); } .scenario-btn { background: linear-gradient(45deg, #0066aa, #00aaff); margin: 3px 0; } .scenario-btn:hover { background: linear-gradient(45deg, #00aaff, #0066aa); } #info { position: absolute; bottom: 20px; left: 20px; background: rgba(0, 20, 20, 0.9); padding: 15px; border-radius: 15px; color: #00ffaa; z-index: 100; max-width: 450px; border: 2px solid #00ffaa; box-shadow: 0 0 20px rgba(0, 255, 170, 0.3); } .status { margin: 8px 0; padding: 8px; border-radius: 6px; border-left: 4px solid; } .status.direct { background: rgba(76, 175, 80, 0.2); border-left-color: #4CAF50; } .status.indirect { background: rgba(255, 193, 7, 0.2); border-left-color: #FFC107; } .status.blocked { background: rgba(244, 67, 54, 0.2); border-left-color: #F44336; } .status.unsafe { background: rgba(156, 39, 176, 0.2); border-left-color: #9C27B0; } #fps { position: absolute; top: 20px; right: 20px; color: #00ffaa; font-weight: bold; font-size: 18px; background: rgba(0, 20, 20, 0.8); padding: 10px; border-radius: 8px; border: 2px solid #00ffaa; } h3, h4 { color: #00ffaa; text-shadow: 0 0 10px rgba(0, 255, 170, 0.5); } /* Estilo para o cursor de arrastar */ .drag-cursor { cursor: grab; } .drag-cursor:active { cursor: grabbing; } /* Estilo para botões de obstáculos */ .obstacle-btn { background: linear-gradient(45deg, #aa6600, #ffaa00); margin: 3px 0; } .obstacle-btn:hover { background: linear-gradient(45deg, #ffaa00, #aa6600); } </style> </head> <body> <div id="container"> <div id="fps">FPS: 60</div> <div id="controls"> <h3>🎮 Pathfinding Avançado</h3> <div class="control-group"> <label>⚡ Cenários de Combate:</label> <button class="scenario-btn" onclick="setScenario('direct')">📍 Visão Direta (45°)</button> <button class="scenario-btn" onclick="setScenario('indirect')">🔄 Contorno (30°)</button> <button class="scenario-btn" onclick="setScenario('blocked')">🚫 Bloqueado (15°)</button> <button class="scenario-btn" onclick="setScenario('unsafe')">⚠️ Desnível (10°)</button> </div> <div class="control-group"> <label>🎯 Configuração Manual:</label> <select id="angleStep" onchange="updateAngleStep()"> <option value="10">10° (Precisão Máxima)</option> <option value="15">15° (Alta Precisão)</option> <option value="30">30° (Balanceado)</option> <option value="45" selected>45° (Performance)</option> </select> </div> <div class="control-group"> <label>🪨 Adicionar Obstáculos:</label> <button class="obstacle-btn" onclick="enableAddObstacle('rock')">➕ Adicionar Rocha</button> <button class="obstacle-btn" onclick="enableAddObstacle('log')">🌲 Adicionar Tronco</button> <button class="obstacle-btn" onclick="removeLastObstacle()">🗑️ Remover Último</button> <button class="obstacle-btn" onclick="clearAllObstacles()">🧹 Limpar Todos</button> </div> <button onclick="startAnimation()">🎬 Iniciar Perseguição</button> <button onclick="resetScene()">🔄 Resetar Posições</button> <button onclick="togglePillars()">🏛️ Toggle Pilares</button> <button onclick="togglePlayerDrag()" id="dragButton">🎯 Mover Jogador</button> </div> <div id="info"> <h4>📊 Status do Sistema IA</h4> <div id="algorithmInfo"> <div class="status direct"> <strong>Condições:</strong> canSeeDirect=true, zUnsafe=false<br> <strong>Ângulo:</strong> 45° | <strong>Tentativas:</strong> 9<br> <strong>Distância:</strong> 12.3m | <strong>Obstáculos:</strong> 0 </div> </div> </div> </div> <script> // Variáveis globais Three.js let scene, camera, renderer; let npc, player, pathLines = [], angleArcs = []; let pillars = [], pillarGroup; let customObstacles = []; let currentAngleStep = 45; let animationRunning = false; let showPillars = true; let playerDragMode = false; let raycaster = new THREE.Raycaster(); let mouse = new THREE.Vector2(); let isDraggingPlayer = false; let dragPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0)); let dragPoint = new THREE.Vector3(); let addObstacleMode = null; // Configurações do algoritmo const VISIBLE_ANGLE = 360; let canSeeDirect = true; let zUnsafe = false; // FPS Counter let lastTime = performance.now(); let frameCount = 0; // Inicialização init(); animate(); function init() { // Cena scene = new THREE.Scene(); scene.background = new THREE.Color(0x1a3d3d); scene.fog = new THREE.Fog(0x1a3d3d, 10, 50); // Câmera camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(20, 30, 20); camera.lookAt(0, 0, 0); // Renderer renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.setClearColor(0x1a3d3d); document.getElementById('container').appendChild(renderer.domElement); // Controles de câmera setupCameraControls(); // Iluminação baseada na imagem setupRealisticLighting(); // Criar ambiente realista createRealisticEnvironment(); // Personagens realistas createRealisticCharacters(); // Inicializar visualização updateVisualization(); // Adicionar listener para adicionar obstáculos renderer.domElement.addEventListener('click', handleAddObstacle); } function setupCameraControls() { let isDragging = false; let previousMousePosition = { x: 0, y: 0 }; renderer.domElement.addEventListener('mousedown', (e) => { if (playerDragMode) { handlePlayerDrag(e); return; } isDragging = true; previousMousePosition = { x: e.clientX, y: e.clientY }; }); renderer.domElement.addEventListener('mousemove', (e) => { if (isDraggingPlayer) { handlePlayerMove(e); return; } if (!isDragging) return; const deltaMove = { x: e.clientX - previousMousePosition.x, y: e.clientY - previousMousePosition.y }; const spherical = new THREE.Spherical(); spherical.setFromVector3(camera.position); spherical.theta -= deltaMove.x * 0.01; spherical.phi += deltaMove.y * 0.01; spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi)); camera.position.setFromSpherical(spherical); camera.lookAt(0, 0, 0); previousMousePosition = { x: e.clientX, y: e.clientY }; }); renderer.domElement.addEventListener('mouseup', () => { isDragging = false; isDraggingPlayer = false; }); renderer.domElement.addEventListener('wheel', (e) => { if (playerDragMode) return; const distance = camera.position.length(); const newDistance = Math.max(8, Math.min(60, distance + e.deltaY * 0.02)); camera.position.normalize().multiplyScalar(newDistance); }); } function togglePlayerDrag() { playerDragMode = !playerDragMode; const button = document.getElementById('dragButton'); if (playerDragMode) { button.textContent = '✅ Mover Jogador'; renderer.domElement.classList.add('drag-cursor'); } else { button.textContent = '🎯 Mover Jogador'; renderer.domElement.classList.remove('drag-cursor'); isDraggingPlayer = false; addObstacleMode = null; // Desativa o modo de adicionar obstáculos } } function handlePlayerDrag(event) { // Calcular posição do mouse normalizada mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; // Configurar raycaster raycaster.setFromCamera(mouse, camera); // Verificar se clicou no jogador const intersects = raycaster.intersectObject(player, true); if (intersects.length > 0) { isDraggingPlayer = true; // Configurar plano de arrasto (plano horizontal na posição do jogador) dragPlane.setFromNormalAndCoplanarPoint( new THREE.Vector3(0, 1, 0), player.position ); // Encontrar ponto de interseção com o plano raycaster.ray.intersectPlane(dragPlane, dragPoint); } } function handlePlayerMove(event) { if (!isDraggingPlayer) return; // Atualizar posição do mouse mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; // Atualizar raycaster raycaster.setFromCamera(mouse, camera); // Encontrar nova posição no plano de arrasto const newPosition = new THREE.Vector3(); if (raycaster.ray.intersectPlane(dragPlane, newPosition)) { // Mover jogador para a nova posição player.position.copy(newPosition); // Atualizar visualização updateVisualization(); } } function setupRealisticLighting() { // Luz ambiente esverdeada (como na imagem) const ambientLight = new THREE.AmbientLight(0x2d5555, 0.4); scene.add(ambientLight); // Luzes direcionais para simular iluminação do templo const mainLight = new THREE.DirectionalLight(0x44aaaa, 0.8); mainLight.position.set(15, 25, 15); mainLight.castShadow = true; mainLight.shadow.mapSize.width = 4096; mainLight.shadow.mapSize.height = 4096; mainLight.shadow.camera.near = 0.5; mainLight.shadow.camera.far = 100; mainLight.shadow.camera.left = -30; mainLight.shadow.camera.right = 30; mainLight.shadow.camera.top = 30; mainLight.shadow.camera.bottom = -30; scene.add(mainLight); // Luz secundária const secondLight = new THREE.DirectionalLight(0x66dddd, 0.3); secondLight.position.set(-10, 20, -10); scene.add(secondLight); // Luzes pontuais nos pilares (efeito mágico) for (let i = 0; i < 4; i++) { const pillarLight = new THREE.PointLight(0x00ffaa, 0.5, 15); const angle = (i / 4) * Math.PI * 2; pillarLight.position.set( Math.cos(angle) * 12, 8, Math.sin(angle) * 12 ); scene.add(pillarLight); } } function createRealisticEnvironment() { // Chão com textura de pedra const floorGeometry = new THREE.PlaneGeometry(60, 60, 20, 20); const floorMaterial = new THREE.MeshLambertMaterial({ color: 0x3d5d5d, transparent: true, opacity: 0.9 }); // Adicionar rugosidade ao chão const vertices = floorGeometry.attributes.position.array; for (let i = 0; i < vertices.length; i += 3) { vertices[i + 2] += (Math.random() - 0.5) * 0.3; } floorGeometry.attributes.position.needsUpdate = true; floorGeometry.computeVertexNormals(); const floor = new THREE.Mesh(floorGeometry, floorMaterial); floor.rotation.x = -Math.PI / 2; floor.receiveShadow = true; scene.add(floor); // Padrões no chão (como na imagem) createFloorPatterns(); // Criar pilares realistas createRealisticPillars(); // Paredes e estruturas createWallStructures(); } function createFloorPatterns() { // Círculos e padrões decorativos no chão const circleGeometry = new THREE.RingGeometry(3, 3.2, 32); const circleMaterial = new THREE.MeshBasicMaterial({ color: 0x00ffaa, transparent: true, opacity: 0.3 }); // Círculo central const centralCircle = new THREE.Mesh(circleGeometry, circleMaterial); centralCircle.rotation.x = -Math.PI / 2; centralCircle.position.y = 0.01; scene.add(centralCircle); // Padrões geométricos for (let i = 0; i < 8; i++) { const angle = (i / 8) * Math.PI * 2; const smallCircle = new THREE.Mesh( new THREE.RingGeometry(1, 1.1, 16), circleMaterial ); smallCircle.rotation.x = -Math.PI / 2; smallCircle.position.set( Math.cos(angle) * 8, 0.01, Math.sin(angle) * 8 ); scene.add(smallCircle); } } function createRealisticPillars() { pillarGroup = new THREE.Group(); // Posições baseadas na imagem const pillarPositions = [ { x: -15, z: -15 }, { x: 15, z: -15 }, { x: -15, z: 15 }, { x: 15, z: 15 }, { x: 0, z: -20 }, { x: 0, z: 20 }, { x: -20, z: 0 }, { x: 20, z: 0 } ]; pillarPositions.forEach((pos, index) => { const pillar = createSinglePillar(); pillar.position.set(pos.x, 0, pos.z); pillarGroup.add(pillar); pillars.push(pillar); }); scene.add(pillarGroup); } function createSinglePillar() { const pillarGroup = new THREE.Group(); // Base do pilar const baseGeometry = new THREE.CylinderGeometry(2.5, 3, 1, 16); const baseMaterial = new THREE.MeshLambertMaterial({ color: 0x4a6d6d, transparent: true, opacity: 0.9 }); const base = new THREE.Mesh(baseGeometry, baseMaterial); base.position.y = 0.5; base.castShadow = true; base.receiveShadow = true; pillarGroup.add(base); // Corpo do pilar const bodyGeometry = new THREE.CylinderGeometry(2, 2, 12, 16); const bodyMaterial = new THREE.MeshLambertMaterial({ color: 0x5d7d7d }); const body = new THREE.Mesh(bodyGeometry, bodyMaterial); body.position.y = 7; body.castShadow = true; body.receiveShadow = true; pillarGroup.add(body); // Topo do pilar const topGeometry = new THREE.CylinderGeometry(3, 2.2, 2, 16); const topMaterial = new THREE.MeshLambertMaterial({ color: 0x6d8d8d }); const top = new THREE.Mesh(topGeometry, topMaterial); top.position.y = 14; top.castShadow = true; top.receiveShadow = true; pillarGroup.add(top); // Efeito mágico no topo const glowGeometry = new THREE.SphereGeometry(0.5, 16, 16); const glowMaterial = new THREE.MeshBasicMaterial({ color: 0x00ffaa, transparent: true, opacity: 0.6 }); const glow = new THREE.Mesh(glowGeometry, glowMaterial); glow.position.y = 15.5; pillarGroup.add(glow); return pillarGroup; } function createWallStructures() { // Paredes distantes para dar profundidade const wallGeometry = new THREE.PlaneGeometry(80, 20); const wallMaterial = new THREE.MeshLambertMaterial({ color: 0x2d4d4d, transparent: true, opacity: 0.7 }); // Parede norte const northWall = new THREE.Mesh(wallGeometry, wallMaterial); northWall.position.set(0, 10, -40); northWall.receiveShadow = true; scene.add(northWall); // Parede sul const southWall = northWall.clone(); southWall.position.z = 40; southWall.rotation.y = Math.PI; scene.add(southWall); // Paredes leste e oeste const sideWallGeometry = new THREE.PlaneGeometry(80, 20); const eastWall = new THREE.Mesh(sideWallGeometry, wallMaterial); eastWall.position.set(40, 10, 0); eastWall.rotation.y = -Math.PI / 2; eastWall.receiveShadow = true; scene.add(eastWall); const westWall = eastWall.clone(); westWall.position.x = -40; westWall.rotation.y = Math.PI / 2; scene.add(westWall); } function createRealisticCharacters() { // NPC (Monstro) - Mais realista baseado na imagem const npcGroup = new THREE.Group(); // Corpo principal do monstro const npcBodyGeometry = new THREE.SphereGeometry(1.2, 16, 16); const npcBodyMaterial = new THREE.MeshLambertMaterial({ color: 0xff3300, emissive: 0x441100, transparent: true, opacity: 0.9 }); const npcBody = new THREE.Mesh(npcBodyGeometry, npcBodyMaterial); npcBody.position.y = 1.2; npcBody.castShadow = true; npcGroup.add(npcBody); // Espinhos e detalhes do monstro for (let i = 0; i < 12; i++) { const spikeGeometry = new THREE.ConeGeometry(0.15, 1.2, 6); const spikeMaterial = new THREE.MeshLambertMaterial({ color: 0x220000, emissive: 0x110000 }); const spike = new THREE.Mesh(spikeGeometry, spikeMaterial); const angle = (i / 12) * Math.PI * 2; const radius = 1 + Math.random() * 0.5; spike.position.set( Math.cos(angle) * radius, 1.2 + Math.random() * 0.5, Math.sin(angle) * radius ); spike.lookAt( spike.position.x * 2, spike.position.y, spike.position.z * 2 ); spike.castShadow = true; npcGroup.add(spike); } // Efeito de fogo/energia const fireGeometry = new THREE.SphereGeometry(0.8, 12, 12); const fireMaterial = new THREE.MeshBasicMaterial({ color: 0xff6600, transparent: true, opacity: 0.4 }); const fire = new THREE.Mesh(fireGeometry, fireMaterial); fire.position.y = 1.2; npcGroup.add(fire); // Posicionar NPC atrás de um pilar (como solicitado) npc = npcGroup; npc.position.set(-18, 0, -12); // Atrás do pilar scene.add(npc); // Player - Mais humanóide e realista const playerGroup = new THREE.Group(); // Corpo do jogador const playerBodyGeometry = new THREE.CylinderGeometry(0.6, 0.8, 2.5, 12); const playerBodyMaterial = new THREE.MeshLambertMaterial({ color: 0x4488cc }); const playerBody = new THREE.Mesh(playerBodyGeometry, playerBodyMaterial); playerBody.position.y = 1.25; playerBody.castShadow = true; playerGroup.add(playerBody); // Cabeça const headGeometry = new THREE.SphereGeometry(0.5, 12, 12); const headMaterial = new THREE.MeshLambertMaterial({ color: 0xffdbac }); const head = new THREE.Mesh(headGeometry, headMaterial); head.position.y = 2.8; head.castShadow = true; playerGroup.add(head); // Braços const armGeometry = new THREE.CylinderGeometry(0.2, 0.2, 1.5, 8); const armMaterial = new THREE.MeshLambertMaterial({ color: 0xffdbac }); const leftArm = new THREE.Mesh(armGeometry, armMaterial); leftArm.position.set(-1, 1.8, 0); leftArm.rotation.z = 0.3; leftArm.castShadow = true; playerGroup.add(leftArm); const rightArm = new THREE.Mesh(armGeometry, armMaterial); rightArm.position.set(1, 1.8, 0); rightArm.rotation.z = -0.3; rightArm.castShadow = true; playerGroup.add(rightArm); // Pernas const legGeometry = new THREE.CylinderGeometry(0.25, 0.25, 1.2, 8); const legMaterial = new THREE.MeshLambertMaterial({ color: 0x333333 }); const leftLeg = new THREE.Mesh(legGeometry, legMaterial); leftLeg.position.set(-0.3, 0.6, 0); leftLeg.castShadow = true; playerGroup.add(leftLeg); const rightLeg = new THREE.Mesh(legGeometry, legMaterial); rightLeg.position.set(0.3, 0.6, 0); rightLeg.castShadow = true; playerGroup.add(rightLeg); // Arma/bastão mágico const weaponGeometry = new THREE.CylinderGeometry(0.05, 0.05, 2, 8); const weaponMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513 }); const weapon = new THREE.Mesh(weaponGeometry, weaponMaterial); weapon.position.set(1.2, 2.5, 0); weapon.rotation.z = -0.5; weapon.castShadow = true; playerGroup.add(weapon); // Cristal na ponta da arma const crystalGeometry = new THREE.OctahedronGeometry(0.2); const crystalMaterial = new THREE.MeshBasicMaterial({ color: 0x00aaff, transparent: true, opacity: 0.8 }); const crystal = new THREE.Mesh(crystalGeometry, crystalMaterial); crystal.position.set(1.5, 3.4, 0); playerGroup.add(crystal); player = playerGroup; player.position.set(8, 0, 6); scene.add(player); // Labels com nomes createCharacterLabels(); } function createCharacterLabels() { // Função auxiliar para criar texto 3D function createTextLabel(text, color = 0xffffff) { const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.width = 256; canvas.height = 64; context.fillStyle = 'rgba(0, 0, 0, 0.8)'; context.fillRect(0, 0, canvas.width, canvas.height); context.font = 'bold 24px Arial'; context.fillStyle = `#${color.toString(16).padStart(6, '0')}`; context.textAlign = 'center'; context.fillText(text, canvas.width / 2, canvas.height / 2 + 8); const texture = new THREE.CanvasTexture(canvas); const material = new THREE.MeshBasicMaterial({ map: texture, transparent: true }); const geometry = new THREE.PlaneGeometry(4, 1); return new THREE.Mesh(geometry, material); } // Label do NPC const npcLabel = createTextLabel('🔥 Fire Elemental', 0xff6600); npcLabel.position.set(0, 4, 0); npc.add(npcLabel); // Label do Player const playerLabel = createTextLabel('⚔️ Player', 0x00aaff); playerLabel.position.set(0, 4.5, 0); player.add(playerLabel); } // Funções para adicionar obstáculos personalizados function enableAddObstacle(type) { addObstacleMode = type; playerDragMode = false; document.getElementById('dragButton').textContent = '🎯 Mover Jogador'; renderer.domElement.classList.remove('drag-cursor'); isDraggingPlayer = false; } function handleAddObstacle(event) { if (!addObstacleMode) return; // Calcular posição do mouse normalizada mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; // Configurar raycaster raycaster.setFromCamera(mouse, camera); // Encontrar interseção com o chão const intersects = raycaster.intersectObjects(scene.children, true); if (intersects.length > 0) { const point = intersects[0].point; // Criar obstáculo na posição clicada let obstacle; if (addObstacleMode === 'rock') { obstacle = createRock(point.x, point.y, point.z); } else if (addObstacleMode === 'log') { obstacle = createLog(point.x, point.y, point.z); } if (obstacle) { scene.add(obstacle); customObstacles.push(obstacle); updateVisualization(); } } // Desativa o modo após adicionar addObstacleMode = null; } function createRock(x, y, z) { const rockGroup = new THREE.Group(); // Criar rocha com vários cubos sobrepostos const rockMaterial = new THREE.MeshLambertMaterial({ color: 0x777777, transparent: true, opacity: 0.9 }); // Base principal const baseGeometry = new THREE.SphereGeometry(1.2, 8, 8); const base = new THREE.Mesh(baseGeometry, rockMaterial); base.castShadow = true; base.receiveShadow = true; rockGroup.add(base); // Detalhes da rocha for (let i = 0; i < 5; i++) { const detailGeometry = new THREE.BoxGeometry( 0.5 + Math.random() * 0.8, 0.3 + Math.random() * 0.5, 0.5 + Math.random() * 0.8 ); const detail = new THREE.Mesh(detailGeometry, rockMaterial); detail.position.set( (Math.random() - 0.5) * 1.5, (Math.random() - 0.5) * 0.5 + 0.8, (Math.random() - 0.5) * 1.5 ); detail.rotation.set( Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI ); detail.castShadow = true; rockGroup.add(detail); } rockGroup.position.set(x, y, z); rockGroup.userData = { type: 'rock', radius: 1.5 }; return rockGroup; } function createLog(x, y, z) { const logGroup = new THREE.Group(); // Tronco principal const logMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513, transparent: true, opacity: 0.9 }); const logGeometry = new THREE.CylinderGeometry(0.5, 0.5, 3, 8); const log = new THREE.Mesh(logGeometry, logMaterial); log.rotation.x = Math.PI / 2; // Deitar o tronco log.castShadow = true; log.receiveShadow = true; logGroup.add(log); // Galhos const branchMaterial = new THREE.MeshLambertMaterial({ color: 0x654321 }); for (let i = 0; i < 3; i++) { const branchGeometry = new THREE.CylinderGeometry(0.1, 0.15, 1 + Math.random(), 6); const branch = new THREE.Mesh(branchGeometry, branchMaterial); branch.position.set( (Math.random() - 0.5) * 2, (Math.random() - 0.5) * 0.5, (Math.random() - 0.5) * 2 ); branch.rotation.set( Math.random() * Math.PI * 0.3, Math.random() * Math.PI * 0.3, Math.random() * Math.PI * 0.3 ); branch.castShadow = true; logGroup.add(branch); } // Folhas const leafMaterial = new THREE.MeshLambertMaterial({ color: 0x228833 }); for (let i = 0; i < 5; i++) { const leafGeometry = new THREE.SphereGeometry(0.3 + Math.random() * 0.2, 6, 6); const leaf = new THREE.Mesh(leafGeometry, leafMaterial); leaf.position.set( (Math.random() - 0.5) * 3, (Math.random() - 0.5) * 0.5 + 0.3, (Math.random() - 0.5) * 3 ); leaf.castShadow = true; logGroup.add(leaf); } logGroup.position.set(x, y, z); logGroup.userData = { type: 'log', radius: 1.2 }; return logGroup; } function removeLastObstacle() { if (customObstacles.length > 0) { const lastObstacle = customObstacles.pop(); scene.remove(lastObstacle); updateVisualization(); } } function clearAllObstacles() { customObstacles.forEach(obstacle => { scene.remove(obstacle); }); customObstacles = []; updateVisualization(); } function updateVisualization() { // Limpar visualizações anteriores clearPreviousVisualization(); // Calcular configurações do algoritmo const sourcePoint = npc.position.clone(); const targetPoint = player.position.clone(); const futureDistance = sourcePoint.distanceTo(targetPoint); // Verificar se há obstáculos (pilares) entre NPC e Player const hasObstacles = checkObstaclesBetween(sourcePoint, targetPoint); // Determinar dynamicAngleStep baseado nas condições let dynamicAngleStep; if (canSeeDirect && !zUnsafe && !hasObstacles) { dynamicAngleStep = 45; } else if (!canSeeDirect && !zUnsafe) { dynamicAngleStep = 30; } else if (!canSeeDirect || hasObstacles) { dynamicAngleStep = 15; } else { dynamicAngleStep = 10; } currentAngleStep = dynamicAngleStep; // Atualizar UI document.getElementById('angleStep').value = dynamicAngleStep; updateAlgorithmInfo(futureDistance, hasObstacles); // Visualizar todos os ângulos possíveis visualizeAngles(sourcePoint, targetPoint, futureDistance, dynamicAngleStep); } function checkObstaclesBetween(source, target) { // Verificar colisão com pilares for (let pillar of pillars) { if (!pillar.visible) continue; const pillarPos = pillar.position.clone(); pillarPos.y = source.y; // Mesmo nível // Verificar se o pilar está na linha de visão const distance = distancePointToLine(pillarPos, source, target); if (distance < 3) { // Raio do pilar + margem return true; } } // Verificar colisão com obstáculos personalizados for (let obstacle of customObstacles) { const obstaclePos = obstacle.position.clone(); obstaclePos.y = source.y; const distance = distancePointToLine(obstaclePos, source, target); if (distance < (obstacle.userData?.radius || 1.5)) { return true; } } return false; } function distancePointToLine(point, lineStart, lineEnd) { const A = point.x - lineStart.x; const B = point.z - lineStart.z; const C = lineEnd.x - lineStart.x; const D = lineEnd.z - lineStart.z; const dot = A * C + B * D; const lenSq = C * C + D * D; let param = -1; if (lenSq !== 0) { param = dot / lenSq; } let xx, zz; if (param < 0) { xx = lineStart.x; zz = lineStart.z; } else if (param > 1) { xx = lineEnd.x; zz = lineEnd.z; } else { xx = lineStart.x + param * C; zz = lineStart.z + param * D; } const dx = point.x - xx; const dz = point.z - zz; return Math.sqrt(dx * dx + dz * dz); } function visualizeAngles(sourcePoint, targetPoint, futureDistance, angleStep) { const offset = VISIBLE_ANGLE / 2; const tries = VISIBLE_ANGLE / angleStep + 1; // Materiais para diferentes tipos de caminhos const validMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.7, linewidth: 2 }); const blockedMaterial = new THREE.LineBasicMaterial({ color: 0xff3300, transparent: true, opacity: 0.8, linewidth: 2 }); const optimalMaterial = new THREE.LineBasicMaterial({ color: 0x00aaff, transparent: true, opacity: 0.9, linewidth: 3 }); let closestPoint = null; let minimalDistance = Number.MAX_VALUE; for (let i = 0; i < tries; i++) { const angle = i * angleStep - offset; const rotatedPoint = rotatePoint(sourcePoint, targetPoint, futureDistance, angle); if (!rotatedPoint) continue; // Verificar se o ponto é válido (sem colisão com pilares ou obstáculos) const isBlocked = checkPointCollision(rotatedPoint); const distanceToTarget = rotatedPoint.distanceTo(targetPoint); // Encontrar o melhor ponto if (!isBlocked && distanceToTarget < minimalDistance) { minimalDistance = distanceToTarget; closestPoint = rotatedPoint; } // Material baseado na validade do ponto const material = isBlocked ? blockedMaterial : validMaterial; // Criar linha do NPC para o ponto rotacionado const lineGeometry = new THREE.BufferGeometry(); const points = [ sourcePoint.clone(), rotatedPoint ]; lineGeometry.setFromPoints(points); const line = new THREE.Line(lineGeometry, material); scene.add(line); pathLines.push(line); // Adicionar marcador no ponto const markerGeometry = new THREE.SphereGeometry(0.15, 8, 8); const markerMaterial = new THREE.MeshBasicMaterial({ color: isBlocked ? 0xff3300 : 0x00ff00, transparent: true, opacity: 0.8 }); const marker = new THREE.Mesh(markerGeometry, markerMaterial); marker.position.copy(rotatedPoint); scene.add(marker); pathLines.push(marker); // Adicionar efeito de partículas nos pontos válidos if (!isBlocked) { createPathParticle(rotatedPoint); } } // Destacar o melhor caminho if (closestPoint) { const optimalLineGeometry = new THREE.BufferGeometry(); optimalLineGeometry.setFromPoints([sourcePoint, closestPoint]); const optimalLine = new THREE.Line(optimalLineGeometry, optimalMaterial); scene.add(optimalLine); pathLines.push(optimalLine); // Marcador especial para o melhor ponto const optimalMarkerGeometry = new THREE.SphereGeometry(0.25, 12, 12); const optimalMarkerMaterial = new THREE.MeshBasicMaterial({ color: 0x00aaff, transparent: true, opacity: 0.9 }); const optimalMarker = new THREE.Mesh(optimalMarkerGeometry, optimalMarkerMaterial); optimalMarker.position.copy(closestPoint); scene.add(optimalMarker); pathLines.push(optimalMarker); } // Linha direta NPC -> Player const directLineGeometry = new THREE.BufferGeometry(); directLineGeometry.setFromPoints([sourcePoint, targetPoint]); const directLine = new THREE.Line(directLineGeometry, new THREE.LineBasicMaterial({ color: 0xffff00, transparent: true, opacity: 0.6, linewidth: 2 })); scene.add(directLine); pathLines.push(directLine); // Adicionar indicador de distância createDistanceIndicator(sourcePoint, targetPoint); } function createPathParticle(position) { const particleGeometry = new THREE.SphereGeometry(0.05, 6, 6); const particleMaterial = new THREE.MeshBasicMaterial({ color: 0x00ffaa, transparent: true, opacity: 0.6 }); const particle = new THREE.Mesh(particleGeometry, particleMaterial); particle.position.copy(position); particle.position.y += 0.5; scene.add(particle); pathLines.push(particle); // Animação de flutuação particle.userData = { originalY: particle.position.y, time: Math.random() * Math.PI * 2 }; } function createDistanceIndicator(source, target) { const distance = source.distanceTo(target); const midPoint = new THREE.Vector3().addVectors(source, target).multiplyScalar(0.5); midPoint.y += 3; // Criar texto de distância const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.width = 256; canvas.height = 64; context.fillStyle = 'rgba(0, 0, 0, 0.8)'; context.fillRect(0, 0, canvas.width, canvas.height); context.font = 'bold 20px Arial'; context.fillStyle = '#00ffaa'; context.textAlign = 'center'; context.fillText(`${distance.toFixed(1)}m`, canvas.width / 2, canvas.height / 2 + 6); const texture = new THREE.CanvasTexture(canvas); const material = new THREE.MeshBasicMaterial({ map: texture, transparent: true }); const geometry = new THREE.PlaneGeometry(3, 0.75); const distanceLabel = new THREE.Mesh(geometry, material); distanceLabel.position.copy(midPoint); scene.add(distanceLabel); pathLines.push(distanceLabel); } function rotatePoint(source, target, distance, angle) { const dx = target.x - source.x; const dz = target.z - source.z; const angleRad = (angle * Math.PI) / 180; const cosAngle = Math.cos(angleRad); const sinAngle = Math.sin(angleRad); const rotatedX = dx * cosAngle - dz * sinAngle; const rotatedZ = dx * sinAngle + dz * cosAngle; const length = Math.sqrt(rotatedX * rotatedX + rotatedZ * rotatedZ); if (length === 0) return null; const normalizedX = rotatedX / length; const normalizedZ = rotatedZ / length; // Usar distância dinâmica baseada na situação const stepDistance = Math.min(4, distance * 0.3); return new THREE.Vector3( source.x + normalizedX * stepDistance, source.y, source.z + normalizedZ * stepDistance ); } function checkPointCollision(point) { // Verificar colisão com pilares for (let pillar of pillars) { if (!pillar.visible) continue; const pillarPos = pillar.position.clone(); const distance = point.distanceTo(pillarPos); if (distance < 2.5) { // Raio de colisão do pilar return true; } } // Verificar colisão com obstáculos personalizados for (let obstacle of customObstacles) { const obstaclePos = obstacle.position.clone(); const distance = point.distanceTo(obstaclePos); if (distance < (obstacle.userData?.radius || 1.5)) { return true; } } // Verificar limites do mapa if (Math.abs(point.x) > 25 || Math.abs(point.z) > 25) { return true; } return false; } function clearPreviousVisualization() { pathLines.forEach(obj => { scene.remove(obj); if (obj.geometry) obj.geometry.dispose(); if (obj.material) { if (obj.material.map) obj.material.map.dispose(); obj.material.dispose(); } }); pathLines = []; } function updateAlgorithmInfo(distance, hasObstacles) { const tries = VISIBLE_ANGLE / currentAngleStep + 1; let statusClass = 'direct'; let statusText = ''; let obstacleCount = hasObstacles ? (pillars.filter(p => p.visible).length + customObstacles.length) : 0; if (canSeeDirect && !zUnsafe && !hasObstacles) { statusClass = 'direct'; statusText = 'canSeeDirect=true, zUnsafe=false, clearPath=true'; } else if (!canSeeDirect && !zUnsafe) { statusClass = 'indirect'; statusText = 'canSeeDirect=false, zUnsafe=false, pathfinding=active'; } else if (!canSeeDirect || hasObstacles) { statusClass = 'blocked'; statusText = 'canSeeDirect=false, obstacles=detected, preciseSearch=true'; } else { statusClass = 'unsafe'; statusText = 'canSeeDirect=true, zUnsafe=true, heightDanger=detected'; } document.getElementById('algorithmInfo').innerHTML = ` <div class="status ${statusClass}"> <strong>Condições:</strong> ${statusText}<br> <strong>Ângulo:</strong> ${currentAngleStep}° | <strong>Tentativas:</strong> ${tries}<br> <strong>Distância:</strong> ${distance.toFixed(1)}m | <strong>Obstáculos:</strong> ${obstacleCount} </div> `; } function setScenario(scenario) { switch(scenario) { case 'direct': canSeeDirect = true; zUnsafe = false; // Mover NPC para posição com visão direta npc.position.set(-10, 0, -8); break; case 'indirect': canSeeDirect = false; zUnsafe = false; // Posição com visão parcialmente bloqueada npc.position.set(-18, 0, -12); break; case 'blocked': canSeeDirect = false; zUnsafe = true; // Posição completamente atrás do pilar npc.position.set(-15, 0, -15); break; case 'unsafe': canSeeDirect = true; zUnsafe = true; // Posição com diferença de altura npc.position.set(-12, 2, -10); break; } updateVisualization(); } function updateAngleStep() { currentAngleStep = parseInt(document.getElementById('angleStep').value); updateVisualization(); } function startAnimation() { if (animationRunning) return; animationRunning = true; const sourcePos = npc.position.clone(); const targetPos = player.position.clone(); // Calcular melhor caminho baseado no algoritmo atual const bestPath = calculateBestPath(sourcePos, targetPos); if (bestPath) { animateNPCMovement(sourcePos, bestPath); } else { // Movimento direto se não há caminho melhor const direction = new THREE.Vector3().subVectors(targetPos, sourcePos).normalize(); const newPos = sourcePos.clone().add(direction.multiplyScalar(3)); animateNPCMovement(sourcePos, newPos); } } function calculateBestPath(source, target) { const futureDistance = source.distanceTo(target); const offset = VISIBLE_ANGLE / 2; const tries = VISIBLE_ANGLE / currentAngleStep + 1; let closestPoint = null; let minimalDistance = Number.MAX_VALUE; for (let i = 0; i < tries; i++) { const angle = i * currentAngleStep - offset; const rotatedPoint = rotatePoint(source, target, futureDistance, angle); if (!rotatedPoint || checkPointCollision(rotatedPoint)) continue; const distanceToTarget = rotatedPoint.distanceTo(target); if (distanceToTarget < minimalDistance) { minimalDistance = distanceToTarget; closestPoint = rotatedPoint; } } return closestPoint; } function animateNPCMovement(startPos, endPos) { const startTime = Date.now(); const animationDuration = 3000; // 3 segundos // Criar trilha de partículas const trailParticles = []; function animateMovement() { const elapsed = Date.now() - startTime; const progress = Math.min(elapsed / animationDuration, 1); // Interpolação suave com easing const easedProgress = 1 - Math.pow(1 - progress, 3); const currentPos = startPos.clone().lerp(endPos, easedProgress); npc.position.copy(currentPos); // Rotação do NPC para olhar na direção do movimento const direction = new THREE.Vector3().subVectors(endPos, startPos).normalize(); npc.lookAt(npc.position.clone().add(direction)); // Animação dos espinhos npc.children.forEach((child, index) => { if (child.geometry && child.geometry.type === 'ConeGeometry') { child.rotation.y += 0.1; child.position.y = 1.2 + Math.sin(Date.now() * 0.01 + index) * 0.1; } }); // Criar trilha de partículas if (Math.random() < 0.3) { createTrailParticle(currentPos.clone()); } if (progress < 1) { requestAnimationFrame(animateMovement); } else { animationRunning = false; updateVisualization(); } } animateMovement(); } function createTrailParticle(position) { const particleGeometry = new THREE.SphereGeometry(0.1, 6, 6); const particleMaterial = new THREE.MeshBasicMaterial({ color: 0xff3300, transparent: true, opacity: 0.8 }); const particle = new THREE.Mesh(particleGeometry, particleMaterial); particle.position.copy(position); particle.position.y += 0.5; scene.add(particle); // Animar partícula desaparecendo let particleOpacity = 0.8; function fadeParticle() { particleOpacity -= 0.02; particle.material.opacity = particleOpacity; particle.position.y += 0.02; if (particleOpacity > 0) { requestAnimationFrame(fadeParticle); } else { scene.remove(particle); particle.geometry.dispose(); particle.material.dispose(); } } fadeParticle(); } function resetScene() { npc.position.set(-18, 0, -12); npc.rotation.set(0, 0, 0); player.position.set(8, 0, 6); canSeeDirect = true; zUnsafe = false; animationRunning = false; updateVisualization(); } function togglePillars() { showPillars = !showPillars; pillarGroup.visible = showPillars; updateVisualization(); } function updateFPS() { const now = performance.now(); const delta = now - lastTime; frameCount++; if (delta >= 1000) { const fps = Math.round((frameCount * 1000) / delta); document.getElementById('fps').textContent = `FPS: ${fps}`; frameCount = 0; lastTime = now; } } function animate() { requestAnimationFrame(animate); updateFPS(); // Animações contínuas if (!animationRunning) { // Rotação suave dos personagens npc.rotation.y += 0.005; player.rotation.y += 0.003; // Animação dos cristais nos pilares pillars.forEach((pillar, index) => { if (pillar.visible && pillar.children.length > 3) { const glow = pillar.children[3]; // Efeito mágico if (glow) { glow.rotation.y += 0.02; glow.position.y = 15.5 + Math.sin(Date.now() * 0.002 + index) * 0.2; } } }); } // Animação das partículas de caminho pathLines.forEach(obj => { if (obj.userData && obj.userData.originalY !== undefined) { obj.userData.time += 0.05; obj.position.y = obj.userData.originalY + Math.sin(obj.userData.time) * 0.3; } }); // Atualizar labels para sempre olhar para a câmera [npc, player].forEach(character => { character.children.forEach(child => { if (child.material && child.material.map && child.material.map.image) { child.lookAt(camera.position); } }); }); renderer.render(scene, camera); } // Responsividade window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); </script> </body> </html>