mirror of
				https://github.com/MarioSpore/Grinch-AP.git
				synced 2025-10-21 20:21:32 -06:00 
			
		
		
		
	 d48e1e447f
			
		
	
	d48e1e447f
	
	
	
		
			
			Adds Adventure for the Atari 2600, NTSC version. New randomizer, not based on prior works. Somewhat atypical of current AP rom patch games; The generator does not require the adventure rom, but writes some data to an .apadvn APContainer file that the client uses along with a base bsdiff patch to generate a final rom file.
		
			
				
	
	
		
			852 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			852 lines
		
	
	
		
			31 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 = 1
 | |
| 
 | |
| local APItemValue = 0xA2
 | |
| local APItemRam = 0xE7
 | |
| local BatAPItemValue = 0xAB
 | |
| local BatAPItemRam = 0xEA
 | |
| local PlayerRoomAddr = 0x8A -- if in number room, we're not in play mode
 | |
| local WinAddr = 0xDE -- if not 0 (I think if 0xff specifically), we won (and should update once, immediately)
 | |
| 
 | |
| -- If any of these are 2, that dragon ate the player (should send update immediately
 | |
| -- once, and reset that when none of them are 2 again)
 | |
| 
 | |
| local DragonState = {0xA8, 0xAD, 0xB2}
 | |
| local last_dragon_state = {0, 0, 0}
 | |
| local carryAddress = 0x9D -- uses rom object table
 | |
| local batRoomAddr = 0xCB
 | |
| local batCarryAddress = 0xD0 -- uses ram object location
 | |
| local batInvalidCarryItem = 0x78
 | |
| local batItemCheckAddr = 0xf69f
 | |
| local batMatrixLen = 11 -- number of pairs
 | |
| local last_carry_item = 0xB4
 | |
| local frames_with_no_item = 0
 | |
| local ItemTableStart = 0xfe9d
 | |
| local PlayerSlotAddress = 0xfff9
 | |
| 
 | |
| local itemMessages = {}
 | |
| 
 | |
| local nullObjectId = 0xB4
 | |
| local ItemsReceived = nil
 | |
| local sha256hash = nil
 | |
| local foreign_items = nil
 | |
| local foreign_items_by_room = {}
 | |
| local bat_no_touch_locations_by_room = {}
 | |
| local bat_no_touch_items = {}
 | |
| local autocollect_items = {}
 | |
| local localItemLocations = {}
 | |
| 
 | |
| local prev_bat_room = 0xff
 | |
| local prev_player_room = 0
 | |
| local prev_ap_room_index = nil
 | |
| 
 | |
| local pending_foreign_items_collected = {}
 | |
| local pending_local_items_collected = {}
 | |
| local rendering_foreign_item = nil
 | |
| local skip_inventory_items = {}
 | |
| 
 | |
| local inventory = {}
 | |
| local next_inventory_item = nil
 | |
| 
 | |
| local input_button_address = 0xD7
 | |
| 
 | |
| local deathlink_rec = nil
 | |
| local deathlink_send = 0
 | |
| 
 | |
| local deathlink_sent = false
 | |
| 
 | |
| local prevstate = ""
 | |
| local curstate =  STATE_UNINITIALIZED
 | |
| local atariSocket = nil
 | |
| local frame = 0
 | |
| 
 | |
| local ItemIndex = 0
 | |
| 
 | |
| local yorgle_speed_address = 0xf725
 | |
| local grundle_speed_address = 0xf740
 | |
| local rhindle_speed_address = 0xf70A
 | |
| 
 | |
| local read_switch_a = 0xf780
 | |
| local read_switch_b = 0xf764
 | |
| 
 | |
| local yorgle_speed = nil
 | |
| local grundle_speed = nil
 | |
| local rhindle_speed = nil
 | |
| 
 | |
| local slow_yorgle_id = tostring(118000000 + 0x103)
 | |
| local slow_grundle_id = tostring(118000000 + 0x104)
 | |
| local slow_rhindle_id = tostring(118000000 + 0x105)
 | |
| 
 | |
| local yorgle_dead = false
 | |
| local grundle_dead = false
 | |
| local rhindle_dead = false
 | |
| 
 | |
| local diff_a_locked = false
 | |
| local diff_b_locked = false
 | |
| 
 | |
| local bat_logic = 0
 | |
| 
 | |
| local is_dead = 0
 | |
| local freeincarnates_available = 0
 | |
| local send_freeincarnate_used = false
 | |
| local current_bat_ap_item = nil
 | |
| 
 | |
| local was_in_number_room = false
 | |
| 
 | |
| local u8 = nil
 | |
| local wU8 = nil
 | |
| local u16
 | |
| 
 | |
| local bizhawk_version = client.getversion()
 | |
| local is23Or24Or25 = (bizhawk_version=="2.3.1") or (bizhawk_version:sub(1,3)=="2.4") or (bizhawk_version:sub(1,3)=="2.5")
 | |
| local is26To28 =  (bizhawk_version:sub(1,3)=="2.6") or (bizhawk_version:sub(1,3)=="2.7") or (bizhawk_version:sub(1,3)=="2.8")
 | |
| 
 | |
| u8 = memory.read_u8
 | |
| wU8 = memory.write_u8
 | |
| u16 = memory.read_u16_le
 | |
| function uRangeRam(address, bytes)
 | |
| 	data = memory.read_bytes_as_array(address, bytes, "Main RAM")
 | |
| 	return data
 | |
| end
 | |
| function uRangeRom(address, bytes)
 | |
| 	data = memory.read_bytes_as_array(address+0xf000, bytes, "System Bus")
 | |
| 	return data
 | |
| end
 | |
| function uRangeAddress(address, bytes)
 | |
| 	data = memory.read_bytes_as_array(address, bytes, "System Bus")
 | |
| 	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
 | |
| 
 | |
| local function createForeignItemsByRoom()
 | |
|     foreign_items_by_room = {}
 | |
|     if foreign_items == nil then
 | |
|         return
 | |
|     end
 | |
|     for _, foreign_item in pairs(foreign_items) do
 | |
|         if foreign_items_by_room[foreign_item.room_id] == nil then
 | |
|             foreign_items_by_room[foreign_item.room_id] = {}
 | |
|         end
 | |
|         new_foreign_item = {}
 | |
|         new_foreign_item.room_id = foreign_item.room_id
 | |
|         new_foreign_item.room_x = foreign_item.room_x
 | |
|         new_foreign_item.room_y = foreign_item.room_y
 | |
|         new_foreign_item.short_location_id = foreign_item.short_location_id
 | |
| 
 | |
|         table.insert(foreign_items_by_room[foreign_item.room_id], new_foreign_item)
 | |
|     end
 | |
| end
 | |
| 
 | |
| function debugPrintNoTouchLocations()
 | |
|     for room_id, list in pairs(bat_no_touch_locations_by_room) do
 | |
|         for index, notouch_location in ipairs(list) do
 | |
|             print("ROOM "..tostring(room_id).. "["..tostring(index).."]: "..tostring(notouch_location.short_location_id))
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| function processBlock(block)
 | |
|     if block == nil then
 | |
|         return
 | |
|     end
 | |
|     local block_identified = 0
 | |
|     local msgBlock = block['messages']
 | |
|     if msgBlock ~= nil then
 | |
|         block_identified = 1
 | |
|         for i, v in pairs(msgBlock) do
 | |
|             if itemMessages[i] == nil then
 | |
|                 local msg = {TTL=450, message=v, color=0xFFFF0000}
 | |
|                 itemMessages[i] = msg
 | |
|             end
 | |
|         end
 | |
|     end
 | |
|     local itemsBlock = block["items"]
 | |
|     if itemsBlock ~= nil then
 | |
|         block_identified = 1
 | |
| 	    ItemsReceived = itemsBlock
 | |
|     end
 | |
|     local apItemsBlock = block["foreign_items"]
 | |
|     if apItemsBlock ~= nil then
 | |
|         block_identified = 1
 | |
|         print("got foreign items block")
 | |
|         foreign_items = apItemsBlock
 | |
|         createForeignItemsByRoom()
 | |
|     end
 | |
|     local autocollectItems = block["autocollect_items"]
 | |
|     if autocollectItems ~= nil then
 | |
|         block_identified = 1
 | |
|         autocollect_items = {}
 | |
|         for _, acitem in pairs(autocollectItems) do
 | |
|             if autocollect_items[acitem.room_id] == nil then
 | |
|                 autocollect_items[acitem.room_id] = {}
 | |
|             end
 | |
|             table.insert(autocollect_items[acitem.room_id], acitem)
 | |
|         end
 | |
|     end
 | |
|     local localLocalItemLocations = block["local_item_locations"]
 | |
|     if localLocalItemLocations ~= nil then
 | |
|         block_identified = 1
 | |
|         localItemLocations = localLocalItemLocations
 | |
|         print("got local item locations")
 | |
|     end
 | |
|     local checkedLocationsBlock = block["checked_locations"]
 | |
|     if checkedLocationsBlock ~= nil then
 | |
|         block_identified = 1
 | |
|         for room_id, foreign_item_list in pairs(foreign_items_by_room) do
 | |
|             for i, foreign_item in pairs(foreign_item_list) do
 | |
|                 short_id = foreign_item.short_location_id
 | |
|                 for j, checked_id in pairs(checkedLocationsBlock) do
 | |
|                     if checked_id == short_id then
 | |
|                         table.remove(foreign_item_list, i)
 | |
|                         break
 | |
|                     end
 | |
|                 end
 | |
|             end
 | |
|         end
 | |
|         if foreign_items ~= nil then
 | |
|             for i, foreign_item in pairs(foreign_items) do
 | |
|                 short_id = foreign_item.short_location_id
 | |
|                 for j, checked_id in pairs(checkedLocationsBlock) do
 | |
|                     if checked_id == short_id then
 | |
|                         foreign_items[i] = nil
 | |
|                         break
 | |
|                     end
 | |
|                 end
 | |
|             end
 | |
|         end
 | |
|     end
 | |
|     local dragon_speeds_block = block["dragon_speeds"]
 | |
|     if dragon_speeds_block ~= nil then
 | |
|         block_identified = 1
 | |
|         yorgle_speed = dragon_speeds_block[slow_yorgle_id]
 | |
|         grundle_speed = dragon_speeds_block[slow_grundle_id]
 | |
|         rhindle_speed = dragon_speeds_block[slow_rhindle_id]
 | |
|     end
 | |
|     local diff_a_block = block["difficulty_a_locked"]
 | |
|     if diff_a_block ~= nil then
 | |
|         block_identified = 1
 | |
|         diff_a_locked = diff_a_block
 | |
|     end
 | |
|     local diff_b_block = block["difficulty_b_locked"]
 | |
|     if diff_b_block ~= nil then
 | |
|         block_identified = 1
 | |
|         diff_b_locked = diff_b_block
 | |
|     end
 | |
|     local freeincarnates_available_block = block["freeincarnates_available"]
 | |
|     if freeincarnates_available_block ~= nil then
 | |
|         block_identified = 1
 | |
|         if freeincarnates_available ~= freeincarnates_available_block then
 | |
|             freeincarnates_available = freeincarnates_available_block
 | |
|             local msg = {TTL=450, message="freeincarnates: "..tostring(freeincarnates_available), color=0xFFFF0000}
 | |
|             itemMessages[-2] = msg
 | |
|         end
 | |
|     end
 | |
|     local bat_logic_block = block["bat_logic"]
 | |
|     if bat_logic_block ~= nil then
 | |
|         block_identified = 1
 | |
|         bat_logic = bat_logic_block
 | |
|     end
 | |
|     local bat_no_touch_locations_block = block["bat_no_touch_locations"]
 | |
|     if bat_no_touch_locations_block ~= nil then
 | |
|         block_identified = 1
 | |
|         for _, notouch_location in pairs(bat_no_touch_locations_block) do
 | |
|             local room_id = tonumber(notouch_location.room_id)
 | |
|             if bat_no_touch_locations_by_room[room_id] == nil then
 | |
|                 bat_no_touch_locations_by_room[room_id] = {}
 | |
|             end
 | |
|             table.insert(bat_no_touch_locations_by_room[room_id], notouch_location)
 | |
| 
 | |
|             if notouch_location.local_item ~= nil and notouch_location.local_item ~= 255 then
 | |
|                 bat_no_touch_items[tonumber(notouch_location.local_item)] = true
 | |
|                 -- print("no touch: "..tostring(notouch_location.local_item))
 | |
|             end
 | |
|         end
 | |
|         -- debugPrintNoTouchLocations()
 | |
|     end
 | |
|     deathlink_rec = deathlink_rec or block["deathlink"]
 | |
|     if( block_identified == 0 ) then
 | |
|         print("unidentified block")
 | |
|         print(block)
 | |
|     end
 | |
| end
 | |
| 
 | |
| local function clearScreen()
 | |
|     if is23Or24Or25 then
 | |
|         return
 | |
|     elseif is26To28 then
 | |
|         drawText(0, 0, "", "black")
 | |
|     end
 | |
| end
 | |
| 
 | |
| local function getMaxMessageLength()
 | |
|     if is23Or24Or25 then
 | |
|         return client.screenwidth()/11
 | |
|     elseif is26To28 then
 | |
|         return client.screenwidth()/12
 | |
|     end
 | |
| end
 | |
| 
 | |
| function drawText(x, y, message, color)
 | |
|     if is23Or24Or25 then
 | |
|         gui.addmessage(message)
 | |
|     elseif is26To28 then
 | |
|         gui.drawText(x, y, message, color, 0xB0000000, 18, "Courier New", nil, nil, nil, "client")
 | |
|     end
 | |
| end
 | |
| 
 | |
| local function drawMessages()
 | |
|     if table.empty(itemMessages) then
 | |
|         clearScreen()
 | |
|         return
 | |
|     end
 | |
|     local y = 10
 | |
|     found = false
 | |
|     maxMessageLength = getMaxMessageLength()
 | |
|     for k, v in pairs(itemMessages) do
 | |
|         if v["TTL"] > 0 then
 | |
|             message = v["message"]
 | |
|             while true do
 | |
|                 drawText(5, y, message:sub(1, maxMessageLength), v["color"])
 | |
|                 y = y + 16
 | |
| 
 | |
|                 message = message:sub(maxMessageLength + 1, message:len())
 | |
|                 if message:len() == 0 then
 | |
|                     break
 | |
|                 end
 | |
|             end
 | |
|             newTTL = 0
 | |
|             if is26To28 then
 | |
|                 newTTL = itemMessages[k]["TTL"] - 1
 | |
|             end
 | |
|             itemMessages[k]["TTL"] = newTTL
 | |
|             found = true
 | |
|         end
 | |
|     end
 | |
|     if found == false then
 | |
|         clearScreen()
 | |
|     end
 | |
| 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 getAllRam()
 | |
|     uRangeRAM(0,128);
 | |
|     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
 | |
| 
 | |
| local function alive_mode()
 | |
|     return (u8(PlayerRoomAddr) ~= 0x00 and u8(WinAddr) == 0x00)
 | |
| end
 | |
| 
 | |
| local function generateLocationsChecked()
 | |
|     list_of_locations = {}
 | |
|     for s, f in pairs(pending_foreign_items_collected) do
 | |
|         table.insert(list_of_locations, f.short_location_id + 118000000)
 | |
|     end
 | |
|     for s, f in pairs(pending_local_items_collected) do
 | |
|         table.insert(list_of_locations, f + 118000000)
 | |
|     end
 | |
|     return list_of_locations
 | |
| end
 | |
| 
 | |
| function receive()
 | |
|     l, e = atariSocket: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
 | |
|         processBlock(json.decode(l))
 | |
|     end
 | |
|     -- Determine Message to send back
 | |
| 
 | |
|     newSha256 = memory.hash_region(0xF000, 0x1000, "System Bus")
 | |
|     if (sha256hash ~= nil and sha256hash ~= newSha256) then
 | |
|         print("ROM changed, quitting")
 | |
|         curstate = STATE_UNINITIALIZED
 | |
|         return
 | |
|     end
 | |
|     sha256hash = newSha256
 | |
|     local retTable = {}
 | |
|     retTable["scriptVersion"] = SCRIPT_VERSION
 | |
|     retTable["romhash"] = sha256hash
 | |
|     if (alive_mode()) then
 | |
|         retTable["locations"] = generateLocationsChecked()
 | |
|     end
 | |
|     if (u8(WinAddr) ~= 0x00) then
 | |
|         retTable["victory"] = 1
 | |
|     end
 | |
|     if( deathlink_sent or deathlink_send == 0 ) then
 | |
|         retTable["deathLink"] = 0
 | |
|     else
 | |
|         print("Sending deathlink "..tostring(deathlink_send))
 | |
|         retTable["deathLink"] = deathlink_send
 | |
|         deathlink_sent = true
 | |
|     end
 | |
|     deathlink_send = 0
 | |
| 
 | |
|     if send_freeincarnate_used == true then
 | |
|         print("Sending freeincarnate used")
 | |
|         retTable["freeincarnate"] = true
 | |
|         send_freeincarnate_used = false
 | |
|     end
 | |
| 
 | |
|     msg = json.encode(retTable).."\n"
 | |
|     local ret, error = atariSocket: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 AutocollectFromRoom()
 | |
|     if autocollect_items ~= nil and autocollect_items[prev_player_room] ~= nil then
 | |
|         for _, item in pairs(autocollect_items[prev_player_room]) do
 | |
|             pending_foreign_items_collected[item.short_location_id] = item
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| function SetYorgleSpeed()
 | |
|     if yorgle_speed ~= nil then
 | |
|         emu.setregister("A", yorgle_speed);
 | |
|     end
 | |
| end
 | |
| 
 | |
| function SetGrundleSpeed()
 | |
|     if grundle_speed ~= nil then
 | |
|         emu.setregister("A", grundle_speed);
 | |
|     end
 | |
| end
 | |
| 
 | |
| function SetRhindleSpeed()
 | |
|     if rhindle_speed ~= nil then
 | |
|         emu.setregister("A", rhindle_speed);
 | |
|     end
 | |
| end
 | |
| 
 | |
| function SetDifficultySwitchB()
 | |
|     if diff_b_locked then
 | |
|         local a = emu.getregister("A")
 | |
|         if a < 128 then
 | |
|             emu.setregister("A", a + 128)
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| function SetDifficultySwitchA()
 | |
|     if diff_a_locked then
 | |
|         local a = emu.getregister("A")
 | |
|         if (a > 128 and a < 128 + 64) or (a < 64) then
 | |
|             emu.setregister("A", a + 64)
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| function TryFreeincarnate()
 | |
|     if freeincarnates_available > 0 then
 | |
|         freeincarnates_available = freeincarnates_available - 1
 | |
|         for index, state_addr in pairs(DragonState) do
 | |
|             if last_dragon_state[index] == 1 then
 | |
|                 send_freeincarnate_used = true
 | |
|                 memory.write_u8(state_addr, 1, "System Bus")
 | |
|                 local msg = {TTL=450, message="used freeincarnate", color=0xFF00FF00}
 | |
|                 itemMessages[-1] = msg
 | |
|             end
 | |
|         end
 | |
| 
 | |
|     end
 | |
| end
 | |
| 
 | |
| function GetLinkedObject()
 | |
|     if emu.getregister("X") == batRoomAddr then
 | |
|         bat_interest_item = emu.getregister("A")
 | |
|         -- if the bat can't touch that item, we'll switch it to the number item, which should never be
 | |
|         -- in the same room as the bat.
 | |
|         if bat_no_touch_items[bat_interest_item] ~= nil then
 | |
|             emu.setregister("A", 0xDD )
 | |
|             emu.setregister("Y", 0xDD )
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| function CheckCollectAPItem(carry_item, target_item_value, target_item_ram, rendering_foreign_item)
 | |
|     if( carry_item == target_item_value and rendering_foreign_item ~= nil ) then
 | |
|         memory.write_u8(carryAddress, nullObjectId, "System Bus")
 | |
|         memory.write_u8(target_item_ram, 0xFF, "System Bus")
 | |
|         pending_foreign_items_collected[rendering_foreign_item.short_location_id] = rendering_foreign_item
 | |
|         for index, fi in pairs(foreign_items_by_room[rendering_foreign_item.room_id]) do
 | |
|             if( fi.short_location_id == rendering_foreign_item.short_location_id ) then
 | |
|                 table.remove(foreign_items_by_room[rendering_foreign_item.room_id], index)
 | |
|                 break
 | |
|             end
 | |
|         end
 | |
|         for index, fi in pairs(foreign_items) do
 | |
|             if( fi.short_location_id == rendering_foreign_item.short_location_id ) then
 | |
|                 foreign_items[index] = nil
 | |
|                 break
 | |
|             end
 | |
|         end
 | |
|         prev_ap_room_index = 0
 | |
|         return true
 | |
|     end
 | |
|     return false
 | |
| end
 | |
| 
 | |
| function BatCanTouchForeign(foreign_item, bat_room)
 | |
|     if bat_no_touch_locations_by_room[bat_room] == nil or bat_no_touch_locations_by_room[bat_room][1] == nil then
 | |
|         return true
 | |
|     end
 | |
| 
 | |
|     for index, location in ipairs(bat_no_touch_locations_by_room[bat_room]) do
 | |
|         if location.short_location_id == foreign_item.short_location_id then
 | |
|             return false
 | |
|         end
 | |
|     end
 | |
|     return true;
 | |
| end
 | |
| 
 | |
| function main()
 | |
|     memory.usememorydomain("System Bus")
 | |
|     if (is23Or24Or25 or is26To28) == false then
 | |
|         print("Must use a version of bizhawk 2.3.1 or higher")
 | |
|         return
 | |
|     end
 | |
|     local playerSlot = memory.read_u8(PlayerSlotAddress)
 | |
|     local port = 17242 + playerSlot
 | |
|     print("Using port"..tostring(port))
 | |
|     server, error = socket.bind('localhost', port)
 | |
|     if( error ~= nil ) then
 | |
|         print(error)
 | |
|     end
 | |
|     event.onmemoryexecute(SetYorgleSpeed, yorgle_speed_address);
 | |
|     event.onmemoryexecute(SetGrundleSpeed, grundle_speed_address);
 | |
|     event.onmemoryexecute(SetRhindleSpeed, rhindle_speed_address);
 | |
|     event.onmemoryexecute(SetDifficultySwitchA, read_switch_a)
 | |
|     event.onmemoryexecute(SetDifficultySwitchB, read_switch_b)
 | |
|     event.onmemoryexecute(GetLinkedObject, batItemCheckAddr)
 | |
|     -- TODO: Add an onmemoryexecute event to intercept the bat reading item rooms, and don't 'see' an item in the
 | |
|     -- room if it is in bat_no_touch_locations_by_room.  Although realistically, I may have to handle this in the rom
 | |
|     -- for it to be totally reliable, because it won't work before the script connects (I might have to reset them?)
 | |
|     -- TODO: Also remove those items from the bat_no_touch_locations_by_room if they have been collected
 | |
|     while true do
 | |
|         frame = frame + 1
 | |
|         drawMessages()
 | |
|         if not (curstate == prevstate) then
 | |
|             print("Current state: "..curstate)
 | |
|             prevstate = curstate
 | |
|         end
 | |
| 
 | |
|         local current_player_room = u8(PlayerRoomAddr)
 | |
|         local bat_room = u8(batRoomAddr)
 | |
|         local bat_carrying_item = u8(batCarryAddress)
 | |
|         local bat_carrying_ap_item = (BatAPItemRam == bat_carrying_item)
 | |
| 
 | |
|         if current_player_room == 0x1E then
 | |
|             if u8(PlayerRoomAddr + 1) > 0x4B then
 | |
|                 memory.write_u8(PlayerRoomAddr + 1, 0x4B)
 | |
|             end
 | |
|         end
 | |
| 
 | |
|         if current_player_room == 0x00 then
 | |
|             if not was_in_number_room then
 | |
|                 print("reset "..tostring(bat_carrying_ap_item).." "..tostring(bat_carrying_item))
 | |
|                 memory.write_u8(batCarryAddress, batInvalidCarryItem)
 | |
|                 memory.write_u8(batCarryAddress+ 1, 0)
 | |
|                 createForeignItemsByRoom()
 | |
|                 memory.write_u8(BatAPItemRam, 0xff)
 | |
|                 memory.write_u8(APItemRam, 0xff)
 | |
|                 prev_ap_room_index = 0
 | |
|                 prev_player_room = 0
 | |
|                 rendering_foreign_item = nil
 | |
|                 was_in_number_room = true
 | |
|             end
 | |
|         else
 | |
|             was_in_number_room = false
 | |
|         end
 | |
| 
 | |
|         if bat_room ~= prev_bat_room then
 | |
|             if bat_carrying_ap_item then
 | |
|                 if foreign_items_by_room[prev_bat_room] ~= nil then
 | |
|                     for r,f in pairs(foreign_items_by_room[prev_bat_room]) do
 | |
|                         if f.short_location_id == current_bat_ap_item.short_location_id then
 | |
|                             -- print("removing item from "..tostring(r).." in "..tostring(prev_bat_room))
 | |
|                             table.remove(foreign_items_by_room[prev_bat_room], r)
 | |
|                             break
 | |
|                         end
 | |
|                     end
 | |
|                 end
 | |
|                 if foreign_items_by_room[bat_room] == nil then
 | |
|                     foreign_items_by_room[bat_room] = {}
 | |
|                 end
 | |
|                 -- print("adding item to "..tostring(bat_room))
 | |
|                 table.insert(foreign_items_by_room[bat_room], current_bat_ap_item)
 | |
|             else
 | |
|                 -- set AP item room and position for new room, or to invalid room
 | |
|                 if foreign_items_by_room[bat_room] ~= nil and foreign_items_by_room[bat_room][1] ~= nil
 | |
|                             and BatCanTouchForeign(foreign_items_by_room[bat_room][1], bat_room) then
 | |
|                     if current_bat_ap_item ~= foreign_items_by_room[bat_room][1] then
 | |
|                         current_bat_ap_item = foreign_items_by_room[bat_room][1]
 | |
|                         -- print("Changing bat item to "..tostring(current_bat_ap_item.short_location_id))
 | |
|                     end
 | |
|                     memory.write_u8(BatAPItemRam, bat_room)
 | |
|                     memory.write_u8(BatAPItemRam + 1, current_bat_ap_item.room_x)
 | |
|                     memory.write_u8(BatAPItemRam + 2, current_bat_ap_item.room_y)
 | |
|                 else
 | |
|                     memory.write_u8(BatAPItemRam, 0xff)
 | |
|                     if current_bat_ap_item ~= nil then
 | |
|                         -- print("clearing bat item")
 | |
|                     end
 | |
|                     current_bat_ap_item = nil
 | |
|                 end
 | |
|             end
 | |
|         end
 | |
|         prev_bat_room = bat_room
 | |
| 
 | |
|         -- update foreign_items_by_room position and room id for bat item if bat carrying an item
 | |
|         if bat_carrying_ap_item then
 | |
|             -- this is setting the item using the bat's position, which is somewhat wrong, but I think
 | |
|             -- there will be more problems with the room not matching sometimes if I use the actual item position
 | |
|             current_bat_ap_item.room_id = bat_room
 | |
|             current_bat_ap_item.room_x = u8(batRoomAddr + 1)
 | |
|             current_bat_ap_item.room_y = u8(batRoomAddr + 2)
 | |
|         end
 | |
| 
 | |
|         if (alive_mode()) then
 | |
|             if (current_player_room ~= prev_player_room) then
 | |
|                 memory.write_u8(APItemRam, 0xFF, "System Bus")
 | |
|                 prev_ap_room_index = 0
 | |
|                 prev_player_room = current_player_room
 | |
|                 AutocollectFromRoom()
 | |
|             end
 | |
|             local carry_item = memory.read_u8(carryAddress, "System Bus")
 | |
|             bat_no_touch_items[carry_item] = nil
 | |
|             if (next_inventory_item ~= nil) then
 | |
|                 if ( carry_item == nullObjectId and last_carry_item == nullObjectId ) then
 | |
|                     frames_with_no_item = frames_with_no_item + 1
 | |
|                     if (frames_with_no_item > 10) then
 | |
|                         frames_with_no_item = 10
 | |
|                         local input_value = memory.read_u8(input_button_address, "System Bus")
 | |
|                         if( input_value >= 64 and input_value < 128 ) then -- high bit clear, second highest bit set
 | |
|                             memory.write_u8(carryAddress, next_inventory_item)
 | |
|                             local item_ram_location = memory.read_u8(ItemTableStart + next_inventory_item)
 | |
|                             if( memory.read_u8(batCarryAddress) ~= 0x78 and
 | |
|                                     memory.read_u8(batCarryAddress) == item_ram_location) then
 | |
|                                 memory.write_u8(batCarryAddress, batInvalidCarryItem)
 | |
|                                 memory.write_u8(batCarryAddress+ 1, 0)
 | |
|                                 memory.write_u8(item_ram_location, current_player_room)
 | |
|                                 memory.write_u8(item_ram_location + 1, memory.read_u8(PlayerRoomAddr + 1))
 | |
|                                 memory.write_u8(item_ram_location + 2, memory.read_u8(PlayerRoomAddr + 2))
 | |
|                             end
 | |
|                             ItemIndex = ItemIndex + 1
 | |
|                             next_inventory_item = nil
 | |
|                         end
 | |
|                     end
 | |
|                 else
 | |
|                     frames_with_no_item = 0
 | |
|                 end
 | |
|             end
 | |
|             if( carry_item ~= last_carry_item ) then
 | |
|                 if ( localItemLocations ~= nil and localItemLocations[tostring(carry_item)] ~= nil ) then
 | |
|                     pending_local_items_collected[localItemLocations[tostring(carry_item)]] =
 | |
|                         localItemLocations[tostring(carry_item)]
 | |
|                     table.remove(localItemLocations, tostring(carry_item))
 | |
|                     skip_inventory_items[carry_item] = carry_item
 | |
|                 end
 | |
|             end
 | |
|             last_carry_item = carry_item
 | |
| 
 | |
|             CheckCollectAPItem(carry_item, APItemValue, APItemRam, rendering_foreign_item)
 | |
|             if CheckCollectAPItem(carry_item, BatAPItemValue, BatAPItemRam, current_bat_ap_item) and bat_carrying_ap_item then
 | |
|                 memory.write_u8(batCarryAddress, batInvalidCarryItem)
 | |
|                 memory.write_u8(batCarryAddress+ 1, 0)
 | |
|             end
 | |
| 
 | |
| 
 | |
|             rendering_foreign_item = nil
 | |
|             if( foreign_items_by_room[current_player_room] ~= nil ) then
 | |
|                 if( foreign_items_by_room[current_player_room][prev_ap_room_index] ~= nil ) and memory.read_u8(APItemRam) ~= 0xff then
 | |
|                     foreign_items_by_room[current_player_room][prev_ap_room_index].room_x = memory.read_u8(APItemRam + 1)
 | |
|                     foreign_items_by_room[current_player_room][prev_ap_room_index].room_y = memory.read_u8(APItemRam + 2)
 | |
|                 end
 | |
|                 prev_ap_room_index = prev_ap_room_index + 1
 | |
|                 local invalid_index = -1
 | |
|                 if( foreign_items_by_room[current_player_room][prev_ap_room_index] == nil ) then
 | |
|                     prev_ap_room_index = 1
 | |
|                 end
 | |
|                 if( foreign_items_by_room[current_player_room][prev_ap_room_index] ~= nil and current_bat_ap_item ~= nil and
 | |
|                     foreign_items_by_room[current_player_room][prev_ap_room_index].short_location_id == current_bat_ap_item.short_location_id) then
 | |
|                     invalid_index = prev_ap_room_index
 | |
|                     prev_ap_room_index = prev_ap_room_index + 1
 | |
|                     if( foreign_items_by_room[current_player_room][prev_ap_room_index] == nil ) then
 | |
|                         prev_ap_room_index = 1
 | |
|                     end
 | |
|                 end
 | |
| 
 | |
|                 if( foreign_items_by_room[current_player_room][prev_ap_room_index] ~= nil and prev_ap_room_index ~= invalid_index ) then
 | |
|                     memory.write_u8(APItemRam, current_player_room)
 | |
|                     rendering_foreign_item = foreign_items_by_room[current_player_room][prev_ap_room_index]
 | |
|                     memory.write_u8(APItemRam + 1, rendering_foreign_item.room_x)
 | |
|                     memory.write_u8(APItemRam + 2, rendering_foreign_item.room_y)
 | |
|                 else
 | |
|                     memory.write_u8(APItemRam, 0xFF, "System Bus")
 | |
|                 end
 | |
|             end
 | |
|             if is_dead == 0 then
 | |
|                 dragons_revived = false
 | |
|                 player_dead = false
 | |
|                 new_dragon_state = {0,0,0}
 | |
|                 for index, dragon_state_addr in pairs(DragonState) do
 | |
|                     new_dragon_state[index] = memory.read_u8(dragon_state_addr, "System Bus" )
 | |
|                     if last_dragon_state[index] == 1 and new_dragon_state[index] ~= 1 then
 | |
|                         dragons_revived = true
 | |
|                     elseif last_dragon_state[index] ~= 1 and new_dragon_state[index] == 1 then
 | |
|                         dragon_real_index = index - 1
 | |
|                         print("Killed dragon: "..tostring(dragon_real_index))
 | |
|                         local dragon_item = {}
 | |
|                         dragon_item["short_location_id"] = 0xD0 + dragon_real_index
 | |
|                         pending_foreign_items_collected[dragon_item.short_location_id] = dragon_item
 | |
|                     end
 | |
|                     if new_dragon_state[index] == 2 then
 | |
|                         player_dead = true
 | |
|                     end
 | |
|                 end
 | |
|                 if dragons_revived and player_dead == false then
 | |
|                     TryFreeincarnate()
 | |
|                 end
 | |
|                 last_dragon_state = new_dragon_state
 | |
|             end
 | |
|         elseif (u8(PlayerRoomAddr) == 0x00) then -- not alive mode, in number room
 | |
|             ItemIndex = 0  -- reset our inventory
 | |
|             next_inventory_item = nil
 | |
|             skip_inventory_items = {}
 | |
|         end
 | |
|         if (curstate == STATE_OK) or (curstate == STATE_INITIAL_CONNECTION_MADE) or (curstate == STATE_TENTATIVELY_CONNECTED) then
 | |
|             if (frame % 5 == 0) then
 | |
|                 receive()
 | |
|                 if alive_mode() then
 | |
|                     local was_dead = is_dead
 | |
|                     is_dead = 0
 | |
|                     for index, dragonStateAddr in pairs(DragonState) do
 | |
|                         local dragonstateval = memory.read_u8(dragonStateAddr, "System Bus")
 | |
|                         if ( dragonstateval == 2) then
 | |
|                             is_dead = index
 | |
|                         end
 | |
|                     end
 | |
|                     if was_dead ~= 0 and is_dead == 0 then
 | |
|                         TryFreeincarnate()
 | |
|                     end
 | |
|                     if deathlink_rec == true and is_dead == 0 then
 | |
|                         print("setting dead from deathlink")
 | |
|                         deathlink_rec = false
 | |
|                         deathlink_sent = true
 | |
|                         is_dead = 1
 | |
|                         memory.write_u8(carryAddress, nullObjectId, "System Bus")
 | |
|                         memory.write_u8(DragonState[1], 2, "System Bus")
 | |
|                     end
 | |
|                     if (is_dead > 0 and deathlink_send == 0 and not deathlink_sent) then
 | |
|                         deathlink_send = is_dead
 | |
|                         print("setting deathlink_send to "..tostring(is_dead))
 | |
|                     elseif (is_dead == 0) then
 | |
|                         deathlink_send = 0
 | |
|                         deathlink_sent = false
 | |
|                     end
 | |
|                     if ItemsReceived ~= nil and ItemsReceived[ItemIndex + 1] ~= nil then
 | |
|                         while ItemsReceived[ItemIndex + 1] ~= nil and skip_inventory_items[ItemsReceived[ItemIndex + 1]] ~= nil do
 | |
|                             print("skip")
 | |
|                             ItemIndex = ItemIndex + 1
 | |
|                         end
 | |
|                         local static_id = ItemsReceived[ItemIndex + 1]
 | |
|                         if static_id ~= nil then
 | |
|                             inventory[static_id] = 1
 | |
|                             if next_inventory_item == nil then
 | |
|                                 next_inventory_item = static_id
 | |
|                             end
 | |
|                         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
 | |
|                     print("Initial connection made")
 | |
|                     curstate = STATE_INITIAL_CONNECTION_MADE
 | |
|                     atariSocket = client
 | |
|                     atariSocket:settimeout(0)
 | |
|                 end
 | |
|             end
 | |
|         end
 | |
|         emu.frameadvance()
 | |
|     end
 | |
| end
 | |
| 
 | |
| main()
 |