Language…
28 users online: 1UPdudes, Abstract,  AmperSam, autisticsceptile1993, badummzi,  Blaagon, CharlieUltra, CroNo, dashlet, Dennsen86, DixyNL, GamesInTweed, GiraffeKiller, Hammerer, Inflagrandy, JezJitzu,  MarioFanGamer, Metal-Yoshi94, MorrieTheMagpie, mtheordinarygamer, Papangu, playagmes169, rafaelfutbal, Romiori, Rykon-V73, Ryrir,  Segment1Zone2, TheOrangeToad - Guests: 297 - Bots: 457
Users: 64,795 (2,369 active)
Latest user: mathew

Loop through all sprites in the level

Looping through all local sprites is no problem (i found existing code for that) but if you want all sprites globally is that possible? More specifically i want their X,Y positions and which sprite it is.

Are non loaded sprites even available before entering their spawn/init zone?


It's possible to check the actual level data at least. $CE contains a 24-bit pointer to the current level's sprite data. You can find information on the actual format of that data over here.

Professional frame-by-frame time wizard. YouTube - Twitter - SMW Glitch List - SMW Randomizer
Originally posted by Thomas
It's possible to check the actual level data at least. $CE contains a 24-bit pointer to the current level's sprite data. You can find information on the actual format of that data over here.


Sounds promising. I am not sure how to loop through them all with lua. Since it is a 24bit pointer is it some sort of special case? Or can i just start at that address then loop through the sprite data 3 byte at the time until the end address is reached?

Is there any example/sample code somewhere?


I don't have an example code, but basically you would take 3 bytes from 0xCE, then use that as a pointer to the ROM address to read the actual sprite data from.

When you parse the sprite data, you have the right idea, though skip the first byte (which is the sprite header byte). From that you can then just go three bytes at a time for each sprite until you reach the FF byte (or FF FE for hacks using LM v3.00+; check bit 5 of the sprite header for that).

There is an exception for the 3-byte rule described on that page, though, which requires a few more lookups. If you're dealing with the original game or older hacks, it's fine to skip that step, but many newer hacks using custom sprites may make use of that feature. If you're not careful about that, you could end up a byte off on the sprite data, resulting in the parsing going screwy.

Professional frame-by-frame time wizard. YouTube - Twitter - SMW Glitch List - SMW Randomizer
Here is what i attempted so far:

Code
  local spritesPointerAddress = memory.read_u24_le(WRAM.sprite_data_pointer) + 0x0
  
  for i=1,256 do
    --First 4 bits for spriteX and spriteY
	local spriteX = bit.band(memory.readbyte(spritesPointerAddress+i), 0x0F)
	local spriteY = bit.band(memory.readbyte(spritesPointerAddress+i+1), 0x0F)
	local spriteNumber = memory.readbyte(spritesPointerAddress+i+2)
  	local spriteData = {["x"]=spriteX, ["y"]=spriteY, ["kind"]=2, ["address"]=spriteNumber,}
    table.insert(levelData.tileMap, spriteData)
  end


This will just give me nonsense data is this on the right track and i just made some small mistakes?
You should increment i by 3, not by 1. Also, your code doesn't skip the sprite data header (change the 0x0 in the first line to 0x1), and it doesn't stop when the sprite data ends. (I don't remember whether lua for loops can increment by something other than 1, but since you need to read the terminator anyways, you're better off just using a while loop.)

Also, next time you should consider mentioning that you're doing this in Lua, since most people tend to assume you're writing ASM with a thread title like that.


Originally posted by randomdude999
Also, your code doesn't skip the sprite data header (change the 0x0 in the first line to 0x1)

It actually does, since i in the loop starts out as 1 rather than 0.

But yes, you should make sure your loop steps i by 3 and not 1, and also keep a lookout for the terminating FF byte. There's also the issue that it doesn't look like you're properly fetching the data; you have the X/Y position as being in the low nibble of the first two bytes, but that's not the case (they're in the upper nibble, plus one bit of Y in the lower nibble).


Pseudo code for that would look like this:

Code
var i = 1;
var b0, b1, id, xPos, yPos;
while ((b0 = memory.readbyte(i++)) != 0xFF) {
  b1 = memory.readbyte(i++);
  id = memory.readbyte(i++);
  yPos = ((b0 & 0x01) << 8) + (b0 & 0xF0);
  xPos = ((b0 & 0x02) << 11) + ((b1 & 0x0F) << 8) + (b1 & 0xF0);
}


Professional frame-by-frame time wizard. YouTube - Twitter - SMW Glitch List - SMW Randomizer
Code
for i=1,256,3 do
    --First 4 bits for spriteX and spriteY
	if (i < 253) then
		local yAdress = memory.readbyte(spritesPointerAddress+i)
		local xAdress = memory.readbyte(spritesPointerAddress+i+1)
	
		local spriteY = bit.lshift(bit.band(yAdress, 0x01), 8) + bit.band(yAdress, 0xF0)
		local spriteX = bit.lshift(bit.band(yAdress, 0x02), 11) + bit.lshift(bit.band(xAdress, 0x0F), 8) + bit.band(xAdress, 0xF0)
		local spriteNumber = memory.readbyte(spritesPointerAddress+i+2)
		local spriteData = {["x"]=spriteX, ["y"]=spriteY, ["kind"]=spriteNumber, 
			["address"]=string.format(" $%4.x", spritesPointerAddress+i),["isSprite"]=true,}
		table.insert(levelData.tileMap, spriteData)
	end
  end


Updated the code to this based on your pseudo code.

The values emitted makes more sense but when checking in Lunar Magic some sprites have XY coordinate far up in the sky were no sprite is ever present. I think its very close to being correct but something is still a bit off


Your error is probably from still having an incorrect terminating condition. You should be explictly checking for the FF end byte to mark the end of the data. If you don't, you'll keep reading in invalid sprites.

Here's an example with my code showing you what you should be getting for level 105, if that helps. You can validate the numbers against LM.

Professional frame-by-frame time wizard. YouTube - Twitter - SMW Glitch List - SMW Randomizer
Originally posted by Thomas
Your error is probably from still having an incorrect terminating condition. You should be explictly checking for the FF end byte to mark the end of the data. If you don't, you'll keep reading in invalid sprites.

Here's an example with my code showing you what you should be getting for level 105, if that helps. You can validate the numbers against LM.


Code
  local spritesPointerAddress = memory.read_u24_le(WRAM.sprite_data_pointer) + 0x0
  local i = 1
  
  while memory.readbyte(spritesPointerAddress+i) ~= 0xFF do
	local yAdress = memory.readbyte(spritesPointerAddress+i)
	local xAdress = memory.readbyte(spritesPointerAddress+i+1)
	--https://www.smwcentral.net/?p=viewthread&t=98597&page=1&pid=1509061#p1509061
	local spriteY = bit.lshift(bit.band(yAdress, 0x01), 8) + bit.band(yAdress, 0xF0)
	local spriteX = bit.lshift(bit.band(yAdress, 0x02), 11) + bit.lshift(bit.band(xAdress, 0x0F), 8) + bit.band(xAdress, 0xF0)
	local spriteNumber = memory.readbyte(spritesPointerAddress+i+2)
	local spriteData = {["x"]=spriteX, ["y"]=spriteY, ["kind"]=spriteNumber, 
		["address"]=string.format(" $%4.x", spritesPointerAddress+i),["isSprite"]=true,}
	print(spriteData)
	table.insert(levelData.tileMap, spriteData)
	i = i + 3
  end


You were right thanks. When i changed the final code to the one above the output matches the one for level 105 exactly. Are they XY values swapped for vertical levels?


Yep. The values are otherwise calculated the same way, though.

Also, if you plan on using this with hacks made in LM v3.00+, you do actually still have to do a little extra work, due to a new sprite system (see the notes at the bottom of this for details). Basically, check bit 5 of the sprite header (the first byte of the sprite data), and if that's set, then you need to parse the data using the new sprite system instead.

Professional frame-by-frame time wizard. YouTube - Twitter - SMW Glitch List - SMW Randomizer