Files
infinitefusion-e18/Data/Scripts/017_Minigames/007_Minigame_TilePuzzles.rb
2021-04-17 23:45:42 +01:00

583 lines
18 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(_INTL("Graphics/Pictures/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
for i in 0...4
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]
for i in 0...4
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
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
for i in 0...@boardwidth*@boardheight
pos=-1
for j in 0...@tiles.length
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=[]
for i in 0...4
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/Pictures/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/Pictures/Tile Puzzle/tiles#{@board}")
@tilebitmap1=nil; @tilebitmap2=nil; @tilebitmap3=nil
if pbResolveBitmap("Graphics/Pictures/Tile Puzzle/tiles#{@board}_1")
@tilebitmap1=AnimatedBitmap.new("Graphics/Pictures/Tile Puzzle/tiles#{@board}_1")
end
if pbResolveBitmap("Graphics/Pictures/Tile Puzzle/tiles#{@board}_2")
@tilebitmap2=AnimatedBitmap.new("Graphics/Pictures/Tile Puzzle/tiles#{@board}_2")
end
if pbResolveBitmap("Graphics/Pictures/Tile Puzzle/tiles#{@board}_3")
@tilebitmap3=AnimatedBitmap.new("Graphics/Pictures/Tile Puzzle/tiles#{@board}_3")
end
@tilewidth=@tilebitmap.width/@boardwidth
@tileheight=@tilebitmap.height/@boardheight
for i in 0...@boardwidth*@boardheight
@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=[]
for i in 0...@boardwidth*@boardheight; ret.push(i); @angles.push(0); end
if @game==6
@tiles=ret
5.times do
pbShiftLine([2,4,6,8][rand(4)],rand(@boardwidth*@boardheight),false)
end
return @tiles
elsif @game==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
for i in 0...ret.length-1
blank=i if ret[i]==@boardwidth*@boardheight-1
for j in i...ret.length
num+=1 if ret[j]<ret[i] && ret[i]!=@boardwidth*@boardheight-1 &&
ret[j]!=@boardwidth*@boardheight-1
end
end
if @boardwidth%2==1
ret=pbShuffleTiles if num%2==1
else
ret=pbShuffleTiles if !((num%2==0 && (@boardheight-(blank/@boardwidth))%2==1) ||
(num%2==1 && (@boardheight-(blank/@boardwidth))%2==0))
end
end
if @game==1 || @game==2
ret2=[]
for i in 0...@boardwidth*@boardheight; ret2.push(-1); end
ret=ret2+ret
end
if @game==2 || @game==5
for i in 0...@angles.length; @angles[i]=rand(4); end
end
end
return ret
end
def pbDefaultCursorPosition
if @game==3
for i in 0...@boardwidth*@boardheight
return i if @tiles[i]==@boardwidth*@boardheight-1
end
return 0
else
return 0
end
end
def pbMoveCursor(pos,dir)
if dir==2
pos+=@boardwidth
elsif dir==4
if pos>=@boardwidth*@boardheight
if pos%@boardwidth==(@boardwidth/2).ceil
pos=((pos%(@boardwidth*@boardheight))/@boardwidth)*@boardwidth+@boardwidth-1
else
pos-=1
end
else
if (pos%@boardwidth)==0
pos=(((pos/@boardwidth)+@boardheight)*@boardwidth)+(@boardwidth/2).ceil-1
else
pos-=1
end
end
elsif dir==6
if pos>=@boardwidth*@boardheight
if pos%@boardwidth==(@boardwidth/2).ceil-1
pos=((pos%(@boardwidth*@boardheight))/@boardwidth)*@boardwidth
else
pos+=1
end
else
if pos%@boardwidth>=@boardwidth-1
pos=(((pos/@boardwidth)+@boardheight)*@boardwidth)+(@boardwidth/2).ceil
else
pos+=1
end
end
elsif dir==8
pos-=@boardwidth
end
return pos
end
def pbCanMoveInDir?(pos,dir,swapping)
return true if @game==6 && swapping
if dir==2
return false if (pos/@boardwidth)%@boardheight>=@boardheight-1
elsif dir==4
if @game==1 || @game==2
return false if pos>=@boardwidth*@boardheight && pos%@boardwidth==0
else
return false if pos%@boardwidth==0
end
elsif dir==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
elsif dir==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
oldAngle = @sprites["tile#{@heldtile}"].angle
rotateTime = Graphics.frame_rate/4
angleDiff = 90.0/rotateTime
rotateTime.times do
@sprites["tile#{@heldtile}"].angle -= angleDiff
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
end
@sprites["tile#{@heldtile}"].z = 0
@sprites["tile#{@heldtile}"].angle = oldAngle-90
@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
oldAngles = []
for i in group
@sprites["tile#{@tiles[i]}"].z = 1
oldAngles[i] = @sprites["tile#{@tiles[i]}"].angle
end
rotateTime = Graphics.frame_rate/4
angleDiff = 90.0/rotateTime
rotateTime.times do
for i in group
@sprites["tile#{@tiles[i]}"].angle -= angleDiff
end
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
end
for i in group
@sprites["tile#{@tiles[i]}"].z = 0
@sprites["tile#{@tiles[i]}"].angle = oldAngles[i]-90
end
@sprites["cursor"].visible=true if !pbCheckWin
end
for i in group
tile=@tiles[i]
@angles[tile]-=1
@angles[tile]+=4 if @angles[tile]<0
end
end
end
def pbGetNearTiles(pos)
ret=[pos]
if @game==7
for i in [2,4,6,8]
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
if dir==2 || dir==8 # Swap vertically
swapTime = Graphics.frame_rate*3/10
distancePerFrame = (@tileheight.to_f/swapTime).ceil
dist = (dir/4).floor-1
swapTime.times do
@sprites["tile#{@tiles[movetile]}"].y += dist*distancePerFrame
@sprites["tile#{@tiles[cursor]}"].y -= dist*distancePerFrame
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
end
else # Swap horizontally
swapTime = Graphics.frame_rate*3/10
distancePerFrame = (@tilewidth.to_f/swapTime).ceil
dist = dir-5
swapTime.times do
@sprites["tile#{@tiles[movetile]}"].x -= dist*distancePerFrame
@sprites["tile#{@tiles[cursor]}"].x += dist*distancePerFrame
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
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 dir==2 || dir==8
dist=(dir/4).floor-1
while (dist>0 && cursor<(@boardwidth-1)*@boardheight) ||
(dist<0 && cursor>=@boardwidth)
cursor+=(@boardwidth*dist)
end
for i in 0...@boardheight
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
for i in 0...@boardwidth
tiles.push(cursor+i*dist)
end
end
# Shift tiles
fadeTime = Graphics.frame_rate*4/10
fadeDiff = (255.0/fadeTime).ceil
if anim
@sprites["cursor"].visible=false
fadeTime.times do
@sprites["tile#{@tiles[tiles[tiles.length-1]]}"].opacity-=fadeDiff
Graphics.update
Input.update
end
shiftTime = Graphics.frame_rate*3/10
if dir==2 || dir==8
distancePerFrame = (@tileheight.to_f/shiftTime).ceil
shiftTime.times do
for i in tiles
@sprites["tile#{@tiles[i]}"].y -= dist*distancePerFrame
end
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
end
else
distancePerFrame = (@tilewidth.to_f/shiftTime).ceil
shiftTime.times do
for i in tiles
@sprites["tile#{@tiles[i]}"].x += dist*distancePerFrame
end
pbUpdateSpriteHash(@sprites)
Graphics.update
Input.update
end
end
end
temp=[]
for i in tiles; temp.push(@tiles[i]); end
for i in 0...temp.length
@tiles[tiles[(i+1)%(temp.length)]]=temp[i]
end
if anim
update
fadeTime.times do
@sprites["tile#{@tiles[tiles[0]]}"].opacity+=fadeDiff
Graphics.update
Input.update
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
for i in 0...@boardwidth*@boardheight
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
appearTime = Graphics.frame_rate*8/10
opacityDiff = (255.0/appearTime).ceil
appearTime.times do
extratile.opacity += opacityDiff
Graphics.update
Input.update
end
else
pbWait(Graphics.frame_rate/2)
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 {
scene = TilePuzzleScene.new(game,board,width,height)
screen = TilePuzzle.new(scene)
ret = screen.pbStartScreen
}
return ret
end