Post subject: Threads of Fate (US) / Dewprism (JP)
Joined: 4/21/2021
Posts: 6
Hey all, recently started trying to TAS this game I played when I was young and also to help my friend investigate new potential strats for his category and just general mechanics. Currently I've just been going through it with TAS Studio and just doing some auto holds here and there as luckily the game is largely cutscenes, however trying to optimise sections where I have control of the character has essentially been brute forcing inputs and using the section fade out as my marker if whether I've improved something or not. I tried my hand at doing some RAM Search to get values like XYZ Position/Speed but a lot of the numbers seems to be calculated in large values like "46219219" and I can't quite figure it out. The second problem that I've encountered is if I make some significant improvements the RNG on boss behaviour changes and trying to manipulate it to get the fastest moves has been even worse as what I've been able to guess changes the RNG are - base RNG - character position - boss hitting character (maybe root is the position change from getting hit) - character hitting boss (maybe button presses affects it) - if character is airborne (maybe button presses, maybe y positioning change?) So if anyone can point me in the right direction on how to figure these out that'd be greatly appreciated as I would love to have some confirmation that I am doing the fastest possible things and maybe not lose my mind at manipulating boss behaviours. Here's the most recent vid that I've recorded so far https://youtu.be/T1rQnWZyOjc the first 4 minutes is basically just a long cutscene so feel free to skip that but I have a further and improved version currently that I've just not recorded as I haven't made much progress on the next few sections.
Editor, Skilled player (1275)
Joined: 1/31/2010
Posts: 327
Location: France
You should first find where the RNG is in the RAM. It is probably a value which change very often, and if you freeze it then you will always get the same random event. Just in case you should check the address 0x9010 (4 byte) because that's the default RNG address for a lot of PSX games.
Joined: 4/21/2021
Posts: 6
Yeah I was trying to isolate the RNG address but there was so many addresses that kept changing per frame it seemed so hard, I will definitely try 0x9010 though thank you very much ^_^
Editor, Skilled player (1275)
Joined: 1/31/2010
Posts: 327
Location: France
If you have many addresses, you can freeze several of them at the same time and check if it had an impact in random events. If so, then the RNG address is probably among them. If you freeze a lot of addresses at the same time, the game may crash, in that case select less addresses and retry.
Joined: 4/21/2021
Posts: 6
Some minor progress, I've managed to find something and it's either the damage display or damage for melee attacks. But freezing or poking it doesn't really do anything and running searches to narrow it down hasn't helped much. Trying to narrow the searches for things that keep changing per frame hasn't helped when I froze them one by one to control the RNG (
Editor, Skilled player (1275)
Joined: 1/31/2010
Posts: 327
Location: France
I got some time to look at this game, and the RNG address is at 0xC4468 It follows the default PSX RNG formula: NextRN = (CurrentRN * 0x41C64E6D + 0x3039) mod 0x100000000 If you want to know how many RNs were used since the beginning, you can use this script:
local RNG = {}
local i, j
local MAXLIM
local CUR_RN
local a, b, c

RNG = {0} --First RN value

local MAXLIM = 10000 --Number of RNs to calculate

for j=2, MAXLIM do
	-- RNG[j] = (0x3039 + 0x41C64E6D*RNG[j-1]) % 0X100000000
	
	-- Very convoluted way to calculate the next RN without any big number rounding issue
	-- 0x41C64E6D = 1103515245
	
	a = (0000005245*RNG[j-1]) % 0X100000000
	b = (0003510000*RNG[j-1]) % 0X100000000
	c = (0010000000*RNG[j-1]) % 0X100000000
	c = c * 110
	RNG[j] = (0x3039 + a + b + c) % 0X100000000
end

local BASE = RNG[1]
local INDEX = 1

while true do
	
	CUR_RN = memory.read_u32_le(0xC4468)
	
	if BASE~=CUR_RN then
		for i=math.max(INDEX-100, 1), INDEX+100 do
			if RNG[i]==CUR_RN then
				INDEX = i
				BASE = CUR_RN
				break
			end
		end
	end
	
	if BASE~=CUR_RN then
		for i=1, MAXLIM do
			if RNG[i]==CUR_RN then
				INDEX = i
				BASE = CUR_RN
				break
			end
			dummy = i
		end
	end
	
	gui.text(0, 100, "RNG")
	gui.text(0, 120, "#" .. string.format("%07d", INDEX))
	
	
	emu.frameadvance()
	
end
Joined: 4/21/2021
Posts: 6
lapogne you're incredible, do you mind sharing your process for locating that? Thank you very much for the LUA script as well *bows*
Editor, Skilled player (1275)
Joined: 1/31/2010
Posts: 327
Location: France
I noticed that the RNG doesn't change often (i.e. same damage and item drop even if I delay the attack by several frames), so I noted at what frame the result was finally different, and I kept all addresses which didn't change before this frame but were different after. Then it's just the elimination process by freezing some addresses I mentioned in a previous message.
Joined: 4/21/2021
Posts: 6
Ah I see, I was only auto searching for things that were changing per frame and then narrowing the results from there. Thank you for explaining. I recorded the current progress I have but with the LUA script you provided and a higher max limit just so I could see and show how many times the RNG advanced and if there was anything I could notice by eye on other things that affected it. https://youtu.be/2CDeZMjZrDw (please ignore the facebook message notification somewhere near the end) Going to attempt to step through the debugger with the RNG address during a boss fight to try and find what the address for boss behaviour is and what else affects it. Will be trying to find XYZ position and speed address this weekend so I can finally go back and check my progress thus far.
Joined: 4/21/2021
Posts: 6
So made some progress on addresses but not entirely sure I have the full picture, but thank you to lapogne for sharing his process which gave me more things to try out 2 Bytes --- 0xA83D6 = Y-Axis Speed 0xA841A = Z-Axis Up "Running Speed" 0xA841C = Z-Axis Down "Running Speed" 0xA841E = X-Axis Left "Running Speed" 0xA8420 = Z-Axis Right "Running Speed" 0xA8430 = Distance to Ground - I believe this one is used to determine if the character can jump again. 0xA838C = X Position 0xA8390 = Y Position 0xA8394 = Z Position Y-axis speed and the "distance to ground" ones are the only ones I'm confident having all the information since freezing and poking them yields the results I expect. The "running speed" is a bit weird since freezing them at 0 prevents the character from running but they can still walk in the corresponding direction, however I cannot seem to find the "walking speed" and watching the general memory area around the "running speed" had nothing that I froze that did it. The position addresses are mostly correct when I freeze them but there's still a lot of wiggle room when the main character pivots, more so than subpixels worth so not sure plus the memory area for that looks weird since it's repeated four times but freezing the others doesn't do anything? Am still watching The8bitbeast's tutorial on how to calculate screen position relative to the camera. So hopefully can get that figured out.