mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-08 21:54:58 +00:00
update 6.7
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
class PokemonBoxArrow < SpriteWrapper
|
||||
attr_accessor :cursormode
|
||||
|
||||
alias _multiSelect_PokemonBoxArrow_initialize initialize
|
||||
|
||||
def initialize(*args)
|
||||
_multiSelect_PokemonBoxArrow_initialize(*args)
|
||||
@cursormode = "default"
|
||||
@handsprite.addBitmap("point1m", "Graphics/Pictures/Storage/cursor_point_1_m")
|
||||
@handsprite.addBitmap("point2m", "Graphics/Pictures/Storage/cursor_point_2_m")
|
||||
@handsprite.addBitmap("grabm", "Graphics/Pictures/Storage/cursor_grab_m")
|
||||
@handsprite.addBitmap("fistm", "Graphics/Pictures/Storage/cursor_fist_m")
|
||||
@multiheldpkmn = []
|
||||
end
|
||||
|
||||
alias _multiSelect_PokemonBoxArrow_dispose dispose
|
||||
|
||||
def dispose
|
||||
_multiSelect_PokemonBoxArrow_dispose
|
||||
@multiheldpkmn.each { |pkmn| pkmn.dispose }
|
||||
end
|
||||
|
||||
alias _multiSelect_PokemonBoxArrow_visible_eq visible=
|
||||
|
||||
def visible=(value)
|
||||
_multiSelect_PokemonBoxArrow_visible_eq(value)
|
||||
multiHeldPokemon.each { |pkmn| pkmn.visible = value }
|
||||
end
|
||||
|
||||
alias _multiSelect_PokemonBoxArrow_color_eq color=
|
||||
|
||||
def color=(value)
|
||||
_multiSelect_PokemonBoxArrow_color_eq(value)
|
||||
multiHeldPokemon.each { |pkmn| pkmn.color = value }
|
||||
end
|
||||
|
||||
alias _multiSelect_PokemonBoxArrow_x_eq x=
|
||||
|
||||
def x=(value)
|
||||
_multiSelect_PokemonBoxArrow_x_eq(value)
|
||||
multiHeldPokemon.each { |pkmn| pkmn.x = self.x + (pkmn.heldox * 48) } if holdingMulti?
|
||||
end
|
||||
|
||||
alias _multiSelect_PokemonBoxArrow_y_eq y=
|
||||
|
||||
def y=(value)
|
||||
_multiSelect_PokemonBoxArrow_y_eq(value)
|
||||
multiHeldPokemon.each { |pkmn| pkmn.y = self.y + 16 + (pkmn.heldoy * 48) } if holdingMulti?
|
||||
end
|
||||
|
||||
def setSprite(sprite)
|
||||
if holdingSingle?
|
||||
@heldpkmn = sprite
|
||||
@heldpkmn.viewport = self.viewport if @heldpkmn
|
||||
@heldpkmn.z = 1 if @heldpkmn
|
||||
@holding = false if !@heldpkmn && @multiheldpkmn.length == 0
|
||||
self.z = 2
|
||||
end
|
||||
end
|
||||
|
||||
def setSprites(sprites)
|
||||
if holdingMulti?
|
||||
@multiheldpkmn = sprites
|
||||
for pkmn in @multiheldpkmn
|
||||
pkmn.viewport = self.viewport
|
||||
pkmn.z = 1
|
||||
end
|
||||
@holding = false if !@heldpkmn && @multiheldpkmn.length == 0
|
||||
self.z = 2
|
||||
end
|
||||
end
|
||||
|
||||
alias _multiSelect_PokemonBoxArrow_deleteSprite deleteSprite
|
||||
|
||||
def deleteSprite
|
||||
_multiSelect_PokemonBoxArrow_deleteSprite
|
||||
@multiheldpkmn.each { |pkmn| pkmn.dispose }
|
||||
@multiheldpkmn = []
|
||||
end
|
||||
|
||||
def grabImmediate(sprite)
|
||||
@grabbingState = 0
|
||||
@holding = true
|
||||
@heldpkmn = sprite
|
||||
@heldpkmn.viewport = self.viewport
|
||||
@heldpkmn.z = 1
|
||||
self.z = 2
|
||||
|
||||
self.x = @spriteX
|
||||
self.y = @spriteY
|
||||
end
|
||||
|
||||
def holdingMulti?
|
||||
return @multiheldpkmn.length > 0 && @holding
|
||||
end
|
||||
|
||||
def heldPokemon
|
||||
@heldpkmn = nil if @heldpkmn && @heldpkmn.disposed?
|
||||
@holding = false if !@heldpkmn && @multiheldpkmn.length == 0
|
||||
return @heldpkmn
|
||||
end
|
||||
|
||||
def getModeSprites
|
||||
case @cursormode
|
||||
when "quickswap"
|
||||
return ["point1q", "point2q", "grabq", "fistq"]
|
||||
when "multiselect"
|
||||
return ["point1m", "point2m", "grabm", "fistm"]
|
||||
else
|
||||
return ["point1", "point2", "grab", "fist"]
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@updating = true
|
||||
super
|
||||
heldpkmn = heldPokemon
|
||||
heldpkmn.update if heldpkmn
|
||||
multiheldpkmn = multiHeldPokemon
|
||||
multiheldpkmn.each { |pkmn| pkmn.update }
|
||||
modeSprites = getModeSprites
|
||||
@handsprite.update
|
||||
@holding = false if !heldpkmn && multiheldpkmn.length == 0
|
||||
if @grabbingState > 0
|
||||
if @grabbingState <= 4 * Graphics.frame_rate / 20
|
||||
@handsprite.changeBitmap(modeSprites[2]) # grab
|
||||
self.y = @spriteY + 4.0 * @grabbingState * 20 / Graphics.frame_rate
|
||||
@grabbingState += 1
|
||||
elsif @grabbingState <= 8 * Graphics.frame_rate / 20
|
||||
@holding = true
|
||||
@handsprite.changeBitmap(modeSprites[3]) # fist
|
||||
self.y = @spriteY + 4 * (8 * Graphics.frame_rate / 20 - @grabbingState) * 20 / Graphics.frame_rate
|
||||
@grabbingState += 1
|
||||
else
|
||||
@grabbingState = 0
|
||||
end
|
||||
elsif @placingState > 0
|
||||
if @placingState <= 4 * Graphics.frame_rate / 20
|
||||
@handsprite.changeBitmap(modeSprites[3]) # fist
|
||||
self.y = @spriteY + 4.0 * @placingState * 20 / Graphics.frame_rate
|
||||
@placingState += 1
|
||||
elsif @placingState <= 8 * Graphics.frame_rate / 20
|
||||
@holding = false
|
||||
@heldpkmn = nil
|
||||
@multiheldpkmn = []
|
||||
@handsprite.changeBitmap(modeSprites[2]) # grab
|
||||
self.y = @spriteY + 4 * (8 * Graphics.frame_rate / 20 - @placingState) * 20 / Graphics.frame_rate
|
||||
@placingState += 1
|
||||
else
|
||||
@placingState = 0
|
||||
end
|
||||
elsif holdingSingle? || holdingMulti?
|
||||
@handsprite.changeBitmap(modeSprites[3]) # fist
|
||||
else
|
||||
self.x = @spriteX
|
||||
self.y = @spriteY
|
||||
if @frame < Graphics.frame_rate / 2
|
||||
@handsprite.changeBitmap(modeSprites[0]) # point1
|
||||
else
|
||||
@handsprite.changeBitmap(modeSprites[1]) # point2
|
||||
end
|
||||
end
|
||||
@handsprite.changeBitmap(getSplicerIcon) if @fusing
|
||||
|
||||
@frame += 1
|
||||
@frame = 0 if @frame >= Graphics.frame_rate
|
||||
@updating = false
|
||||
end
|
||||
|
||||
def multiHeldPokemon
|
||||
@multiheldpkmn.delete_if { |pkmn| pkmn.disposed? }
|
||||
@holding = false if !@heldpkmn && @multiheldpkmn.length == 0
|
||||
return @multiheldpkmn
|
||||
end
|
||||
|
||||
def holdingSingle?
|
||||
return self.heldPokemon && @holding
|
||||
end
|
||||
|
||||
def grabMulti(sprites)
|
||||
@grabbingState = 1
|
||||
@multiheldpkmn = sprites
|
||||
for pkmn in @multiheldpkmn
|
||||
pkmn.viewport = self.viewport
|
||||
pkmn.z = 1
|
||||
end
|
||||
self.z = 2
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,152 @@
|
||||
# selection_helpers.rb (can be split into separate file)
|
||||
module SelectionConstants
|
||||
BOX_NAME = -1
|
||||
PARTY = -2
|
||||
CLOSE = -3
|
||||
PREV_BOX = -4
|
||||
NEXT_BOX = -5
|
||||
end
|
||||
|
||||
module SelectionHelper
|
||||
# Compute visual selection rectangle. Returns a Rect or nil.
|
||||
def self.compute_rect(scene, screen, box, selected)
|
||||
return nil unless screen.multiSelectRange
|
||||
|
||||
displayRect = Rect.new(0, 0, 1, 1)
|
||||
if box == SelectionConstants::BOX_NAME
|
||||
xvalues = []
|
||||
yvalues = []
|
||||
for i in 0...Settings::MAX_PARTY_SIZE
|
||||
xvalues << scene.sprites["boxparty"].x + 18 + 72 * (i % 2)
|
||||
yvalues << scene.sprites["boxparty"].y + 2 + 16 * (i % 2) + 64 * (i / 2)
|
||||
end
|
||||
indexes = screen.getMultiSelection(box, selected)
|
||||
# defensive: if indexes empty, return nil
|
||||
return nil if indexes.nil? || indexes.empty?
|
||||
minx = xvalues[indexes[0]]
|
||||
miny = yvalues[indexes[0]] + 16
|
||||
maxx = xvalues[indexes[indexes.length - 1]] + 72 - 8
|
||||
maxy = yvalues[indexes[indexes.length - 1]] + 64
|
||||
displayRect.set(minx, miny, maxx - minx, maxy - miny)
|
||||
else
|
||||
indexRect = screen.getSelectionRect(box, selected)
|
||||
return nil if indexRect.nil?
|
||||
displayRect.x = scene.sprites["box"].x + 10 + (48 * indexRect.x)
|
||||
displayRect.y = scene.sprites["box"].y + 30 + (48 * indexRect.y) + 16
|
||||
displayRect.width = indexRect.width * 48 + 16
|
||||
displayRect.height = indexRect.height * 48
|
||||
end
|
||||
displayRect
|
||||
end
|
||||
end
|
||||
|
||||
module SelectionNavigator
|
||||
include SelectionConstants
|
||||
|
||||
# Navigate general box grid (supports wrap & special indices)
|
||||
def self.navigate_box(key, selection, screen)
|
||||
case key
|
||||
when Input::UP
|
||||
if screen.multiSelectRange
|
||||
selection -= PokemonBox::BOX_WIDTH
|
||||
selection += PokemonBox::BOX_SIZE if selection < 0
|
||||
elsif selection == BOX_NAME
|
||||
selection = PARTY
|
||||
elsif selection == PARTY
|
||||
selection = PokemonBox::BOX_SIZE - 1 - PokemonBox::BOX_WIDTH * 2 / 3
|
||||
elsif selection == CLOSE
|
||||
selection = PokemonBox::BOX_SIZE - PokemonBox::BOX_WIDTH / 3
|
||||
else
|
||||
selection -= PokemonBox::BOX_WIDTH
|
||||
selection = BOX_NAME if selection < 0
|
||||
end
|
||||
when Input::DOWN
|
||||
if screen.multiSelectRange
|
||||
selection += PokemonBox::BOX_WIDTH
|
||||
selection -= PokemonBox::BOX_SIZE if selection >= PokemonBox::BOX_SIZE
|
||||
elsif selection == BOX_NAME
|
||||
selection = PokemonBox::BOX_WIDTH / 3
|
||||
elsif selection == PARTY
|
||||
selection = BOX_NAME
|
||||
elsif selection == CLOSE
|
||||
selection = BOX_NAME
|
||||
else
|
||||
selection += PokemonBox::BOX_WIDTH
|
||||
if selection >= PokemonBox::BOX_SIZE
|
||||
if selection < PokemonBox::BOX_SIZE + PokemonBox::BOX_WIDTH / 2
|
||||
selection = PARTY
|
||||
else
|
||||
selection = CLOSE
|
||||
end
|
||||
end
|
||||
end
|
||||
when Input::LEFT
|
||||
if screen.multiSelectRange
|
||||
if (selection % PokemonBox::BOX_WIDTH) == 0
|
||||
selection += PokemonBox::BOX_WIDTH - 1
|
||||
else
|
||||
selection -= 1
|
||||
end
|
||||
elsif selection == BOX_NAME
|
||||
selection = PREV_BOX
|
||||
elsif selection == PARTY
|
||||
selection = CLOSE
|
||||
elsif selection == CLOSE
|
||||
selection = PARTY
|
||||
elsif (selection % PokemonBox::BOX_WIDTH) == 0
|
||||
selection += PokemonBox::BOX_WIDTH - 1
|
||||
else
|
||||
selection -= 1
|
||||
end
|
||||
when Input::RIGHT
|
||||
if screen.multiSelectRange
|
||||
if (selection % PokemonBox::BOX_WIDTH) == PokemonBox::BOX_WIDTH - 1
|
||||
selection -= PokemonBox::BOX_WIDTH - 1
|
||||
else
|
||||
selection += 1
|
||||
end
|
||||
elsif selection == BOX_NAME
|
||||
selection = NEXT_BOX
|
||||
elsif selection == PARTY
|
||||
selection = CLOSE
|
||||
elsif selection == CLOSE
|
||||
selection = PARTY
|
||||
elsif (selection % PokemonBox::BOX_WIDTH) == PokemonBox::BOX_WIDTH - 1
|
||||
selection -= PokemonBox::BOX_WIDTH - 1
|
||||
else
|
||||
selection += 1
|
||||
end
|
||||
end
|
||||
selection
|
||||
end
|
||||
|
||||
# Navigate party (left/right/up/down)
|
||||
def self.navigate_party(key, selection, screen)
|
||||
maxIndex = screen.multiSelectRange ? Settings::MAX_PARTY_SIZE - 1 : Settings::MAX_PARTY_SIZE
|
||||
case key
|
||||
when Input::LEFT
|
||||
selection -= 1
|
||||
selection = maxIndex if selection < 0
|
||||
when Input::RIGHT
|
||||
selection += 1
|
||||
selection = 0 if selection > maxIndex
|
||||
when Input::UP
|
||||
if selection == Settings::MAX_PARTY_SIZE
|
||||
selection = Settings::MAX_PARTY_SIZE - 1
|
||||
else
|
||||
selection -= 2
|
||||
selection = selection % Settings::MAX_PARTY_SIZE if screen.multiSelectRange
|
||||
selection = maxIndex if selection < 0
|
||||
end
|
||||
when Input::DOWN
|
||||
if selection == Settings::MAX_PARTY_SIZE
|
||||
selection = 0
|
||||
else
|
||||
selection += 2
|
||||
selection = selection % Settings::MAX_PARTY_SIZE if screen.multiSelectRange
|
||||
selection = maxIndex if selection > maxIndex
|
||||
end
|
||||
end
|
||||
selection
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
class PokemonBoxIcon < IconSprite
|
||||
attr_accessor :heldox
|
||||
attr_accessor :heldoy
|
||||
|
||||
alias _pokemonBoxIconInitialize initialize
|
||||
|
||||
def initialize(*args)
|
||||
@heldox = 0
|
||||
@heldoy = 0
|
||||
_pokemonBoxIconInitialize(*args)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
class PokemonBoxPartySprite < SpriteWrapper
|
||||
def placePokemonMulti(index, sprites)
|
||||
partyIndex = @pokemonsprites.count { |i| i && i.pokemon && !i.disposed? }
|
||||
for sprite in sprites
|
||||
@pokemonsprites[partyIndex] = sprite
|
||||
partyIndex += 1
|
||||
end
|
||||
if sprites.length > 0
|
||||
@pokemonsprites.compact!
|
||||
refresh
|
||||
end
|
||||
end
|
||||
|
||||
def grabPokemonMulti(indexes, arrowIndex, arrow)
|
||||
grabbedSprites = []
|
||||
arrowX = arrowIndex % 2
|
||||
arrowY = (arrowIndex / 2).floor
|
||||
for index in indexes
|
||||
sprite = @pokemonsprites[index]
|
||||
if sprite && sprite.pokemon && !sprite.disposed?
|
||||
sprite.heldox = (index % 2) - arrowX
|
||||
sprite.heldoy = (index / 2).floor - arrowY
|
||||
grabbedSprites.push(sprite)
|
||||
@pokemonsprites[index] = nil
|
||||
end
|
||||
end
|
||||
if grabbedSprites.length > 0
|
||||
arrow.grabMulti(grabbedSprites)
|
||||
@pokemonsprites.compact!
|
||||
refresh
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,39 @@
|
||||
class PokemonBoxSprite < SpriteWrapper
|
||||
def placePokemonMulti(index, sprites)
|
||||
arrowX = index % PokemonBox::BOX_WIDTH
|
||||
arrowY = (index / PokemonBox::BOX_WIDTH).floor
|
||||
for sprite in sprites
|
||||
spriteIndex = (sprite.heldox + arrowX) + (sprite.heldoy + arrowY) * PokemonBox::BOX_WIDTH
|
||||
@pokemonsprites[spriteIndex] = sprite
|
||||
@pokemonsprites[spriteIndex].refresh
|
||||
end
|
||||
if sprites.length > 0
|
||||
refresh
|
||||
end
|
||||
end
|
||||
|
||||
def grabPokemonMulti(indexes, arrowIndex, arrow)
|
||||
grabbedSprites = []
|
||||
arrowX = arrowIndex % PokemonBox::BOX_WIDTH
|
||||
arrowY = (arrowIndex / PokemonBox::BOX_WIDTH).floor
|
||||
for index in indexes
|
||||
sprite = @pokemonsprites[index]
|
||||
if sprite && sprite.pokemon && !sprite.disposed?
|
||||
sprite.heldox = (index % PokemonBox::BOX_WIDTH) - arrowX
|
||||
sprite.heldoy = (index / PokemonBox::BOX_WIDTH).floor - arrowY
|
||||
grabbedSprites.push(sprite)
|
||||
@pokemonsprites[index] = nil
|
||||
end
|
||||
end
|
||||
if grabbedSprites.length > 0
|
||||
arrow.grabMulti(grabbedSprites)
|
||||
update
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
class PokemonStorage
|
||||
def pbDeleteMulti(box, indexes)
|
||||
for index in indexes
|
||||
self[box, index] = nil
|
||||
end
|
||||
self.party.compact! if box == -1
|
||||
end
|
||||
|
||||
def pbStoreBatch(pokemon_positions_array, box_index = @currentBox, cursor_x = 0, cursor_y = 0)
|
||||
return -1 if invalid_input?(pokemon_positions_array, box_index)
|
||||
|
||||
box_width = PokemonBox::BOX_WIDTH
|
||||
box_height = PokemonBox::BOX_HEIGHT
|
||||
coords = all_coords(box_width, box_height)
|
||||
|
||||
assignments, unplaced, intended = initialize_assignments(pokemon_positions_array.length)
|
||||
|
||||
spiral = spiral_offsets
|
||||
|
||||
# Phase 1: Lock in guaranteed spots
|
||||
lock_guaranteed_spots(pokemon_positions_array, box_index, cursor_x, cursor_y, box_width, box_height,
|
||||
assignments, unplaced, intended)
|
||||
|
||||
available_coords = filter_available_coords(coords, box_index, box_width, assignments)
|
||||
return :CANT_PLACE if available_coords.length < unplaced.length
|
||||
|
||||
# Phase 2: Assign leftovers using spiral fallback
|
||||
assign_fallback_positions(unplaced, intended, assignments, available_coords, box_width, box_height, cursor_x, cursor_y, spiral)
|
||||
return :CANT_PLACE if assignments.compact.uniq.length < assignments.compact.length
|
||||
|
||||
# Phase 3: Commit placements
|
||||
commit_assignments(assignments, pokemon_positions_array, box_index, box_width)
|
||||
|
||||
unplaced.empty? ? :PLACED_ALL_FREE : :PLACED_OCCUPIED
|
||||
end
|
||||
|
||||
# -----------------------------
|
||||
# Helper methods
|
||||
# -----------------------------
|
||||
def invalid_input?(arr, box_index)
|
||||
arr.nil? || arr.empty? || self[box_index].is_a?(StorageTransferBox)
|
||||
end
|
||||
|
||||
def all_coords(box_width, box_height)
|
||||
(0...box_height).flat_map { |y| (0...box_width).map { |x| [x, y] } }
|
||||
end
|
||||
|
||||
def initialize_assignments(length)
|
||||
[Array.new(length), [], []] # assignments, unplaced, intended
|
||||
end
|
||||
|
||||
def spiral_offsets
|
||||
[[0,0],[1,0],[0,1],[-1,0],[0,-1],[1,1],[-1,1],[1,-1],[-1,-1],[2,0],[0,2],[-2,0],[0,-2]]
|
||||
end
|
||||
|
||||
def lock_guaranteed_spots(pokemon_positions_array, box_index, cursor_x, cursor_y, box_width, box_height, assignments, unplaced, intended)
|
||||
pokemon_positions_array.each_with_index do |pk_data, i|
|
||||
rel_x, rel_y = pk_data[1], pk_data[2]
|
||||
target_x, target_y = cursor_x + rel_x, cursor_y + rel_y
|
||||
intended[i] = [target_x, target_y]
|
||||
|
||||
if target_x.between?(0, box_width-1) && target_y.between?(0, box_height-1)
|
||||
index = target_y * box_width + target_x
|
||||
assignments[i] = [target_x, target_y] if self[box_index, index].nil?
|
||||
unplaced << i if assignments[i].nil?
|
||||
else
|
||||
unplaced << i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def filter_available_coords(coords, box_index, box_width, assignments)
|
||||
used_coords = assignments.compact
|
||||
coords.reject { |cx, cy| !self[box_index, cy * box_width + cx].nil? || used_coords.include?([cx, cy]) }
|
||||
end
|
||||
|
||||
def assign_fallback_positions(unplaced, intended, assignments, available_coords, box_width, box_height, cursor_x, cursor_y, spiral)
|
||||
unplaced.each_with_index do |i, idx|
|
||||
tx, ty = intended[i] || [cursor_x, cursor_y]
|
||||
chosen = spiral.map { |offx, offy| [tx + offx, ty + offy] }
|
||||
.find { |nx, ny| nx.between?(0, box_width-1) && ny.between?(0, box_height-1) && available_coords.include?([nx, ny]) }
|
||||
chosen ||= available_coords.min_by { |cx, cy| (cx - tx).abs + (cy - ty).abs }
|
||||
raise :CANT_PLACE unless chosen
|
||||
assignments[i] = chosen
|
||||
available_coords.delete(chosen)
|
||||
end
|
||||
end
|
||||
|
||||
def commit_assignments(assignments, pokemon_positions_array, box_index, box_width)
|
||||
assignments.each_with_index do |coords, i|
|
||||
next unless coords
|
||||
cx, cy = coords
|
||||
index = cy * box_width + cx
|
||||
if self[box_index, index].nil?
|
||||
self[box_index, index] = pokemon_positions_array[i][0]
|
||||
else
|
||||
rollback(assignments, pokemon_positions_array, box_index, box_width)
|
||||
raise :CANT_PLACE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def rollback(assignments, pokemon_positions_array, box_index, box_width)
|
||||
assignments.each do |coords|
|
||||
next unless coords
|
||||
rx, ry = coords
|
||||
rindex = ry * box_width + rx
|
||||
self[box_index, rindex] = nil if pokemon_positions_array.any? { |pk| pk[0] == self[box_index, rindex] }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,318 @@
|
||||
class PokemonStorageScene
|
||||
include SelectionConstants
|
||||
|
||||
attr_reader :cursormode
|
||||
attr_reader :screen
|
||||
attr_reader :sprites
|
||||
attr_accessor :selection
|
||||
|
||||
alias _storageMultiselect_pbStartBox pbStartBox
|
||||
|
||||
def pbStartBox(*args)
|
||||
_storageMultiselect_pbStartBox(*args)
|
||||
@cursormode = "default"
|
||||
# create a single selection rect sprite used when needed
|
||||
@sprites["selectionrect"] = BitmapSprite.new(Graphics.width, Graphics.height, @arrowviewport)
|
||||
@sprites["selectionrect"].visible = false
|
||||
end
|
||||
|
||||
def pbChangeSelection(key, selection)
|
||||
if @choseFromParty
|
||||
return SelectionNavigator.navigate_party(key, selection, @screen)
|
||||
else
|
||||
return SelectionNavigator.navigate_box(key, selection, @screen)
|
||||
end
|
||||
end
|
||||
|
||||
def pbPartyChangeSelection(key, selection)
|
||||
SelectionNavigator.navigate_party(key, selection, @screen)
|
||||
end
|
||||
|
||||
def pbSelectPartyInternal(party, depositing)
|
||||
selection = @selection
|
||||
pbPartySetArrow(@sprites["arrow"], selection)
|
||||
pbUpdateOverlay(selection, party)
|
||||
pbSetMosaic(selection)
|
||||
lastsel = 1
|
||||
loop do
|
||||
Graphics.update
|
||||
Input.update
|
||||
key = -1
|
||||
key = Input::DOWN if Input.repeat?(Input::DOWN)
|
||||
key = Input::RIGHT if Input.repeat?(Input::RIGHT)
|
||||
key = Input::LEFT if Input.repeat?(Input::LEFT)
|
||||
key = Input::UP if Input.repeat?(Input::UP)
|
||||
if key >= 0
|
||||
pbPlayCursorSE
|
||||
newselection = pbPartyChangeSelection(key, selection)
|
||||
if newselection == -1
|
||||
return -1 if !depositing
|
||||
elsif newselection == -2
|
||||
selection = lastsel
|
||||
else
|
||||
selection = newselection
|
||||
end
|
||||
pbPartySetArrow(@sprites["arrow"], selection)
|
||||
lastsel = selection if selection > 0
|
||||
pbUpdateOverlay(selection, party)
|
||||
pbSetMosaic(selection)
|
||||
if @screen.multiSelectRange
|
||||
pbUpdateSelectionRect(-1, selection)
|
||||
end
|
||||
end
|
||||
self.update
|
||||
if Input.trigger?(Input::ACTION) && @command == 0 # Organize only
|
||||
if !@screen.pbHolding?
|
||||
pbPlayDecisionSE
|
||||
pbNextCursorMode
|
||||
end
|
||||
elsif Input.trigger?(Input::BACK)
|
||||
@selection = selection
|
||||
return -1
|
||||
elsif Input.trigger?(Input::USE)
|
||||
if selection >= 0 && selection < Settings::MAX_PARTY_SIZE
|
||||
@selection = selection
|
||||
return selection
|
||||
elsif selection == Settings::MAX_PARTY_SIZE # Close Box
|
||||
@selection = selection
|
||||
return (depositing) ? -3 : -1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pbSelectBoxInternal(_party)
|
||||
selection = @selection
|
||||
pbSetArrow(@sprites["arrow"], selection)
|
||||
pbUpdateOverlay(selection)
|
||||
pbSetMosaic(selection)
|
||||
loop do
|
||||
Graphics.update
|
||||
Input.update
|
||||
key = -1
|
||||
key = Input::DOWN if Input.repeat?(Input::DOWN)
|
||||
key = Input::RIGHT if Input.repeat?(Input::RIGHT)
|
||||
key = Input::LEFT if Input.repeat?(Input::LEFT)
|
||||
key = Input::UP if Input.repeat?(Input::UP)
|
||||
|
||||
if key >= 0
|
||||
pbPlayCursorSE
|
||||
selection = pbChangeSelection(key, selection)
|
||||
pbSetArrow(@sprites["arrow"], selection)
|
||||
if selection == SelectionConstants::PREV_BOX
|
||||
nextbox = (@storage.currentBox + @storage.maxBoxes - 1) % @storage.maxBoxes
|
||||
pbSwitchBoxToLeft(nextbox)
|
||||
@storage.currentBox = nextbox
|
||||
elsif selection == SelectionConstants::NEXT_BOX
|
||||
nextbox = (@storage.currentBox + 1) % @storage.maxBoxes
|
||||
pbSwitchBoxToRight(nextbox)
|
||||
@storage.currentBox = nextbox
|
||||
end
|
||||
selection = BOX_NAME if selection == SelectionConstants::PREV_BOX || selection == SelectionConstants::NEXT_BOX
|
||||
pbUpdateOverlay(selection)
|
||||
pbSetMosaic(selection)
|
||||
if @screen.multiSelectRange
|
||||
pbUpdateSelectionRect(@storage.currentBox, selection)
|
||||
end
|
||||
end
|
||||
self.update
|
||||
if Input.trigger?(Input::JUMPUP)
|
||||
pbPlayCursorSE
|
||||
nextbox = (@storage.currentBox + @storage.maxBoxes - 1) % @storage.maxBoxes
|
||||
pbSwitchBoxToLeft(nextbox)
|
||||
@storage.currentBox = nextbox
|
||||
pbUpdateOverlay(selection)
|
||||
pbSetMosaic(selection)
|
||||
elsif Input.trigger?(Input::JUMPDOWN)
|
||||
pbPlayCursorSE
|
||||
nextbox = (@storage.currentBox + 1) % @storage.maxBoxes
|
||||
pbSwitchBoxToRight(nextbox)
|
||||
@storage.currentBox = nextbox
|
||||
pbUpdateOverlay(selection)
|
||||
pbSetMosaic(selection)
|
||||
elsif Input.trigger?(Input::SPECIAL) # Jump to box name
|
||||
if selection != BOX_NAME
|
||||
pbPlayCursorSE
|
||||
selection = BOX_NAME
|
||||
pbSetArrow(@sprites["arrow"], selection)
|
||||
pbUpdateOverlay(selection)
|
||||
pbSetMosaic(selection)
|
||||
end
|
||||
elsif Input.trigger?(Input::ACTION) && @command == 0 # Organize only
|
||||
pbPlayDecisionSE
|
||||
pbNextCursorMode
|
||||
elsif Input.trigger?(Input::BACK)
|
||||
@selection = selection
|
||||
return nil
|
||||
elsif Input.trigger?(Input::USE)
|
||||
@selection = selection
|
||||
if selection >= 0
|
||||
return [@storage.currentBox, selection]
|
||||
elsif selection == BOX_NAME
|
||||
return [SelectionConstants::PREV_BOX, BOX_NAME]
|
||||
elsif selection == PARTY
|
||||
return [SelectionConstants::PARTY, BOX_NAME]
|
||||
elsif selection == CLOSE
|
||||
return [SelectionConstants::CLOSE, BOX_NAME]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def restartBox(screen, command, animate = true)
|
||||
pbCloseBox(animate)
|
||||
cursormode = @cursormode
|
||||
selection = @selection
|
||||
pbStartBox(screen, command, animate)
|
||||
pbSetCursorMode(cursormode)
|
||||
@selection = selection
|
||||
end
|
||||
|
||||
def pbNextCursorMode()
|
||||
return if @screen.pbHolding?
|
||||
case @cursormode
|
||||
when "default"
|
||||
pbSetCursorMode("multiselect")
|
||||
when "quickswap" #Disabled
|
||||
pbSetCursorMode("default")
|
||||
when "multiselect"
|
||||
#pbSetCursorMode("quickswap") if !@screen.pbHolding?
|
||||
pbSetCursorMode("default")
|
||||
end
|
||||
end
|
||||
|
||||
def pbSetCursorMode(value)
|
||||
@cursormode = value
|
||||
@sprites["arrow"].cursormode = value
|
||||
if @screen.multiSelectRange
|
||||
@screen.multiSelectRange = nil
|
||||
pbUpdateSelectionRect(@choseFromParty ? -1 : @storage.currentBox, 0)
|
||||
end
|
||||
end
|
||||
|
||||
def pbSetHeldPokemon(pokemon)
|
||||
pokesprite = PokemonBoxIcon.new(pokemon, @arrowviewport)
|
||||
@sprites["arrow"].grabImmediate(pokesprite)
|
||||
end
|
||||
|
||||
# Scene draws the rect by asking SelectionHelper for the rect
|
||||
# in PokemonStorageScene
|
||||
def pbUpdateSelectionRect(box, selected)
|
||||
rect = SelectionHelper.compute_rect(self, @screen, box, selected)
|
||||
if rect.nil?
|
||||
@sprites["selectionrect"].visible = false
|
||||
return
|
||||
end
|
||||
|
||||
bmp = @sprites["selectionrect"].bitmap
|
||||
bmp.clear
|
||||
|
||||
color = Color.new(0, 255, 0, 50) # semi-transparent green
|
||||
radius = 12 # corner roundness in pixels
|
||||
|
||||
draw_rounded_rect(bmp, rect.x, rect.y, rect.width, rect.height, radius, color)
|
||||
@sprites["selectionrect"].visible = true
|
||||
end
|
||||
|
||||
# helper method (add to PokemonStorageScene)
|
||||
def draw_rounded_rect(bmp, x, y, w, h, r, color)
|
||||
# central rectangle
|
||||
bmp.fill_rect(x + r, y, w - 2 * r, h, color)
|
||||
bmp.fill_rect(x, y + r, w, h - 2 * r, color)
|
||||
|
||||
# corner circles
|
||||
(0..r).each do |dy|
|
||||
dx = (r * r - dy * dy) ** 0.5
|
||||
# top-left
|
||||
bmp.fill_rect(x + r - dx, y + r - dy, 2 * dx, 1, color)
|
||||
# top-right
|
||||
bmp.fill_rect(x + w - r - dx, y + r - dy, 2 * dx, 1, color)
|
||||
# bottom-left
|
||||
bmp.fill_rect(x + r - dx, y + h - r + dy - 1, 2 * dx, 1, color)
|
||||
# bottom-right
|
||||
bmp.fill_rect(x + w - r - dx, y + h - r + dy - 1, 2 * dx, 1, color)
|
||||
end
|
||||
end
|
||||
|
||||
# --- Animation methods (Scene-only) ---
|
||||
# The scene only animates; it does not change game state or run rules.
|
||||
def animate_hold_multi(box, selected, selected_index)
|
||||
pbSEPlay("GUI storage pick up")
|
||||
if box == BOX_NAME
|
||||
@sprites["boxparty"].grabPokemonMulti(selected, selected_index, @sprites["arrow"])
|
||||
else
|
||||
@sprites["box"].grabPokemonMulti(selected, selected_index, @sprites["arrow"])
|
||||
end
|
||||
while @sprites["arrow"].grabbing?
|
||||
Graphics.update
|
||||
Input.update
|
||||
self.update
|
||||
end
|
||||
end
|
||||
|
||||
def animate_place_multi(box, index)
|
||||
pbSEPlay("GUI storage put down")
|
||||
heldpokesprites = @sprites["arrow"].multiHeldPokemon
|
||||
@sprites["arrow"].place
|
||||
while @sprites["arrow"].placing?
|
||||
Graphics.update
|
||||
Input.update
|
||||
self.update
|
||||
end
|
||||
if box == BOX_NAME
|
||||
@sprites["boxparty"].placePokemonMulti(index, heldpokesprites)
|
||||
else
|
||||
@sprites["box"].placePokemonMulti(index, heldpokesprites)
|
||||
end
|
||||
@boxForMosaic = @storage.currentBox
|
||||
@selectionForMosaic = index
|
||||
end
|
||||
|
||||
def animate_release_multi(box, selected)
|
||||
release_sprites = []
|
||||
for index in selected
|
||||
sprite = nil
|
||||
if box == BOX_NAME
|
||||
sprite = @sprites["boxparty"].getPokemon(index)
|
||||
else
|
||||
sprite = @sprites["box"].getPokemon(index)
|
||||
end
|
||||
release_sprites << sprite if sprite
|
||||
end
|
||||
if release_sprites.length > 0
|
||||
for sprite in release_sprites
|
||||
sprite.release
|
||||
end
|
||||
while release_sprites[0].releasing?
|
||||
Graphics.update
|
||||
for sprite in release_sprites
|
||||
sprite.update
|
||||
end
|
||||
self.update
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pbHardRefresh
|
||||
oldPartyY = @sprites["boxparty"].y
|
||||
@sprites["box"].dispose
|
||||
@sprites["box"] = PokemonBoxSprite.new(@storage, @storage.currentBox, @boxviewport)
|
||||
@sprites["boxparty"].dispose
|
||||
@sprites["boxparty"] = PokemonBoxPartySprite.new(@storage.party, @boxsidesviewport)
|
||||
@sprites["boxparty"].y = oldPartyY
|
||||
end
|
||||
|
||||
def pbCloseBox(animate = true)
|
||||
pbFadeOutAndHide(@sprites) if animate
|
||||
pbDisposeSpriteHash(@sprites)
|
||||
@markingbitmap.dispose if @markingbitmap
|
||||
@boxviewport.dispose
|
||||
@boxsidesviewport.dispose
|
||||
@arrowviewport.dispose
|
||||
end
|
||||
|
||||
# Convenience wrapper for external code that expects pbPlaceMulti/pbHoldMulti naming:
|
||||
alias pbPlaceMulti animate_place_multi
|
||||
alias pbHoldMulti animate_hold_multi
|
||||
alias pbReleaseMulti animate_release_multi
|
||||
end
|
||||
@@ -0,0 +1,415 @@
|
||||
class PokemonStorageScreen
|
||||
include SelectionConstants
|
||||
|
||||
attr_accessor :multiheldpkmn
|
||||
attr_accessor :multiSelectRange
|
||||
|
||||
alias _storageMultiSelect_initialize initialize
|
||||
|
||||
def initialize(*args)
|
||||
_storageMultiSelect_initialize(*args)
|
||||
@multiheldpkmn = []
|
||||
@multiSelectRange = nil
|
||||
end
|
||||
|
||||
# Top-level loop: delegates move and release actions to screen-level methods.
|
||||
def pcOrganizeCommand()
|
||||
isTransferBox = @storage[@storage.currentBox].is_a?(StorageTransferBox)
|
||||
loop do
|
||||
selected = @scene.pbSelectBox(@storage.party)
|
||||
if selected == nil
|
||||
if pbHolding?
|
||||
if @fusionMode
|
||||
cancelFusion
|
||||
else
|
||||
pbDisplay(_INTL("You're holding a Pokémon!"))
|
||||
end
|
||||
next
|
||||
end
|
||||
if @multiSelectRange
|
||||
pbPlayCancelSE
|
||||
@multiSelectRange = nil
|
||||
@scene.pbUpdateSelectionRect(0, 0)
|
||||
next
|
||||
end
|
||||
next if pbConfirm(_INTL("Continue Box operations?"))
|
||||
break
|
||||
elsif selected[0] == SelectionConstants::CLOSE
|
||||
if pbHolding?
|
||||
if @multiSelectRange
|
||||
pbPlayCancelSE
|
||||
@multiSelectRange = nil
|
||||
@scene.pbUpdateSelectionRect(0, 0)
|
||||
next
|
||||
else
|
||||
pbDisplay(_INTL("You're holding a Pokémon!"))
|
||||
next
|
||||
end
|
||||
end
|
||||
if pbConfirm(_INTL("Exit from the Box?"))
|
||||
pbSEPlay("PC close")
|
||||
break
|
||||
end
|
||||
next
|
||||
elsif selected[0] == SelectionConstants::PREV_BOX
|
||||
pbBoxCommands
|
||||
else
|
||||
pokemon = @storage[selected[0], selected[1]]
|
||||
heldpoke = pbHeldPokemon
|
||||
next if !heldpoke && !pokemon && @scene.cursormode != "multiselect"
|
||||
if @scene.cursormode == "multiselect"
|
||||
multiSelectAction(selected)
|
||||
elsif @scene.cursormode == "quickswap"
|
||||
quickSwap(selected, pokemon)
|
||||
elsif @fusionMode
|
||||
pbFusionCommands(selected)
|
||||
else
|
||||
echoln "pcOrganizeCommand?"
|
||||
organizeActions(selected, pokemon, heldpoke, isTransferBox)
|
||||
end
|
||||
end
|
||||
end
|
||||
@scene.pbCloseBox
|
||||
end
|
||||
|
||||
def selectAllBox
|
||||
@scene.pbSetCursorMode("multiselect")
|
||||
selected_index = PokemonBox::BOX_SIZE - 1
|
||||
@multiSelectRange = [0, nil]
|
||||
box = @storage.currentBox
|
||||
@scene.pbUpdateSelectionRect(box, selected_index,)
|
||||
@scene.selection = selected_index
|
||||
end
|
||||
|
||||
def pbBoxCommands
|
||||
is_holding_pokemon = pbHolding?
|
||||
if @scene.cursormode == "multiselect"
|
||||
if is_holding_pokemon
|
||||
return dropAllHeldPokemon
|
||||
else
|
||||
return selectAllBox
|
||||
end
|
||||
|
||||
end
|
||||
cmd_jump = _INTL("Jump")
|
||||
cmd_select = _INTL("Select all")
|
||||
cmd_wallpaper = _INTL("Wallpaper")
|
||||
cmd_name = _INTL("Name")
|
||||
cmd_info = _INTL("Info")
|
||||
cmd_cancel = _INTL("Cancel")
|
||||
|
||||
commands = []
|
||||
commands << cmd_jump
|
||||
commands << cmd_select unless is_holding_pokemon
|
||||
commands << cmd_wallpaper
|
||||
commands << cmd_name if !@storage[@storage.currentBox].is_a?(StorageTransferBox)
|
||||
commands << cmd_info if @storage[@storage.currentBox].is_a?(StorageTransferBox)
|
||||
commands << cmd_cancel
|
||||
|
||||
command = pbShowCommands(
|
||||
_INTL("What do you want to do?"), commands)
|
||||
case commands[command]
|
||||
when cmd_jump
|
||||
boxCommandJump
|
||||
when cmd_wallpaper
|
||||
boxCommandSetWallpaper
|
||||
when cmd_name
|
||||
boxCommandName
|
||||
when cmd_info
|
||||
boxCommandTransferInfo
|
||||
when cmd_select
|
||||
selectAllBox
|
||||
end
|
||||
end
|
||||
|
||||
def singlePokemonCommands(selected)
|
||||
#@multiSelectRange = nil
|
||||
#@scene.pbUpdateSelectionRect(selected[0], selected[1])
|
||||
isTransferBox = @storage[@storage.currentBox].is_a?(StorageTransferBox)
|
||||
pokemon = @storage[selected[0], selected[1]]
|
||||
heldpoke = pbHeldPokemon
|
||||
return organizeActions(selected, pokemon, heldpoke, isTransferBox)
|
||||
end
|
||||
|
||||
def multipleSelectedPokemonCommands(selected, pokemonCount)
|
||||
commands = []
|
||||
cmdMove = -1
|
||||
cmdRelease = -1
|
||||
cmdCancel = -1
|
||||
cmdSort = -1
|
||||
|
||||
helptext = _INTL("Selected {1} Pokémon.", pokemonCount)
|
||||
|
||||
commands[cmdMove = commands.length] = _INTL("Move")
|
||||
commands[cmdSort = commands.length] = _INTL("Sort")
|
||||
commands[cmdRelease = commands.length] = _INTL("Release") if $DEBUG
|
||||
commands[cmdCancel = commands.length] = _INTL("Cancel")
|
||||
|
||||
command = pbShowCommands(helptext, commands)
|
||||
|
||||
if command == cmdMove
|
||||
pbHoldMulti(selected[0], selected[1])
|
||||
elsif command == cmdSort
|
||||
pbSortMulti(selected[0])
|
||||
elsif command == cmdRelease
|
||||
pbReleaseMulti(selected[0])
|
||||
end
|
||||
end
|
||||
|
||||
def dropAllHeldPokemon
|
||||
multiSelectAction([@storage.currentBox, 0])
|
||||
end
|
||||
|
||||
# Multi-select flow: validates and delegates animations to scene.
|
||||
def multiSelectAction(selected)
|
||||
return unless @scene.cursormode == "multiselect"
|
||||
if pbMultiHeldPokemon.length > 0
|
||||
# placing multi-held from screen's held list
|
||||
place_result = pbPlaceMulti(selected[0], selected[1])
|
||||
if place_result == :PLACED_OCCUPIED
|
||||
pbSEPlay("GUI party switch")
|
||||
end
|
||||
|
||||
elsif !@multiSelectRange
|
||||
pbPlayDecisionSE
|
||||
@multiSelectRange = [selected[1], nil]
|
||||
@scene.pbUpdateSelectionRect(selected[0], selected[1])
|
||||
return
|
||||
elsif !@multiSelectRange[1]
|
||||
@multiSelectRange[1] = selected[1]
|
||||
pokemonCount = 0
|
||||
noneggCount = 0
|
||||
for index in getMultiSelection(selected[0], nil)
|
||||
pokemonCount += 1 if @storage[selected[0], index]
|
||||
if @storage[selected[0], index]
|
||||
noneggCount += 1 unless @storage[selected[0], index].egg?
|
||||
end
|
||||
end
|
||||
if pokemonCount == 0
|
||||
pbPlayCancelSE
|
||||
@multiSelectRange = nil
|
||||
@scene.pbUpdateSelectionRect(selected[0], selected[1])
|
||||
return
|
||||
elsif pokemonCount == 1 && @storage[selected[0], selected[1]]
|
||||
singlePokemonCommands(selected)
|
||||
else
|
||||
multipleSelectedPokemonCommands(selected, pokemonCount)
|
||||
end
|
||||
@multiSelectRange = nil
|
||||
@scene.pbUpdateSelectionRect(selected[0], selected[1])
|
||||
end
|
||||
end
|
||||
|
||||
# --- Screen-side game-rule methods (validate, commit, then animate) ---
|
||||
# Validate & pick up a selection of multiple Pokémon (logical)
|
||||
def pbHoldMulti(box, selected_index)
|
||||
selected = getMultiSelection(box, nil)
|
||||
return if selected.length == 0
|
||||
selected_pos = getBoxPosition(box, selected_index)
|
||||
able_count = 0
|
||||
new_held = []
|
||||
final_selected = []
|
||||
for index in selected
|
||||
pokemon = @storage[box, index]
|
||||
next if !pokemon
|
||||
able_count += 1 if pbAble?(pokemon)
|
||||
pos = getBoxPosition(box, index)
|
||||
new_held << [pokemon, pos[0] - selected_pos[0], pos[1] - selected_pos[1]]
|
||||
final_selected << index
|
||||
end
|
||||
|
||||
# Prevent taking last pokemon out of party
|
||||
if box == BOX_NAME && pbAbleCount == able_count
|
||||
if new_held.length > 1
|
||||
# deselect first able pokemon for convenience
|
||||
for i in 0...new_held.length
|
||||
if pbAble?(new_held[i][0])
|
||||
new_held.delete_at(i)
|
||||
final_selected.delete_at(i)
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
pbPlayBuzzerSE
|
||||
pbDisplay(_INTL("That's your last Pokémon!"))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
# Clear selection, animate pickup, update logical state
|
||||
@multiSelectRange = nil
|
||||
@scene.pbUpdateSelectionRect(0, 0)
|
||||
@scene.animate_hold_multi(box, final_selected, selected_index) # animation
|
||||
@multiheldpkmn = new_held
|
||||
@storage.pbDeleteMulti(box, final_selected)
|
||||
@scene.pbRefresh
|
||||
end
|
||||
|
||||
# Check if all held pokémon can strictly fit at the target positions (no overlap, no OOB).
|
||||
# Commit them to storage and animate the placement.
|
||||
def pbPlaceMulti(box, selected_index)
|
||||
return if @multiheldpkmn.nil? || @multiheldpkmn.empty?
|
||||
|
||||
selected_pos = getBoxPosition(box, selected_index)
|
||||
if box >= 0
|
||||
# Validate every target slot is in-bounds and unoccupied
|
||||
need_fill = false
|
||||
for held in @multiheldpkmn
|
||||
held_x = held[1] + selected_pos[0]
|
||||
held_y = held[2] + selected_pos[1]
|
||||
if out_of_bounds?(box, held_x, held_y) || occupied?(box, held_x, held_y)
|
||||
need_fill = true
|
||||
end
|
||||
end
|
||||
if need_fill
|
||||
store_result = @storage.pbStoreBatch(@multiheldpkmn, box, selected_pos[0], selected_pos[1])
|
||||
if store_result == :CANT_PLACE || !store_result
|
||||
pbDisplay(_INTL("There's not enough room!"))
|
||||
return
|
||||
end
|
||||
@scene.animate_place_multi(box, selected_index)
|
||||
@multiheldpkmn = []
|
||||
@scene.pbHardRefresh
|
||||
|
||||
@scene.restartBox(self, @command, false)
|
||||
@storage = $PokemonStorage
|
||||
@scene.pbRefresh
|
||||
@multiheldpkmn = []
|
||||
@boxForMosaic = @storage.currentBox
|
||||
@selectionForMosaic = selected_index
|
||||
return store_result
|
||||
end
|
||||
|
||||
# All validated: animate then commit
|
||||
@scene.animate_place_multi(box, selected_index)
|
||||
for held in @multiheldpkmn
|
||||
pokemon = held[0]
|
||||
held_x = held[1] + selected_pos[0]
|
||||
held_y = held[2] + selected_pos[1]
|
||||
idx = held_x + held_y * PokemonBox::BOX_WIDTH
|
||||
@storage[box, idx] = pokemon
|
||||
end
|
||||
else
|
||||
# Party placement: validate space
|
||||
party_count = @storage.party.length
|
||||
if party_count + @multiheldpkmn.length > Settings::MAX_PARTY_SIZE
|
||||
pbDisplay(_INTL("There's not enough room!"))
|
||||
return
|
||||
end
|
||||
|
||||
@scene.animate_place_multi(box, selected_index)
|
||||
for held in @multiheldpkmn
|
||||
pokemon = held[0]
|
||||
@storage.party.push(pokemon)
|
||||
end
|
||||
end
|
||||
@scene.pbRefresh
|
||||
@multiheldpkmn = []
|
||||
end
|
||||
|
||||
# Validate release rules, animate release, then delete from storage
|
||||
def pbReleaseMulti(box)
|
||||
selected = getMultiSelection(box, nil)
|
||||
return if selected.length == 0
|
||||
able_count = 0
|
||||
final_released = []
|
||||
for index in selected
|
||||
pokemon = @storage[box, index]
|
||||
next if !pokemon
|
||||
if pokemon.owner.name == "RENTAL"
|
||||
pbDisplay(_INTL("This Pokémon cannot be released"))
|
||||
return
|
||||
elsif pokemon.egg?
|
||||
pbDisplay(_INTL("You can't release an Egg."))
|
||||
return false
|
||||
elsif pokemon.mail
|
||||
pbDisplay(_INTL("Please remove the mail."))
|
||||
return false
|
||||
end
|
||||
able_count += 1 if pbAble?(pokemon)
|
||||
final_released << index
|
||||
end
|
||||
|
||||
if box == BOX_NAME && pbAbleCount == able_count
|
||||
pbPlayBuzzerSE
|
||||
pbDisplay(_INTL("That's your last Pokémon!"))
|
||||
return
|
||||
end
|
||||
command = pbShowCommands(_INTL("Release {1} Pokémon?", final_released.length), [_INTL("No"), _INTL("Yes")])
|
||||
if command == 1
|
||||
command_confirm = pbShowCommands(_INTL("The {1} Pokémon will be lost forever. Release them?", final_released.length), [_INTL("No"), _INTL("Yes")])
|
||||
if command_confirm == 1
|
||||
@multiSelectRange = nil
|
||||
@scene.pbUpdateSelectionRect(0, 0)
|
||||
@scene.animate_release_multi(box, final_released)
|
||||
@storage.pbDeleteMulti(box, final_released)
|
||||
@scene.pbRefresh
|
||||
pbDisplay(_INTL("The Pokémon were released."))
|
||||
pbDisplay(_INTL("Bye-bye!"))
|
||||
@scene.pbRefresh
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
def out_of_bounds?(box, x, y)
|
||||
width = (box == BOX_NAME ? 2 : PokemonBox::BOX_WIDTH)
|
||||
height = (box == BOX_NAME ? (Settings::MAX_PARTY_SIZE / 2.0).ceil : PokemonBox::BOX_HEIGHT)
|
||||
x < 0 || y < 0 || x >= width || y >= height
|
||||
end
|
||||
|
||||
def occupied?(box, x, y)
|
||||
idx = x + y * PokemonBox::BOX_WIDTH
|
||||
!!@storage[box, idx]
|
||||
end
|
||||
|
||||
def pbMultiHeldPokemon
|
||||
@multiheldpkmn
|
||||
end
|
||||
|
||||
def pbHolding?
|
||||
return @heldpkmn != nil || (@multiheldpkmn && @multiheldpkmn.length > 0)
|
||||
end
|
||||
|
||||
def getSelectionRect(box, currentSelected)
|
||||
range_end = (currentSelected != nil ? currentSelected : @multiSelectRange && @multiSelectRange[1])
|
||||
return nil unless @multiSelectRange && @multiSelectRange[0] && range_end
|
||||
|
||||
box_width = box == BOX_NAME ? 2 : PokemonBox::BOX_WIDTH
|
||||
|
||||
ax = @multiSelectRange[0] % box_width
|
||||
ay = (@multiSelectRange[0].to_f / box_width).floor
|
||||
bx = range_end % box_width
|
||||
by = (range_end.to_f / box_width).floor
|
||||
|
||||
minx = [ax, bx].min
|
||||
miny = [ay, by].min
|
||||
maxx = [ax, bx].max
|
||||
maxy = [ay, by].max
|
||||
|
||||
Rect.new(minx, miny, maxx - minx + 1, maxy - miny + 1)
|
||||
end
|
||||
|
||||
def getMultiSelection(box, currentSelected)
|
||||
rect = getSelectionRect(box, currentSelected)
|
||||
return [] if rect.nil?
|
||||
|
||||
ret = []
|
||||
for j in (rect.y)..(rect.y + rect.height - 1)
|
||||
for i in (rect.x)..(rect.x + rect.width - 1)
|
||||
ret << getBoxIndex(box, i, j)
|
||||
end
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
def getBoxIndex(box, x, y)
|
||||
box_width = box == BOX_NAME ? 2 : PokemonBox::BOX_WIDTH
|
||||
x + y * box_width
|
||||
end
|
||||
|
||||
def getBoxPosition(box, index)
|
||||
box_width = box == BOX_NAME ? 2 : PokemonBox::BOX_WIDTH
|
||||
[index % box_width, (index.to_f / box_width).floor]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,151 @@
|
||||
class PokemonStorageScreen
|
||||
# --- Define sortable criteria ---
|
||||
def sortable_criteria
|
||||
[
|
||||
{
|
||||
key: :dex,
|
||||
label: _INTL("By Pokédex number"),
|
||||
value_proc: ->(p) { p.id_number || 0 },
|
||||
friendly: [_INTL("Lowest to Highest Pokédex #"), _INTL("Highest to Lowest Pokédex #")]
|
||||
},
|
||||
{
|
||||
key: :head_dex,
|
||||
label: _INTL("By head Pokédex number"),
|
||||
value_proc: ->(p) { p.head_id || 0 },
|
||||
friendly: [_INTL("Lowest to Highest Pokédex #"), _INTL("Highest to Lowest Pokédex #")]
|
||||
},
|
||||
{
|
||||
key: :body_dex,
|
||||
label: _INTL("By body Pokédex number"),
|
||||
value_proc: ->(p) { p.body_id || 0 },
|
||||
friendly: [_INTL("Lowest to Highest Pokédex #"), _INTL("Highest to Lowest Pokédex #")]
|
||||
},
|
||||
{
|
||||
key: :alpha_species,
|
||||
label: _INTL("By species name"),
|
||||
value_proc: ->(p) { (p.species.to_s || "").downcase },
|
||||
friendly: [_INTL("A to Z"), _INTL("Z to A")]
|
||||
},
|
||||
{
|
||||
key: :alpha,
|
||||
label: _INTL("By nickname"),
|
||||
value_proc: ->(p) { (p.name || "").downcase },
|
||||
friendly: [_INTL("A to Z"), _INTL("Z to A")]
|
||||
},
|
||||
{
|
||||
key: :level,
|
||||
label: _INTL("By level"),
|
||||
value_proc: ->(p) { p.level || 0 },
|
||||
friendly: [_INTL("Lowest to Highest level"), _INTL("Highest to Lowest level")]
|
||||
},
|
||||
{
|
||||
key: :type,
|
||||
label: _INTL("By type"),
|
||||
value_proc: ->(p) {
|
||||
# Grab both types (fallbacks in case one is nil)
|
||||
t1 = p.type1 || :NORMAL
|
||||
t2 = p.type2 || ""
|
||||
[
|
||||
GameData::Type.get(t1).name,
|
||||
GameData::Type.get(t2).name
|
||||
]
|
||||
},
|
||||
friendly: [_INTL("A to Z by type"), _INTL("Z to A by type")]
|
||||
},
|
||||
{
|
||||
key: :date,
|
||||
label: _INTL("By date caught"),
|
||||
value_proc: ->(p) { p.timeReceived || Time.at(0) },
|
||||
friendly: [_INTL("Oldest to Newest"), _INTL("Newest to Oldest")]
|
||||
},
|
||||
{
|
||||
key: :invert,
|
||||
label: _INTL("Reverse"),
|
||||
value_proc: ->(p) { reverse },
|
||||
friendly: [_INTL("Reverse the order")]
|
||||
},
|
||||
{
|
||||
key: :random,
|
||||
label: _INTL("Shuffle"),
|
||||
value_proc: ->(p) { rand },
|
||||
friendly: [_INTL("Randomize the order")]
|
||||
},
|
||||
|
||||
]
|
||||
end
|
||||
|
||||
# --- Ask which criterion to sort by ---
|
||||
def pbAskSortCriterion
|
||||
commands = sortable_criteria.map { |c| c[:label] } + [_INTL("Cancel")]
|
||||
cmd = pbShowCommands(_INTL("Sort selected Pokémon how?"), commands)
|
||||
return nil if cmd == commands.length - 1
|
||||
return nil if cmd <= -1
|
||||
return cmd
|
||||
end
|
||||
|
||||
# --- Ask for order using friendly text ---
|
||||
def pbAskSortOrder(criterion_index)
|
||||
crit = sortable_criteria[criterion_index]
|
||||
orders = crit[:friendly] + [_INTL("Cancel")]
|
||||
ord = pbShowCommands(_INTL("Sort order?"), orders)
|
||||
return nil if ord <= -1
|
||||
return nil if ord == orders.length - 1
|
||||
return ord == 1 # true if descending
|
||||
end
|
||||
|
||||
# --- Sort the array according to criterion and order ---
|
||||
def sort_pokemon_array!(arr, criterion_index, descending)
|
||||
crit = sortable_criteria[criterion_index]
|
||||
if crit[:key] == :invert
|
||||
arr.reverse!
|
||||
return
|
||||
end
|
||||
arr.sort_by! { |p| crit[:value_proc].call(p) }
|
||||
arr.reverse! if descending
|
||||
end
|
||||
|
||||
# --- Main method stays mostly unchanged ---
|
||||
def pbSortMulti(box)
|
||||
selected = getMultiSelection(box, nil)
|
||||
return if selected.empty?
|
||||
|
||||
pokes = selected.map { |idx| @storage[box, idx] }.compact
|
||||
return if pokes.empty?
|
||||
|
||||
criterion = pbAskSortCriterion
|
||||
return if criterion.nil?
|
||||
|
||||
descending = pbAskSortOrder(criterion)
|
||||
return if descending.nil?
|
||||
|
||||
sort_pokemon_array!(pokes, criterion, descending)
|
||||
|
||||
# Clear selected slots
|
||||
selected.each { |idx| @storage[box, idx] = nil }
|
||||
|
||||
# Refill the rectangle row-by-row
|
||||
rect = getSelectionRect(box, nil)
|
||||
i = 0
|
||||
if rect
|
||||
for y in rect.y...(rect.y + rect.height)
|
||||
for x in rect.x...(rect.x + rect.width)
|
||||
break if i >= pokes.length
|
||||
idx = getBoxIndex(box, x, y)
|
||||
@storage[box, idx] = pokes[i]
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
else
|
||||
selected.each do |idx|
|
||||
break if i >= pokes.length
|
||||
@storage[box, idx] = pokes[i]
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
pbSEPlay("GUI party switch")
|
||||
@scene.pbHardRefresh
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user