################################################################################
# "Slot Machine" mini-game
# By Maruno
#-------------------------------------------------------------------------------
# Run with:      pbSlotMachine(1)
# - The number is either 0 (easy), 1 (default) or 2 (hard).
################################################################################
class SlotMachineReel < BitmapSprite
  attr_accessor :reel
  attr_accessor :toppos
  attr_accessor :spinning
  attr_accessor :stopping
  attr_accessor :slipping
  SCROLLSPEED = 16   # Must be a divisor of 48
  ICONSPOOL = [[0,0,0,0,1,1,2,2,3,3,3,4,4,4,5,5,6,6,7], # 0 - Easy
               [0,0,0,0,1,1,1,2,2,2,3,3,4,4,5,6,7],     # 1 - Medium (default)
               [0,0,1,1,1,2,2,2,3,3,4,4,5,6,7]          # 2 - Hard
              ]
  SLIPPING = [0,0,0,0,0,0,1,1,1,2,2,3]

  def initialize(x,y,difficulty=1)
    @viewport=Viewport.new(x,y,64,144)
    @viewport.z=99999
    super(64,144,@viewport)
    @reel=[]
    for i in 0...ICONSPOOL[difficulty].length
      @reel.push(ICONSPOOL[difficulty][i])
    end
    @reel.shuffle!
    @toppos=0
    @spinning=false
    @stopping=false
    @slipping=0
    @index=rand(@reel.length)
    @images=AnimatedBitmap.new(_INTL("Graphics/Pictures/Slot Machine/images"))
    @shading=AnimatedBitmap.new(_INTL("Graphics/Pictures/Slot Machine/ReelOverlay"))
    update
  end

  def startSpinning
    @spinning=true
  end

  def stopSpinning(noslipping=false)
    @stopping=true
    @slipping=SLIPPING[rand(SLIPPING.length)]
    @slipping=0 if noslipping
  end

  def showing
    array=[]
    for i in 0...3
      num=@index-i
      num+=@reel.length if num<0
      array.push(@reel[num])
    end
    return array   # [0] = top, [1] = middle, [2] = bottom
  end

  def update
    self.bitmap.clear
    if @toppos==0 && @stopping && @slipping==0
      @spinning=@stopping=false
    end
    if @spinning
      @toppos+=SCROLLSPEED
      if @toppos>0
        @toppos-=48
        @index=(@index+1)%@reel.length
        @slipping-=1 if @slipping>0
      end
    end
    for i in 0...4
      num=@index-i
      num+=@reel.length if num<0
      self.bitmap.blt(0,@toppos+i*48,@images.bitmap,Rect.new(@reel[num]*64,0,64,48))
    end
    self.bitmap.blt(0,0,@shading.bitmap,Rect.new(0,0,64,144))
  end
end



class SlotMachineScore < BitmapSprite
  attr_reader :score

  def initialize(x,y,score=0)
    @viewport=Viewport.new(x,y,70,22)
    @viewport.z=99999
    super(70,22,@viewport)
    @numbers=AnimatedBitmap.new(_INTL("Graphics/Pictures/Slot Machine/numbers"))
    self.score=score
  end

  def score=(value)
    @score = value
    @score = Settings::MAX_COINS if @score > Settings::MAX_COINS
    refresh
  end

  def refresh
    self.bitmap.clear
    for i in 0...5
      digit=(@score/(10**i))%10 # Least significant digit first
      self.bitmap.blt(14*(4-i),0,@numbers.bitmap,Rect.new(digit*14,0,14,22))
    end
  end
end



class SlotMachineScene
  attr_accessor :gameRunning
  attr_accessor :gameEnd
  attr_accessor :wager
  attr_accessor :replay

  def update
    pbUpdateSpriteHash(@sprites)
  end

  def pbPayout
    @replay=false
    payout=0
    bonus=0
    wonRow=[]
    # Get reel pictures
    reel1=@sprites["reel1"].showing
    reel2=@sprites["reel2"].showing
    reel3=@sprites["reel3"].showing
    combinations=[[reel1[1],reel2[1],reel3[1]], # Centre row
                  [reel1[0],reel2[0],reel3[0]], # Top row
                  [reel1[2],reel2[2],reel3[2]], # Bottom row
                  [reel1[0],reel2[1],reel3[2]], # Diagonal top left -> bottom right
                  [reel1[2],reel2[1],reel3[0]], # Diagonal bottom left -> top right
                 ]
    for i in 0...combinations.length
      break if i>=1 && @wager<=1 # One coin = centre row only
      break if i>=3 && @wager<=2 # Two coins = three rows only
      wonRow[i]=true
      case combinations[i]
      when [1,1,1]   # Three Magnemites
        payout += 8
      when [2,2,2]   # Three Shellders
        payout += 8
      when [3,3,3]   # Three Pikachus
        payout += 15
      when [4,4,4]   # Three Psyducks
        payout += 15
      when [5,5,6], [5,6,5], [6,5,5], [6,6,5], [6,5,6], [5,6,6]   # 777 multi-colored
        payout += 90
        bonus = 1 if bonus<1
      when [5,5,5], [6,6,6]   # Red 777, blue 777
        payout += 300
        bonus = 2 if bonus<2
      when [7,7,7]   # Three replays
        @replay = true
      else
        if combinations[i][0]==0   # Left cherry
          if combinations[i][1]==0   # Centre cherry as well
            payout+=4
          else
            payout+=2
          end
        else
          wonRow[i]=false
        end
      end
    end
    @sprites["payout"].score=payout
    frame=0
    if payout>0 || @replay
      if bonus>0
        pbMEPlay("Slots big win")
      else
        pbMEPlay("Slots win")
      end
      # Show winning animation
      timePerFrame = Graphics.frame_rate/8
      until frame==Graphics.frame_rate*3
        Graphics.update
        Input.update
        update
        @sprites["window2"].bitmap.clear if @sprites["window2"].bitmap
        @sprites["window1"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/win"))
        @sprites["window1"].src_rect.set(152*((frame/timePerFrame)%4),0,152,208)
        if bonus>0
          @sprites["window2"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/bonus"))
          @sprites["window2"].src_rect.set(152*(bonus-1),0,152,208)
        end
        @sprites["light1"].visible=true
        @sprites["light1"].src_rect.set(0,26*((frame/timePerFrame)%4),96,26)
        @sprites["light2"].visible=true
        @sprites["light2"].src_rect.set(0,26*((frame/timePerFrame)%4),96,26)
        for i in 1..5
          if wonRow[i-1]
            @sprites["row#{i}"].visible = ((frame/timePerFrame)%2)==0
          else
            @sprites["row#{i}"].visible = false
          end
        end
        frame += 1
      end
      @sprites["light1"].visible=false
      @sprites["light2"].visible=false
      @sprites["window1"].src_rect.set(0,0,152,208)
      # Pay out
      loop do
        break if @sprites["payout"].score<=0
        Graphics.update
        Input.update
        update
        @sprites["payout"].score -= 1
        @sprites["credit"].score += 1
        if Input.trigger?(Input::USE) || @sprites["credit"].score == Settings::MAX_COINS
          @sprites["credit"].score += @sprites["payout"].score
          @sprites["payout"].score = 0
        end
      end
      (Graphics.frame_rate/2).times do
        Graphics.update
        Input.update
        update
      end
    else
      # Show losing animation
      timePerFrame = Graphics.frame_rate/4
      until frame==Graphics.frame_rate*2
        Graphics.update
        Input.update
        update
        @sprites["window2"].bitmap.clear if @sprites["window2"].bitmap
        @sprites["window1"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/lose"))
        @sprites["window1"].src_rect.set(152*((frame/timePerFrame)%2),0,152,208)
        frame += 1
      end
    end
    @wager = 0
  end

  def pbStartScene(difficulty)
    @sprites={}
    @viewport=Viewport.new(0,0,Graphics.width,Graphics.height)
    @viewport.z=99999
    addBackgroundPlane(@sprites,"bg","Slot Machine/bg",@viewport)
    @sprites["reel1"]=SlotMachineReel.new(64,112,difficulty)
    @sprites["reel2"]=SlotMachineReel.new(144,112,difficulty)
    @sprites["reel3"]=SlotMachineReel.new(224,112,difficulty)
    for i in 1..3
      @sprites["button#{i}"]=IconSprite.new(68+80*(i-1),260,@viewport)
      @sprites["button#{i}"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/button"))
      @sprites["button#{i}"].visible=false
    end
    for i in 1..5
      y=[170,122,218,82,82][i-1]
      @sprites["row#{i}"]=IconSprite.new(2,y,@viewport)
      @sprites["row#{i}"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/line%1d%s",
         1+i/2,(i>=4) ? ((i==4) ? "a" : "b") : ""))
      @sprites["row#{i}"].visible=false
    end
    @sprites["light1"]=IconSprite.new(16,32,@viewport)
    @sprites["light1"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/lights"))
    @sprites["light1"].visible=false
    @sprites["light2"]=IconSprite.new(240,32,@viewport)
    @sprites["light2"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/lights"))
    @sprites["light2"].mirror=true
    @sprites["light2"].visible=false
    @sprites["window1"]=IconSprite.new(358,96,@viewport)
    @sprites["window1"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/insert"))
    @sprites["window1"].src_rect.set(0,0,152,208)
    @sprites["window2"]=IconSprite.new(358,96,@viewport)
    @sprites["credit"]=SlotMachineScore.new(360,66,$Trainer.coins)
    @sprites["payout"]=SlotMachineScore.new(438,66,0)
    @wager=0
    update
    pbFadeInAndShow(@sprites)
  end

  def pbMain
    frame=0
    spinFrameTime   = Graphics.frame_rate/4
    insertFrameTime = Graphics.frame_rate*4/10
    loop do
      Graphics.update
      Input.update
      update
      @sprites["window1"].bitmap.clear if @sprites["window1"].bitmap
      @sprites["window2"].bitmap.clear if @sprites["window2"].bitmap
      if @sprites["credit"].score == Settings::MAX_COINS
        pbMessage(_INTL("You've got {1} Coins.", Settings::MAX_COINS.to_s_formatted))
        break
      elsif $Trainer.coins==0
        pbMessage(_INTL("You've run out of Coins.\nGame over!"))
        break
      elsif @gameRunning   # Reels are spinning
        @sprites["window1"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/stop"))
        @sprites["window1"].src_rect.set(152*((frame/spinFrameTime)%4),0,152,208)
        if Input.trigger?(Input::USE)
          pbSEPlay("Slots stop")
          if @sprites["reel1"].spinning
            @sprites["reel1"].stopSpinning(@replay)
            @sprites["button1"].visible=true
          elsif @sprites["reel2"].spinning
            @sprites["reel2"].stopSpinning(@replay)
            @sprites["button2"].visible=true
          elsif @sprites["reel3"].spinning
            @sprites["reel3"].stopSpinning(@replay)
            @sprites["button3"].visible=true
          end
        end
        if !@sprites["reel3"].spinning
          @gameEnd=true
          @gameRunning=false
        end
      elsif @gameEnd   # Reels have been stopped
        pbPayout
        # Reset graphics
        @sprites["button1"].visible=false
        @sprites["button2"].visible=false
        @sprites["button3"].visible=false
        for i in 1..5
          @sprites["row#{i}"].visible=false
        end
        @gameEnd=false
      else   # Awaiting coins for the next spin
        @sprites["window1"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/insert"))
        @sprites["window1"].src_rect.set(152*((frame/insertFrameTime)%2),0,152,208)
        if @wager>0
          @sprites["window2"].setBitmap(sprintf("Graphics/Pictures/Slot Machine/press"))
          @sprites["window2"].src_rect.set(152*((frame/insertFrameTime)%2),0,152,208)
        end
        if Input.trigger?(Input::DOWN) && @wager<3 && @sprites["credit"].score>0
          pbSEPlay("Slots coin")
          @wager+=1
          @sprites["credit"].score-=1
          if @wager>=3
            @sprites["row5"].visible=true
            @sprites["row4"].visible=true
          elsif @wager>=2
            @sprites["row3"].visible=true
            @sprites["row2"].visible=true
          elsif @wager>=1
            @sprites["row1"].visible=true
          end
        elsif @wager>=3 || (@wager>0 && @sprites["credit"].score==0) ||
              (Input.trigger?(Input::USE) && @wager>0) || @replay
          if @replay
            @wager=3
            for i in 1..5
              @sprites["row#{i}"].visible=true
             end
          end
          @sprites["reel1"].startSpinning
          @sprites["reel2"].startSpinning
          @sprites["reel3"].startSpinning
          frame=0
          @gameRunning=true
        elsif Input.trigger?(Input::BACK) && @wager==0
          break
        end
      end
      frame = (frame+1)%(Graphics.frame_rate*4)
    end
    $Trainer.coins = @sprites["credit"].score
  end

  def pbEndScene
    pbFadeOutAndHide(@sprites)
    pbDisposeSpriteHash(@sprites)
    @viewport.dispose
  end
end



class SlotMachine
  def initialize(scene)
    @scene=scene
  end

  def pbStartScreen(difficulty)
    @scene.pbStartScene(difficulty)
    @scene.pbMain
    @scene.pbEndScene
  end
end



def pbSlotMachine(difficulty=1)
  if GameData::Item.exists?(:COINCASE) && !$PokemonBag.pbHasItem?(:COINCASE)
    pbMessage(_INTL("It's a Slot Machine."))
  elsif $Trainer.coins == 0
    pbMessage(_INTL("You don't have any Coins to play!"))
  elsif $Trainer.coins == Settings::MAX_COINS
    pbMessage(_INTL("Your Coin Case is full!"))
  else
    pbFadeOutIn {
      scene = SlotMachineScene.new
      screen = SlotMachine.new(scene)
      screen.pbStartScreen(difficulty)
    }
  end
end
