Files
infinitefusion-e18/Data/Scripts/017_Minigames/007_Minigame_TilePuzzles.rb

609 lines
21 KiB
Ruby

#===============================================================================
# "Tile Puzzle" mini-games.
# By Maruno.
# Graphics by the__end.
#-------------------------------------------------------------------------------
# Run with: pbTilePuzzle(game,board,width,height)
# game = 1 (Ruins of Alph puzzle),
# 2 (Ruins of Alph puzzle plus tile rotations),
# 3 (Mystic Square),
# 4 (swap two adjacent tiles),
# 5 (swap two adjacent tiles plus tile rotations),
# 6 (Rubik's square),
# 7 (rotate selected tile plus adjacent tiles at once).
# board = The name/number of the graphics to be used.
# width,height = Optional, the number of tiles wide/high the puzzle is (0 for
# the default value of 4).
#===============================================================================
class TilePuzzleCursor < BitmapSprite
attr_accessor :game
attr_accessor :position
attr_accessor :arrows
attr_accessor :selected
attr_accessor :holding
def initialize(game, position, tilewidth, tileheight, boardwidth, boardheight)
@viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
@viewport.z = 99999
super(Graphics.width, Graphics.height, @viewport)
@game = game
@position = position
@tilewidth = tilewidth
@tileheight = tileheight
@boardwidth = boardwidth
@boardheight = boardheight
@arrows = []
@selected = false
@holding = false
@cursorbitmap = AnimatedBitmap.new("Graphics/UI/Tile Puzzle/cursor")
update
end
def update
self.bitmap.clear
x = (Graphics.width - (@tilewidth * @boardwidth)) / 2
if @position >= @boardwidth * @boardheight
x = ((x - (@tilewidth * (@boardwidth / 2).ceil)) / 2) - 10
if (@position % @boardwidth) >= (@boardwidth / 2).ceil
x = Graphics.width - x - (@tilewidth * @boardwidth)
end
end
x += @tilewidth * (@position % @boardwidth)
y = ((Graphics.height - (@tileheight * @boardheight)) / 2) - 32
y += @tileheight * ((@position % (@boardwidth * @boardheight)) / @boardwidth)
self.tone = Tone.new(0, (@holding ? 64 : 0), (@holding ? 64 : 0), 0)
# Cursor
if @game != 3
expand = (@holding) ? 0 : 4
4.times do |i|
self.bitmap.blt(
x + ((i % 2) * (@tilewidth - (@cursorbitmap.width / 4))) + (expand * (((i % 2) * 2) - 1)),
y + ((i / 2) * (@tileheight - (@cursorbitmap.height / 2))) + (expand * (((i / 2) * 2) - 1)),
@cursorbitmap.bitmap, Rect.new((i % 2) * @cursorbitmap.width / 4,
(i / 2) * @cursorbitmap.height / 2,
@cursorbitmap.width / 4, @cursorbitmap.height / 2)
)
end
end
# Arrows
if @selected || @game == 3
expand = (@game == 3) ? 0 : 4
xin = [(@tilewidth - (@cursorbitmap.width / 4)) / 2, -expand,
@tilewidth - (@cursorbitmap.width / 4) + expand, (@tilewidth - (@cursorbitmap.width / 4)) / 2]
yin = [@tileheight - (@cursorbitmap.height / 2) + expand, (@tileheight - (@cursorbitmap.height / 2)) / 2,
(@tileheight - (@cursorbitmap.height / 2)) / 2, -expand]
4.times do |i|
next if !@arrows[i]
self.bitmap.blt(x + xin[i], y + yin[i], @cursorbitmap.bitmap,
Rect.new((@cursorbitmap.width / 2) + ((i % 2) * (@cursorbitmap.width / 4)),
(i / 2) * (@cursorbitmap.height / 2),
@cursorbitmap.width / 4, @cursorbitmap.height / 2))
end
end
end
end
#===============================================================================
#
#===============================================================================
class TilePuzzleScene
def initialize(game, board, width, height)
@game = game
@board = board
@boardwidth = (width > 0) ? width : 4
@boardheight = (height > 0) ? height : 4
end
def update
xtop = (Graphics.width - (@tilewidth * @boardwidth)) / 2
ytop = ((Graphics.height - (@tileheight * @boardheight)) / 2) + (@tileheight / 2) - 32
(@boardwidth * @boardheight).times do |i|
pos = -1
@tiles.length.times do |j|
pos = j if @tiles[j] == i
end
@sprites["tile#{i}"].z = 0
@sprites["tile#{i}"].tone = Tone.new(0, 0, 0, 0)
if @heldtile == i
pos = @sprites["cursor"].position
@sprites["tile#{i}"].z = 1
@sprites["tile#{i}"].tone = Tone.new(64, 0, 0, 0) if @tiles[pos] >= 0
end
thisx = xtop
if pos >= 0
if pos >= @boardwidth * @boardheight
thisx = ((xtop - (@tilewidth * (@boardwidth / 2).ceil)) / 2) - 10
if (pos % @boardwidth) >= (@boardwidth / 2).ceil
thisx = Graphics.width - thisx - (@tilewidth * @boardwidth)
end
end
@sprites["tile#{i}"].x = thisx + (@tilewidth * (pos % @boardwidth)) + (@tilewidth / 2)
@sprites["tile#{i}"].y = ytop + (@tileheight * ((pos % (@boardwidth * @boardheight)) / @boardwidth))
next if @game == 3
rotatebitmaps = [@tilebitmap, @tilebitmap1, @tilebitmap2, @tilebitmap3]
@sprites["tile#{i}"].bitmap.clear
if rotatebitmaps[@angles[i]]
@sprites["tile#{i}"].bitmap.blt(0, 0, rotatebitmaps[@angles[i]].bitmap,
Rect.new(@tilewidth * (i % @boardwidth), @tileheight * (i / @boardwidth), @tilewidth, @tileheight))
@sprites["tile#{i}"].angle = 0
else
@sprites["tile#{i}"].bitmap.blt(0, 0, @tilebitmap.bitmap,
Rect.new(@tilewidth * (i % @boardwidth), @tileheight * (i / @boardwidth), @tilewidth, @tileheight))
@sprites["tile#{i}"].angle = @angles[i] * 90
end
end
end
updateCursor
pbUpdateSpriteHash(@sprites)
end
def updateCursor
arrows = []
4.times do |i|
arrows.push(pbCanMoveInDir?(@sprites["cursor"].position, (i + 1) * 2, @game == 6))
end
@sprites["cursor"].arrows = arrows
end
def pbStartScene
@sprites = {}
@viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
@viewport.z = 99999
if pbResolveBitmap("Graphics/UI/Tile Puzzle/bg#{@board}")
addBackgroundPlane(@sprites, "bg", "Tile Puzzle/bg#{@board}", @viewport)
else
addBackgroundPlane(@sprites, "bg", "Tile Puzzle/bg", @viewport)
end
@tilebitmap = AnimatedBitmap.new("Graphics/UI/Tile Puzzle/tiles#{@board}")
@tilebitmap1 = nil
@tilebitmap2 = nil
@tilebitmap3 = nil
if pbResolveBitmap("Graphics/UI/Tile Puzzle/tiles#{@board}_1")
@tilebitmap1 = AnimatedBitmap.new("Graphics/UI/Tile Puzzle/tiles#{@board}_1")
end
if pbResolveBitmap("Graphics/UI/Tile Puzzle/tiles#{@board}_2")
@tilebitmap2 = AnimatedBitmap.new("Graphics/UI/Tile Puzzle/tiles#{@board}_2")
end
if pbResolveBitmap("Graphics/UI/Tile Puzzle/tiles#{@board}_3")
@tilebitmap3 = AnimatedBitmap.new("Graphics/UI/Tile Puzzle/tiles#{@board}_3")
end
@tilewidth = @tilebitmap.width / @boardwidth
@tileheight = @tilebitmap.height / @boardheight
(@boardwidth * @boardheight).times do |i|
@sprites["tile#{i}"] = BitmapSprite.new(@tilewidth, @tileheight, @viewport)
@sprites["tile#{i}"].ox = @tilewidth / 2
@sprites["tile#{i}"].oy = @tileheight / 2
break if @game == 3 && i >= (@boardwidth * @boardheight) - 1
@sprites["tile#{i}"].bitmap.blt(0, 0, @tilebitmap.bitmap,
Rect.new(@tilewidth * (i % @boardwidth), @tileheight * (i / @boardwidth), @tilewidth, @tileheight))
end
@heldtile = -1
@angles = []
@tiles = pbShuffleTiles
@sprites["cursor"] = TilePuzzleCursor.new(@game, pbDefaultCursorPosition,
@tilewidth, @tileheight, @boardwidth, @boardheight)
update
pbFadeInAndShow(@sprites)
end
def pbShuffleTiles
ret = []
(@boardwidth * @boardheight).times do |i|
ret.push(i)
@angles.push(0)
end
case @game
when 6
@tiles = ret
5.times do
pbShiftLine([2, 4, 6, 8][rand(4)], rand(@boardwidth * @boardheight), false)
end
return @tiles
when 7
@tiles = ret
5.times do
pbRotateTile(rand(@boardwidth * @boardheight), false)
end
else
ret.shuffle!
if @game == 3 # Make sure only solvable Mystic Squares are allowed.
num = 0
blank = -1
(ret.length - 1).times do |i|
blank = i if ret[i] == (@boardwidth * @boardheight) - 1
(i...ret.length).each do |j|
num += 1 if ret[j] < ret[i] && ret[i] != (@boardwidth * @boardheight) - 1 &&
ret[j] != (@boardwidth * @boardheight) - 1
end
end
if @boardwidth.odd?
ret = pbShuffleTiles if num.odd?
elsif num.even? == (@boardheight - (blank / @boardwidth)).even?
ret = pbShuffleTiles
end
end
if @game == 1 || @game == 2
ret2 = []
(@boardwidth * @boardheight).times do |i|
ret2.push(-1)
end
ret = ret2 + ret
end
if @game == 2 || @game == 5
@angles.length.times do |i|
@angles[i] = rand(4)
end
end
end
return ret
end
def pbDefaultCursorPosition
if @game == 3
(@boardwidth * @boardheight).times do |i|
return i if @tiles[i] == (@boardwidth * @boardheight) - 1
end
end
return 0
end
def pbMoveCursor(pos, dir)
case dir
when 2
pos += @boardwidth
when 4
if pos >= @boardwidth * @boardheight
if pos % @boardwidth == (@boardwidth / 2).ceil
pos = (((pos % (@boardwidth * @boardheight)) / @boardwidth) * @boardwidth) + @boardwidth - 1
else
pos -= 1
end
elsif (pos % @boardwidth) == 0
pos = (((pos / @boardwidth) + @boardheight) * @boardwidth) + (@boardwidth / 2).ceil - 1
else
pos -= 1
end
when 6
if pos >= @boardwidth * @boardheight
if pos % @boardwidth == (@boardwidth / 2).ceil - 1
pos = ((pos % (@boardwidth * @boardheight)) / @boardwidth) * @boardwidth
else
pos += 1
end
elsif pos % @boardwidth >= @boardwidth - 1
pos = (((pos / @boardwidth) + @boardheight) * @boardwidth) + (@boardwidth / 2).ceil
else
pos += 1
end
when 8
pos -= @boardwidth
end
return pos
end
def pbCanMoveInDir?(pos, dir, swapping)
return true if @game == 6 && swapping
case dir
when 2
return false if (pos / @boardwidth) % @boardheight >= @boardheight - 1
when 4
if @game == 1 || @game == 2
return false if pos >= @boardwidth * @boardheight && pos % @boardwidth == 0
else
return false if pos % @boardwidth == 0
end
when 6
if @game == 1 || @game == 2
return false if pos >= @boardwidth * @boardheight && pos % @boardwidth >= @boardwidth - 1
else
return false if pos % @boardwidth >= @boardwidth - 1
end
when 8
return false if (pos / @boardwidth) % @boardheight == 0
end
return true
end
def pbRotateTile(pos, anim = true)
if @heldtile >= 0
if anim
@sprites["cursor"].visible = false
@sprites["tile#{@heldtile}"].z = 1
old_angle = @sprites["tile#{@heldtile}"].angle
timer_start = System.uptime
loop do
@sprites["tile#{@heldtile}"].angle = lerp(old_angle, old_angle - 90, 0.25, timer_start, System.uptime)
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
break if @sprites["tile#{@heldtile}"].angle == old_angle - 90
end
@sprites["tile#{@heldtile}"].z = 0
@sprites["cursor"].visible = true if !pbCheckWin
end
@angles[@heldtile] -= 1
@angles[@heldtile] += 4 if @angles[@heldtile] < 0
else
return if @tiles[pos] < 0
group = pbGetNearTiles(pos)
if anim
@sprites["cursor"].visible = false
old_angles = []
group.each do |i|
@sprites["tile#{@tiles[i]}"].z = 1
old_angles.push(@sprites["tile#{@tiles[i]}"].angle)
end
timer_start = System.uptime
loop do
group.each_with_index do |idx, i|
@sprites["tile#{@tiles[idx]}"].angle = lerp(old_angles[i], old_angles[i] - 90, 0.25, timer_start, System.uptime)
end
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
break if @sprites["tile#{@tiles[group[0]]}"].angle == old_angles[0] - 90
end
group.each do |i|
@sprites["tile#{@tiles[i]}"].z = 0
end
@sprites["cursor"].visible = true if !pbCheckWin
end
group.each do |i|
tile = @tiles[i]
@angles[tile] -= 1
@angles[tile] += 4 if @angles[tile] < 0
end
end
end
def pbGetNearTiles(pos)
ret = [pos]
if @game == 7
[2, 4, 6, 8].each do |i|
ret.push(pbMoveCursor(pos, i)) if pbCanMoveInDir?(pos, i, true)
end
end
return ret
end
def pbSwapTiles(dir)
cursor = @sprites["cursor"].position
return pbShiftLine(dir, cursor) if @game == 6
movetile = pbMoveCursor(cursor, dir)
@sprites["cursor"].visible = false
@sprites["tile#{@tiles[cursor]}"].z = 1
duration = 0.3
timer_start = System.uptime
if [2, 8].include?(dir) # Swap vertically
start_sprite_pos = @sprites["tile#{@tiles[movetile]}"].y
start_cursor_pos = @sprites["tile#{@tiles[cursor]}"].y
dist = (dir / 4).floor - 1
loop do
delta_y = lerp(0, @tileheight * dist, duration, timer_start, System.uptime)
@sprites["tile#{@tiles[movetile]}"].y = start_sprite_pos + delta_y
@sprites["tile#{@tiles[cursor]}"].y = start_cursor_pos - delta_y
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
break if @sprites["tile#{@tiles[movetile]}"].y == start_sprite_pos + (@tileheight * dist)
end
else # Swap horizontally
start_sprite_pos = @sprites["tile#{@tiles[movetile]}"].x
start_cursor_pos = @sprites["tile#{@tiles[cursor]}"].x
dist = dir - 5
loop do
delta_x = lerp(0, @tilewidth * dist, duration, timer_start, System.uptime)
@sprites["tile#{@tiles[movetile]}"].x = start_sprite_pos - delta_x
@sprites["tile#{@tiles[cursor]}"].x = start_cursor_pos + delta_x
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
break if @sprites["tile#{@tiles[movetile]}"].x == start_sprite_pos - (@tilewidth * dist)
end
end
@tiles[cursor], @tiles[movetile] = @tiles[movetile], @tiles[cursor]
@sprites["tile#{@tiles[cursor]}"].z = 0
@sprites["cursor"].position = movetile
@sprites["cursor"].selected = false
@sprites["cursor"].visible = true if !pbCheckWin
return true
end
def pbShiftLine(dir, cursor, anim = true)
# Get tiles involved
tiles = []
dist = 0
if [2, 8].include?(dir)
dist = (dir / 4).floor - 1
while (dist > 0 && cursor < (@boardwidth - 1) * @boardheight) ||
(dist < 0 && cursor >= @boardwidth)
cursor += (@boardwidth * dist)
end
@boardheight.times do |i|
tiles.push(cursor - (i * dist * @boardwidth))
end
else
dist = dir - 5
while (dist > 0 && cursor % @boardwidth > 0) ||
(dist < 0 && cursor % @boardwidth < @boardwidth - 1)
cursor -= dist
end
@boardwidth.times do |i|
tiles.push(cursor + (i * dist))
end
end
# Shift tiles
fade_duration = 0.4
if anim
@sprites["cursor"].visible = false
timer_start = System.uptime
loop do
end_tile = @sprites["tile#{@tiles[tiles[tiles.length - 1]]}"]
end_tile.opacity = lerp(255, 0, fade_duration, timer_start, System.uptime)
Graphics.update
Input.update
break if end_tile.opacity == 0
end
duration = 0.3
timer_start = System.uptime
start_pos = []
if [2, 8].include?(dir)
tiles.each { |i| start_pos.push(@sprites["tile#{@tiles[i]}"].y) }
loop do
tiles.each_with_index do |idx, i|
@sprites["tile#{@tiles[idx]}"].y = lerp(start_pos[i], start_pos[i] - (@tileheight * dist), duration, timer_start, System.uptime)
end
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
break if @sprites["tile#{@tiles[tiles[0]]}"].y == start_pos[0] - (@tileheight * dist)
end
else
tiles.each { |i| start_pos.push(@sprites["tile#{@tiles[i]}"].x) }
loop do
tiles.each_with_index do |idx, i|
@sprites["tile#{@tiles[idx]}"].x = lerp(start_pos[i], start_pos[i] + (@tilewidth * dist), duration, timer_start, System.uptime)
end
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
break if @sprites["tile#{@tiles[tiles[0]]}"].x == start_pos[0] + (@tilewidth * dist)
end
end
end
temp = []
tiles.each do |i|
temp.push(@tiles[i])
end
temp.length.times do |i|
@tiles[tiles[(i + 1) % (temp.length)]] = temp[i]
end
if anim
update
timer_start = System.uptime
loop do
end_tile = @sprites["tile#{@tiles[tiles[0]]}"]
end_tile.opacity = lerp(0, 255, fade_duration, timer_start, System.uptime)
Graphics.update
Input.update
break if end_tile.opacity == 255
end
@sprites["cursor"].selected = false
@sprites["cursor"].visible = true if !pbCheckWin
end
return true
end
def pbGrabTile(pos)
@heldtile, @tiles[pos] = @tiles[pos], @heldtile
@sprites["cursor"].holding = (@heldtile >= 0)
@sprites["cursor"].visible = false if pbCheckWin
end
def pbCheckWin
(@boardwidth * @boardheight).times do |i|
return false if @tiles[i] != i
return false if @angles[i] != 0
end
return true
end
def pbMain
loop do
update
Graphics.update
Input.update
# Check end conditions
if pbCheckWin
@sprites["cursor"].visible = false
if @game == 3
extratile = @sprites["tile#{(@boardwidth * @boardheight) - 1}"]
extratile.bitmap.clear
extratile.bitmap.blt(0, 0, @tilebitmap.bitmap,
Rect.new(@tilewidth * (@boardwidth - 1), @tileheight * (@boardheight - 1),
@tilewidth, @tileheight))
extratile.opacity = 0
timer_start = System.uptime
loop do
extratile.opacity = lerp(0, 255, 0.8, timer_start, System.uptime)
Graphics.update
Input.update
break if extratile.opacity >= 255
end
else
pbWait(0.5)
end
loop do
Graphics.update
Input.update
break if Input.trigger?(Input::USE) || Input.trigger?(Input::BACK)
end
return true
end
# Input
@sprites["cursor"].selected = (Input.press?(Input::USE) && @game >= 3 && @game <= 6)
dir = 0
dir = 2 if Input.trigger?(Input::DOWN) || Input.repeat?(Input::DOWN)
dir = 4 if Input.trigger?(Input::LEFT) || Input.repeat?(Input::LEFT)
dir = 6 if Input.trigger?(Input::RIGHT) || Input.repeat?(Input::RIGHT)
dir = 8 if Input.trigger?(Input::UP) || Input.repeat?(Input::UP)
if dir > 0
if @game == 3 || (@game != 3 && @sprites["cursor"].selected)
if pbCanMoveInDir?(@sprites["cursor"].position, dir, true)
pbSEPlay("Tile Game cursor")
pbSwapTiles(dir)
end
else
if pbCanMoveInDir?(@sprites["cursor"].position, dir, false)
pbSEPlay("Tile Game cursor")
@sprites["cursor"].position = pbMoveCursor(@sprites["cursor"].position, dir)
end
end
elsif (@game == 1 || @game == 2) && Input.trigger?(Input::USE)
pbGrabTile(@sprites["cursor"].position)
elsif (@game == 2 && Input.trigger?(Input::ACTION)) ||
(@game == 5 && Input.trigger?(Input::ACTION)) ||
(@game == 7 && Input.trigger?(Input::USE))
pbRotateTile(@sprites["cursor"].position)
elsif Input.trigger?(Input::BACK)
return false
end
end
end
def pbEndScene
pbFadeOutAndHide(@sprites)
pbDisposeSpriteHash(@sprites)
@viewport.dispose
end
end
#===============================================================================
#
#===============================================================================
class TilePuzzle
def initialize(scene)
@scene = scene
end
def pbStartScreen
@scene.pbStartScene
ret = @scene.pbMain
@scene.pbEndScene
return ret
end
end
#===============================================================================
#
#===============================================================================
def pbTilePuzzle(game, board, width = 0, height = 0)
ret = false
pbFadeOutIn do
scene = TilePuzzleScene.new(game, board, width, height)
screen = TilePuzzle.new(scene)
ret = screen.pbStartScreen
end
return ret
end