#######################################################################
# Sistema de gestión de logros y visualización en un interfaz de grid #
# Script original : Polectron y Caeles #
# Editado por : Bezier #
#######################################################################
# Define tus LOGROS en el apartado correspondiente de este script #
# Puedes configurar el interfaz cambiando las CONSTANTES #
# #
# Para mostrar el interfaz de logros hay que llamar al script: #
# Logros_Scene.new #
# #
# Para consultar o modificar el estado de un logro: #
# Devuelve el estado de un logro #
# getLogro(indice) #
# Cambia el estado de un logro #
# setLogro(indice, nuevoEstado) #
# Los valores de los estados de logro está definidos en este script #
#######################################################################
######## CONSTANTES ########
FONDO = "fondo" # Nombre de la imagen que contiene el fondo. Si no se usa imagen escribir nil (sin comillas)
NOMBREOCULTO = "????" # Nombre para logros ocultos
DESCOCULTO = "Logro no disponible." # Descripción para logros ocultos
OCULTO = "hidden" # Imagen para logros ocultos
ICONOSPORFILA = 5 # Número de iconos por cada fila
NUMFILAS = 3 # Número de filas
ICONO_W = 74 # Ancho del icono de logro
ICONO_H = 74 # Alto del icono de logro
ESPACIADOX = 10 # Espacio entre iconos en horizontal
ESPACIADOY = 8 # Espacio entre iconos en vertical
MARGENX = 20 # Margen con los bordes en horizontal
MARGENY = 20 # Margen con los bordes en vertical
SLIDERRANGO = [73,183] # Rango de movimiento del slider
COLORBASE = MessageConfig::DARKTEXTBASE # Color base del texto
COLORSOMBRA = MessageConfig::DARKTEXTSHADOW # Color para la sombra del texto
COLORCOMPLETADO = Color.new(100, 250, 75) # Color para el nombre de logro completado
######## LOGROS ########
# Estados de los logros (no editar si no se modifica el script)
LOGRO_OCULTO = 1 # El logro no está disponible
LOGRO_ACTIVO = 2 # El logro no se ha completado
LOGRO_COMPLETADO = 3 # El logro se ha completado
# Definición de los logros dispobibles en el juego.
# Formato de cada logro
# ["Nombre", "Descripción", Estado_inicial (Opcional)]
# Estado_inicial = Estado inicial del logro. Si no se indica, usa el valor de LOGRO_ACTIVO por defecto.
# La imágen del logro se compondrá con el índice del logro. El primer logro, con índice 0, cargará la imágen 0.png, si no está oculto
LOGROS = [
["Logro1", "Descripción corta de logro 1"],
["Logro2", "Descripción corta de logro 2"],
["Logro3", "Descripción ultra larga de logro 3 para comprobar si cuántas líneas se pintan en la caja de texto y testear si se hace bien el scroll al pulsar la tecla A."],
["Logro4", "Descripción corta de logro 4"],
["Logro5", "Descripción corta de logro 5", LOGRO_OCULTO],
["Logro6", "Descripción corta de logro 6", LOGRO_OCULTO],
["Logro7", "Descripción corta de logro 7", LOGRO_OCULTO],
["Logro8", "Descripción corta de logro 8"],
]
# Devuelve el estado del logro iésimo o -1 si no existe el logro
def getLogro(i)
$PokemonGlobal.loadLogros
return (i >= 0 && i < $PokemonGlobal.logros.size) ? $PokemonGlobal.logros[i] : -1
end
# Devuelve true si ha cambiado el estado del logro, false en caso contrario
def setLogro(i, status)
$PokemonGlobal.loadLogros
if (i >= 0 && i < $PokemonGlobal.logros.size)
$PokemonGlobal.logros[i] = status
return true
end
return false
end
######## PARTIDA ########
# Guarda los logros en $PokemonGlobal (solo la información vital)
class PokemonGlobalMetadata
# Los logros deberán contener los datos mínimos que variarán a lo largo del tiempo
# En este caso será un array de enteros que indique el estado de cada logro
# Los datos constantes como el nombre o la descripción no se deben guardar
# [EstadoLogro_0, EstadoLogro_1, EstadoLogro_2, ...]
attr_accessor :logros
def loadLogros
# Crea los logros que no existen y sus estados
self.logros = [] if !self.logros
for i in 0...LOGROS.size
if !self.logros[i]
self.logros[i] = LOGROS[i].size == 2 ? LOGRO_ACTIVO : LOGROS[i][2]
end
end
end
end
######## ESCENA ########
# Sprite de logro
class LogroIcon < SpriteWrapper
attr_reader :index
def initialize(viewport, index, status)
super(viewport)
case status
when LOGRO_OCULTO
self.bitmap = Bitmap.new("Graphics/Pictures/Logros/img/hidden")
when LOGRO_ACTIVO
self.bitmap = File.exists?("Graphics/Pictures/Logros/img/#{index}.png") ? Bitmap.new("Graphics/Pictures/Logros/img/#{index}") : Bitmap.new("Graphics/Pictures/Logros/img/hidden")
else LOGRO_COMPLETADO
self.bitmap = File.exists?("Graphics/Pictures/Logros/img/#{index}c.png") ? Bitmap.new("Graphics/Pictures/Logros/img/#{index}c") : Bitmap.new("Graphics/Pictures/Logros/img/hidden")
end
@index = index
end
def name
if index >= 0 && index < $PokemonGlobal.logros.size
return LOGROS[index][0]
else
return "---"
end
end
def desc
if index >= 0 && index < $PokemonGlobal.logros.size
return LOGROS[index][1]
else
return "--------"
end
end
def status
if index >= 0 && index < $PokemonGlobal.logros.size
return $PokemonGlobal.logros[index]
else
return LOGRO_OCULTO
end
end
end
# UI para mostrar el estado de los logros
class Logros_Scene
def initialize
$PokemonGlobal.loadLogros
@logros = []
@indexSel = 0 # Índice del logro seleccionado
@lastDiagonalIndex = 0 # Índice del logro anterior cuando se hace un cambio de fila hacia abajo y se selecciona en diagonal el último icono de la última fila
@logrosOffset = 0 # Desplazamiento de los logros al hacer scroll
@currentRow = 0 # Fila actual
@descOffset = 0 # Scroll para el texto de descripción
@viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
@viewport.z = 99999
@sprites = {}
# Fondo
@sprites["bg"] = Sprite.new(@viewport)
if !FONDO
@sprites["bg"].bitmap = RPG::Cache.picture("helpbg")
else
@sprites["bg"].bitmap = RPG::Cache.picture("Logros/"+FONDO)
end
# Slider de la barra de scroll
@sprites["slider"] = Sprite.new(@viewport)
@sprites["slider"].bitmap = RPG::Cache.picture("Logros/slider")
@sprites["slider"].x = 453
@sprites["slider"].y = SLIDERRANGO[0]
@sliderStep = 0 # Movimiento del slider para cada cambio de fila
# Carga los sprites de los logros y cuenta el total de filas para calcular el paso de slider
lasty = -1
totalfilas = 0
for index in 0...$PokemonGlobal.logros.size
logroi = LogroIcon.new(@viewport, index, $PokemonGlobal.logros[index])
pos = getInitialPos(index)
logroi.x = pos[0]
logroi.y = pos[1]
logroi.visible = pos[2] < NUMFILAS
@logros.push(logroi)
@sprites["logro#{index}"] = logroi
if lasty < logroi.y
lasty = logroi.y
totalfilas += 1
end
end
if totalfilas > 0
@sliderStep = (SLIDERRANGO[1] - SLIDERRANGO[0]) / (totalfilas - 1)
end
# Fondo para la caja de texto
@sprites["textbox"] = Sprite.new(@viewport)
@sprites["textbox"].bitmap = RPG::Cache.picture("Logros/textbox")
@sprites["textbox"].x = 20
@sprites["textbox"].y = 275
# Overlay donde se pintan los textos (en la versión por defecto se usa solo para el nombre del logro)
@sprites["overlay"] = BitmapSprite.new(Graphics.width, Graphics.height, @viewport)
@overlay = @sprites["overlay"].bitmap
pbSetSystemFont(@overlay)
# Overlay adaptado a la caja de texto para pintar la descripción y poder hacer scroll de texto en una descripción larga
@sprites["description"] = BitmapSprite.new(468, 62, @viewport)
@sprites["description"].x = 30
@sprites["description"].y = 308
@overlayDesc = @sprites["description"].bitmap
@maxheight = @sprites["description"].bitmap.height
pbSetSystemFont(@overlayDesc)
# Sprite con el selector de logro
@sprites["selector"] = Sprite.new(@viewport)
@sprites["selector"].bitmap = RPG::Cache.picture("/Logros/selector")
@selLTCorner = getInitialPos(0)
@sprites["selector"].x = @selLTCorner[0] - 4
@sprites["selector"].y = @selLTCorner[1] - 4
@selMinY = getFilaY(0)
@selMaxY = getFilaY(NUMFILAS - 1)
pbUpdate
end
# Devuelve la posición de la fila en base a la configuración del interfaz
def getFilaY(fila); return MARGENY + (ICONO_H + ESPACIADOY) * fila; end
# Devuelve un array con las coordenadas [x, y] de un logro según el índice y en base a la configuración
def getInitialPos(index)
index = @logros.size if index >= @logros.size
x = MARGENX + (ICONO_W + ESPACIADOX) * (index % ICONOSPORFILA)
fila = (index / ICONOSPORFILA).floor
y = getFilaY(fila)
return [x,y,fila]
end
# Mueve los logros en vertical, mostrando y ocultando los que quedan fuera del área visible
def moveLogros(y)
for l in @logros
l.y += y
l.visible = l.y >= getFilaY(0) && l.y <= getFilaY(NUMFILAS - 1)
end
end
# Función con el bucle principal
# Gestiona los inputs para mover la selección y cambiar la vista
def pbUpdate
showTexts(0)
loop do
Graphics.update
Input.update
break if Input.trigger?(Input::B)
if @logros.size > 0
# Selección hacia abajo, Input::DOWN cambia a la fila inferior y Input::R cambia a la página siguiente (salta tantas filas como indique NUMFILAS)
if Input.trigger?(Input::DOWN) || Input.trigger?(Input::R)
numsalto = 1
numsalto = NUMFILAS if Input.trigger?(Input::R)
numsalto.times do
salto = ICONOSPORFILA
# No puede mover hacia abajo. Prueba si se puede diagonal al último icono de la siguiente fila
if @indexSel + salto > @logros.size - 1 && @indexSel % ICONOSPORFILA > 0
rest = (@logros.size - 1) - @indexSel # Logros que faltan hasta el final
# Hay salto de línea en diagonal
if (@indexSel % ICONOSPORFILA) + rest > ICONOSPORFILA
while @indexSel + salto > @logros.size - 1 && salto > 0
salto -= 1
end
if @indexSel + salto <= @logros.size - 1 && salto > 0 &&
(@indexSel % ICONOSPORFILA) + 1 + salto > ICONOSPORFILA
@lastDiagonalIndex = @indexSel
end
end
end
if @indexSel + salto <= @logros.size - 1 && salto > 0
@descOffset = 0
@indexSel += salto
if @currentRow + 1 < NUMFILAS
@currentRow += 1
@sprites["selector"].y = getFilaY(@currentRow) - 4
else
moveLogros(-(ICONO_H + ESPACIADOY))
@logrosOffset -= 1
end
@sprites["slider"].y += @sliderStep
if @lastDiagonalIndex != 0
@sprites["selector"].x = getInitialPos(@indexSel % ICONOSPORFILA)[0] - 4
end
showTexts(@indexSel)
end
end
# Selección hacia arriba, Input::UP cambia a la fila superior y Input::L cambia a la página anterior (salta tantas filas como indique NUMFILAS)
elsif Input.trigger?(Input::UP) || Input.trigger?(Input::L)
numsalto = 1
numsalto = NUMFILAS if Input.trigger?(Input::L)
numsalto.times do
salto = ICONOSPORFILA
if @lastDiagonalIndex != 0
@descOffset = 0
@indexSel = @lastDiagonalIndex
@lastDiagonalIndex = 0
@currentRow -= 1
pos = getInitialPos(@indexSel % ICONOSPORFILA)
@sprites["selector"].x = getInitialPos(@indexSel % ICONOSPORFILA)[0] - 4
@sprites["selector"].y = getFilaY(@currentRow) - 4
@sprites["slider"].y -= @sliderStep
showTexts(@indexSel)
elsif @indexSel - salto >= 0
@logrosOffset = 0
@indexSel -= salto
if @currentRow - 1 >= 0
@currentRow -= 1
@sprites["selector"].y = getFilaY(@currentRow) - 4
else
moveLogros(ICONO_H + ESPACIADOY)
@logrosOffset += 1
end
@sprites["slider"].y -= @sliderStep
showTexts(@indexSel)
end
end
# Selecciona el logro de la izquierda. Si está en la primera columna, cambia a la fila siguiente y selecciona el logro más a la derecha
elsif Input.trigger?(Input::LEFT)
if @indexSel - 1 >= 0
@descOffset = 0
@indexSel -= 1
if @indexSel % ICONOSPORFILA == ICONOSPORFILA - 1
if @currentRow - 1 >= 0
@currentRow -= 1
@lastDiagonalIndex = 0
@sprites["selector"].y = getFilaY(@currentRow) - 4
else
moveLogros(ICONO_H + ESPACIADOY)
@logrosOffset += 1
end
@sprites["slider"].y -= @sliderStep
end
@sprites["selector"].x = getInitialPos(@indexSel % ICONOSPORFILA)[0] - 4
showTexts(@indexSel)
end
# Selecciona el logro de la derecha. Si está en la última columna, cambia a la fila anterior y selecciona el logro más a la izquierda
elsif Input.trigger?(Input::RIGHT)
if @indexSel + 1 <= @logros.size - 1
@descOffset = 0
@indexSel += 1
if @indexSel % ICONOSPORFILA == 0
if @currentRow + 1 < NUMFILAS
@currentRow += 1
@sprites["selector"].y = getFilaY(@currentRow) - 4
else
moveLogros(-(ICONO_H + ESPACIADOY))
@logrosOffset -= 1
end
@sprites["slider"].y += @sliderStep
end
@sprites["selector"].x = getInitialPos(@indexSel % ICONOSPORFILA)[0] - 4
showTexts(@indexSel)
end
# Con la tecla A del teclado se hace scroll al texto de la descripción si es posible
elsif Input.trigger?(Input::X)
# normtext = [word,x,y,textwidth,textheight,color]
normtext = getLineBrokenChunks(@overlayDesc, @logros[@indexSel].desc, @overlayDesc.width,nil,true)
hiddenlines=0
lasty=0
for i in 0...normtext.length
# Esta letra está fuera de los límites en una nueva línea
if @maxheight > 0 && normtext[i][2]>= @maxheight && lasty<normtext[i][2]
hiddenlines+=1
end
lasty=normtext[i][2]
end
# Aplica el desplazamiento de la descripción
hiddenlines += @descOffset
# Si hay líneas ocultas, desplaza el texto
if hiddenlines > 0
@descOffset -= 1
showTexts(@indexSel)
# Si no hay líneas ocultas y el texto está desplazado, lo devuelve al principio
elsif hiddenlines == 0 && @descOffset < 0
@descOffset = 0
showTexts(@indexSel)
end
end # if Input.trigger?(XXX)
end # if @logros.size > 0
end
Input.update
pbDisposeSpriteHash(@sprites)
@overlay.dispose
@viewport.dispose
end
# Pinta los textos del logro
def showTexts(index)
return if index < 0 || index > @logros.size
logro = @logros[index]
@overlay.clear
textpos = [
[logro.name,30,276,0,logro.status == LOGRO_COMPLETADO ? COLORCOMPLETADO : COLORBASE,COLORSOMBRA]
]
pbDrawTextPositions(@overlay,textpos)
@overlayDesc.clear
drawTextEx(@overlayDesc,0,@descOffset*32,@overlayDesc.width,2-@descOffset,logro.desc,COLORBASE,COLORSOMBRA)
end
end