Registrarse

[Script] Poké2048: un minijuego para tu fangame

samuko

Profesional de WaH
¿Conocéis el juego 2048? Ese en el que hay que unir números para sumarlos e ir aumentándolos. Hay muchas variantes (no solo con números), pero aún no entiendo cómo no existe (o yo no me he enterado) con Pokémon, que literalmente evolucionan.

Así que he decidido crearlo :) Os presento el Poké2048: ¡combina evoluciones de Eevee para ganar puntos! Me ha llevado un tiempo, pero creo que está entretenido. Además, lo puedes instalar en tu propio fangame de forma muy sencilla.



[IMG width="345px"] width="339px"] width="359px"]
Descarga el archivo y copia todas las carpetas EXCEPTO 'DATA/' en la carpeta de tu Fangame.
Advertencia: copiar estos archivos supondrá reemplazar los iconos de los Pokémon en la carpeta Icons/ y de la carpeta Windowskins/

Luego copia este código encima del Script 'Main':
Ruby:
# =============================================================================
# ** Poke2048 by Samuko

# You can copy this page and paste in your Pokemon Essentials scripts.
# To use it, call method 'pb2048'

# Para usarlo, llama el script 'pb2048'
# =============================================================================

# undos: nº de veces que puedes deshacer una jugada
# animations: muestra las animaciones al evolucionar
# showtext: muestra las instrucciones al iniciar el juego
def pb2048(undos=3,animations=true,showtext=true)
  Poke2048_Scene.new.start(undos,animations,showtext)
end

class Poke2048_Scene
    POKEMON = [0,:EEVEE,:VAPOREON,:JOLTEON,:FLAREON,:ESPEON,:UMBREON,:LEAFEON,:GLACEON,
        :STONEON,:FLIYEON,:RHYEON,:SYLVEON]

    COLORS = [[255]*3,[255]*3,[0,0,255],[255,255,0],[255,0,0],[255,0,0],[245,40,170],[0,0,80],[0,255,0],[0,200,250],
        [140,50,0],[170,110,255],[0,0,120],[200,200,0]]

    X0 = (Graphics.width-64*4)/2
    Y0 = (Graphics.height-64*4)/2

    def start(undos=0,animations=true,showtext=false)
        @animations = true
        @table = Table.new(4,4) # => Table 4x4
        @tables_history = Array.new(undos)
        #@pokes = [] # => Array of [id,i,j] where id=index of POKEMON, (i,j) is position of the pokemon
        @viewport = Viewport.new(0,0,Graphics.width,Graphics.height); @viewport.z = 99999
        @sprites = {}
        @sprites["bg"] = s = SpriteWrapper.new(@viewport)
        s.z = 0
        s.bitmap = BitmapWrapper.new(Graphics.width,Graphics.height)
        s.bitmap.fill_rect(0,0,Graphics.width,Graphics.height,Color.new(255,255,255))#COLORS[0])
        for i in 0...4
            for j in 0...4
                s.bitmap.fill_rect(X0 + 1+64*i,Y0 + 1+64*j,60,60,Color.new(200,200,200)) #color to tone be changed
                @sprites["poke#{i}#{j}"] = Poke2048_Pokemon.new(@viewport)
            end
        end
        for i in 0...undos_left
            @sprites["celebi#{i}"] = s = Poke2048_Pokemon.new(@viewport,false)
            s.poke = PBSpecies::CELEBI
            s.y = Graphics.height - 64
            s.x = 64*i
        end
        pbFadeInAndShow(@sprites)
        @turn = 0
        @maxspecies = 1
        ##
        self.add(1)
        self.add(1)
        Kernel.pbMessage(_INTL("¡Utiliza las flechas ^ v <- -> para combinar a los Pokémon y evolucionarlos!")) if showtext
        Kernel.pbMessage(_INTL("¡Pulsa Z para deshacer un movimiento!")) if showtext && undos > 0
        @sprites["window"] = w = Window_AdvancedTextPokemon.newWithSize("",0,0,Graphics.width,64) # if !@sprites["window"]
        w.viewport = @viewport
        w.z = 500
        refresh_window
        yield(self) if block_given?
        loop do
            break if self.turn
            @turn += 1
        end
        pbFadeOutAndHide(@sprites)
        pbDisposeSpriteHash(@sprites)
        @viewport.dispose
        return evos
    end

    # i,j => 0...4
    # force => 0=ignore if already exist, 1=find another pos if already exist, 2=delete if already exist
    def add(poke,i=rand(4),j=rand(4),force = 1)
        if @table[i,j] != 0 || force == 2
            return if force == 0
            return if tableFull?
            i,j = rand(4),rand(4)    until @table[i,j] == 0
            return add(poke,i,j,force)
        end
        @table[i,j] = poke
        scene_add(poke,i,j)
    end

    def turn
        wait
        if gameOver?
            Kernel.pbMessage(_INTL("\\me[Voltorb Flip game over]¡Ohhh! El tablero está lleno...\\wtnp[80]"))
            Kernel.pbMessage(_INTL("¡Enhorabuena! ¡Has conseguido {1} evoluciones!",evos)) if evos > 1
            return true
        end
        if Input.trigger?(Input::B)
            return true if Kernel.pbConfirmMessageSerious(_INTL("¿Quieres retirarte con {1} evoluci{2}?",
                evos,evos == 1 ? _INTL("ón") : _INTL("ones")))
        elsif Input.trigger?(Input::A)
            if undos_left == 0 || !@tables_history.last; pbPlayBuzzerSE
            else
                pbPlayCrySpecies(:CELEBI)
                @table = @tables_history.pop
                #@tables_history.push(nil).delete_at(0) # make the last one nil to forbid repeating
                refresh_boxes
                refresh_undos
                t = 10
                wait(t) do |i|
                    c = 255*(t-i-1)/t
                    self.iterate { |i,j|
                        s = @sprites["poke#{i}#{j}"]
                        s.pokemon.color.set(c,c,c,c) if s && s.visible
                    }
                end
            end
        end
        dir = Input.dir4
        if dir && [2,4,6,8] === dir
            oldtable = nil
            oldtable = @table.clone if undos_left > 0
            done = self.movement(dir)
            if done
                thenew = 1
                thenew = 2 if rand(5) == 0
                thenew = 3 if rand(50) == 0
                self.add(thenew)
                if undos_left > 0
                    @tables_history.push(oldtable).delete_at(0)
                end
            end
        end
        return false
    end

    def evos; @maxspecies-1; end
    def undos_left; @tables_history.length; end

    def movement(dir)
        @move_table = {} # [i, j] => no. of movements
        @evolve_table = {} # [i-origin, j-origin] => [i-matching,j-matching]
        @direction = dir
        self.iterate(dir) { |x,y|
            movethis(dir,x,y)
        }
        return self.scene_animation
    end

    def movethis(dir,x,y)
        this = @table[x,y]
        return if this == 0 # no poke here
        @move_table[[x,y]] = 0 if !@move_table[[x,y]]
        i = x; j = y # temporal i, j
        loop do # iterate while not in the border
            nexti,nextj,following = tableGetNext(dir,i,j)
            break if following && following < 0 # Limit of the table -> stop
            break if following != 0 && following != this # Different pokes -> stop here
            # Here, next can be the same species (move & evolve) or either nil species (just move)
            @move_table[[x,y]] += 1
            @table[i,j] = 0#nil # Leave this tile
            if following == 0
                @table[nexti,nextj] = this # Fill the next gap
            elsif following == this
                @table[nexti,nextj] += 1 # Fuse with next
                @evolve_table[[x,y]] = [nexti,nextj]
                break # stop here
            else
                # shouldn't happen
                PBDebug.forceLog("ERROR!! this=(#{i},#{j})=#{this}, following=(#{nexti},#{nextj})=#{following} -> will disappear")
            end
            i = nexti; j = nextj
        end
    end

    def tableFull?
        return @table.all? { |e| e != 0 }
    end

    def gameOver?
        return false if !tableFull?
        self.iterate do |i,j|
            list = [[i,j+1],[i+1,j],[i,j-1],[i-1,j]].map { |e| @table[*e] }.compact
            return false if list === @table[i,j]
        end
        return true
    end

    def tableGetNext(dir,i,j) #
        iplus,jplus = {2=>[0,1], 4=>[-1,0], 6=>[1,0], 8=>[0,-1]}[dir]
        i += iplus
        j += jplus
        return [i,j,@table[i,j]] if ((0...4)===i && (0...4)===j)
        return [i,j,-1]
    end

    def iterate(dir=4,&block)
        case dir
        when 4
            for i in 0...4
                for j in 0...4
                    yield(i,j)
                end
            end
        when 6
            for i in [3,2,1,0]
                for j in 0...4
                    yield(i,j)
                end
            end
        when 8
            for j in 0...4
                for i in 0...4
                    yield(i,j)
                end
            end
        when 2
            for j in [3,2,1,0]
                for i in 0...4
                    yield(i,j)
                end
            end
        end
    end

    # SCENE METHODS #

    def scene_add(poke,i,j) # add and wait
        @sprites["poke#{i}#{j}"] = Poke2048_Pokemon.new(@viewport) if !@sprites["poke#{i}#{j}"]
        s = @sprites["poke#{i}#{j}"]
        time = 10
        s.x = X0 + 64*i + 32
        s.y = Y0 + 64*j + 32
        s.zoom = 0.0
        refresh_box(i,j)
        wait(time) { |k|
            s.zoom = (k+1)*1.0/time
            s.x = X0 + 64*i + 32*(1-s.zoom_x)
            s.y = Y0 + 64*j + 32*(1-s.zoom_y)
        } if @animations
        refresh_box(i,j)
    end

    # Animate data stored in @move_table (to @direction) & @evolve_table

    def scene_animation
        # MOVEMENTS
        @move_table.delete_if { |point,moves| !moves || moves == 0 }
        return false if @move_table.empty?
        pbSEPlay("Mining cursor")
        animations = []
        time = 6
        @move_table.each { |point,moves|
            #next if !point || !moves || moves == 0
            sprite = @sprites["poke#{point[0]}#{point[1]}"]
            if !sprite && $DEBUG;    p "!!! no existe el sprite #{point}. Tablero:"; print; next; end
            xdesp = ydesp = 0
            desp = moves*64.0/time
            case @direction
            when 8; ydesp = -desp
            when 6; xdesp = desp
            when 4; xdesp = -desp
            when 2; ydesp = desp
            end
            animations.push([sprite,xdesp,ydesp])
        }
        wait(time) {
            animations.each { |e|
                e[0].x += e[1]
                e[0].y += e[2]
            }
        }
        animations.clear
        @evolve_table.compact!#.filter! { |e| @evolve_table[e] }
        if !@evolve_table.empty?
            # Here, there're two same sprites for each point, so make one invisible.
            if @animations
            evolvesprites = @evolve_table.values.map { |point| @sprites["poke#{point[0]}#{point[1]}"] }
            @evolve_table.keys.each { |point| s = @sprites["poke#{point[0]}#{point[1]}"]; s.visible = false if s }
            #oldcolors = evolvesprites.map { |e| e.pokemon.color }
            # turn white
            #wait(6) { |i|
                c = 255#*(i+1)/6
                evolvesprites.each { |s|
                    s.pokemon.color.set(c,c,c,c)
                }
            #}
            # minimize
            spritesoldposes = evolvesprites.map { |e| [e,e.x,e.y] }
            pbSEPlay("show",60,150)
            t = 6
            wait(t) { |i|
                spritesoldposes.each { |a|
                    sprite,oldx,oldy = a
                    sprite.zoom = (t-i)/t.to_f
                    sprite.x = oldx + 32*(1-sprite.zoom)
                    sprite.y = oldy + 32*(1-sprite.zoom)
                }
            }
            # update species
            @evolve_table.each_value { |point|
                sprite = @sprites["poke#{point[0]}#{point[1]}"]
                newspecies = POKEMON[@table[*point]]
                sprite.poke = newspecies
            }
            # maximize
            wait(t) { |i|
                spritesoldposes.each { |a|
                    sprite,oldx,oldy = a
                    sprite.zoom = i/t.to_f
                    sprite.x = oldx + 32*(1-sprite.zoom)
                    sprite.y = oldy + 32*(1-sprite.zoom)
                }
            }  
            # make visible
            #wait(6) { |i|
                c = 0#*(6-i)/6
                evolvesprites.each { |s|
                    s.pokemon.color.set(c,c,c,c)
                }
            #}
            end
            maxspecies = @table.to_a.max
            if maxspecies > @maxspecies
                @maxspecies = maxspecies
                pbPlayCrySpecies(POKEMON[maxspecies]) # play latest cry
                if maxspecies == 12 # 2048
                    wait(20)
                    Kernel.pbMessage(_INTL("\\me[Voltorb Flip win]¡Enhorabuena! ¡Has obtenido {1} puntos!\\wtnp[80]",2**evos))
                end
                if maxspecies == POKEMON.length-2 # almost reach the end
                    POKEMON.push(rand(493)+1) rescue nil
                end
            end
            refresh_window
        end
        refresh_boxes # Refresh all
        return true
        #pbPlayCrySpecies(POKEMON[@table[*@evolve_table.keys.sample]]) # play random cry
    end

    def wait(frames=1)
        frames.times do |f|
            Graphics.update
            Input.update
            pbUpdateSpriteHash(@sprites)
            yield(f) if block_given?
        end
    end

    def refresh_box(i,j)
        @sprites["poke#{i}#{j}"] = Poke2048_Pokemon.new(@viewport) if !@sprites["poke#{i}#{j}"]
        s = @sprites["poke#{i}#{j}"]
        data = @table[i,j]
        s.x = X0 + 64*i
        s.y = Y0 + 64*j
        s.zoom = 1.0
        s.poke = POKEMON[data] # auto visibilize
        s.pokemon.color.set(0,0,0,0)
    end

    def refresh_boxes
        (0...4).each { |i| (0...4).each { |j| refresh_box(i,j) } }
    end

    def refresh_window
        win = @sprites["window"]
        text = _INTL("Evoluciones: {1}",evos)
        if evos > 0
            text << "    -    "
            text << _INTL("Puntuación: {1}",(2**evos).floor)
        end
        win.text = text
    end

    def refresh_undos
        for i in 0..@tables_history.length
            s = @sprites["celebi#{i}"]
            next if !s
            s.visible = i < undos_left
        end
    end

    def print
        a = []
        for j in 0...4
            ret = ""
            row = []
            for i in 0...4
                t = @table[i,j]
                row.push(t && t!=0 ? (2**(t-1)).to_s : "*")
            end
            ret << row.join("     ")
            #ret << "\n"
            a.push(ret)
        end
        p *a
    end
end

class Poke2048_Pokemon < IconSprite
    attr_reader    :pokemon
    def initialize(viewport=nil,box=true)
        super(viewport)
        setBitmap("Graphics/Pictures/poke2048") if box
        self.z = 100
        @pokemon = PokemonSpeciesIconSprite.new(0,viewport)
        #self.poke = poke
        @pokemon.z = 101
        self.visible = @pokemon.visible = false
        @animation = nil
        @animframe = 0
    end

    def poke=(species)
        if species && species != 0 && (species.is_a?(Integer) || hasConst?(PBSpecies,species))
            species = getID(PBSpecies,species)
            @pokemon.species = species
            self.visible = true
        else
            self.visible = false
        end
    end

    def visible=(v)
        super
        @pokemon.visible = !!v
    end

    def x=(v)
        super
        @pokemon.x = v
    end

    def y=(v)
        super
        @pokemon.y = v
    end

    def zoom=(v)
        self.zoom_x = v
        self.zoom_y = v
        @pokemon.zoom_x = v
        @pokemon.zoom_y = v
    end

    def zoom
        return zoom_x
    end

    def color=(c)
        super
        @pokemon.color = c.clone
    end

    def update
        if @animation
            frames = @animation[:frames]
            sprites.each { |s| s.x += @animation[:x]/frames } if @animation[:x]
            sprites.each { |s| s.x += @animation[:y]/frames } if @animation[:y]
            @animframe += 1
            if @animframe >= frames
                @animframe = 0
                @animation = nil
            end
        end
        super
        @pokemon.update
    end

    def sprites
        return [self,@pokemon]
    end
end

class Table
    def to_a
        ret = []
        for i in 0...xsize
            if ysize && ysize > 1
                for j in 0...ysize
                    if zsize && zsize > 1
                        for k in 0...zsize
                            ret.push(self[i,j,k])
                        end
                    else
                        ret.push(self[i,j])
                    end
                end
            else
                ret.push(self[i])
            end
        end
        return ret
    end

    def all?(&block)
        to_a.all?(&block)
    end

    def any?(&block)
        to_a.any?(&block)
    end

    # Returns nil if indexes are not between 0 and x,y,z sizes
    def atStrict(x,y=nil,z=nil)
        return nil if [x,y,z].compact.any? { |e| e<0 }
        return self[x,y,z] if y && z
        return self[x,y] if y
        return self[x]
    end
end

class Array
    def any?(&block)
        return map(&block).include?(true)
    end unless method_defined?(:any?)

    def all?(&block)
        return !map(&block).include?(false)
    end unless method_defined?(:all?)

    def filter!(&block)
        self.delete_if { |it| next !yield(it) }
        self
    end unless method_defined?(:filter!)

    def filter(&block)
        ret = dup
        ret.filter!(&block)
        return ret
    end unless method_defined?(:filter)
    alias intersect filter

    def compactSet!
        return self if length <= 1
        indexes = []
        for i in 1...length
            indexes.push(i) if self[0...i].include?(self[i])
        end
        counter = 0
        for i in 0...indexes.length
            self.delete_at(indexes[i]-counter)
            counter += 1
        end
        return self
    end

    def compactSet
        return dup.compactSet!
    end

    def find(&block)
        for i in self
            if yield(i)
                return i
            end
        end
        return nil
    end unless method_defined?(:find)

    def subarray?(ary)
        return ary.all? { |o| self.include?(o) }
    end

    # If param is a Array, return wether it's a subarray of self
    # else return wether param is in Array
    def ===(param)
        if param.is_a?(Array)
            return self.subarray?(param)
        else
            return self.include?(param)
        end
    end

    def count(item)
        return filter { |it| item == it }.length
    end

    alias_method "cartesian_prod", "*"
    def *(param)
        if param.is_a?(Array) # => Array of pairs
            ret = []
            for i in self
                for j in param
                    ret.push([i,j])
                end
            end
            return ret
        else
            return cartesian_prod(param)
        end
    end

    def **(int)
        return [] if int < 0
        return [nil] if int == 0
        ret = dup
        (int-1).times do |i|
            ret*=ret
        end
        return ret
    end

    def to_h
        ret = nil
        if length == 2
            ret = {}
            for i in 0...self[0].length
                key = self[0][i]
                value = self[1][i]
                ret[key] = value
            end
        else
            ret = {}
            for i in self
                ret[i[0]] = i[1]
            end
        end if all? { |a| a.is_a?(Array) }
        return ret
    end #unless method_defined?    :to_h
end

class Hash
    def compact!
        self.delete_if { |k,value|
            value === nil
        }
    end unless method_defined?(:compact!)

    def compact
        ret = self.clone
        ret.compact!
        return ret
    end unless method_defined?(:compact)
end
Por último, para empezar a jugar puedes usar el script 'pb2048', que devuelve el número de evoluciones conseguidas (si evo son el nº de evoluciones, 2**evo devuelve la puntuación alcanzada).

Advertencia: Para "ganar" el juego se necesita llegar a 11 evoluciones, mientras que Eevee tiene 8. Por eso he añadido tres nuevas (Stoneon, Fliyeon, Rhyeon). Para que funcione, debes añadirlas a tu lista de Pokémon o cambiarlas por otros Pokémon.
¡Espero que os guste! ^^
 
Última edición:
Arriba