Registrarse

GBC | Pokecristal | Múltiples datos útiles para el desarrollo

Micael_Alighieri

Emperador Kaktiácero
Redactor/a
Miembro de honor
Buenos días, he creado este tema con el objetivo de dejar anotaciones útiles para mí y para otras personas para el desarrollo en Pokecristal, y por extensión, en Pokecrystal. Más adelante, si el tema crece y organizo las anotaciones por bloques, crearé nuevos temas para organizar los contenidos. He de comentar que Github cuenta también con una wiki para estos contenidos, pero me parece igualmente útil traer este tipo de información a nuestra propia plataforma de foros.

Estas anotaciones son compatibles con Pokecristal, versión 1.0-LTS.

Ubicación de efectos de sonido

Archivo "/constants/sfx_constants.asm"

Listado de comandos de scripting

playsound

Descripción: Sirve para reproducir un sonido de la lista de efectos de sonido (véase "Ubicación de efectos de sonido")
Estructura: playsound [NOMBRE_DEL_SONIDO]
Ejemplo: playsound SFX_STRENGTH

Recreación de comandos de scripting en ASM

playsound

Estructura:
Código:
    push de
    ld de, [NOMBRE_DEL_SONIDO]
    call PlaySFX
    pop de
Ejemplo:
Código:
    push de
    ld de, SFX_STRENGTH
    call PlaySFX
    pop de
PlayMusic

Estructura:
Código:
    ld de, [NOMBRE_MUSICA]
    call PlayMusic
Ejemplo:
Código:
    ld de, MUSIC_ROUTE_30
    call PlayMusic
Giveitem

Este comando es muy especial debido a la naturaleza de los objetos en los juegos principales de Pokémon a partir de la Segunda Generación, y por ese motivo, recrearlo no es una tarea fácil.

En este caso, cada bolsillo de la mochila incluye dos tipos de direcciones de memoria, uno que indica cuántos lotes de objetos tenemos en ese bolsillo, y otro que indica qué objeto hay en cada lote y su cantidad. A continuación, os indico cuáles son estas direcciones.

Bolsillo "Objetos"
  • wNumItems
  • wItems
Bolsillo "Objeto Clave"
  • wNumKeyItems
  • wKeyItems
Bolsillo "Balls"
  • wNumBalls
  • wBalls
La información dentro de las variables de los lotes, wItems, wKeyItems y wBalls, cuenta con la siguiente estructura, compuesta de pares de objeto y cantidad, seguido al final de un terminador de lista, "FF":

[ID_HEXADECIMAL_DEL_OBJETO][CANTIDAD_EN_HEXADECIMAL][...][TERMINADOR]

Sabiendo esto, podemos manipular la memoria del juego para cargar los objetos que queramos, teniendo siempre cuidado de no sobrepasar el límite de objetos del bolsillo. Por ejemplo, en este ejemplo caso, voy a hacer que la mochila sólo tenga un único objeto en el Bolsillo "Objetos", que dicho objeto sea una Superpoción y que esté en un lote de 32 unidades (en otras palabras, meter 32 Súperpociones).

En este caso, el valor en hexadecimal de la Súperpoción sería 11, la cantidad sería 32 (valor hexadecimal 20), y el total de lotes de objetos que tendrá la mochila será sólo 1, así que usaría la siguiente rutina ASM:

Código:
; Fijo el total de objetos del bolsillo
ld a, $1
ld [wNumItems], a
; Fijo el lote (objeto y cantidad)
ld a, $11
ld [wItems], a
ld a, $20
ld [wItems+1], a
Hay otro detalle importante, el bolsillo de las MTs y MOs funciona de forma distinta, puesto que al poder contenerlas todas, no necesita una dirección en la que guardar el número de objetos de la bolsa, tan sólo debemos indicarle la cantidad en la posición que le corresponda:

Bolsillo "MTs/MOs"
  • wTMsHMs
En este caso, por ejemplo, vamos a incluir la primera MT, 5 unidades, y la segunda MT, 6 unidades:

Código:
; Fijo el total de objetos del bolsillo
ld a, $5
ld [wTMsHMs], a
ld a, $6
ld [wTMsHMs+1], a
ld a, $ff
ld [wTMsHMs+2], a
Si bien ya existen otras rutinas dentro del juego y podemos reutilizarlas o adaptarlas, poder conocer sobre estos punteros y cómo funcionan nos permite obtener un control absoluto de toda la mochila.

PlayMonCry

Estructura:
Código:
    ld a, [NOMBRE_DEL_POKÉMON]
    call PlayMonCry
    call WaitSFX
Ejemplo:
Código:
    ld a, WOOPER
    call PlayMonCry
    call WaitSFX
Curiosidades muy útiles en ASM

Cómo cambiar el día, la hora y los minutos del Pokégear

Estructura:
Código:
    ; Ajustar hora y minutos del Pokégear
    ld a, [HORA]
    ld [wInitHourBuffer], a
    ld a, [wInitHourBuffer]
    ld [wStringBuffer2 + 1], a
    ld a, [MINUTOS]
    ld [wInitMinuteBuffer], a
    ld a, [wInitMinuteBuffer]
    ld [wStringBuffer2 + 2], a
    call InitTimeOfDay
    ; Ajustar día del Pokégear
    ld a, [DÍA]
    ld [wTempDayOfWeek], a
    ld [wStringBuffer2], a
    call InitDayOfWeek
Ejemplo:
Código:
    ; Ajustar hora y minutos del Pokégear
    ld a, 14 ; hora elegida = 2 PM
    ld [wInitHourBuffer], a
    ld a, [wInitHourBuffer]
    ld [wStringBuffer2 + 1], a
    ld a, 45 ; minutos elegidos = 45 minutos
    ld [wInitMinuteBuffer], a
    ld a, [wInitMinuteBuffer]
    ld [wStringBuffer2 + 2], a
    call InitTimeOfDay
    ; Ajustar día del Pokégear
    ld a, MONDAY ; día elegido: Lunes
    ld [wTempDayOfWeek], a
    ld [wStringBuffer2], a
    call InitDayOfWeek
Cómo hardcodear nombres de protagonistas

Muy útil cuando alguien juega y queremos fijar los detalles de su personaje, podemos utilizar la siguiente rutina, por ejemplo:

Código:
; Sets a fixed name for the protagonist
SetFixedName:
    ld a, '@'
    ld bc, NAME_LENGTH
    ld hl, wPlayerName
    call ByteFill
    ld hl, wPlayerName
    ld de, .ProtagonistName
    call CopyName2
    farcall ApplyMonOrTrainerPals
    ret

.ProtagonistName:
    dname "PROTA", NAME_LENGTH
Cómo agregar pausas

Con este par de líneas, podemos agregar pausas en la pantalla durante la ejecución de rutinas, algo muy útil y estético. En este caso, se indica el número de fotogramas que se debe esperar, y la unidad de referencia son 60 fotogramas, lo que equivale a 1 segundo.

En el siguiente ejemplo, implementamos una pausa de 2 segundos:

Código:
    ld c, 120
    call DelayFrames
Cómo marcar Pokémon como vistos o capturados

La información con los Pokémon vistos y atrapados se localiza en las direcciones etiquetadas como wPokedexSeen y wPokedexCaught, respectivamente. Cada bit define si el Pokémon está marcado como visto o capturado, dichos bits se agrupan en grupos de 8, por lo que podremos cambiar los primeros 8 Pokémon de la Pokédex por defecto. Por ejemplo, si queremos marcar los 8 primeros, debemos asignar el valor binario 0x11111111, o en hexadecimal, 0xFF, podemos usar este bloque de código, en el que alojamos la dirección en el registro hl y cargamos estos valores:

Código:
ld hl, wPokedexSeen
ld a, $ff
ld [hl], a
En cambio, si queremos continuar asignando valores, debemos avanzar el registro hasta los siguientes 8 bits y repetir:

Código:
...
inc hl
ld a, $ff
ld [hl], a
Para facilitar un poco la labor de asignación, también podemos usar un bucle. Por ejemplo, en el siguiente bloque de código, marcamos todos los Pokémon de la Pokédex como vistos:

Código:
MarcarTodosLosPokemonComoVistos:
    ld hl, wPokedexSeen
    ; Cubre a los primeros 248 Pokémon
    ; (0x1F * 0x8, since the seen Pokemon are distributed in 8-bit groups)
    ld bc, $1F
    ld a, $FF

.loop:
    ld [hl], a
    inc hl
    dec c
    ld a, a ; just for not losing a (optional)
    jr nz, .loop
    jr z, .addFinalMons

; Cubre al resto (Ho-Oh, Lugia y Celebi)
.addFinalMons
    ld a, $7
    ld [hl], a
    ret
Rutinas ASM a investigar más adelante

ResetGameTime
NamePlayer (engine/menus/intro_menu.asm)

Punteros ASM a investigar más adelante

Localizado en ram/wram.asm:

Código:
SECTION "Party", WRAMX

wPokemonData::

wPartyCount::   db
wPartySpecies:: ds PARTY_LENGTH
wPartyEnd::     db ; older code doesn't check wPartyCount

wPartyMons::
; wPartyMon1 - wPartyMon6
for n, 1, PARTY_LENGTH + 1
wPartyMon{d:n}:: party_struct wPartyMon{d:n}
endr

wPartyMonOTs::
; wPartyMon1OT - wPartyMon6OT
for n, 1, PARTY_LENGTH + 1
wPartyMon{d:n}OT:: ds NAME_LENGTH
endr

wPartyMonNicknames::
; wPartyMon1Nickname - wPartyMon6Nickname
for n, 1, PARTY_LENGTH + 1
wPartyMon{d:n}Nickname:: ds MON_NAME_LENGTH
endr
wPartyMonNicknamesEnd::

    ds 22
Listado de valores de datos de punteros en ASM

PokéGear

Lo controla el puntero wPhoneList, permite hasta un máximo de 9 números de teléfono por pantalla, pero internamente hay espacio para más. A continuación, os detallo los valores de cada contacto de teléfono:

00 -> Vacío
01 -> MAMÁ
- Hay que hacer modificaciones extra dentro de otros datos del juego, puesto que la madre pregunta según nuestro progreso
02 -> TIENDA DE BICIS
03 -> BILL
04 -> PROF. ELM
05 -> AITOR: COLEGIAL
06 -> JULIA: POKÉFAN
07 -> KEN: MARINERO
08 -> NADA
09 -> NADA
0A -> NADA
0B -> GALO: ENTRE. GUAY
0C -> TINA: ENTRE. GUAY
0D -> JOSÉ: ORNITÓLOGO
0E -> TARA: ENTRE. GUAY
0F -> CHANO: JOVEN
10 -> TOMO: CAZABICHOS
11 -> JOSERRA: PESCADOR
12 -> LISI: DOMINGUERA
13 -> LUIS: MONTAÑERO
14 -> GORKA: CAMPISTA
15 -> GINA: DOMINGUERA
16 -> RAFI: MALABARISTA
17 -> ÁNGEL1: CAZABICHOS
18 -> ARTURO: COLEGIAL
19 -> NADA
1A -> SARA: CHICA
1B -> JAVIER: COLEGIAL
1C -> JONAY: POKÉFAN
1D -> ÓSCAR: PESCADOR
1E -> TONI: POKÉMANÍACO
1F -> VERA: DOMINGUERA
20 -> VIDAL: ORNITÓLOGO
21 -> DAVID: PESCADOR
22 -> LEUCO: KARATEKA
23 -> ÁNGEL2: MONTAÑERO
24 -> ELISA: DOMINGUERA
25 -> BUENA: DJ

Técnicamente podríamos extendernos más allá de los valores de esta tabla, pero por defecto encontraremos contactos basura (algunos incluso pueden tildar el juego o forzar un reinicio en modo GB).

Otras anotaciones en ASM

ram/wram.asm

Contiene múltiples punteros a direcciones de memoria, tales como los de la Pokédex, los del Pokégear, etc.

engine/menus/intro_menu.asm

Este archivo contiene rutinas que cargan la intro, desde la portada hasta el Nuevo Juego. Hay algunos bloques de código interesantes.

Por ejemplo, este bloque de código se ejecuta durante la pantalla de Nuevo Juego y es el responsable de la carga del sprite del Profesor Oak en memoria, y posteriormente, de mostrarlo por pantalla.
Código:
    ; Carga al Profesor Oak en memoria
    ld [wCurPartySpecies], a
    ld a, POKEMON_PROF
    ld [wTrainerClass], a

    ; Prepara el sprite del Profesor Oak
    call Intro_PrepTrainerPic
    ld b, SCGB_TRAINER_OR_MON_FRONTPIC_PALS

    ; Muestra el sprite del Profesor Oak
    call GetSGBLayout
    call Intro_RotatePalettesLeftFrontpic

Este otro bloque deja un efecto de transición precioso entre una pantalla negra y una pantalla blanca durante dicha intro:
Código:
    call RotateFourPalettesLeft
    call ClearTilemap

    call RotateFourPalettesRight
    call RotateThreePalettesRight

    ; Querrás usar esta línea si no agregaste nada más abajo, o la pantalla se quedará blanca
    call Intro_RotatePalettesLeftFrontpic
Este otro pinta a un Pokémon:

Código:
    ld a, WOOPER
    ld [wCurSpecies], a
    ld [wCurPartySpecies], a
    call GetBaseData

    hlcoord 6, 4
    call PrepMonFrontpic

    xor a
    ld [wTempMonDVs], a
    ld [wTempMonDVs + 1], a

    ld b, SCGB_TRAINER_OR_MON_FRONTPIC_PALS
    call GetSGBLayout
    call Intro_WipeInFrontpic
Y este os encantará, se utiliza para pintar en pantalla el sprite del protagonista al inicio. ¿Por qué se pintó de esta manera? Pues parece que el bloque de más arriba sólo pinta entrenadores o Pokémon, y el nuevo sprite del protagonista no está marcado como entrenador:

Código:
    ld [wCurPartySpecies], a
    farcall DrawIntroPlayerPic

    ld b, SCGB_TRAINER_OR_MON_FRONTPIC_PALS
    call GetSGBLayout
    call Intro_RotatePalettesLeftFrontpic
Cómo desarrollar scripts de gatillo

En Pokecristal, los scripts de gatillo se conocen como "escenas" (scene), su activación depende de un puntero en memoria y se pueden activar y desactivar a voluntad. Además, al pisar sobre ellos, no notaremos ningún tipo de lag, ni demora dentro del juego.

Pero eso sí, debemos declarar el puntero en el archivo "ram/wram.asm" o reciclar uno en desuso, después asociarle un nombre de escena en el archivo "data/maps/scenes.asm", y finalmente, declararlo dentro del archivo de scripts (.ASM ubicado en el directorio "maps"). En sí, existen varios tipos de escenas, pero el más sencillo de utilizar es este:

Código:
    scene_const SCENE_NOMBRE_DE_TU_ESCENA
Más abajo, debemos indicar también el nombre clave de nuestra escena, y junto a él, las coordenadas dentro del mapa y el script de gatillo que activará, de esta forma:

Código:
def_coord_events
    coord_event  coordenada_X_en_decimal,  coordenada_Y_en_decimal, SCENE_NOMBRE_DE_TU_ESCENA, Mapa_NombreDeTuScript
Por otro lado, algunos mapas carecen de puntero por defecto, así que no os olvidéis de incluir el nombre del mapa y su puntero en el archivo "constants/map_constants.ASM".

Aparentemente, el proceso parece tedioso, pero debido a que el juego cuenta con muchas flags sin utilizar, podemos configurar todas las que necesitemos, y así, reutilizar el mismo registro para todas nuestras escenas. Recordad, además, que para declarar nuevas flags, debéis dirigiros al archivo "constants/event_flag.ASM" (he contado más de 200, así que tenéis hasta aburrir).
 
Última edición:
Arriba