mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00

* Coin items received or found in the Game Corner are now shuffled, locations require Coin Case * Prizesanity option (shuffle Game Corner Prizes) * DexSanity option: location checks for marking Pokémon as caught in your Pokédex. Also an option to set all Pokémon in your Pokédex as seen from the start, to aid in locating them. * Option to randomize the layout of the Rock Tunnel. * Area 1-to-1 mapping: When one instance of a Wild Pokémon in a given area is randomized, all instances of that Pokémon will be the same. So that if a route had 3 different Pokémon before, it will have 3 after randomization. * Option to randomize the moves taught by TMs. * Exact controls for TM/HM compatibility chances. * Option to randomize Pokémon's pallets or set them based on primary type. * Added Cinnabar Gym trainers to Trainersanity and randomized the quiz questions and answers. Getting a correct answer will flag the trainer as defeated so that you can obtain the Trainersanity check without defeating the trainer if you answer correctly.
255 lines
6.8 KiB
Lua
255 lines
6.8 KiB
Lua
local socket = require("socket")
|
|
local json = require('json')
|
|
local math = require('math')
|
|
|
|
local STATE_OK = "Ok"
|
|
local STATE_TENTATIVELY_CONNECTED = "Tentatively Connected"
|
|
local STATE_INITIAL_CONNECTION_MADE = "Initial Connection Made"
|
|
local STATE_UNINITIALIZED = "Uninitialized"
|
|
|
|
local SCRIPT_VERSION = 3
|
|
|
|
local APIndex = 0x1A6E
|
|
local APDeathLinkAddress = 0x00FD
|
|
local APItemAddress = 0x00FF
|
|
local EventFlagAddress = 0x1735
|
|
local MissableAddress = 0x161A
|
|
local HiddenItemsAddress = 0x16DE
|
|
local RodAddress = 0x1716
|
|
local DexSanityAddress = 0x1A71
|
|
local InGameAddress = 0x1A84
|
|
local ClientCompatibilityAddress = 0xFF00
|
|
|
|
local ItemsReceived = nil
|
|
local playerName = nil
|
|
local seedName = nil
|
|
|
|
local deathlink_rec = nil
|
|
local deathlink_send = false
|
|
|
|
local prevstate = ""
|
|
local curstate = STATE_UNINITIALIZED
|
|
local gbSocket = nil
|
|
local frame = 0
|
|
|
|
local u8 = nil
|
|
local wU8 = nil
|
|
local u16
|
|
local compat = nil
|
|
|
|
local function defineMemoryFunctions()
|
|
local memDomain = {}
|
|
local domains = memory.getmemorydomainlist()
|
|
memDomain["rom"] = function() memory.usememorydomain("ROM") end
|
|
memDomain["wram"] = function() memory.usememorydomain("WRAM") end
|
|
return memDomain
|
|
end
|
|
|
|
local memDomain = defineMemoryFunctions()
|
|
u8 = memory.read_u8
|
|
wU8 = memory.write_u8
|
|
u16 = memory.read_u16_le
|
|
function uRange(address, bytes)
|
|
data = memory.readbyterange(address - 1, bytes + 1)
|
|
data[0] = nil
|
|
return data
|
|
end
|
|
|
|
|
|
function table.empty (self)
|
|
for _, _ in pairs(self) do
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
function slice (tbl, s, e)
|
|
local pos, new = 1, {}
|
|
for i = s + 1, e do
|
|
new[pos] = tbl[i]
|
|
pos = pos + 1
|
|
end
|
|
return new
|
|
end
|
|
|
|
function difference(a, b)
|
|
local aa = {}
|
|
for k,v in pairs(a) do aa[v]=true end
|
|
for k,v in pairs(b) do aa[v]=nil end
|
|
local ret = {}
|
|
local n = 0
|
|
for k,v in pairs(a) do
|
|
if aa[v] then n=n+1 ret[n]=v end
|
|
end
|
|
return ret
|
|
end
|
|
|
|
function generateLocationsChecked()
|
|
memDomain.wram()
|
|
events = uRange(EventFlagAddress, 0x140)
|
|
missables = uRange(MissableAddress, 0x20)
|
|
hiddenitems = uRange(HiddenItemsAddress, 0x0E)
|
|
dexsanity = uRange(DexSanityAddress, 19)
|
|
rod = u8(RodAddress)
|
|
|
|
data = {}
|
|
|
|
table.foreach(events, function(k, v) table.insert(data, v) end)
|
|
table.foreach(missables, function(k, v) table.insert(data, v) end)
|
|
table.foreach(hiddenitems, function(k, v) table.insert(data, v) end)
|
|
table.insert(data, rod)
|
|
|
|
if compat > 1 then
|
|
table.foreach(dexsanity, function(k, v) table.insert(data, v) end)
|
|
end
|
|
return data
|
|
end
|
|
|
|
local function arrayEqual(a1, a2)
|
|
if #a1 ~= #a2 then
|
|
return false
|
|
end
|
|
|
|
for i, v in ipairs(a1) do
|
|
if v ~= a2[i] then
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function receive()
|
|
l, e = gbSocket:receive()
|
|
if e == 'closed' then
|
|
if curstate == STATE_OK then
|
|
print("Connection closed")
|
|
end
|
|
curstate = STATE_UNINITIALIZED
|
|
return
|
|
elseif e == 'timeout' then
|
|
return
|
|
elseif e ~= nil then
|
|
print(e)
|
|
curstate = STATE_UNINITIALIZED
|
|
return
|
|
end
|
|
if l ~= nil then
|
|
block = json.decode(l)
|
|
if block ~= nil then
|
|
local itemsBlock = block["items"]
|
|
if itemsBlock ~= nil then
|
|
ItemsReceived = itemsBlock
|
|
end
|
|
deathlink_rec = block["deathlink"]
|
|
|
|
end
|
|
end
|
|
-- Determine Message to send back
|
|
memDomain.rom()
|
|
newPlayerName = uRange(0xFFF0, 0x10)
|
|
newSeedName = uRange(0xFFDB, 21)
|
|
if (playerName ~= nil and not arrayEqual(playerName, newPlayerName)) or (seedName ~= nil and not arrayEqual(seedName, newSeedName)) then
|
|
print("ROM changed, quitting")
|
|
curstate = STATE_UNINITIALIZED
|
|
return
|
|
end
|
|
playerName = newPlayerName
|
|
seedName = newSeedName
|
|
local retTable = {}
|
|
retTable["scriptVersion"] = SCRIPT_VERSION
|
|
|
|
if compat == nil then
|
|
compat = u8(ClientCompatibilityAddress)
|
|
if compat < 2 then
|
|
InGameAddress = 0x1A71
|
|
end
|
|
end
|
|
|
|
retTable["clientCompatibilityVersion"] = compat
|
|
retTable["playerName"] = playerName
|
|
retTable["seedName"] = seedName
|
|
memDomain.wram()
|
|
|
|
in_game = u8(InGameAddress)
|
|
if in_game == 0x2A or in_game == 0xAC then
|
|
retTable["locations"] = generateLocationsChecked()
|
|
elseif in_game ~= 0 then
|
|
print("Game may have crashed")
|
|
curstate = STATE_UNINITIALIZED
|
|
return
|
|
end
|
|
|
|
retTable["deathLink"] = deathlink_send
|
|
deathlink_send = false
|
|
|
|
msg = json.encode(retTable).."\n"
|
|
local ret, error = gbSocket:send(msg)
|
|
if ret == nil then
|
|
print(error)
|
|
elseif curstate == STATE_INITIAL_CONNECTION_MADE then
|
|
curstate = STATE_TENTATIVELY_CONNECTED
|
|
elseif curstate == STATE_TENTATIVELY_CONNECTED then
|
|
print("Connected!")
|
|
curstate = STATE_OK
|
|
end
|
|
end
|
|
|
|
function main()
|
|
if (is23Or24Or25 or is26To28) == false then
|
|
print("Must use a version of bizhawk 2.3.1 or higher")
|
|
return
|
|
end
|
|
server, error = socket.bind('localhost', 17242)
|
|
|
|
while true do
|
|
frame = frame + 1
|
|
if not (curstate == prevstate) then
|
|
print("Current state: "..curstate)
|
|
prevstate = curstate
|
|
end
|
|
if (curstate == STATE_OK) or (curstate == STATE_INITIAL_CONNECTION_MADE) or (curstate == STATE_TENTATIVELY_CONNECTED) then
|
|
if (frame % 5 == 0) then
|
|
receive()
|
|
in_game = u8(InGameAddress)
|
|
if in_game == 0x2A or in_game == 0xAC then
|
|
if u8(APItemAddress) == 0x00 then
|
|
ItemIndex = u16(APIndex)
|
|
if deathlink_rec == true then
|
|
wU8(APDeathLinkAddress, 1)
|
|
elseif u8(APDeathLinkAddress) == 3 then
|
|
wU8(APDeathLinkAddress, 0)
|
|
deathlink_send = true
|
|
end
|
|
if ItemsReceived[ItemIndex + 1] ~= nil then
|
|
item_id = ItemsReceived[ItemIndex + 1] - 172000000
|
|
if item_id > 255 then
|
|
item_id = item_id - 256
|
|
end
|
|
wU8(APItemAddress, item_id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
elseif (curstate == STATE_UNINITIALIZED) then
|
|
if (frame % 60 == 0) then
|
|
|
|
print("Waiting for client.")
|
|
|
|
emu.frameadvance()
|
|
server:settimeout(2)
|
|
print("Attempting to connect")
|
|
local client, timeout = server:accept()
|
|
if timeout == nil then
|
|
curstate = STATE_INITIAL_CONNECTION_MADE
|
|
gbSocket = client
|
|
gbSocket:settimeout(0)
|
|
end
|
|
end
|
|
end
|
|
emu.frameadvance()
|
|
end
|
|
end
|
|
|
|
main()
|