MMBN3: Modernizations and Minor Bugfixes (#2991)
This commit is contained in:
parent
727915040d
commit
f89cee4b15
|
@ -27,14 +27,9 @@ local mmbn3Socket = nil
|
||||||
local frame = 0
|
local frame = 0
|
||||||
|
|
||||||
-- States
|
-- States
|
||||||
local ITEMSTATE_NONINITIALIZED = "Game Not Yet Started" -- Game has not yet started
|
|
||||||
local ITEMSTATE_NONITEM = "Non-Itemable State" -- Do not send item now. RAM is not capable of holding
|
local ITEMSTATE_NONITEM = "Non-Itemable State" -- Do not send item now. RAM is not capable of holding
|
||||||
local ITEMSTATE_IDLE = "Item State Ready" -- Ready for the next item if there are any
|
local ITEMSTATE_IDLE = "Item State Ready" -- Ready for the next item if there are any
|
||||||
local ITEMSTATE_SENT = "Item Sent Not Claimed" -- The ItemBit is set, but the dialog has not been closed yet
|
local itemState = ITEMSTATE_NONITEM
|
||||||
local itemState = ITEMSTATE_NONINITIALIZED
|
|
||||||
|
|
||||||
local itemQueued = nil
|
|
||||||
local itemQueueCounter = 120
|
|
||||||
|
|
||||||
local debugEnabled = false
|
local debugEnabled = false
|
||||||
local game_complete = false
|
local game_complete = false
|
||||||
|
@ -104,21 +99,19 @@ end
|
||||||
local IsInBattle = function()
|
local IsInBattle = function()
|
||||||
return memory.read_u8(0x020097F8) == 0x08
|
return memory.read_u8(0x020097F8) == 0x08
|
||||||
end
|
end
|
||||||
local IsItemQueued = function()
|
|
||||||
return memory.read_u8(0x2000224) == 0x01
|
|
||||||
end
|
|
||||||
|
|
||||||
-- This function actually determines when you're on ANY full-screen menu (navi cust, link battle, etc.) but we
|
-- This function actually determines when you're on ANY full-screen menu (navi cust, link battle, etc.) but we
|
||||||
-- don't want to check any locations there either so it's fine.
|
-- don't want to check any locations there either so it's fine.
|
||||||
local IsOnTitle = function()
|
local IsOnTitle = function()
|
||||||
return bit.band(memory.read_u8(0x020097F8),0x04) == 0
|
return bit.band(memory.read_u8(0x020097F8),0x04) == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local IsItemable = function()
|
local IsItemable = function()
|
||||||
return not IsInMenu() and not IsInTransition() and not IsInDialog() and not IsInBattle() and not IsOnTitle() and not IsItemQueued()
|
return not IsInMenu() and not IsInTransition() and not IsInDialog() and not IsInBattle() and not IsOnTitle()
|
||||||
end
|
end
|
||||||
|
|
||||||
local is_game_complete = function()
|
local is_game_complete = function()
|
||||||
if IsOnTitle() or itemState == ITEMSTATE_NONINITIALIZED then return game_complete end
|
-- If on the title screen don't read RAM, RAM can't be trusted yet
|
||||||
|
if IsOnTitle() then return game_complete end
|
||||||
|
|
||||||
-- If the game is already marked complete, do not read memory
|
-- If the game is already marked complete, do not read memory
|
||||||
if game_complete then return true end
|
if game_complete then return true end
|
||||||
|
@ -177,14 +170,6 @@ local Check_Progressive_Undernet_ID = function()
|
||||||
end
|
end
|
||||||
return 9
|
return 9
|
||||||
end
|
end
|
||||||
local GenerateTextBytes = function(message)
|
|
||||||
bytes = {}
|
|
||||||
for i = 1, #message do
|
|
||||||
local c = message:sub(i,i)
|
|
||||||
table.insert(bytes, charDict[c])
|
|
||||||
end
|
|
||||||
return bytes
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Item Message Generation functions
|
-- Item Message Generation functions
|
||||||
local Next_Progressive_Undernet_ID = function(index)
|
local Next_Progressive_Undernet_ID = function(index)
|
||||||
|
@ -196,150 +181,6 @@ local Next_Progressive_Undernet_ID = function(index)
|
||||||
item_index=ordered_IDs[index]
|
item_index=ordered_IDs[index]
|
||||||
return item_index
|
return item_index
|
||||||
end
|
end
|
||||||
local Extra_Progressive_Undernet = function()
|
|
||||||
fragBytes = int32ToByteList_le(20)
|
|
||||||
bytes = {
|
|
||||||
0xF6, 0x50, fragBytes[1], fragBytes[2], fragBytes[3], fragBytes[4], 0xFF, 0xFF, 0xFF
|
|
||||||
}
|
|
||||||
bytes = TableConcat(bytes, GenerateTextBytes("The extra data\ndecompiles into:\n\"20 BugFrags\"!!"))
|
|
||||||
return bytes
|
|
||||||
end
|
|
||||||
|
|
||||||
local GenerateChipGet = function(chip, code, amt)
|
|
||||||
chipBytes = int16ToByteList_le(chip)
|
|
||||||
bytes = {
|
|
||||||
0xF6, 0x10, chipBytes[1], chipBytes[2], code, amt,
|
|
||||||
charDict['G'], charDict['o'], charDict['t'], charDict[' '], charDict['a'], charDict[' '], charDict['c'], charDict['h'], charDict['i'], charDict['p'], charDict[' '], charDict['f'], charDict['o'], charDict['r'], charDict['\n'],
|
|
||||||
|
|
||||||
}
|
|
||||||
if chip < 256 then
|
|
||||||
bytes = TableConcat(bytes, {
|
|
||||||
charDict['\"'], 0xF9,0x00,chipBytes[1],0x01,0x00,0xF9,0x00,code,0x03, charDict['\"'],charDict['!'],charDict['!']
|
|
||||||
})
|
|
||||||
else
|
|
||||||
bytes = TableConcat(bytes, {
|
|
||||||
charDict['\"'], 0xF9,0x00,chipBytes[1],0x02,0x00,0xF9,0x00,code,0x03, charDict['\"'],charDict['!'],charDict['!']
|
|
||||||
})
|
|
||||||
end
|
|
||||||
return bytes
|
|
||||||
end
|
|
||||||
local GenerateKeyItemGet = function(item, amt)
|
|
||||||
bytes = {
|
|
||||||
0xF6, 0x00, item, amt,
|
|
||||||
charDict['G'], charDict['o'], charDict['t'], charDict[' '], charDict['a'], charDict['\n'],
|
|
||||||
charDict['\"'], 0xF9, 0x00, item, 0x00, charDict['\"'],charDict['!'],charDict['!']
|
|
||||||
}
|
|
||||||
return bytes
|
|
||||||
end
|
|
||||||
local GenerateSubChipGet = function(subchip, amt)
|
|
||||||
-- SubChips have an extra bit of trouble. If you have too many, they're supposed to skip to another text bank that doesn't give you the item
|
|
||||||
-- Instead, I'm going to just let it get eaten
|
|
||||||
bytes = {
|
|
||||||
0xF6, 0x20, subchip, amt, 0xFF, 0xFF, 0xFF,
|
|
||||||
charDict['G'], charDict['o'], charDict['t'], charDict[' '], charDict['a'], charDict['\n'],
|
|
||||||
charDict['S'], charDict['u'], charDict['b'], charDict['C'], charDict['h'], charDict['i'], charDict['p'], charDict[' '], charDict['f'], charDict['o'], charDict['r'], charDict['\n'],
|
|
||||||
charDict['\"'], 0xF9, 0x00, subchip, 0x00, charDict['\"'],charDict['!'],charDict['!']
|
|
||||||
}
|
|
||||||
return bytes
|
|
||||||
end
|
|
||||||
local GenerateZennyGet = function(amt)
|
|
||||||
zennyBytes = int32ToByteList_le(amt)
|
|
||||||
bytes = {
|
|
||||||
0xF6, 0x30, zennyBytes[1], zennyBytes[2], zennyBytes[3], zennyBytes[4], 0xFF, 0xFF, 0xFF,
|
|
||||||
charDict['G'], charDict['o'], charDict['t'], charDict[' '], charDict['a'], charDict['\n'], charDict['\"']
|
|
||||||
}
|
|
||||||
-- The text needs to be added one char at a time, so we need to convert the number to a string then iterate through it
|
|
||||||
zennyStr = tostring(amt)
|
|
||||||
for i = 1, #zennyStr do
|
|
||||||
local c = zennyStr:sub(i,i)
|
|
||||||
table.insert(bytes, charDict[c])
|
|
||||||
end
|
|
||||||
bytes = TableConcat(bytes, {
|
|
||||||
charDict[' '], charDict['Z'], charDict['e'], charDict['n'], charDict['n'], charDict['y'], charDict['s'], charDict['\"'],charDict['!'],charDict['!']
|
|
||||||
})
|
|
||||||
return bytes
|
|
||||||
end
|
|
||||||
local GenerateProgramGet = function(program, color, amt)
|
|
||||||
bytes = {
|
|
||||||
0xF6, 0x40, (program * 4), amt, color,
|
|
||||||
charDict['G'], charDict['o'], charDict['t'], charDict[' '], charDict['a'], charDict[' '], charDict['N'], charDict['a'], charDict['v'], charDict['i'], charDict['\n'],
|
|
||||||
charDict['C'], charDict['u'], charDict['s'], charDict['t'], charDict['o'], charDict['m'], charDict['i'], charDict['z'], charDict['e'], charDict['r'], charDict[' '], charDict['P'], charDict['r'], charDict['o'], charDict['g'], charDict['r'], charDict['a'], charDict['m'], charDict[':'], charDict['\n'],
|
|
||||||
charDict['\"'], 0xF9, 0x00, program, 0x05, charDict['\"'],charDict['!'],charDict['!']
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes
|
|
||||||
end
|
|
||||||
local GenerateBugfragGet = function(amt)
|
|
||||||
fragBytes = int32ToByteList_le(amt)
|
|
||||||
bytes = {
|
|
||||||
0xF6, 0x50, fragBytes[1], fragBytes[2], fragBytes[3], fragBytes[4], 0xFF, 0xFF, 0xFF,
|
|
||||||
charDict['G'], charDict['o'], charDict['t'], charDict[':'], charDict['\n'], charDict['\"']
|
|
||||||
}
|
|
||||||
-- The text needs to be added one char at a time, so we need to convert the number to a string then iterate through it
|
|
||||||
bugFragStr = tostring(amt)
|
|
||||||
for i = 1, #bugFragStr do
|
|
||||||
local c = bugFragStr:sub(i,i)
|
|
||||||
table.insert(bytes, charDict[c])
|
|
||||||
end
|
|
||||||
bytes = TableConcat(bytes, {
|
|
||||||
charDict[' '], charDict['B'], charDict['u'], charDict['g'], charDict['F'], charDict['r'], charDict['a'], charDict['g'], charDict['s'], charDict['\"'],charDict['!'],charDict['!']
|
|
||||||
})
|
|
||||||
return bytes
|
|
||||||
end
|
|
||||||
local GenerateGetMessageFromItem = function(item)
|
|
||||||
--Special case for progressive undernet
|
|
||||||
if item["type"] == "undernet" then
|
|
||||||
undernet_id = Check_Progressive_Undernet_ID()
|
|
||||||
if undernet_id > 8 then
|
|
||||||
return Extra_Progressive_Undernet()
|
|
||||||
end
|
|
||||||
return GenerateKeyItemGet(Next_Progressive_Undernet_ID(undernet_id),1)
|
|
||||||
elseif item["type"] == "chip" then
|
|
||||||
return GenerateChipGet(item["itemID"], item["subItemID"], item["count"])
|
|
||||||
elseif item["type"] == "key" then
|
|
||||||
return GenerateKeyItemGet(item["itemID"], item["count"])
|
|
||||||
elseif item["type"] == "subchip" then
|
|
||||||
return GenerateSubChipGet(item["itemID"], item["count"])
|
|
||||||
elseif item["type"] == "zenny" then
|
|
||||||
return GenerateZennyGet(item["count"])
|
|
||||||
elseif item["type"] == "program" then
|
|
||||||
return GenerateProgramGet(item["itemID"], item["subItemID"], item["count"])
|
|
||||||
elseif item["type"] == "bugfrag" then
|
|
||||||
return GenerateBugfragGet(item["count"])
|
|
||||||
end
|
|
||||||
|
|
||||||
return GenerateTextBytes("Empty Message")
|
|
||||||
end
|
|
||||||
|
|
||||||
local GetMessage = function(item)
|
|
||||||
startBytes = {0x02, 0x00}
|
|
||||||
playerLockBytes = {0xF8,0x00, 0xF8, 0x10}
|
|
||||||
msgOpenBytes = {0xF1, 0x02}
|
|
||||||
textBytes = GenerateTextBytes("Receiving\ndata from\n"..item["sender"]..".")
|
|
||||||
dotdotWaitBytes = {0xEA,0x00,0x0A,0x00,0x4D,0xEA,0x00,0x0A,0x00,0x4D}
|
|
||||||
continueBytes = {0xEB, 0xE9}
|
|
||||||
-- continueBytes = {0xE9}
|
|
||||||
playReceiveAnimationBytes = {0xF8,0x04,0x18}
|
|
||||||
chipGiveBytes = GenerateGetMessageFromItem(item)
|
|
||||||
playerFinishBytes = {0xF8, 0x0C}
|
|
||||||
playerUnlockBytes={0xEB, 0xF8, 0x08}
|
|
||||||
-- playerUnlockBytes={0xF8, 0x08}
|
|
||||||
endMessageBytes = {0xF8, 0x10, 0xE7}
|
|
||||||
|
|
||||||
bytes = {}
|
|
||||||
bytes = TableConcat(bytes,startBytes)
|
|
||||||
bytes = TableConcat(bytes,playerLockBytes)
|
|
||||||
bytes = TableConcat(bytes,msgOpenBytes)
|
|
||||||
bytes = TableConcat(bytes,textBytes)
|
|
||||||
bytes = TableConcat(bytes,dotdotWaitBytes)
|
|
||||||
bytes = TableConcat(bytes,continueBytes)
|
|
||||||
bytes = TableConcat(bytes,playReceiveAnimationBytes)
|
|
||||||
bytes = TableConcat(bytes,chipGiveBytes)
|
|
||||||
bytes = TableConcat(bytes,playerFinishBytes)
|
|
||||||
bytes = TableConcat(bytes,playerUnlockBytes)
|
|
||||||
bytes = TableConcat(bytes,endMessageBytes)
|
|
||||||
return bytes
|
|
||||||
end
|
|
||||||
|
|
||||||
local getChipCodeIndex = function(chip_id, chip_code)
|
local getChipCodeIndex = function(chip_id, chip_code)
|
||||||
chipCodeArrayStartAddress = 0x8011510 + (0x20 * chip_id)
|
chipCodeArrayStartAddress = 0x8011510 + (0x20 * chip_id)
|
||||||
|
@ -353,6 +194,10 @@ local getChipCodeIndex = function(chip_id, chip_code)
|
||||||
end
|
end
|
||||||
|
|
||||||
local getProgramColorIndex = function(program_id, program_color)
|
local getProgramColorIndex = function(program_id, program_color)
|
||||||
|
-- For whatever reason, OilBody (ID 24) does not follow the rules and should be color index 3
|
||||||
|
if program_id == 24 then
|
||||||
|
return 3
|
||||||
|
end
|
||||||
-- The general case, most programs use white pink or yellow. This is the values the enums already have
|
-- The general case, most programs use white pink or yellow. This is the values the enums already have
|
||||||
if program_id >= 20 and program_id <= 47 then
|
if program_id >= 20 and program_id <= 47 then
|
||||||
return program_color-1
|
return program_color-1
|
||||||
|
@ -401,11 +246,11 @@ local changeZenny = function(val)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if memory.read_u32_le(0x20018F4) <= math.abs(tonumber(val)) and tonumber(val) < 0 then
|
if memory.read_u32_le(0x20018F4) <= math.abs(tonumber(val)) and tonumber(val) < 0 then
|
||||||
memory.write_u32_le(0x20018f4, 0)
|
memory.write_u32_le(0x20018F4, 0)
|
||||||
val = 0
|
val = 0
|
||||||
return "empty"
|
return "empty"
|
||||||
end
|
end
|
||||||
memory.write_u32_le(0x20018f4, memory.read_u32_le(0x20018F4) + tonumber(val))
|
memory.write_u32_le(0x20018F4, memory.read_u32_le(0x20018F4) + tonumber(val))
|
||||||
if memory.read_u32_le(0x20018F4) > 999999 then
|
if memory.read_u32_le(0x20018F4) > 999999 then
|
||||||
memory.write_u32_le(0x20018F4, 999999)
|
memory.write_u32_le(0x20018F4, 999999)
|
||||||
end
|
end
|
||||||
|
@ -417,30 +262,17 @@ local changeFrags = function(val)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if memory.read_u16_le(0x20018F8) <= math.abs(tonumber(val)) and tonumber(val) < 0 then
|
if memory.read_u16_le(0x20018F8) <= math.abs(tonumber(val)) and tonumber(val) < 0 then
|
||||||
memory.write_u16_le(0x20018f8, 0)
|
memory.write_u16_le(0x20018F8, 0)
|
||||||
val = 0
|
val = 0
|
||||||
return "empty"
|
return "empty"
|
||||||
end
|
end
|
||||||
memory.write_u16_le(0x20018f8, memory.read_u16_le(0x20018F8) + tonumber(val))
|
memory.write_u16_le(0x20018F8, memory.read_u16_le(0x20018F8) + tonumber(val))
|
||||||
if memory.read_u16_le(0x20018F8) > 9999 then
|
if memory.read_u16_le(0x20018F8) > 9999 then
|
||||||
memory.write_u16_le(0x20018F8, 9999)
|
memory.write_u16_le(0x20018F8, 9999)
|
||||||
end
|
end
|
||||||
return val
|
return val
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Fix Health Pools
|
|
||||||
local fix_hp = function()
|
|
||||||
-- Current Health fix
|
|
||||||
if IsInBattle() and not (memory.read_u16_le(0x20018A0) == memory.read_u16_le(0x2037294)) then
|
|
||||||
memory.write_u16_le(0x20018A0, memory.read_u16_le(0x2037294))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Max Health Fix
|
|
||||||
if IsInBattle() and not (memory.read_u16_le(0x20018A2) == memory.read_u16_le(0x2037296)) then
|
|
||||||
memory.write_u16_le(0x20018A2, memory.read_u16_le(0x2037296))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local changeRegMemory = function(amt)
|
local changeRegMemory = function(amt)
|
||||||
regMemoryAddress = 0x02001897
|
regMemoryAddress = 0x02001897
|
||||||
currentRegMem = memory.read_u8(regMemoryAddress)
|
currentRegMem = memory.read_u8(regMemoryAddress)
|
||||||
|
@ -448,34 +280,18 @@ local changeRegMemory = function(amt)
|
||||||
end
|
end
|
||||||
|
|
||||||
local changeMaxHealth = function(val)
|
local changeMaxHealth = function(val)
|
||||||
fix_hp()
|
|
||||||
if val == nil then
|
if val == nil then
|
||||||
fix_hp()
|
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if math.abs(tonumber(val)) >= memory.read_u16_le(0x20018A2) and tonumber(val) < 0 then
|
|
||||||
memory.write_u16_le(0x20018A2, 0)
|
|
||||||
if IsInBattle() then
|
|
||||||
memory.write_u16_le(0x2037296, memory.read_u16_le(0x20018A2))
|
|
||||||
if memory.read_u16_le(0x2037296) >= memory.read_u16_le(0x20018A2) then
|
|
||||||
memory.write_u16_le(0x2037296, memory.read_u16_le(0x20018A2))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
fix_hp()
|
|
||||||
return "lethal"
|
|
||||||
end
|
|
||||||
memory.write_u16_le(0x20018A2, memory.read_u16_le(0x20018A2) + tonumber(val))
|
memory.write_u16_le(0x20018A2, memory.read_u16_le(0x20018A2) + tonumber(val))
|
||||||
if memory.read_u16_le(0x20018A2) > 9999 then
|
if memory.read_u16_le(0x20018A2) > 9999 then
|
||||||
memory.write_u16_le(0x20018A2, 9999)
|
memory.write_u16_le(0x20018A2, 9999)
|
||||||
end
|
end
|
||||||
if IsInBattle() then
|
|
||||||
memory.write_u16_le(0x2037296, memory.read_u16_le(0x20018A2))
|
|
||||||
end
|
|
||||||
fix_hp()
|
|
||||||
return val
|
return val
|
||||||
end
|
end
|
||||||
|
|
||||||
local SendItem = function(item)
|
local SendItemToGame = function(item)
|
||||||
if item["type"] == "undernet" then
|
if item["type"] == "undernet" then
|
||||||
undernet_id = Check_Progressive_Undernet_ID()
|
undernet_id = Check_Progressive_Undernet_ID()
|
||||||
if undernet_id > 8 then
|
if undernet_id > 8 then
|
||||||
|
@ -553,13 +369,6 @@ local OpenShortcuts = function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local RestoreItemRam = function()
|
|
||||||
if backup_bytes ~= nil then
|
|
||||||
memory.write_bytes_as_array(0x203fe10, backup_bytes)
|
|
||||||
end
|
|
||||||
backup_bytes = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local process_block = function(block)
|
local process_block = function(block)
|
||||||
-- Sometimes the block is nothing, if this is the case then quietly stop processing
|
-- Sometimes the block is nothing, if this is the case then quietly stop processing
|
||||||
if block == nil then
|
if block == nil then
|
||||||
|
@ -574,14 +383,7 @@ local process_block = function(block)
|
||||||
end
|
end
|
||||||
|
|
||||||
local itemStateMachineProcess = function()
|
local itemStateMachineProcess = function()
|
||||||
if itemState == ITEMSTATE_NONINITIALIZED then
|
if itemState == ITEMSTATE_NONITEM then
|
||||||
itemQueueCounter = 120
|
|
||||||
-- Only exit this state the first time a dialog window pops up. This way we know for sure that we're ready to receive
|
|
||||||
if not IsInMenu() and (IsInDialog() or IsInTransition()) then
|
|
||||||
itemState = ITEMSTATE_NONITEM
|
|
||||||
end
|
|
||||||
elseif itemState == ITEMSTATE_NONITEM then
|
|
||||||
itemQueueCounter = 120
|
|
||||||
-- Always attempt to restore the previously stored memory in this state
|
-- Always attempt to restore the previously stored memory in this state
|
||||||
-- Exit this state whenever the game is in an itemable status
|
-- Exit this state whenever the game is in an itemable status
|
||||||
if IsItemable() then
|
if IsItemable() then
|
||||||
|
@ -592,26 +394,11 @@ local itemStateMachineProcess = function()
|
||||||
if not IsItemable() then
|
if not IsItemable() then
|
||||||
itemState = ITEMSTATE_NONITEM
|
itemState = ITEMSTATE_NONITEM
|
||||||
end
|
end
|
||||||
if itemQueueCounter == 0 then
|
if #itemsReceived > loadItemIndexFromRAM() then
|
||||||
if #itemsReceived > loadItemIndexFromRAM() and not IsItemQueued() then
|
|
||||||
itemQueued = itemsReceived[loadItemIndexFromRAM()+1]
|
itemQueued = itemsReceived[loadItemIndexFromRAM()+1]
|
||||||
SendItem(itemQueued)
|
SendItemToGame(itemQueued)
|
||||||
itemState = ITEMSTATE_SENT
|
|
||||||
end
|
|
||||||
else
|
|
||||||
itemQueueCounter = itemQueueCounter - 1
|
|
||||||
end
|
|
||||||
elseif itemState == ITEMSTATE_SENT then
|
|
||||||
-- Once the item is sent, wait for the dialog to close. Then clear the item bit and be ready for the next item.
|
|
||||||
if IsInTransition() or IsInMenu() or IsOnTitle() then
|
|
||||||
itemState = ITEMSTATE_NONITEM
|
|
||||||
itemQueued = nil
|
|
||||||
RestoreItemRam()
|
|
||||||
elseif not IsInDialog() then
|
|
||||||
itemState = ITEMSTATE_IDLE
|
|
||||||
saveItemIndexToRAM(itemQueued["itemIndex"])
|
saveItemIndexToRAM(itemQueued["itemIndex"])
|
||||||
itemQueued = nil
|
itemState = ITEMSTATE_NONITEM
|
||||||
RestoreItemRam()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -702,18 +489,8 @@ function main()
|
||||||
-- Handle the debug data display
|
-- Handle the debug data display
|
||||||
gui.cleartext()
|
gui.cleartext()
|
||||||
if debugEnabled then
|
if debugEnabled then
|
||||||
-- gui.text(0,0,"Item Queued: "..tostring(IsItemQueued()))
|
gui.text(0,0,itemState)
|
||||||
-- gui.text(0,16,"In Battle: "..tostring(IsInBattle()))
|
gui.text(0,16,"Item Index: "..loadItemIndexFromRAM())
|
||||||
-- gui.text(0,32,"In Dialog: "..tostring(IsInDialog()))
|
|
||||||
-- gui.text(0,48,"In Menu: "..tostring(IsInMenu()))
|
|
||||||
gui.text(0,48,"Item Wait Time: "..tostring(itemQueueCounter))
|
|
||||||
gui.text(0,64,itemState)
|
|
||||||
if itemQueued == nil then
|
|
||||||
gui.text(0,80,"No item queued")
|
|
||||||
else
|
|
||||||
gui.text(0,80,itemQueued["type"].." "..itemQueued["itemID"])
|
|
||||||
end
|
|
||||||
gui.text(0,96,"Item Index: "..loadItemIndexFromRAM())
|
|
||||||
end
|
end
|
||||||
|
|
||||||
emu.frameadvance()
|
emu.frameadvance()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from Options import Choice, Range, DefaultOnToggle
|
from dataclasses import dataclass
|
||||||
|
from Options import Choice, Range, DefaultOnToggle, PerGameCommonOptions
|
||||||
|
|
||||||
|
|
||||||
class ExtraRanks(Range):
|
class ExtraRanks(Range):
|
||||||
|
@ -41,8 +42,9 @@ class TradeQuestHinting(Choice):
|
||||||
default = 2
|
default = 2
|
||||||
|
|
||||||
|
|
||||||
MMBN3Options = {
|
@dataclass
|
||||||
"extra_ranks": ExtraRanks,
|
class MMBN3Options(PerGameCommonOptions):
|
||||||
"include_jobs": IncludeJobs,
|
extra_ranks: ExtraRanks
|
||||||
"trade_quest_hinting": TradeQuestHinting,
|
include_jobs: IncludeJobs
|
||||||
}
|
trade_quest_hinting: TradeQuestHinting
|
||||||
|
|
|
@ -7,6 +7,7 @@ from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification, Region,
|
||||||
LocationProgressType
|
LocationProgressType
|
||||||
|
|
||||||
from worlds.AutoWorld import WebWorld, World
|
from worlds.AutoWorld import WebWorld, World
|
||||||
|
|
||||||
from .Rom import MMBN3DeltaPatch, LocalRom, get_base_rom_path
|
from .Rom import MMBN3DeltaPatch, LocalRom, get_base_rom_path
|
||||||
from .Items import MMBN3Item, ItemData, item_table, all_items, item_frequencies, items_by_id, ItemType
|
from .Items import MMBN3Item, ItemData, item_table, all_items, item_frequencies, items_by_id, ItemType
|
||||||
from .Locations import Location, MMBN3Location, all_locations, location_table, location_data_table, \
|
from .Locations import Location, MMBN3Location, all_locations, location_table, location_data_table, \
|
||||||
|
@ -51,7 +52,8 @@ class MMBN3World(World):
|
||||||
threat the Internet has ever faced!
|
threat the Internet has ever faced!
|
||||||
"""
|
"""
|
||||||
game = "MegaMan Battle Network 3"
|
game = "MegaMan Battle Network 3"
|
||||||
option_definitions = MMBN3Options
|
options_dataclass = MMBN3Options
|
||||||
|
options: MMBN3Options
|
||||||
settings: typing.ClassVar[MMBN3Settings]
|
settings: typing.ClassVar[MMBN3Settings]
|
||||||
topology_present = False
|
topology_present = False
|
||||||
|
|
||||||
|
@ -71,10 +73,10 @@ class MMBN3World(World):
|
||||||
Already has access to player options and RNG.
|
Already has access to player options and RNG.
|
||||||
"""
|
"""
|
||||||
self.item_frequencies = item_frequencies.copy()
|
self.item_frequencies = item_frequencies.copy()
|
||||||
if self.multiworld.extra_ranks[self.player] > 0:
|
if self.options.extra_ranks > 0:
|
||||||
self.item_frequencies[ItemName.Progressive_Undernet_Rank] = 8 + self.multiworld.extra_ranks[self.player]
|
self.item_frequencies[ItemName.Progressive_Undernet_Rank] = 8 + self.options.extra_ranks
|
||||||
|
|
||||||
if not self.multiworld.include_jobs[self.player]:
|
if not self.options.include_jobs:
|
||||||
self.excluded_locations = always_excluded_locations + [job.name for job in jobs]
|
self.excluded_locations = always_excluded_locations + [job.name for job in jobs]
|
||||||
else:
|
else:
|
||||||
self.excluded_locations = always_excluded_locations
|
self.excluded_locations = always_excluded_locations
|
||||||
|
@ -160,7 +162,7 @@ class MMBN3World(World):
|
||||||
|
|
||||||
remaining = len(all_locations) - len(required_items)
|
remaining = len(all_locations) - len(required_items)
|
||||||
for i in range(remaining):
|
for i in range(remaining):
|
||||||
filler_item_name = self.multiworld.random.choice(filler_items)
|
filler_item_name = self.random.choice(filler_items)
|
||||||
item = self.create_item(filler_item_name)
|
item = self.create_item(filler_item_name)
|
||||||
self.multiworld.itempool.append(item)
|
self.multiworld.itempool.append(item)
|
||||||
filler_items.remove(filler_item_name)
|
filler_items.remove(filler_item_name)
|
||||||
|
@ -411,10 +413,10 @@ class MMBN3World(World):
|
||||||
long_item_text = ""
|
long_item_text = ""
|
||||||
|
|
||||||
# No item hinting
|
# No item hinting
|
||||||
if self.multiworld.trade_quest_hinting[self.player] == 0:
|
if self.options.trade_quest_hinting == 0:
|
||||||
item_name_text = "Check"
|
item_name_text = "Check"
|
||||||
# Partial item hinting
|
# Partial item hinting
|
||||||
elif self.multiworld.trade_quest_hinting[self.player] == 1:
|
elif self.options.trade_quest_hinting == 1:
|
||||||
if item.progression == ItemClassification.progression \
|
if item.progression == ItemClassification.progression \
|
||||||
or item.progression == ItemClassification.progression_skip_balancing:
|
or item.progression == ItemClassification.progression_skip_balancing:
|
||||||
item_name_text = "Progress"
|
item_name_text = "Progress"
|
||||||
|
@ -466,7 +468,7 @@ class MMBN3World(World):
|
||||||
return MMBN3Item(event, ItemClassification.progression, None, self.player)
|
return MMBN3Item(event, ItemClassification.progression, None, self.player)
|
||||||
|
|
||||||
def fill_slot_data(self):
|
def fill_slot_data(self):
|
||||||
return {name: getattr(self.multiworld, name)[self.player].value for name in self.option_definitions}
|
return self.options.as_dict("extra_ranks", "include_jobs", "trade_quest_hinting")
|
||||||
|
|
||||||
|
|
||||||
def explore_score(self, state):
|
def explore_score(self, state):
|
||||||
|
|
|
@ -18,11 +18,12 @@ on Steam, you can obtain a copy of this ROM from the game's files, see instructi
|
||||||
|
|
||||||
Once Bizhawk has been installed, open Bizhawk and change the following settings:
|
Once Bizhawk has been installed, open Bizhawk and change the following settings:
|
||||||
|
|
||||||
- Go to Config > Customize. Switch to the Advanced tab, then switch the Lua Core from "NLua+KopiLua" to
|
- **If you are using a version of BizHawk older than 2.9**, you will need to modify the Lua Core.
|
||||||
|
Go to Config > Customize. Switch to the Advanced tab, then switch the Lua Core from "NLua+KopiLua" to
|
||||||
"Lua+LuaInterface". This is required for the Lua script to function correctly.
|
"Lua+LuaInterface". This is required for the Lua script to function correctly.
|
||||||
**NOTE: Even if "Lua+LuaInterface" is already selected, toggle between the two options and reselect it. Fresh installs**
|
**NOTE:** Even if "Lua+LuaInterface" is already selected, toggle between the two options and reselect it. Fresh installs
|
||||||
**of newer versions of Bizhawk have a tendency to show "Lua+LuaInterface" as the default selected option but still load**
|
of newer versions of Bizhawk have a tendency to show "Lua+LuaInterface" as the default selected option but still load
|
||||||
**"NLua+KopiLua" until this step is done.**
|
"NLua+KopiLua" until this step is done.
|
||||||
- Under Config > Customize > Advanced, make sure the box for AutoSaveRAM is checked, and click the 5s button.
|
- Under Config > Customize > Advanced, make sure the box for AutoSaveRAM is checked, and click the 5s button.
|
||||||
This reduces the possibility of losing save data in emulator crashes.
|
This reduces the possibility of losing save data in emulator crashes.
|
||||||
- Under Config > Customize, check the "Run in background" and "Accept background input" boxes. This will allow you to
|
- Under Config > Customize, check the "Run in background" and "Accept background input" boxes. This will allow you to
|
||||||
|
@ -37,7 +38,7 @@ and select EmuHawk.exe.
|
||||||
|
|
||||||
## Extracting a ROM from the Legacy Collection
|
## Extracting a ROM from the Legacy Collection
|
||||||
|
|
||||||
The Steam version of the Legacy Collection contains unmodified GBA ROMs in its files. You can extract these for use with Archipelago.
|
The Steam version of the Battle Network Legacy Collection contains unmodified GBA ROMs in its files. You can extract these for use with Archipelago.
|
||||||
|
|
||||||
1. Open the Legacy Collection Vol. 1's Game Files (Right click on the game in your Library, then open Properties -> Installed Files -> Browse)
|
1. Open the Legacy Collection Vol. 1's Game Files (Right click on the game in your Library, then open Properties -> Installed Files -> Browse)
|
||||||
2. Open the file `exe/data/exe3b.dat` in a zip-extracting program such as 7-Zip or WinRAR.
|
2. Open the file `exe/data/exe3b.dat` in a zip-extracting program such as 7-Zip or WinRAR.
|
||||||
|
@ -74,6 +75,8 @@ Once both the client and the emulator are started, you must connect them. Within
|
||||||
menu and select "Lua Console". Click the folder button or press Ctrl+O to open a Lua script.
|
menu and select "Lua Console". Click the folder button or press Ctrl+O to open a Lua script.
|
||||||
|
|
||||||
Navigate to your Archipelago install folder and open `data/lua/connector_mmbn3.lua`.
|
Navigate to your Archipelago install folder and open `data/lua/connector_mmbn3.lua`.
|
||||||
|
**NOTE:** The MMBN3 Lua file depends on other shared Lua files inside of the `data` directory in the Archipelago
|
||||||
|
installation. Do not move this Lua file from its default location or you may run into issues connecting.
|
||||||
|
|
||||||
To connect the client to the multiserver simply put `<address>:<port>` on the textfield on top and press enter (if the
|
To connect the client to the multiserver simply put `<address>:<port>` on the textfield on top and press enter (if the
|
||||||
server uses password, type in the bottom textfield `/connect <address>:<port> [password]`)
|
server uses password, type in the bottom textfield `/connect <address>:<port> [password]`)
|
||||||
|
|
Loading…
Reference in New Issue