O TEMA DO FÓRUM ESTÁ EM MANUTENÇÃO. FEEDBACKS AQUI: ACESSAR

MBS - DLC

Iniciado por Brandt, 14/01/2015 às 19:02

14/01/2015 às 19:02 Última edição: 02/03/2015 às 12:44 por Masked
MBS - DLC
por Masked

[box2]Características[/box2]O script cria um módulo de DLC, que compacta os dados de um projeto para um arquivo ou carrega dados de um arquivo criado com ele e adiciona ao jogo.
O script conta também com um método de encriptação, para proteger os dados da DLC, assim a DLC só poderá ser rodada por jogos que tenham a chave de encriptação.

[box2]Instruções[/box2]No script, nos comentários em verde, queria ressaltar é necessário o script MBS - Crypt caso queira encriptar sua DLC.

[box2]Script[/box2]
Código: auto:1
#==============================================================================
# MBS - DLC
#------------------------------------------------------------------------------
# por Masked
#==============================================================================
#==============================================================================
# Instruções
#------------------------------------------------------------------------------
# Criando o projeto:
#------------------------------------------------------------------------------
#    Para criar uma DLC, primeiro crie um novo projeto no RPG Maker e faça tudo
#  normalmente, não é preciso colocar no projeto da DLC o conteúdo já existente
#  na versão original do jogo, e lembre-se de criar as coisas no database com o 
#  ID a partir do último ID já usado no jogo.
#
#    Por questão de segurança, deixe, na versão original do jogo, todos os
#  primeiros espaços do database vazios.
#
#  (OBS.: Para fazer os mapas, recomendo que os faça no projeto original e 
#  depois passe tudo que foi criado ou alterado para o projeto da DLC, assim
#  o ID do mapa não fica igual ao de outro mapa)
#
#    Lembre-se também que, exceto os mapas, todos os elementos da DLC são 
#  "somados" aos originais.
#    No caso dos mapas, todo mapa que existir na DLC vai substituir qualquer 
#  mapa que tenha o mesmo ID.
#
#------------------------------------------------------------------------------
# Exportando a DLC:
#------------------------------------------------------------------------------
#    Para exportar a DLC, coloque num script acima do main:
#      MBS::DLC.export(nome, itens_exportados, encriptar, chave)
#    
#    Nesse caso, nome deve ser o nome do arquivo da DLC a ser criada, e deve
#  vir entre aspas, e itens exportados deve ser uma Array com a lista de itens
#  que a DLC vai ter, existem algumas listas prontas que podem ser usadas, 
#  elas são: 
#
#    - MBS::DLC::ALL       = Exporta tudo que for possível
#
#    - MBS::DLC::ALL_DATA  = Exporta tudo exceto as pastas Graphics e Audio
#
#    - MBS::DLC::MAPS      = Exporta todos os mapas, dados dos mapas, 
#                            tilesets e eventos comuns
#
#    - MBS::DLC::BATTLERS  = Exporta heróis, habilidades, animações, inimigos,
#                            tropas, estados, classes, itens, armas e armaduras
#
#    - MBS::DLC::ITEMS     = Exporta itens, armas e armaduras
#
#    - MBS::DLC::USABLE    = Exporta itens e habilidades
#
#    - MBS::DLC::RESOURCES = Exporta gráficos, audio e scripts
#
#    Além dessas listas pré-definidas, podem ser feitas outras listas, usando
#   para isso os seguintes itens precedidos por : (dois pontos):
#
#    - maps
#    - tilesets
#    - c_events
#    - map_data
#    - items
#    - weapons
#    - armors
#    - skills
#    - actors
#    - classes
#    - enemies
#    - troops
#    - states
#    - animations
#    - system
#    - scripts
#    - graphics
#    - audio
#
# 'Encriptar' deve ser true, para encriptar, e false, para não encriptar, a
# encriptação exige o script MS - Crypt no projeto.
# A chave pode ser um texto ou um número.
#------------------------------------------------------------------------------
#   Ex.:
#------------------------------------------------------------------------------
#
#  # exporta tudo, encriptando com a senha 'maçã'
#  MBS::DLC.export('dlc1', MBS::DLC::ALL, true, 'maçã') 
#
#  # exporta itens, animações, estados e scripts encriptando com a senha 123345
#  MBS::DLC.save('dlc2', [:items, :animations, :states, :scripts], true, 12345)
#
#------------------------------------------------------------------------------
# Colocando a DLC no jogo
#------------------------------------------------------------------------------
#   Para colocar a DLC no jogo, é só copiar o arquivo que aparece depois de
# exportar a DLC (algumacoisa.rvdlc2) na pasta do jogo, com isso, o script
# carrega os dados do arquivo junto com o resto.
#   Caso queira importar todas as DLCs disponíveis automaticamente, coloque 
# num script acima do main:
#
# MBS::DLC.import_all(encriptar, chave)
#
# Trocando encriptar por true (caso as DLCs estejam encriptadas) ou por false 
# (caso não) e chave pela chave de encriptação usada nas DLCs (pode ser um 
# texto entre aspas ou um número)
#
# Para importar uma DLC separadamente:
# MBS::DLC.import(nome, encriptar, chave)
#
# Trocando nome pelo nome do arquivo sem a extensão e encritar e chave como no
# import_all
#==============================================================================
($imported ||= {})[:mbs_dlc] = true
#==============================================================================
# >> MBS
#------------------------------------------------------------------------------
# Este é o módulo básico dos scripts MS/MBS
#==============================================================================
module MBS
  #============================================================================
  # >> DLC
  #----------------------------------------------------------------------------
  # Este é o módulo de importação e exportação da DLC
  #============================================================================
  module DLC
    #==========================================================================
    # Configurações
    #==========================================================================
    
    # Extensão do arquivo de DLC
    EXTENSION = 'rvdlc2'
    
    # Opção padrão para encriptar ou não os arquivos de DLC
    ENCRYPT = false
    
    # Chave de encriptação padrão
    CRYPT = ''
    
    #==========================================================================
    # Fim das configurações
    #==========================================================================
    
    # Variável com os dados das DLCs
    @@data = {}
    
    #==========================================================================
    # << @@data
    #--------------------------------------------------------------------------
    # Esta é a classe singleton da variável @@data
    #==========================================================================
    class << @@data
      #------------------------------------------------------------------------
      # * Aquisição do valor em um índice
      #------------------------------------------------------------------------
      def [](index)
        if index.nil?
          return values.inject({}) do |r, v| 
            r.merge(v)
          end
        else
          return super(index)
        end
      end
    end
    
    #--------------------------------------------------------------------------
    # * Aquisição dos dados de uma DLC
    #--------------------------------------------------------------------------
    def self.data(dlc)
      @@data[dlc] || {}
    end
    
    #==========================================================================
    # ** DLCExportError
    #--------------------------------------------------------------------------
    # Esta é a classe dos erros no salvamento das DLCs
    #==========================================================================
    class DLCExportError < RuntimeError
      #------------------------------------------------------------------------
      # * Inicialização do objeto
      #------------------------------------------------------------------------
      def initialize(dlcname, parenterror)
        super("Erro ao exportar DLC '#{dlcname}' (#{parenterror.class})")
      end
    end
    
    #==========================================================================
    # ** DLCLoadError
    #--------------------------------------------------------------------------
    # Esta é a classe dos erros no carregamento das DLCs
    #==========================================================================
    class DLCLoadError < RuntimeError
      #------------------------------------------------------------------------
      # * Inicialização do objeto
      #------------------------------------------------------------------------
      def initialize(dlcname, parenterror)
        super("Erro ao carregar DLC '#{dlcname}' (#{parenterror.class})")
      end
    end
    
    #==========================================================================
    # ** DLCCryptError
    #--------------------------------------------------------------------------
    # Esta é a classe dos erros com a encriptação das DLCs
    #==========================================================================
    class DLCCryptError < RuntimeError
      #------------------------------------------------------------------------
      # * Inicialização do objeto
      #------------------------------------------------------------------------
      def initialize
        super("O script MBS - DLC requer o script MS - Crypt para " +
              "encriptar os arquivos de DLC")
      end
    end
    
    #--------------------------------------------------------------------------
    # Constantes
    #--------------------------------------------------------------------------
    
    # Todos os componentes possíveis para a DLC
    ALL = [
            :maps,      :c_events,   :actors,    :classes,  :items,   
            :weapons,   :armors,     :skills,    :enemies,  :troops,   
            :states,    :animations, :tilesets,  :system,   :graphics,  
            :audio,     :scripts,    :map_info
          ]
          
    # Todos os componentes da DLC relacionados a dados
    ALL_DATA = ALL - [:audio, :graphics]
    
    # Todos os componentes da DLC relacionados a itens
    ITEMS = [:items, :weapons, :armors]
    
    # Todos os components da DLC relacionados à batalha
    BATTLERS = [:actors, :classes, :skills,     :armors, :weapons, :enemies, 
                :troops, :states,  :animations, :items]
                
    # Todos os componentes da DLC relacionados a itens usáveis
    USABLE = [:skills, :items]
    
    # Todos os componentes da DLC relacionados aos mapas
    MAPS = [:maps, :c_events, :tilesets, :map_info]
    
    # Todos os componentes da DLC relacionados aos recursos
    RESOURCES = [:graphics, :audio, :scripts]
    
    # Tabela associativa dos símbolos dos componentes com seus respectivos
    # arquivos/pastas
    FILES = {
      maps:       'Data/Map%03d.rvdata2', 
      c_events:   'Data/CommonEvents.rvdata2',
      actors:     'Data/Actors.rvdata2',
      classes:    'Data/Classes.rvdata2',
      items:      'Data/Items.rvdata2',
      weapons:    'Data/Weapons.rvdata2',
      armors:     'Data/Armors.rvdata2',
      skills:     'Data/Skills.rvdata2',
      enemies:    'Data/Enemies.rvdata2',
      troops:     'Data/Troops.rvdata2',
      states:     'Data/States.rvdata2',
      animations: 'Data/Animations.rvdata2',
      tilesets:   'Data/Tilesets.rvdata2',
      system:     'Data/System.rvdata2',
      graphics:   'Graphics',
      audio:      'Audio',
      scripts:    'Data/Scripts.rvdata2',
      map_info:   'Data/MapInfos.rvdata2'
    }
    
    #--------------------------------------------------------------------------
    # * Exportação de uma DLC
    #--------------------------------------------------------------------------
    def self.export(name, content=ALL, encrypt=ENCRYPT, crypt=CRYPT)
      pack(name, content)
      if encrypt
        raise(MBSCryptError.new) unless $imported[:mbs_crypt]
        MBS::Crypt.encrypt_file(name + '.' + EXTENSION, crypt)
      end
    end
    
    #--------------------------------------------------------------------------
    # * Importação de uma DLC
    #--------------------------------------------------------------------------
    def self.import(name, encrypted=ENCRYPT, crypt=CRYPT)
      return unless FileTest.file?(name + '.' + EXTENSION)
      begin
        content = File.open(name + '.' + EXTENSION, 'rb') do |f| 
          c = f.read
          if encrypted
            raise(MBSCryptError.new) unless $imported[:mbs_crypt]
            c = Zlib::Inflate.inflate(MBS::Crypt.decrypt(c, crypt))
          end
          Zlib::Inflate.inflate(c)
        end
        @@data[name] = Marshal.load(content)
      rescue
        raise DLCLoadError.new(name, $!)
      end
    end
    
    #--------------------------------------------------------------------------
    # * Importação de todas as DLCs possíveis
    #--------------------------------------------------------------------------
    def self.import_all(encrypted=ENCRYPT, crypt=CRYPT)
      (Dir.entries('.') - ['.', '..']).collect do |entry|
        entry =~ /(.*)\.#{EXTENSION}/
        $1
      end.compact.each do |dlc|
        import(dlc, encrypted, crypt)
      end
    end
    
    #--------------------------------------------------------------------------
    # * Empacotamento dos arquivos da DLC
    #--------------------------------------------------------------------------
    def self.pack(name, content)
      hash = {}
      content.each do |symbol|
        case symbol
        when :maps
          hash[symbol] = []
          Dir.entries('Data').select do |e| 
            e =~ /^Map(\d+)\.rvdata2$/
          end.size.times do |i|
            hash[symbol][i] = load_data(sprintf(FILES[symbol], i + 1))
          end
        when :graphics, :audio
          hash[symbol] = {}
          entries = get_entries(FILES[symbol])
          entries.each do |filename|
            File.open(FILES[symbol] + '/' + filename, 'rb') do |file|
              index = filename.split('.')[0...-1].inject('') {|r, v| r + v}
              hash[symbol][index] = Zlib::Deflate.deflate(file.read)
            end
          end
        else
          hash[symbol] = load_data(FILES[symbol])
        end
      end
      File.open(name + '.' + EXTENSION, 'wb') do |file|
        file.write(Zlib::Deflate.deflate(Marshal.dump(hash)))
      end
    end
    private_class_method :pack
    
    #--------------------------------------------------------------------------
    # * Aquisição da lista de arquivos em uma pasta e suas subpastas
    #--------------------------------------------------------------------------
    def self.get_entries(foldername)
      return (Dir.entries(foldername) - ['.', '..']).inject([]) do |r, v|
        if FileTest.file?(foldername + '/' + v) 
          r << v 
        elsif FileTest.directory?(foldername + '/' + v)
          r += get_entries(foldername + '/' + v).collect {|f| v + '/' + f}
        end
        r
      end 
    end
    private_class_method :get_entries
    
    #--------------------------------------------------------------------------
    # * Carregamento de um arquivo da DLC
    #--------------------------------------------------------------------------
    def self.load(file, dlc=nil)
      if FILES.values.include?(file)
        @@data[dlc][FILES.keys[FILES.values.index(file)]]
      elsif file =~ /^Data\/Map(\d+)\.rvdata2$/
        @@data[dlc][:maps][$1.to_i-1]
      elsif file =~ /^(Graphics|Audio)\/(.*)/
        Zlib::Inflate.inflate(@@data[dlc][$1.downcase.to_sym][$2])
      else
        return false
      end
    end
    
    #--------------------------------------------------------------------------
    # * Carregamento de um Bitmap da DLC
    #--------------------------------------------------------------------------
    def self.load_bitmap(file, dlc=nil)
      rnd = rand(0xFFFFFFFFFFFFFFFF).to_s(16)
      File.open(rnd, 'wb') do |f|
        f.write(load(file, dlc))
      end
      begin
        b = Bitmap.new(rnd)
      ensure
        File.delete(rnd)
      end
      return b
    end
    
    #--------------------------------------------------------------------------
    # * Execução de uma BGM
    #--------------------------------------------------------------------------
    def self.play_bgm(file, dlc=nil, vol=100, pitch=100, pos=0)
      return if RPG::BGM.last.name == file
      File.open(file, 'wb') do |f|
        f.write(load(file, dlc))
      end
      Audio.bgm_play(file, vol, pitch, pos)
    end
    
    #--------------------------------------------------------------------------
    # * Execução de uma BGS
    #--------------------------------------------------------------------------
    def self.play_bgs(file, dlc=nil, vol=100, pitch=100, pos=0)
      return if RPG::BGS.last.name == file
      File.open(file, 'wb') do |f|
        f.write(load(file, dlc))
      end
      Audio.bgs_play(file, vol, pitch, pos)
    end
    
    #--------------------------------------------------------------------------
    # * Execução de uma ME
    #--------------------------------------------------------------------------
    def self.play_me(file, dlc=nil, vol=100, pitch=100)
      unless FileTest.file?(file)
        File.open(file, 'wb') do |f|
          f.write(load(file, dlc))
        end
      end
      Audio.me_play(file, vol, pitch)
    end
    
    #--------------------------------------------------------------------------
    # * Execução de uma SE
    #--------------------------------------------------------------------------
    def self.play_se(file, dlc=nil, vol=100, pitch=100)
      unless FileTest.file?(file)
        File.open(file, 'wb') do |f|
          f.write(load(file, dlc))
        end
      end
      Audio.se_play(file, vol, pitch)
    end
  end
end

#==============================================================================
# >> Audio
#------------------------------------------------------------------------------
# Este é o módulo responsável pelo áudio do jogo
#==============================================================================
module Audio
  #----------------------------------------------------------------------------
  # Alias
  #----------------------------------------------------------------------------
  class << self
    alias mbsplbgm bgm_play
    alias mbsplbgs bgs_play
    alias mbsplme  me_play
    alias mbsplse  se_play
  end
  
  #----------------------------------------------------------------------------
  # * Tocamento de uma BGM
  #----------------------------------------------------------------------------
  def self.bgm_play(*args, &block)
    bgm_play(*args, &block) rescue MBS::DLC.play_bgm(*args)
  end
  
  #----------------------------------------------------------------------------
  # * Tocamento de uma BGS
  #----------------------------------------------------------------------------
  def self.bgm_play(*args, &block)
    bgs_play(*args, &block) rescue MBS::DLC.play_bgs(*args)
  end
  
  #----------------------------------------------------------------------------
  # * Tocamento de uma ME
  #----------------------------------------------------------------------------
  def self.bgm_play(*args, &block)
    me_play(*args, &block) rescue MBS::DLC.play_me(*args)
  end
  
  #----------------------------------------------------------------------------
  # * Tocamento de uma SE
  #----------------------------------------------------------------------------
  def self.bgm_play(*args, &block)
    se_play(*args, &block) rescue MBS::DLC.play_se(*args)
  end
end

#==============================================================================
# ** Bitmap
#------------------------------------------------------------------------------
# Esta é a classe dos bitmaps
#==============================================================================
class Bitmap
  
  #----------------------------------------------------------------------------
  # Alias
  #----------------------------------------------------------------------------
  class << self
    alias mbsbmpnew new
  end
  
  #----------------------------------------------------------------------------
  # * Criação de um novo objeto Bitmap
  #----------------------------------------------------------------------------
  def self.new(*args, &block)
    mbsbmpnew(*args, &block) rescue MBS::DLC.load_bitmap(*args)
  end
end

#==============================================================================
# >> Built-in
#------------------------------------------------------------------------------
# Estes são os valores/métodos que podem ser usados de qualquer lugar
#==============================================================================
#------------------------------------------------------------------------------
# Alias
#------------------------------------------------------------------------------
alias mbslddt load_data

#------------------------------------------------------------------------------
# * Carregamento de dados
#------------------------------------------------------------------------------
def load_data(*args)
  d = (MBS::DLC.load(*args) rescue nil)
  return d if d
  return mbslddt(*args) 
end


[box2]Créditos e Agradecimentos[/box2]- A mim, por criar.
~ Masked

Eu fico com um pé atrás quanto à essa questão de DLC e de updates. Tipo, a menos
que tu já faça o projeto para receber a dlc futuramente, pois até mesmo quando tu
modifica um mapa, se o save depender dele já não funciona mais. Isso me dá bastante
receio de usar algo do tipo.
Mas também não sei bem como funciona ainda, deve funcionar tudo bem se o criador
tiver consciência do que tá fazendo, né!?

Well, de qualquer forma é uma função bem bacana. Em um Steam da vida poderia
até servir como conteúdo adicional mesmo, vendido separadamente, bem legal.

Exatamente, é algo que precisa ser usado com cautela mas é uma ferramenta poderosa. É o tipo de coisa que você realmente planeja com antecedência e faz de modo cuidadoso, definitivamente não é um script para um jogo "iniciante", hahah xD

Mas eu fiquei bem feliz com isso, me parece super legal ;w; <3

Atualização :)



O script inteiro foi reescrito! Com isso temos:

  • Melhoria (e redução) do código
  • Desengambiarração (lol) do script, adeus pasta DLC que não servia pra nada!
  • Flexibilidade na importação das DLCs, agora cada DLC pode ser importada com uma chave de encriptação diferente ou estar encriptada ou não
  • Encriptação com o MBS - Crypt, o que significa que aquele problema da demora de satanás para carregar arquivos de DLC encriptados acabou (Eeeeeeeeeeeeeeee)
  • Melhoria no sistema, alguns bugs devem ter desaparecido com isso.
  • Melhor compactação do arquivo (fiz uma comparação num projeto aqui, e antes o .rvdlc pesava 4Mb, agora pesa 970kb... xP)
~ Masked

Hey Sr. da encriptação k x). Ah legal essa parte de encriptação e tudo mais, é algo que pouca gente no maker mexe, e pelo visto está conectando os seus scripts o que deixa o trabalho em um nível bem melhor. Parabéns manolo, está se dedicando mesmo para fazer coisas diferentes :)