touhouworldcup / thprac

MIT License
210 stars 20 forks source link

Developing Pointdevice Mode for all TH danmaku games #174

Open ijwzac opened 4 months ago

ijwzac commented 4 months ago

Hi all, I am a CS student who is developing a fork of thprac that implements a Pointdevice Mode that supports all TH danmaku games (probably except th9 and th19). Some basic functionalities are already done like detecting loss of life and overwriting section in practice mode. But there are some obstacles.

Two biggest challenge: (1) Is there a function that can immediately reload the current stage? (2) How to get the current section number? (Edit: the second is solved by Cheat Engine. For example, 4e73f0/4e73f8 is the stage/section in th15.) Forgive my limited knowledge of reverse engineering...


Here is something I tried but doesn't work well:

What will happen:

I also don't understand what are ECLJump and ECLSetChapter. I just invoke THPatch() with modified parameters, and sometimes it works sometimes it doesn't.


Any help is greatly appreciated! I hope this mode will bring joy to all of us soon.

32th-System commented 4 months ago

First, I would recommend that you check out https://github.com/exphp-share/th-re-data. Now, to answer your questions.

Is there a function that can immediately reload the current stage?

Yes... sort of.

In Touhou there is a variable called the game mode. Well, it's three actually (the current gamemode, the next gamemode (which is the variable you actually want to modify if you want to change it), and the previous gamemode). Examples of gamemodes are:

So setting the gamemode to 14 (or 0xE) does exactly what you want.

I just invoke THPatch() with modified parameters, and sometimes it works sometimes it doesn't + Sometimes boss' AI can be messed up

This is because reloading does not reload the ECL scripts. The game keeps using the scripts it had already loaded previously.

thprac also handles warping to a chapter within the stage vs warping to a boss with different functions. THStageWarp is used to warp to stage sections, not THPatch. The function THSectionPatch knows which one to call. The reason for that is that thprac didn't originally support stage section warping. As a result, the feature is very hacked on.

Of course, if you perform a reset in a stage that's not 1, the game will reload the script for stage 1. However, setting the gamemode to 14 will never reload the script.

ECLJump

This is a utility function that creates a jump instruction in the ECL script to skip over a few other instructions. This does turn the data immediately after the newly created jump into garbage, but the ECL parser will never try to run that since it will jump over that due to the jump instruction.

ECLSetChapter

Because making sure that the internal chapter number is set correctly matters a lot in th15, thprac will also do that. It wants to make sure though that it sets the chapter number at the correct time though. The code Because of that, instead of just doing it directly, it will activate a hook that will set the chapter number and then deactivate itself. The thXX_patch_main hook is at the end of GameThread::thread_start

ijwzac commented 4 months ago

Hi 32th-System,

Thank you so much for the detailed explanation! I have been checking out th-re-data (astonishing work!) and implementing PointDevice mode on TH15's EX stage. Still have trouble reading ECL-related instructions but the existing ones are really helpful.

By setting gamemode_next to 14 and invoking THPatch() and THWarpStage() with tweaked parameters that represents the current section of player, I am glad to say now it kind of works! I am able to play through EX stage and reset to the checkpoint when about to lose life.

However, my current implementation has some bugs:

  1. The BGM always replays, and is always the first BGM "A World of Nightmares Never Seen Before", even if I use Everlasting BGM. I think I can solve this one by myself with the help of the existing ElBgmTest() function
  2. Seems that simply invoking THPatch() with tweaked parameters doesn't work well sometimes. Reseting during any Non Spellcard will actually reset the game to the previous Spellcard, and Doremi insists to come out and cast her third Spellcard if player resets at warp section 6~9, LOL

Reset in warp section 7: Capture

Doremi cut into section 7 where she doesn't belong to: Capture2

About the second problem, do you think it's because the ECL script manipulation instructions in THPatch() only works for jumping to a section with Practice mode, and doesn't work for cleaning the game's current script and jumping to a section with gamemode_next = 14?

If so, it would be more tricky than I thought since I don't know how you did those fantastic job with ECL scripts. Is there a tutorial for this? (Edit: found Priw8's tutorial, but still hard to understand how thprac developers figured out those instructions) Maybe I can force player to exit to practice mode and start practice with parameters that will jump to the current section, but this would be a bad design when I develop the PointDevice mode for other TH story modes.