Mundo Maker
¡Bienvenid@ a Mundo Maker!

¿Quieres aprender todo sobre el RPG Maker?



Regístrate y forma parte de Mundo Maker.

[XP] EMSTA - Lleva el editor de mapas al límite

Ver el tema anterior Ver el tema siguiente Ir abajo

RPG Maker XP [XP] EMSTA - Lleva el editor de mapas al límite

Mensaje por Wecoc el 2018-02-15, 18:57

Me retaron a hacer esta locura y yo a un reto así no puedo decir que no.
Este script hecho entre Eron y yo hace que puedas poner un sprite de un tile o autotile al mapa, más o menos como cuando asignas un tile como gráfico de evento, pero en este caso sin evento alguno.

No afecta a la pasabilidad de ningún modo, pero a parte de eso es como un tile completamente normal. Además en cuanto a la pasabilidad se puede solucionar fácilmente asignando un tile vacío pero no pasable.

Hasta ahí tampoco parece gran cosa, pero ¿qué ventajas tiene esto entonces?

- Más de 3 tiles por coordenada sin tener que usar un evento "vacío" para ello (es decir, menos lag)
- Más de 4 tiles por coordenada; ahora no hay límite
- Más prioridades de tile; ahora ilimado, y también acepta valores negativos
- Más de 7 autotiles por tileset/mapa, solo hay que cargarlo de otro tileset
- [Complejo] Animaciones de batalla aplicadas en esos sprites de tiles o autotiles, entre otras cosas

En el script encontraréis de modo detallado como usar este monstruo.

Código:
#==============================================================================
# ** [XP] Extended Map Sprite Tiles & Autotiles (EMSTA)
#------------------------------------------------------------------------------
# Autores: Wecoc & Eron (créditos opcionales)
# Versión: 1.0
#==============================================================================
# Este script permite insertar sprites de tiles y autotiles en el mapa.
# Se verán como tiles normales, aunque no afectan a la pasabilidad.
# Su ventaja es que se pueden definir tantos como se quiera en una coordenada,
# además pueden sacarse de otro tileset, por lo que se rompe el límite de
# autotiles por mapa. Aún así es algo complicado de usar así que se recomienda
# limitarlo para aquellos casos en los que realmente sea necesario.
#------------------------------------------------------------------------------
# Nota: Al colocar sprites de Tile o Autotile se insertan asignándoles una ID
# concreta, como si se tratara de un evento. Usándola de referencia luego se
# puede controlar diretamente ese sprite concreto, por ejemplo para borrarlo.
#------------------------------------------------------------------------------
# 1) Definir un sprite de tile en el mapa:
# $game_map.set_sprite_tile(x, y, tile_id)
# $game_map.set_sprite_tile(x, y, tileset_id, tile_id)
# $game_map.set_sprite_tile(x, y, tileset_id, tile_id, z)
#    tile_id - ID del tile a insertar (posición dentro del tileset)
#    tileset_id - ID del tileset (base de datos), si no se define es el actual
#    z - Prioridad del tile, si no se define se usa el asignado en el tileset
# Ejemplo: $game_map.set_sprite_tile(4, 10, 3, 384)
#------------------------------------------------------------------------------
# 2) Definir un sprite de autotile en el mapa:
# $game_map.set_sprite_autotile(x, y, autotile_id, tile_id)
# $game_map.set_sprite_autotile(x, y, tileset_id, autotile_id, tile_id)
# $game_map.set_sprite_autotile(x, y, tileset_id, autotile_id, tile_id, z)
#    autotile_id - ID del autotile a insertar, de 1 a 7
#    tile_id - ID del subtile actual, de 0 a 47
#    tileset_id - ID del tileset (base de datos), si no se define es el actual
#    z - Prioridad del tile, si no se define se usa el asignado en el tileset
# Ejemplo: $game_map.set_sprite_autotile(4, 10, 3, 1, 24)
#------------------------------------------------------------------------------
# 3) Cambiar un autotile del mapa por un sprite de Autotile:
# (usará tile_id y z de el autotile de referencia)
# $game_map.change_autotile_sprite(x, y, layer, autotile_id)
# $game_map.change_autotile_sprite(x, y, layer, tileset_id, autotile_id)
#    layer - Capa del mapa, de 0 a 2
#    Además se pueden añadir dos argumentos extra después del autotile_id:
#      clear_tile_id - ID del tile que se usará para borrar el autotile de ref.
#      clear - Valor true/false según si quieres que quite o no el autotile
#      Por defecto sus valores son 0, true respectivamente
#------------------------------------------------------------------------------
# 4) Cambiar un autotile del mapa por un sprite de Autotile, toda la capa:
# $game_map.change_autotile_layer(autotile, layer, autotile_id)
# $game_map.change_autotile_layer(autotile, layer, tileset_id, autotile_id)
#    autotile - ID del autotile de referencia, de 1 a 7
#    Funciona exactamente como el caso anterior (incluyendo clear) pero
#    asignado en toda una capa entera directamente, en vez de una sola coord.
#==============================================================================
# Preguntas frecuentes:
#
# - ¿Cómo obtener un sprite concreto de Tile o Autotile previamente insertado?
#
# Llamada de script para Tile:
# $game_map.sprite_tiles[ID]
# Llamada de script para Autotile:
# $game_map.sprite_autotiles[ID]
#
# - ¿Cómo obtenerlo si no sé su ID concreta pero sí su coordenada y tile_id?
# Ejemplo hecho con Tile. Cambiar X, Y e TILE_ID por los valores correctos
#
# tiles = $game_map.sprite_tiles.values
# tile = tiles.select do |sprite|
#  sprite[0] == X
#  sprite[1] == Y
#  sprite[3] == TILE_ID
# end[0]
#
# Si tile vale nil es que no había ninguno con esos parámetros
#
# - ¿Cómo obtener su ID concreta (partiendo de lo anterior)?
# id = $game_map.sprite_tiles.key(tile)
#
# - ¿Qué incompatibilidades tiene el script?
# Debería ser compatible con prácticamente todo
#==============================================================================

#==============================================================================
# ** EronAutotileCache
#==============================================================================

module EronAutotileCache
  @@hash = {}
  module_function
  def hash
    @@hash
  end
end

#==============================================================================
# ** Hash
#==============================================================================

class Hash
  def key(value)
    for k in self.keys
      return k if self[k] == value
    end
    return nil
  end
end

#==============================================================================
# ** Game_Map
#==============================================================================

class Game_Map
  #--------------------------------------------------------------------------
  attr_accessor :sprite_tiles, :sprite_autotiles
  #--------------------------------------------------------------------------
  # * Setup
  #--------------------------------------------------------------------------
  alias sprite_tile_setup setup unless $@
  def setup(*args)
    sprite_tile_setup(*args)
    @sprite_tiles = {}
    @sprite_autotiles = {}
  end
  #--------------------------------------------------------------------------
  # * [Interno] Obtener el próximo índice no usado de Tile o Autotile
  #--------------------------------------------------------------------------
  def get_next_sprite_id(hash)
    return 1 if hash.size == 0
    for i in 1..(hash.size + 1)
      return i if hash[i].nil?
    end
  end
  #--------------------------------------------------------------------------
  # * Mostrar un sprite de Tile en el mapa
  #--------------------------------------------------------------------------
  def set_sprite_tile(x, y, *args)
    case args.size
    when 1 # tile_id
      tileset_id = @map.tileset_id
      tile_id = args[0]
      z = nil
    else # tileset_id, tile_id[, z]
      tileset_id = args[0]
      tile_id = args[1]
      z = args[2]
    end
    id = get_next_sprite_id(@sprite_tiles)
    if z == nil
      tileset = $data_tilesets[tileset_id]
      z = tileset.priorities[tile_id]
    end
    return if @sprite_tiles.values.include?([x, y, tileset_id, tile_id, z])
    @sprite_tiles[id] = [x, y, tileset_id, tile_id, z]
    return true
  end
  #--------------------------------------------------------------------------
  # * Mostrar un sprite de Autotile en el mapa
  #--------------------------------------------------------------------------
  def set_sprite_autotile(x, y, *args)
    case args.size
    when 2 # autotile_id, tile_id
      tileset_id = @map.tileset_id
      autotile_id = args[0]
      tile_id = args[1]
    else # tileset_id, autotile_id, tile_id[, z]
      tileset_id = args[0]
      autotile_id = args[1]
      tile_id = args[2]
      z = args[3]
    end
    id = get_next_sprite_id(@sprite_autotiles)
    if z == nil
      tileset = $data_tilesets[tileset_id]
      z = tileset.priorities[autotile_id * 48 + tile_id]
    end
    ary = @sprite_autotiles.values
    return if ary.include?([x, y, tileset_id, autotile_id, tile_id, z])
    @sprite_autotiles[id] = [x, y, tileset_id, autotile_id, tile_id, z]
    return true
  end
  #--------------------------------------------------------------------------
  # * Cambiar un autotile normal del mapa por un sprite de Autotile
  #--------------------------------------------------------------------------
  def change_autotile_sprite(x, y, layer, *args)
    current_autotile = self.data[x, y, layer]
    return if current_autotile == 0 or current_autotile >= 384
    clear = true
    clear_tile = 0
    if args[-1].is_a?(TrueClass) or args[-1].is_a?(FalseClass)
      clear = args.pop
      clear_tile = args.pop
    end
    case args.size
    when 1 # autotile_id
      tileset_id = @map.tileset_id
      autotile_id = args[0]
    else # tileset_id, autotile_id
      tileset_id = args[0]
      autotile_id = args[1]
    end
    tile_id = current_autotile % 48
    z = $data_tilesets[tileset_id].priorities[current_autotile]
    set_sprite_autotile(x, y, tileset_id, autotile_id, tile_id, z)
    self.data[x, y, layer] = clear_tile if clear
  end
  #--------------------------------------------------------------------------
  # * Cambiar un autotile normal del mapa por un sprite de Autotile a lo largo
  # de toda una determinada capa
  #--------------------------------------------------------------------------
  def change_autotile_layer(autotile, layer, *args)
    for iy in 0...self.height
      for ix in 0...self.width
        current = (self.data[ix, iy, layer].to_f / 48).floor
        if current == autotile
          change_autotile_sprite(ix, iy, layer, *args)
        end
      end
    end
  end
end

#==============================================================================
# ** Sprite_Tile
#==============================================================================

class Sprite_Tile < RPG::Sprite
  #--------------------------------------------------------------------------
  attr_accessor :real_x, :real_y, :real_z
  #--------------------------------------------------------------------------
  # * Update
  #--------------------------------------------------------------------------
  def update
    super
    self.x = (@real_x - $game_map.display_x + 3) / 4 + 16
    self.y = (@real_y - $game_map.display_y + 3) / 4 + 32
    self.z = @real_z + (@real_y - $game_map.display_y + 3) / 4 + 32
  end
end

#==============================================================================
# ** Sprite_Autotile
#==============================================================================

class Sprite_Autotile < Sprite_Tile
  #--------------------------------------------------------------------------
  attr_accessor :complete_bitmap, :frame, :frames
  #--------------------------------------------------------------------------
  # * Update
  #--------------------------------------------------------------------------
  def update
    super
    if @frames > 1 && @current_frame != @frame
      @current_frame = @frame
      refresh_bitmap
    end
  end
  #--------------------------------------------------------------------------
  # * Refresh Bitmap
  #--------------------------------------------------------------------------
  def refresh_bitmap
    self.bitmap = Bitmap.new(32, 32)
    self.bitmap.blt(0, 0, @complete_bitmap, Rect.new(@frame * 32, 0, 32, 32))
  end
end

#==============================================================================
# ** Spriteset_Map
#==============================================================================

class Spriteset_Map
  #--------------------------------------------------------------------------
  # * Initialize
  #--------------------------------------------------------------------------
  alias sprite_tile_ini initialize unless $@
  def initialize
    @sprite_tiles = []
    @sprite_autotiles = []
    @sprite_tile_copy = {}
    @sprite_autotile_copy = {}
    sprite_tile_ini
  end
  #--------------------------------------------------------------------------
  # * Dispose
  #--------------------------------------------------------------------------
  alias sprite_tile_dis dispose unless $@
  def dispose
    sprite_tile_dis
    @sprite_tiles.each {|sprite| sprite.dispose}
    @sprite_autotiles.each {|sprite| sprite.dispose}
  end
  #--------------------------------------------------------------------------
  # * Refresh Sprite Tiles
  #--------------------------------------------------------------------------
  def refresh_sprite_tiles
    for sprite in @sprite_tiles
      sprite.dispose
    end
    @sprite_tiles.clear
    for i in $game_map.sprite_tiles.keys
      tile = $game_map.sprite_tiles[i]
      sprite = Sprite_Tile.new(@viewport1)
      sprite.real_x = tile[0] * 128
      sprite.real_y = tile[1] * 128
      sprite.ox = 16
      sprite.oy = 32
      tileset = $data_tilesets[tile[2]]
      sprite.bitmap = RPG::Cache.tile(tileset.tileset_name, tile[3], 0)
      sprite.real_z = tile[4] * 32
      @sprite_tiles.push(sprite)
    end
  end
  #--------------------------------------------------------------------------
  DECONSTRUCT_AUTOTILE_PARTS =
    [27, 28,  5, 28, 27,  6,  5,  6, 27, 28,  5, 28, 27,  6,  5,  6,
    33, 34, 33, 34, 33, 34, 33, 34, 33, 12, 33, 12, 33, 12, 33, 12,
    27, 28,  5, 28, 27,  6,  5,  6, 27, 28,  5, 28, 27,  6,  5,  6,
    11, 34, 11, 34, 11, 34, 11, 34, 11, 12, 11, 12, 11, 12, 11, 12,
    25, 26, 25,  6, 25, 26, 25,  6, 15, 16, 15, 16, 15, 16, 15, 16,
    31, 32, 31, 32, 31, 12, 31, 12, 21, 22, 21, 12, 11, 22, 11, 12,
    29, 30, 29, 30,  5, 30,  5, 30, 39, 40,  5, 40, 39,  6,  5,  6,
    35, 36, 11, 36, 35, 36, 11, 36, 45, 46, 45, 46, 45, 46, 45, 46,
    25, 30, 15, 16, 13, 14, 13, 14, 17, 18, 17, 18, 41, 42,  5, 42,
    31, 36, 45, 46, 19, 20, 19, 12, 23, 24, 11, 24, 47, 48, 47, 48,
    37, 38, 37,  6, 13, 18, 13, 14, 37, 42, 17, 18, 13, 18,  1,  2,
    43, 44, 43, 44, 19, 24, 43, 44, 43, 48, 47, 48, 43, 48,  7,  8]
  #--------------------------------------------------------------------------
  # * Refresh Sprite Autotiles
  #--------------------------------------------------------------------------
  def refresh_sprite_autotiles
    for sprite in @sprite_autotiles
      sprite.dispose
    end
    @sprite_autotiles.clear
    for i in $game_map.sprite_autotiles.keys
      autotile = $game_map.sprite_autotiles[i]
      sprite = Sprite_Autotile.new(@viewport1)
      sprite.real_x = autotile[0] * 128
      sprite.real_y = autotile[1] * 128
      sprite.ox = 16
      sprite.oy = 32
      sprite.frame = 0
      tileset = $data_tilesets[autotile[2]]
      autotile_name = tileset.autotile_names[autotile[3] - 1]
      bitmap = RPG::Cache.autotile(autotile_name)
      case bitmap.height
      when 32
        sprite.complete_bitmap = bitmap
        sprite.frames = bitmap.width / 32
      when 128
        frames = [bitmap.width / 96, 1].max
        if EronAutotileCache.hash.include?(autotile_name)
          result = EronAutotileCache.hash[autotile_name]
        else
          result = Bitmap.new(256, 192 * frames)
          deconstruct_autotile(bitmap, result, frames)
          EronAutotileCache.hash[autotile_name] = result
        end
        bitmap = Bitmap.new(32 * frames, 32)
        for f in 0...frames
          ix = (autotile[4] % 8) * 32
          iy = ((autotile[4].to_f / 8).floor) * 32
          bitmap.blt(f * 32, 0, result, Rect.new(ix, iy + 192 * f, 32, 32))
        end
        sprite.complete_bitmap = bitmap
        sprite.frames = frames
      end
      sprite.bitmap = sprite.complete_bitmap if sprite.frames == 1
      sprite.real_z = autotile[5] * 32
      @sprite_autotiles.push(sprite)
    end
  end
  #--------------------------------------------------------------------------
  # * [Interno] Deconstruct Autotile
  #--------------------------------------------------------------------------
  def deconstruct_autotile(image, result, frames)
    t = DECONSTRUCT_AUTOTILE_PARTS
    for f in 0...frames
      fx = 96 * f
      fy = 192 * f
      bitmaps = [nil]
      for iy in 0...8
        for ix in 0...6
          bitmap = Bitmap.new(16, 16)
          bitmap.blt(0, 0, image, Rect.new(fx + 16 * ix, 16 * iy, 16, 16))
          bitmaps << bitmap
        end
      end
      for iy in 0...12
        for ix in 0...16
          index = ix + iy * 16
          bitmap = bitmaps[t[index]]
          result.blt(ix * 16, fy + iy * 16, bitmap, bitmap.rect)
        end
      end
    end
    return result
  end
  #--------------------------------------------------------------------------
  # * Update
  #--------------------------------------------------------------------------
  alias sprite_tile_upd update unless $@
  def update
    if @sprite_tile_copy != $game_map.sprite_tiles
      @sprite_tile_copy = $game_map.sprite_tiles.clone
      refresh_sprite_tiles
    end
    if @sprite_autotile_copy != $game_map.sprite_autotiles
      @sprite_autotile_copy = $game_map.sprite_autotiles.clone
      refresh_sprite_autotiles
    end
    @autotile_frame = 0 if @autotile_frame.nil?
    @autotile_frame += 1
    if @autotile_frame % 16 == 0
      @sprite_autotiles.each do |sprite|
        sprite.frame = (@autotile_frame / 16) % sprite.frames
      end
    end
    for sprite in @sprite_tiles + @sprite_autotiles
      sprite.update
    end
    sprite_tile_upd
  end
end

Cualquier duda la posteáis aquí.
avatar
Wecoc
Administrador
Administrador



Créditos 12314

Gracias : 655

Volver arriba Ir abajo

RPG Maker XP Re: [XP] EMSTA - Lleva el editor de mapas al límite

Mensaje por Wecoc el 2018-11-01, 18:40

Encontré un pequeño bug: al borrar tiles EMSTA el Spriteset_Map no se actualizaba.
Era solo cuestión de un par de líneas, ya lo he cambiado.

Si tenéis alguna duda sobre este script no dudéis en postearla aquí. Aunque parezca un poco complicado, no es muy distinto a colocar tiles en mapa por script como normalmente.

Me propusieron hacer un editor in-game para tiles EMSTA similar al de Doodads, pero por ahora descarté esa idea porque no quiero que se use para rellenar el mapa completamente con estos, lo cual causaría lag, está pensado para casos puntuales de uso mediante engine o script.
avatar
Wecoc
Administrador
Administrador



Créditos 12314

Gracias : 655

Volver arriba Ir abajo

Ver el tema anterior Ver el tema siguiente Volver arriba


Permisos de este foro:
No puedes responder a temas en este foro.