|
|
@@ -0,0 +1,275 @@
|
|
|
+<!DOCTYPE html>
|
|
|
+<html lang="es">
|
|
|
+<head>
|
|
|
+ <meta charset="UTF-8">
|
|
|
+ <title>Player Simulator</title>
|
|
|
+
|
|
|
+ <!-- Bootstrap 5 -->
|
|
|
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
|
+
|
|
|
+ <style>
|
|
|
+ body {
|
|
|
+ background-color: #f5f6fa;
|
|
|
+ }
|
|
|
+ .log {
|
|
|
+ background: #1e1e1e;
|
|
|
+ color: #00ff9c;
|
|
|
+ height: 220px;
|
|
|
+ overflow-y: auto;
|
|
|
+ font-family: monospace;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 5px;
|
|
|
+ font-size: 0.85rem;
|
|
|
+ }
|
|
|
+
|
|
|
+ </style>
|
|
|
+</head>
|
|
|
+
|
|
|
+<body>
|
|
|
+
|
|
|
+<div class="container my-4">
|
|
|
+ <h1 class="mb-4 text-center">🌍 - Player Simulator</h1>
|
|
|
+
|
|
|
+ <div class="row g-4">
|
|
|
+
|
|
|
+ <div class="col-md-12">
|
|
|
+ <div class="alert alert-primary">
|
|
|
+ 🧑 Jugador actual: <strong id="playerId"></strong>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Acciones manuales -->
|
|
|
+ <div class="col-md-6">
|
|
|
+ <div class="card shadow-sm">
|
|
|
+ <div class="card-header bg-primary text-white">
|
|
|
+ 🎮 Acciones manuales
|
|
|
+ </div>
|
|
|
+ <div class="card-body d-flex flex-wrap gap-2">
|
|
|
+ <button class="btn btn-outline-primary" onclick="sendAction('EXPLORE')">🧭 Explorar</button>
|
|
|
+ <button class="btn btn-outline-success" onclick="sendAction('GATHER')">🌿 Recolectar</button>
|
|
|
+ <button class="btn btn-outline-danger" onclick="sendAction('FIGHT')">⚔ Combatir</button>
|
|
|
+ <button class="btn btn-outline-secondary" onclick="sendAction('REST')">💤 Descansar</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Acciones automáticas fijas -->
|
|
|
+ <div class="col-md-6">
|
|
|
+ <div class="card shadow-sm">
|
|
|
+ <div class="card-header bg-warning">
|
|
|
+ ⏱ Acciones automáticas
|
|
|
+ </div>
|
|
|
+ <div class="card-body">
|
|
|
+ <div class="row g-2">
|
|
|
+ <div class="col-md-6">
|
|
|
+ <label class="form-label">Acción</label>
|
|
|
+ <select id="autoAction" class="form-select">
|
|
|
+ <option value="EXPLORE">Explorar</option>
|
|
|
+ <option value="GATHER">Recolectar</option>
|
|
|
+ <option value="FIGHT">Combatir</option>
|
|
|
+ <option value="REST">Descansar</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div class="col-md-6">
|
|
|
+ <label class="form-label">Cada (ms)</label>
|
|
|
+ <input type="number" id="interval" class="form-control" value="2000">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="mt-3 d-flex gap-2">
|
|
|
+ <button class="btn btn-success" onclick="startAuto()">▶ Iniciar</button>
|
|
|
+ <button class="btn btn-danger" onclick="stopAuto()">⏹ Detener</button>
|
|
|
+ <span class="badge bg-secondary align-self-center" id="autoStatus">Detenido</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Simulación aleatoria -->
|
|
|
+ <div class="col-md-6">
|
|
|
+ <div class="card shadow-sm">
|
|
|
+ <div class="card-header bg-dark text-white">
|
|
|
+ 🎲 Simulación aleatoria
|
|
|
+ </div>
|
|
|
+ <div class="card-body">
|
|
|
+ <label class="form-label">Intervalo (ms)</label>
|
|
|
+ <input type="number" id="randomInterval" class="form-control mb-3" value="1000">
|
|
|
+
|
|
|
+ <div class="d-flex gap-2">
|
|
|
+ <button class="btn btn-success" onclick="startRandomSimulation()">🎲 Iniciar</button>
|
|
|
+ <button class="btn btn-danger" onclick="stopRandomSimulation()">⏹ Detener</button>
|
|
|
+ <span class="badge bg-secondary align-self-center" id="randomStatus">Detenida</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Consultas -->
|
|
|
+ <div class="col-md-6">
|
|
|
+ <div class="card shadow-sm">
|
|
|
+ <div class="card-header bg-info text-white">
|
|
|
+ 📊 Consultas
|
|
|
+ </div>
|
|
|
+ <div class="card-body d-flex flex-wrap gap-2">
|
|
|
+ <button class="btn btn-outline-info" onclick="fetchState()">🌍 Estado del mundo</button>
|
|
|
+ <button class="btn btn-outline-info" onclick="fetchStats()">👤 Stats del jugador</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Log -->
|
|
|
+ <div class="col-12">
|
|
|
+ <div class="card shadow-sm">
|
|
|
+ <div class="card-header bg-secondary text-white">
|
|
|
+ 🧾 Log de eventos
|
|
|
+ </div>
|
|
|
+ <div class="card-body">
|
|
|
+ <div class="log" id="log"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+
|
|
|
+<script>
|
|
|
+ const WORLD_API = "http://localhost:8080/world";
|
|
|
+ const ACTIONS = ["EXPLORE", "GATHER", "FIGHT", "REST"];
|
|
|
+
|
|
|
+ let autoTimer = null;
|
|
|
+ let randomTimer = null;
|
|
|
+
|
|
|
+ function generatePlayerId() {
|
|
|
+ return "player-" + crypto.randomUUID();
|
|
|
+ }
|
|
|
+
|
|
|
+ let playerId = localStorage.getItem("playerId");
|
|
|
+
|
|
|
+ if (!playerId) {
|
|
|
+ playerId = generatePlayerId();
|
|
|
+ localStorage.setItem("playerId", playerId);
|
|
|
+ }
|
|
|
+
|
|
|
+ document.addEventListener("DOMContentLoaded", () => {
|
|
|
+ document.getElementById("playerId").innerText = playerId;
|
|
|
+ log(`Jugador registrado con id: ${playerId}`);
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function log(message) {
|
|
|
+ const logDiv = document.getElementById("log");
|
|
|
+
|
|
|
+ let output;
|
|
|
+
|
|
|
+ if (typeof message === "object") {
|
|
|
+ output = JSON.stringify(message, null, 2);
|
|
|
+ }
|
|
|
+ else if (typeof message === "string") {
|
|
|
+ try {
|
|
|
+ const parsed = JSON.parse(message);
|
|
|
+ output = JSON.stringify(parsed, null, 2);
|
|
|
+ } catch {
|
|
|
+ output = message;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ output = String(message);
|
|
|
+ }
|
|
|
+
|
|
|
+ const entry = document.createElement("pre");
|
|
|
+ entry.textContent = output;
|
|
|
+
|
|
|
+ logDiv.appendChild(entry);
|
|
|
+ logDiv.scrollTop = logDiv.scrollHeight;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ function sendAction(action) {
|
|
|
+ console.log('sending action '+action);
|
|
|
+ fetch(`${WORLD_API}/action`, {
|
|
|
+ method: "POST",
|
|
|
+ headers: {
|
|
|
+ "Content-Type": "application/json"
|
|
|
+ },
|
|
|
+ body: JSON.stringify({
|
|
|
+ playerId: playerId,
|
|
|
+ action: action
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .then(res => res.json())
|
|
|
+ .then(data => {
|
|
|
+ log(`➡ Acción (${action}): `);
|
|
|
+ log(data);
|
|
|
+ })
|
|
|
+ .catch(err => { log(`❌ Error (${action}): ${err}`); });
|
|
|
+ }
|
|
|
+
|
|
|
+ function fetchState() {
|
|
|
+ fetch(`${WORLD_API}/state`)
|
|
|
+ .then(res => res.json())
|
|
|
+ .then(data => {
|
|
|
+ log(`🌍 Estado mundo: `);
|
|
|
+ log(data);
|
|
|
+ })
|
|
|
+ .catch(err => log("❌ Error al obtener el estado del mundo"));
|
|
|
+ }
|
|
|
+
|
|
|
+ function fetchStats() {
|
|
|
+ fetch(`${WORLD_API}/players/${playerId}/stats`)
|
|
|
+ .then(res => res.json())
|
|
|
+ .then(data => {
|
|
|
+ log(`📊 Stats jugador:`);
|
|
|
+ log(data);
|
|
|
+ })
|
|
|
+ .catch(err => log("❌ Error al obtener estadísticas"));
|
|
|
+ }
|
|
|
+
|
|
|
+ function startAuto() {
|
|
|
+ if (autoTimer) return;
|
|
|
+
|
|
|
+ const action = document.getElementById("autoAction").value;
|
|
|
+ const interval = parseInt(document.getElementById("interval").value);
|
|
|
+
|
|
|
+ autoTimer = setInterval(() => sendAction(action), interval);
|
|
|
+
|
|
|
+ document.getElementById("autoStatus").className = "badge bg-success";
|
|
|
+ document.getElementById("autoStatus").innerText = "En ejecución";
|
|
|
+ log(`▶ Acción automática: ${action} cada ${interval} ms`);
|
|
|
+ }
|
|
|
+
|
|
|
+ function stopAuto() {
|
|
|
+ clearInterval(autoTimer);
|
|
|
+ autoTimer = null;
|
|
|
+
|
|
|
+ document.getElementById("autoStatus").className = "badge bg-secondary";
|
|
|
+ document.getElementById("autoStatus").innerText = "Detenido";
|
|
|
+ log(`⏹ Acción automática detenida`);
|
|
|
+ }
|
|
|
+
|
|
|
+ function startRandomSimulation() {
|
|
|
+ if (randomTimer) return;
|
|
|
+
|
|
|
+ const interval = parseInt(document.getElementById("randomInterval").value);
|
|
|
+ randomTimer = setInterval(() => {
|
|
|
+ const action = ACTIONS[Math.floor(Math.random() * ACTIONS.length)];
|
|
|
+ sendAction(action);
|
|
|
+ }, interval);
|
|
|
+
|
|
|
+ document.getElementById("randomStatus").className = "badge bg-success";
|
|
|
+ document.getElementById("randomStatus").innerText = "En ejecución";
|
|
|
+ log(`🎲 Simulación aleatoria cada ${interval} ms`);
|
|
|
+ }
|
|
|
+
|
|
|
+ function stopRandomSimulation() {
|
|
|
+ clearInterval(randomTimer);
|
|
|
+ randomTimer = null;
|
|
|
+
|
|
|
+ document.getElementById("randomStatus").className = "badge bg-secondary";
|
|
|
+ document.getElementById("randomStatus").innerText = "⛔ Detenida";
|
|
|
+ log(`⏹ Simulación aleatoria detenida`);
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+</body>
|
|
|
+</html>
|