Registrarse

[pokeemerald] Igualar niveles de los entrenadores a los tuyos

Diego Mertens

Dartrix Joven
¡Hola! Después de un montón de prueba y error, ¡orgullosamente puedo
decir que este sistema está listo!
Estas son las características:
  • Los niveles de los Pokémon de Entrenadores se ajustan automáticamente a valores entre -2 y +2 niveles a comparación del nivel de tu equipo, dependiendo si usan items equipados, movimientos especiales o ambos.
  • Hace un promedio del nivel del equipo, para no llevar un Poké nivel 3 en el inicio del equipo y otro nivel 20 para pasarte el juego muy fácil.
  • si el resultado da un número decimal, se redondea.
  • Si el resultado da dos o uno, se setea automáticamente en 3, para, al momento de hacer la resta, que no quede nivel -1 o 0.

¿Cómo puedo implementarlo? ¡Fácil!
Soólo vayan a "src/battle_main.c" y dentro de: "CreateNPCTrainerParty", definan esto:
Código:
	u8 fixedLVL = 0;
	{
	if (GetMonData(&gPlayerParty[5], MON_DATA_SPECIES) != SPECIES_NONE)
		fixedLVL = (GetMonData(&gPlayerParty[0], MON_DATA_LEVEL) + GetMonData(&gPlayerParty[1], MON_DATA_LEVEL) + GetMonData(&gPlayerParty[2], MON_DATA_LEVEL) + GetMonData(&gPlayerParty[3], MON_DATA_LEVEL) + GetMonData(&gPlayerParty[4], MON_DATA_LEVEL) + GetMonData(&gPlayerParty[5], MON_DATA_LEVEL)) / 6;
		if (fixedLVL <= 2)
			fixedLVL = 3;
	else if ((GetMonData(&gPlayerParty[5], MON_DATA_SPECIES) == SPECIES_NONE) && (GetMonData(&gPlayerParty[4], MON_DATA_SPECIES) != SPECIES_NONE))
			fixedLVL = (GetMonData(&gPlayerParty[0], MON_DATA_LEVEL)+GetMonData(&gPlayerParty[1], MON_DATA_LEVEL)+GetMonData(&gPlayerParty[2], MON_DATA_LEVEL)+GetMonData(&gPlayerParty[3], MON_DATA_LEVEL)+GetMonData(&gPlayerParty[4], MON_DATA_LEVEL)) / 5;
			if (fixedLVL <= 2)
				fixedLVL = 3;
		else if ((GetMonData(&gPlayerParty[4], MON_DATA_SPECIES) == SPECIES_NONE) && (GetMonData(&gPlayerParty[3], MON_DATA_SPECIES) != SPECIES_NONE))
			fixedLVL = (GetMonData(&gPlayerParty[0], MON_DATA_LEVEL)+GetMonData(&gPlayerParty[1], MON_DATA_LEVEL)+GetMonData(&gPlayerParty[2], MON_DATA_LEVEL)+GetMonData(&gPlayerParty[3], MON_DATA_LEVEL)) / 4;
				if (fixedLVL <= 2)
				fixedLVL = 3;
			else if ((GetMonData(&gPlayerParty[3], MON_DATA_SPECIES) == SPECIES_NONE) && (GetMonData(&gPlayerParty[2], MON_DATA_SPECIES) != SPECIES_NONE))
				fixedLVL = (GetMonData(&gPlayerParty[0], MON_DATA_LEVEL)+GetMonData(&gPlayerParty[1], MON_DATA_LEVEL)+GetMonData(&gPlayerParty[2], MON_DATA_LEVEL)) / 3;
					if (fixedLVL <= 2)
						fixedLVL = 3;
				else if ((GetMonData(&gPlayerParty[2], MON_DATA_SPECIES) == SPECIES_NONE) && (GetMonData(&gPlayerParty[1], MON_DATA_SPECIES) != SPECIES_NONE))
					fixedLVL = (GetMonData(&gPlayerParty[0], MON_DATA_LEVEL)+GetMonData(&gPlayerParty[1], MON_DATA_LEVEL)) / 2;
						if (fixedLVL <= 2)
							fixedLVL = 3;
					else if ((GetMonData(&gPlayerParty[1], MON_DATA_SPECIES) == SPECIES_NONE) && (GetMonData(&gPlayerParty[0], MON_DATA_SPECIES) != SPECIES_NONE))
						fixedLVL = GetMonData(&gPlayerParty[0], MON_DATA_LEVEL);
						if (fixedLVL <= 2)
							fixedLVL = 3;
	}
Lo que hace es simple, suma los niveles de todos los Pokémon de tu equipo y los divide en la cantidad que haya, si tienes 3 Pokémon, uno nivel 7, otro 8 y otro 6, los suma, da 21, y los divide en 3, dando como resultado el promedio de nivel de tu equipo: 7
Ahora, busquen: " CreateMon(&party, partyData.species, partyData.lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);" y reemplacen esa línea (cada vez que aparezca) con:
Código:
                CreateMon(&party[i], partyData[i].species, fixedLVL, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
Ahora, para nivelar un poco el juego, en el primer resultado, cambien el " fixedLVL" por un " (fixedLVL)-2"
Y en el último resultado, el " fixedLVL" cámbienlo por " (fixedLVL)+2"
Esto es para hacer que los Entrenadores que no tengan items equipados o movimientos diferentes, sean siempre 2 niveles más débiles que tu equipo (por ejemplo, un Cazabichos), que los que tengan sólo movimientos o sólo items equipados, sean iguales a tu nivel (ciertos NPC) y que los que tienen ambos sean 2 niveles superiores al promedio de tu equipo (líderes de gimnasio, alto mando) y así balancear un poco el juego para que sea difícil sin la necesidad de levelear.
 

Samu

Usuario de Oro
Es curioso, tengo algo parecido desde hace tiempo, de hecho creo que también subí una versión ASM de algo parecido a esto hace dos años.

A ver, te comento. Por lo que puedo ver, me imagino que estás aprendiendo a programar por tu cuenta pegándote con pokeemerald y que te habrá llevado tu rato de prueba y error conseguirlo. Se ve que hasta has hecho un pequeño control de errores al evitar que salgan pokémon con nivel inferior a 0 (por cierto, te has dejado el caso de que tu equipo sea entero lvl 100 y le sumes 2, te sacaría FixedLVL 102).

Lo que te voy a decir ahora, no es para fastidiar, es para que aprendas. El código es bastante pesado, si te das cuenta estás realizando las mismas acciones una y otra vez. Has escrito casi lo mismo 6 veces. En programación hay algo llamado bucles, que te permiten realizar acciones de forma repetitiva, entiendo que esto te va a resultar confuso al principio, así que te dejo un código equivalente a la primera parrafada que has dejado:
Código:
u8 fixedLVL = 0;
u8 i;

for (i = 0; i < gPlayerPartyCount; i++)
{
	fixedLVL += GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
}
	
fixedLVL /= gPlayerPartyCount;

fixedLVL = fixedLVL > 2 ? fixedLVL : 3;


Como puedes ver es bastante más corto y más claro (aunque ahora te resulte confuso, creemé, a la larga se ve más claro.
Te explico un poco por encima:
Código:
u8 fixedLVL = 0;
u8 i;

/*for, es un bucle que podría escribirse más o menos así:
i inicia en 0
Mientras i sea menor que gPlayerPartyCount -> Se ejecuta un paso del bucle
Por cada paso del bucle le sumo uno a i (i++)*/

/*Nota:gPlayerPartyCount es una variable global del juego, que contiene el número de pokémon de tu equipo, por lo que no tienes que comprobar si la especie es 'NONE'*/

for (i = 0; i < gPlayerPartyCount; i++)
{
	fixedLVL += GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
}
En este fragmento de código, el bucle se ejecutará tantas veces como pokemon tengas en tu equipo y añadirá a fixedLVL el nivel de cada uno de estos pokemon. Para ir cambiando el pokemon que se comprueba en cada vuelta del bucle se utiliza la variable 'i', cuyo valor aumenta en 1 por cada vuelta.

Al salir del bucle, tendrás en fixedLVL los niveles totales de tu equipo, por lo que solo te quedará dividirlo entre el número total de pokémon (gPlayerPartyCount).
Código:
fixedLVL /= gPlayerPartyCount;
Por último estaría el pequeño control que realizas, comprobando que fixedLVL sea mayor a 2.
Código:
fixedLVL = fixedLVL > 2 ? fixedLVL : 3;
Esto de aquí es un operador ternario y se lee:
fixexLVL = (condición) ? (si es cierta) : (si es falsa).
Es decir, es equivalente a:
Código:
if (fixedLVL < 3)
{
	fixedLVL = 3;
}

Por último, decirte que tengas cuidado con este sistema. Meterlo en un juego genérico para evitar tener que diseñar una correcta curva de dificultad, seguramente no de buen resultado.
Yo utilizo una mecánica similar a esta porque tiene mucho sentido en el tipo de proyecto que hago.
Pero bueno, el único que puede juzgar eso eres tu mismo. Esto es más una advertencia a cualquier insensato que quiera insertar esta mecánica como quien mete el repartir experiencia de 6º generación al bulto, y jode el juego entero.
 

Diego Mertens

Dartrix Joven
Respuesta: Re: Igualar niveles de los entrenadores a los tuyos

Es curioso, tengo algo parecido desde hace tiempo, de hecho creo que también subí una versión ASM de algo parecido a esto hace dos años.

A ver, te comento. Por lo que puedo ver, me imagino que estás aprendiendo a programar por tu cuenta pegándote con pokeemerald y que te habrá llevado tu rato de prueba y error conseguirlo. Se ve que hasta has hecho un pequeño control de errores al evitar que salgan pokémon con nivel inferior a 0 (por cierto, te has dejado el caso de que tu equipo sea entero lvl 100 y le sumes 2, te sacaría FixedLVL 102).

Lo que te voy a decir ahora, no es para fastidiar, es para que aprendas. El código es bastante pesado, si te das cuenta estás realizando las mismas acciones una y otra vez. Has escrito casi lo mismo 6 veces. En programación hay algo llamado bucles, que te permiten realizar acciones de forma repetitiva, entiendo que esto te va a resultar confuso al principio, así que te dejo un código equivalente a la primera parrafada que has dejado:
Código:
u8 fixedLVL = 0;
u8 i;

for (i = 0; i < gPlayerPartyCount; i++)
{
	fixedLVL += GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
}
	
fixedLVL /= gPlayerPartyCount;

fixedLVL = fixedLVL > 2 ? fixedLVL : 3;


Como puedes ver es bastante más corto y más claro (aunque ahora te resulte confuso, creemé, a la larga se ve más claro.
Te explico un poco por encima:
Código:
u8 fixedLVL = 0;
u8 i;

/*for, es un bucle que podría escribirse más o menos así:
i inicia en 0
Mientras i sea menor que gPlayerPartyCount -> Se ejecuta un paso del bucle
Por cada paso del bucle le sumo uno a i (i++)*/

/*Nota:gPlayerPartyCount es una variable global del juego, que contiene el número de pokémon de tu equipo, por lo que no tienes que comprobar si la especie es 'NONE'*/

for (i = 0; i < gPlayerPartyCount; i++)
{
	fixedLVL += GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
}
En este fragmento de código, el bucle se ejecutará tantas veces como pokemon tengas en tu equipo y añadirá a fixedLVL el nivel de cada uno de estos pokemon. Para ir cambiando el pokemon que se comprueba en cada vuelta del bucle se utiliza la variable 'i', cuyo valor aumenta en 1 por cada vuelta.

Al salir del bucle, tendrás en fixedLVL los niveles totales de tu equipo, por lo que solo te quedará dividirlo entre el número total de pokémon (gPlayerPartyCount).
Código:
fixedLVL /= gPlayerPartyCount;
Por último estaría el pequeño control que realizas, comprobando que fixedLVL sea mayor a 2.
Código:
fixedLVL = fixedLVL > 2 ? fixedLVL : 3;
Esto de aquí es un operador ternario y se lee:
fixexLVL = (condición) ? (si es cierta) : (si es falsa).
Es decir, es equivalente a:
Código:
if (fixedLVL < 3)
{
	fixedLVL = 3;
}

Por último, decirte que tengas cuidado con este sistema. Meterlo en un juego genérico para evitar tener que diseñar una correcta curva de dificultad, seguramente no de buen resultado.
Yo utilizo una mecánica similar a esta porque tiene mucho sentido en el tipo de proyecto que hago.
Pero bueno, el único que puede juzgar eso eres tu mismo. Esto es más una advertencia a cualquier insensato que quiera insertar esta mecánica como quien mete el repartir experiencia de 6º generación al bulto, y jode el juego entero.
¡Muchas gracias! Sí, en realidad estaba pensando en usar bucles, pero no sabía cómo funcionaban en este lenguaje, además, tampoco conocía la función 'gPlayerPartyCount', así que aunque supiera usarla, creo que no habría quedado bien.
Lo de diseñar una curva de dificultad correcta... Yo planeaba usar este sistema más bien para no tener que levelear demasiado, pero sí, puede romper el juego. Estuve haciendo pruebas y había olvidado los diferentes sets de movimientos, habilidades, estadísticas y objetos que usan los líderes del Gimnasio. Las Pociones y Baya Aranja de Roxxane podrán recuperar mucha vida al inicio del juego, pero si la llegáramos a enfrentar con Pokémon nivel 40, no serán de mucha utilidad. Además, las Baya Zidra e Híper Poción de Flannery Y su set de movimientos serían demasiado para que Pokémon de nivel 5 resistieran, aunque los demás sean también nivel 5/7.
AH! Los nivel 100! No había pensado en ellos, y había olvidado también probar con ellos el sistema...
Así que sí, este sistema no es muy útil que digamos, a menos que se quiera hacer un juego de Mundo Abierto, pero habría que cambiar los Equipos Pokémon y movimientos dependiendo de tus niveles y las Medallas, y eso sería demasiado... Quizás esto sea mejor dejarlo para algún PostGame o algo...
 

Samu

Usuario de Oro
Re: Respuesta: Re: Igualar niveles de los entrenadores a los tuyos

¡Muchas gracias! Sí, en realidad estaba pensando en usar bucles, pero no sabía cómo funcionaban en este lenguaje, además, tampoco conocía la función 'gPlayerPartyCount', así que aunque supiera usarla, creo que no habría quedado bien.
Lo de diseñar una curva de dificultad correcta... Yo planeaba usar este sistema más bien para no tener que levelear demasiado, pero sí, puede romper el juego. Estuve haciendo pruebas y había olvidado los diferentes sets de movimientos, habilidades, estadísticas y objetos que usan los líderes del Gimnasio. Las Pociones y Baya Aranja de Roxxane podrán recuperar mucha vida al inicio del juego, pero si la llegáramos a enfrentar con Pokémon nivel 40, no serán de mucha utilidad. Además, las Baya Zidra e Híper Poción de Flannery Y su set de movimientos serían demasiado para que Pokémon de nivel 5 resistieran, aunque los demás sean también nivel 5/7.
AH! Los nivel 100! No había pensado en ellos, y había olvidado también probar con ellos el sistema...
Así que sí, este sistema no es muy útil que digamos, a menos que se quiera hacer un juego de Mundo Abierto, pero habría que cambiar los Equipos Pokémon y movimientos dependiendo de tus niveles y las Medallas, y eso sería demasiado... Quizás esto sea mejor dejarlo para algún PostGame o algo...
Como pequeña nota, tal y como lo has planteado, una persona podría tener 5 Pokémon de nivel 5 en el equipo y uno de nivel 35, a fin de explotar el sistema.
Estaría usando un Pokémon de nivel 35 contra Pokémon de nivel (8-12).
 

Mariofan

Héroe de WaH
Aportando al comentario de Samu, decir que a esto:


Se le conoce como Piramide de la Muerte, e implica llenar el código de una complejidad tal, que luego modificarlo u arreglarlo en caso de que este malo se vuelve un infierno. Si bien en este caso un bucle es la solución, no siempre será así ya podría suceder que requieras hacer muchas comparaciones distintas, que nada se parecen entre sí. No obstante, en esos casos lo mejor es desglosar el código en funciones (Hace mucho que no uso C, pero si mal no recuerdo, si que permitía crear funciones), u otro tipo de simplifaciones que sean posibles con tal de evitar que se forme la temida piramide.

Siempre piensa, si el código se te empieza a correr mucho a la derecha, intenta replanteartelo. Más adelante, lo agradecerás.

Ya hablando del sistema en si: Considero que es el tipo de sistema ideal para un juego de pokémon de libertad total, donde puedas jugar la historia y pasar los gimnasios, en el orden que se desee. De lo contrario, podría suponer una curva de dificultad rota.

Un saludo.
 

Jason

Usuario de Platino
Justo había hecho mi propia versión del código usando un for y vi que samu tbn hizo el suyo ^^u

@Samu deberías tener cuidado, la variable fixedLVL debería ser como mínimo un u16, ya que si los niveles suman más de 255, tienes un overflow y vuelves a cero. Por ejemplo, si tuvieras todos tus pokémon al 50 (octavo gimnasio o liga) te da 44, y si lo divides entre 6 te queda que el campeón te intenta detener con pokémon de nivel 7. Además, si gPlayerPartyCount es cero por algún bug (que el jugador consiga intentar combatir contra un entrenador teniendo el equipo vacío), te va a dar error de división por cero y va a crashear, por lo que te recomiendo hacer una comprobación de eso antes. Y por último, si la especie de un pokémon es NONE, ¿estamos 100% seguros de que el nivel de este es 0 y no cualquier basura que esté perdida por la RAM? Yo no me arriesgaría.

Aportando a la discusión del balance, supongo que podrían tomar el pokémon de nivel más alto, multiplicarlo por 10, sumar los niveles de los siguientes pokémon y luego dividir por 10 + la cantidad de pokémon del equipo - 1. (Un promedio ponderado).

Eso hace que en la ponderación, el nivel del pokémon más fuerte valga mucho más. En el caso de un pokémon al 35 y luego 5 al 5, estaríamos combatiendo con pokémon al 25 más menos. En cambio si los niveles son muy parecidos no importaría tanto. Sigue siendo mejorable, claramente.

Como jugador, además, me gustaría ver cierta variedad en los niveles (y que el juego los recuerde), así que un random nunca estaría de más, un "fixedLVL = randomGauss(fixedLVL, desviación estándar)" (pero luego hay que asegurarse de que no se salga del rango [1, 100]. Implementar una distribución beta (implementación en C++) puede ayudar a eso, de hecho. Si no, un random con pesos siempre puede servir.

Código:
u8 findMaxLevelMonIndex() {
    u8 index = 0;
    u8 lvl;
    for (u8 i = 0; i < 6; i++) {
        if (GMonData(&gPlayerParty[i], MON_DATA_SPECIES) != SPECIES_NONE) {
            lvl = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
            if (lvl > GetMonData(&gPlayerParty[index], MON_DATA_LEVEL)) {
                index = i;
            }
        }
    }

    return index;
}


u16 fixedLVL = 0;
u8 indexMax = findMaxLevelMonIndex()

for (u8 i = 0; i < 6; i++) {  // itero sobre los pokémon del equipo
    if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) != SPECIES_NONE) {  // si no es NONE
        if (i == indexMax) {
            fixedLVL += 10 * GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);  // Le sumo 10 veces el nivel
        } else {
            fixedLVL += GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);  // Le sumo el nivel
        }
    }
}
if (gPlayerPartyCount > 0) {  // si el equipo no está vacío
    fixedLVL /= 10 + gPlayerPartyCount - 1;
}

if (fixedLVL < 2) {
    fixedLVL = 3;
    // si el nivel resultante es menor a 2, pongo 3
}
 
Arriba