Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using FFF.Fuente.Gameplay.Entidades.Jugador;
- using FFF.Fuente.Gameplay.Reglas;
- using FFF.Fuente.Interfaz;
- using FFF.Fuente.Matematicas;
- using FFF.Fuente.Metadatos;
- using FFF.Fuente.Progreso;
- using FFF.Fuente.Progreso.Misiones.Tienda;
- using FFF.Herramientas;
- using Sirenix.OdinInspector;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Threading;
- using Unity.Jobs;
- using UnityEngine;
- using static FFF.Fuente.Matematicas.Probabilidad;
- namespace FFF.Fuente.Generacion
- {
- public class Generador : MonoBehaviour
- {
- private void Update()
- {
- if (Input.GetKeyDown(KeyCode.Q))
- {
- CargarBoss();
- }
- }
- private void CargarBoss()
- {
- // Se oculta la sala de la que se sale y se guarda la nueva sala
- EscenaActual.Mapa.gameObject.SetActive(false);
- // Se cambia la EscenaV2 en la que se encuentra al de la direccion deseada
- EscenaActual = EscenaLobbyBoss;
- EscenaActual.Cargar();
- // Se mueve el personaje al punto de spawn
- var salaActual = EscenaActual.Mapa;
- var contrario = EscenaActual.Mapa.Reemplazos.First().Direccion;
- Transform puntoTransicion;
- if (EscenaActual.Tipo != TipoReemplazo.Boss) puntoTransicion = salaActual.Transiciones.Find(x => x.Direccion == contrario).transform;
- else puntoTransicion = EscenaActual.Mapa.GetComponent<SalaBoss>().SpawnPointPersonaje.transform;
- Personaje.Instancia.transform.position = puntoTransicion.position;
- Personaje.Instancia.Controlador.ProCamera2D.transform.position = new Vector3(puntoTransicion.position.x, puntoTransicion.position.y, -100);
- // Evento Sala entrar
- Constantes.Instancia.EVENTO_SALA_ENTRANDO?.Invoke(EscenaActual, EventArgs.Empty);
- Anchura(EscenaActual).ForEach(x =>
- {
- if (x.Item2 <= 2)
- {
- if (x.Item1 != EscenaActual)
- x.Item1.Cargar(false);
- }
- });
- }
- #region Singleton
- private static Generador _instancia;
- public static Generador Instancia
- {
- get
- {
- if (_instancia == null)
- {
- _instancia = FindObjectOfType<Generador>();
- }
- return _instancia;
- }
- }
- #endregion Singleton
- [TabGroup("Generacion"), Space(15)]
- [Tooltip("Cantidad mínimo de salas a generar")]
- [MinMaxSlider(1, 50)]
- public Vector2 CantidadSalas = new Vector2(10, 25);
- [Tooltip("Semilla para generar los numeros aleatorios")]
- [TabGroup("Generacion"), ReadOnly]
- public int Semilla = -1;
- [Range(0, 5)]
- [TabGroup("Generacion")]
- [Tooltip("Cantidad de salas de bosses a generar")]
- public int CantidadSalasBosses = 1;
- [Range(0, 5)]
- [TabGroup("Generacion")]
- [Tooltip("Cantidad de salas de tiendas a generar")]
- public int CantidadSalasTiendas = 1;
- [Range(0, 5)]
- [TabGroup("Generacion")]
- [Tooltip("Cantidad de salas de recompensas a generar")]
- public int CantidadSalasRecompensas = 1;
- [Header("Parametros de generación")]
- public List<MetadataSala> PoolSalas;
- public List<MetadataSala> PoolBosses;
- public List<MetadataSala> PoolTiendas;
- public List<MetadataSala> PoolInicios;
- public List<MetadataSala> PoolLobbys;
- public List<MetadataSala> PoolRecompensas;
- [Header("Configuraciones de nivel")]
- public PoolEntidades PoolEntidades;
- public PoolItems PoolItems;
- public PoolObjetos PoolObjetos;
- [HideInInspector]
- public Escena EscenaInicio, EscenaActual, EscenaLobbyBoss, EscenaRecompensa, EscenaBoss;
- private int SalasGeneradas;
- private bool _generacionIniciada = false;
- public bool Generado { get; private set; }
- private const int TAMAÑO_MATRIZ = 50;
- private void OnValidate()
- {
- CantidadSalas.x = (int)CantidadSalas.x;
- CantidadSalas.y = (int)CantidadSalas.y;
- }
- private void Awake()
- {
- Generado = false;
- // SOLO PUEDE EXISTIR UNA INSTANCIA
- if (Generador.Instancia != this) Destroy(this);
- }
- private void GenerarPreset()
- {
- _generacionIniciada = true;
- // Eliminas las salas que no son validas
- PoolSalas.RemoveAll(x => !x.Desbloqueado());
- IniciarGeneracion();
- }
- private void IniciarGeneracion()
- {
- // Se escoge el numero de salas.
- int cantidadSalas = Constantes.Instancia.RANDOM_GENERACION.Generar((int)CantidadSalas.x, (int)CantidadSalas.y);
- SalasGeneradas = cantidadSalas + CantidadSalasBosses + CantidadSalasRecompensas + CantidadSalasTiendas;
- // Matriz de ocupacion
- Escena[,] matrizOcupacion = new Escena[TAMAÑO_MATRIZ, TAMAÑO_MATRIZ];
- // De las pools se escoge una sala de inicio aleatoria segun la probabilidad de cada sala.
- EscenaInicio = new Escena(EventoAleatorioSala(BuscarTipo(TipoReemplazo.Inicio)), Constantes.Instancia.RANDOM_GENERACION.Generar(0, int.MaxValue));
- // Se asigna a la mitad de toda la matriz la escena de inicio.
- Vector2Int posicionInicio = new Vector2Int(TAMAÑO_MATRIZ / 2, TAMAÑO_MATRIZ / 2);
- matrizOcupacion[posicionInicio.y, posicionInicio.x] = EscenaInicio;
- // Se genera la salas, se suma uno mas como offset de la funcion, que genera n - 1 salas en realidad.
- // TODO 31/01/19 - Leonardo Arteaga: Por alguna razón por lo visto siempre se generan 2 salas menos de las que se deberían.
- Generar(EscenaInicio, null, cantidadSalas + 2, matrizOcupacion, posicionInicio);
- GenerarSalaBoss();
- GenerarSalaRecompensa();
- GenerarSalaTienda();
- var recorrido = Anchura(EscenaInicio);
- recorrido.ForEach(x =>
- {
- //if (x.Item2 <= 2)
- x.Item1.Cargar(false);
- });
- Generado = true;
- }
- // Metodo para la generación
- // Pregunta si la sala colindante puede generar una entrada a la sala actual (EJ: Izquierda en sala 1 NECESITA (->PUERTA<-) Derecha en sala 2)
- Func<Escena, Direcciones, bool> Posible = (x, y) => x.DireccionesNoGeneradas().Contains(Direccion.DireccionContraria(y));
- private Vector2Int PosicionDireccion(Vector2Int posicionActual, Direcciones direccion)
- {
- switch (direccion)
- {
- case Direcciones.Arriba:
- return new Vector2Int(posicionActual.x, posicionActual.y - 1);
- case Direcciones.Derecha:
- return new Vector2Int(posicionActual.x + 1, posicionActual.y);
- case Direcciones.Abajo:
- return new Vector2Int(posicionActual.x, posicionActual.y + 1);
- case Direcciones.Izquierda:
- return new Vector2Int(posicionActual.x - 1, posicionActual.y);
- default:
- return posicionActual;
- }
- }
- private List<Direcciones> DireccionesLibres(Escena[,] MatrizOcupacion, Vector2Int pos)
- {
- List<Direcciones> res = new List<Direcciones>();
- if (MatrizOcupacion[pos.y, pos.x + 1] == null) res.Add(Direcciones.Derecha);
- if (MatrizOcupacion[pos.y, pos.x - 1] == null) res.Add(Direcciones.Izquierda);
- if (MatrizOcupacion[pos.y + 1, pos.x] == null) res.Add(Direcciones.Abajo);
- if (MatrizOcupacion[pos.y - 1, pos.x] == null) res.Add(Direcciones.Arriba);
- return res;
- }
- /// <summary>
- /// Genera el layout del nivel
- /// </summary>
- /// El metodo recursivo, devuelve la cantidad de salas que no se pudo generar.
- private void Generar(Escena EscenaGenerar, Escena EscenaAnterior, int CantidadSalas, Escena[,] MatrizOcupacion, Vector2Int Posicion)
- {
- if (CantidadSalas < 1) return;
- // De las conexiones totales que se deben generar en esta sala, verificar del máximo real que se pueden generar se generarán.
- // Direcciones disponibles para generar conexiones
- var libres = DireccionesLibres(MatrizOcupacion, Posicion);
- // Entre la cantidad de direcciones disponibles y las cantidad de salas que todavian faltan generar, cuanto debe generar?
- int minimax = Math.Min(libres.Count, CantidadSalas);
- // Cuantas conexiones se deben generar en esta sala
- if (minimax <= 0) return;
- // Escoge cuantas salas se deben generar
- int generarConexiones = Constantes.Instancia.RANDOM_GENERACION.Generar(1, minimax + 1);
- for (int i = 0; i < generarConexiones; ++i)
- {
- // Se selecciona una direccion aleatoria y se elimina de las posibilidades.
- var direccion = Constantes.Instancia.RANDOM_GENERACION.EventoAleatorio(libres);
- libres.Remove(direccion);
- // Se obtiene la posicion en la matriz de la direccion seleccionada
- var posicionDireccion = PosicionDireccion(Posicion, direccion);
- // Se genera la sala adyacente
- var salaAleatoria = EventoAleatorioSala(PoolSalas);
- Escena nuevaEscena = new Escena(salaAleatoria, Constantes.Instancia.RANDOM_GENERACION.Generar(0, int.MaxValue));
- MatrizOcupacion[posicionDireccion.y, posicionDireccion.x] = nuevaEscena;
- // Se 'conectan' las salas por sus adyacentes, el anterior conectada a la actual.
- EscenaGenerar.Adyacentes.Add(direccion, nuevaEscena);
- nuevaEscena.Adyacentes.Add(Direccion.DireccionContraria(direccion), EscenaGenerar);
- }
- // Despues de generar todas las salas, se debe ir recursivamente por sus adyacentes
- var valores = Fuente.Matematicas.Operaciones.Separacion(CantidadSalas - generarConexiones, generarConexiones, Constantes.Instancia.RANDOM_GENERACION);
- // Se quita una adyacencia a cada sala, porque una de estas es la 'conexion' entre las dos salas (la generada y la que se esta generando)
- valores.ForEach(x => x--);
- // Se visita recursivamente cada escena para proseguir la generación
- foreach (var val in EscenaGenerar.Adyacentes)
- {
- if (val.Value != EscenaAnterior)
- {
- // Se generará siempre y cuando se debe generar por lo menos una sala
- if (valores.Count > 0)
- {
- Generar(val.Value, EscenaGenerar, valores[0], MatrizOcupacion, PosicionDireccion(Posicion, val.Key));
- valores.RemoveAt(0);
- }
- }
- }
- }
- /// <summary>
- /// De las salas existentes, busca una de las más lejanas y genera alli la sala del boss
- /// </summary>
- private void GenerarSalaBoss()
- {
- // Se escoge una sala de boss aleatoria para generar
- var salaLobbyBoss = EventoAleatorioSala(BuscarTipo(TipoReemplazo.LobbyBoss));
- // Se hace el recorrido del arbol para obtener las distancias a todas las salas desde el inicio
- var recorrido = Anchura(EscenaInicio);
- recorrido.RemoveAll(x => x.Item1 == EscenaInicio);
- // Del recorrido busca cual es la mayor distancia
- var mayorDistancia = recorrido.Max(x => x.Item2);
- // Busca las salas con mayor distancia
- var salasMasLejanas = recorrido.FindAll(x => x.Item2 == mayorDistancia);
- // De las salas con mayor distancia escoge una aleatoriamente
- var escenaEscogida = Constantes.Instancia.RANDOM_GENERACION.EventoAleatorio(salasMasLejanas).Item1;
- // Cambia la sala normal por la del boss
- escenaEscogida.Metadatos = salaLobbyBoss;
- EscenaLobbyBoss = escenaEscogida;
- EscenaLobbyBoss.Tipo = TipoReemplazo.LobbyBoss;
- // Ahora sobre la sala del lobby boss, se debe generar el boss
- var salaBoss = EventoAleatorioSala(BuscarTipo(TipoReemplazo.Boss));
- EscenaBoss = new Escena(salaBoss, Constantes.Instancia.RANDOM_GENERACION.Generar(0, int.MaxValue));
- }
- /// <summary>
- /// De las salas existentes, busca la sala más lejana a la sala del boss y genera alli la sal de recompensa.
- /// </summary>
- private void GenerarSalaRecompensa()
- {
- // Se escoge una sala de boss aleatoria para generar
- var salaRecompensa = EventoAleatorioSala(BuscarTipo(TipoReemplazo.Recompensa));
- // Se hace el recorrido del arbol para obtener las distancias a todas las salas desde el inicio
- var recorrido = Anchura(EscenaLobbyBoss);
- recorrido.RemoveAll(x => x.Item1 == EscenaInicio);
- // Del recorrido busca cual es la mayor distancia
- var mayorDistancia = recorrido.Max(x => x.Item2);
- // Busca las salas con mayor distancia
- var salasMasLejanas = recorrido.FindAll(x => x.Item2 == mayorDistancia);
- // De las salas con mayor distancia escoge una aleatoriamente
- var escenaEscogida = Constantes.Instancia.RANDOM_GENERACION.EventoAleatorio(salasMasLejanas).Item1;
- // Cambia la sala normal por la del boss
- escenaEscogida.Metadatos = salaRecompensa;
- EscenaRecompensa = escenaEscogida;
- EscenaRecompensa.Tipo = TipoReemplazo.Recompensa;
- }
- private void GenerarSalaTienda()
- {
- List<MetadataSala> salasPosibles = new List<MetadataSala>();
- MetadataSala salaEscogida;
- // Se hace el recorrido del arbol para obtener las distancias a todas las salas desde el inicio
- var recorrido = Anchura(EscenaLobbyBoss);
- recorrido.RemoveAll(x => x.Item1 == EscenaInicio);
- // Busca que tienda se debe generar segun el progreso del jugador
- var nivel_2 = ControladorProgreso.Instancia.EstadoMision(Mision_Tienda_Nivel_2.CodigoMision);
- var nivel_3 = ControladorProgreso.Instancia.EstadoMision(Mision_Tienda_Nivel_3.CodigoMision);
- // Se escoge una sala de boss aleatoria para generar
- var tiendas = BuscarTipo(TipoReemplazo.Tienda);
- // La tienda es de nivel 3
- if (nivel_3) salasPosibles = tiendas.FindAll(x => x.Misiones.Exists(y => y == Mision_Tienda_Nivel_3.CodigoMision));
- // La tienda es de nivel 2
- else if (nivel_2) salasPosibles = tiendas.FindAll(x => x.Misiones.Exists(y => y == Mision_Tienda_Nivel_2.CodigoMision));
- // La tienda es de nivel 1
- else salasPosibles = tiendas.FindAll(x => x.Misiones.TrueForAll(y => y != Mision_Tienda_Nivel_2.CodigoMision && y != Mision_Tienda_Nivel_3.CodigoMision));
- salaEscogida = EventoAleatorioSala(salasPosibles);
- // Busca las salas con solo un adyacente
- var salasUnaEntrada = recorrido.FindAll(x => x.Item1.Adyacentes.Count == 1 && x.Item1.Tipo == TipoReemplazo.Normal);
- // Validación por si no es posible generar una sala de tienda
- if (salasUnaEntrada.Count == 0) return;
- // De las salas con mayor distancia escoge una aleatoriamente
- var escenaEscogida = Constantes.Instancia.RANDOM_GENERACION.EventoAleatorio(salasUnaEntrada).Item1;
- // Cambia la sala normal por la de la tienda
- escenaEscogida.Metadatos = salaEscogida;
- EscenaRecompensa = escenaEscogida;
- EscenaRecompensa.Tipo = TipoReemplazo.Tienda;
- }
- /// <summary>
- /// Calcula las distancias desde un escenario de inicio a todos los puntos
- /// </summary>
- private List<Tuple<Escena, int>> Anchura(Escena inicio)
- {
- List<Tuple<Escena, int>> resultado = new List<Tuple<Escena, int>>();
- // Cola para el recorrido
- Queue<Escena> nivel = new Queue<Escena>(), hijos = new Queue<Escena>();
- nivel.Enqueue(inicio);
- Escena tempEsc;
- // El primer elemento siempre tiene distancia 0
- resultado.Add(new Tuple<Escena, int>(inicio, 0));
- int distancia = 1;
- while (nivel.Count != 0)
- {
- tempEsc = nivel.Dequeue();
- foreach (var val in tempEsc.Adyacentes)
- {
- // Si no existe, quiere decir que no se ha calculado su distancia
- if (!resultado.Exists(x => x.Item1 == val.Value) && val.Value != inicio)
- {
- // Se añade la EscenaV2 con la distancia al punto
- resultado.Add(new Tuple<Escena, int>(val.Value, distancia));
- // Se agregan todos los adyacentes de esta sala
- hijos.Enqueue(val.Value);
- }
- }
- if (nivel.Count == 0 && hijos.Count != 0)
- {
- nivel = hijos;
- hijos = new Queue<Escena>();
- distancia++;
- }
- }
- return resultado;
- }
- /// <summary>
- /// Instancia la sala o teletransporta a la primera sala del layout generado.
- /// </summary>
- public void Iniciar(int Semilla = -1)
- {
- // Inicia la generacion
- if (!_generacionIniciada) GenerarPreset();
- StartCoroutine(FinalizarGeneracion());
- }
- private IEnumerator FinalizarGeneracion()
- {
- while (!EscenaInicio.Iniciado) yield return null;
- // La Escena actual es la de inicio
- EscenaActual = EscenaInicio;
- // Se carga la sala, y se asigna la sala creada a la EscenaV2. Si habia una sala cargada, se desactiva.
- EscenaActual.Cargar();
- while (Personaje.Instancia == null) yield return new WaitForEndOfFrame();
- Personaje.Instancia.transform.position = EscenaInicio.Mapa.GetComponent<SalaInicio>().SpawnPersonaje.position;
- while (UI.Instancia.Juego_Instancia_HUD == null) yield return null;
- var hud = UI.Instancia.Juego_Instancia_HUD;
- hud.IniciarMinimapa();
- }
- public void CambiarNivel(Direcciones direccion)
- {
- if (EscenaActual == null) return;
- if (EscenaActual.Adyacentes.ContainsKey(direccion))
- {
- // Se oculta la sala de la que se sale y se guarda la nueva sala
- EscenaActual.Mapa.gameObject.SetActive(false);
- // Se cambia la EscenaV2 en la que se encuentra al de la direccion deseada
- EscenaActual = EscenaActual.Adyacentes[direccion];
- EscenaActual.Cargar();
- // Se mueve el personaje al punto de spawn
- var salaActual = EscenaActual.Mapa;
- var contrario = Direccion.DireccionContraria(direccion);
- Transform puntoTransicion;
- if (EscenaActual.Tipo != TipoReemplazo.Boss) puntoTransicion = salaActual.Transiciones.Find(x => x.Direccion == contrario).transform;
- else puntoTransicion = EscenaActual.Mapa.GetComponent<SalaBoss>().SpawnPointPersonaje.transform;
- Personaje.Instancia.transform.position = puntoTransicion.position;
- Personaje.Instancia.Controlador.ProCamera2D.transform.position = new Vector3(puntoTransicion.position.x, puntoTransicion.position.y, -100);
- // Evento Sala entrar
- Constantes.Instancia.EVENTO_SALA_ENTRANDO?.Invoke(EscenaActual, EventArgs.Empty);
- Anchura(EscenaActual).ForEach(x =>
- {
- if (x.Item2 <= 2)
- {
- if (x.Item1 != EscenaActual)
- x.Item1.Cargar(false);
- }
- });
- }
- }
- public MetadataSala EventoAleatorioSala(List<MetadataSala> datos)
- {
- var prob = Constantes.Instancia.RANDOM_GENERACION;
- ListaEventos<MetadataSala> elementos = new ListaEventos<MetadataSala>();
- datos.ForEach(x => elementos.Add(new Evento<MetadataSala>(x, x.Probabilidad)));
- return prob.EventoAleatorio(elementos);
- }
- public List<MetadataSala> BuscarTipo(TipoReemplazo tipo)
- {
- switch (tipo)
- {
- case TipoReemplazo.Normal: return PoolSalas;
- case TipoReemplazo.Boss: return PoolBosses;
- case TipoReemplazo.Tienda: return PoolTiendas;
- case TipoReemplazo.Recompensa: return PoolRecompensas;
- case TipoReemplazo.Secreta: return new List<MetadataSala>();
- case TipoReemplazo.Inicio: return PoolInicios;
- case TipoReemplazo.LobbyBoss: return PoolLobbys;
- default: return new List<MetadataSala>();
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement