Venoman98 wrote: ↑Wed Feb 19, 2025 8:32 pm
I'm the first in this form, how to like you buddy, you're the only one who made a change in access skills for officers
You liked my previous reply asking if you want to learn how I found the skills editor. I shall try to explain how I did it in this post.
The key functionality is to use the Mono feature built-in to Cheat Engine. For any games built on Unity engine, by default they use Mono to compile codes just-in-time. That’s some technicalities developers need to consider. For us, all we need to know is that, by default, we will have access to all variable and function names. So when the data structure has been laid out to you and annotated just as the developers see it, finding the right variable to make changes becomes rather easy.
Let’s show an example of this in action. For simplicity we shall use my table script to by-pass the process of finding variables.
- Open the game and enable the script for “player’s ship and fired torpedoes”.
- In the full view mode, select on Memory View.
- In the newly popped-up memory viewer window, go Tools, then Dissect data/structures.
- In the newly popped-up struct dissect window, in the box under “Group 1”, type
[pPlayerShip]
, a pointer already created and populated by the script, and should be colored black as opposed to red if done successfully.
- Now go Structures, then Define new structures. The structure name should already be populated as PlayerShip, which indicates that Cheat Engine’s Mono feature was able to identify the corresponding data structure pointed by our “pPlayerShip” pointer.
- Once we click Ok, you will see a page full of the fields associated with the PlayerShip data structure.
For example, we can see that
+6C0
is a pointer to the
electricEngine
struct, and if we expand it, we will see all the fields associated with this data structure. In particular
+108
is another pointer to the
power
struct, and further
+034
is the actual value of the power output of the electric engine. This is the pointer address included in my table, and you can see how easy it is to experiment and find other interesting addresses.
I should point out that pPlayerShip is a pointer that I created within my script. There are
label(pPlayerShip)
and
registersymbol(pPlayerShip)
statements that declares this pointer and allow it to be used outside the script respectively. Then in the injected code, I moved the value of
rax
into this pointer for storing the actual value at run-time. There is also an initialization part where
pPlayerShip: dq 0
is set to be 8-btyes zero value. Finally when we deactivate the script, this pointer is unregistered and unallocated. Note that
pPlayerShip
access the address of this pointer, which is the address where I created it;
[pPlayerShip]
access the value of the pointer, which I used to store the address of the player ship data structure. That’s why in the struct dissect window you need to enter
[pPlayerShip]
as opposed to
pPlayerShip
.
Create a script to capture the address of the player ship struct is rather straightforward, and uses techniques taught in the cheat engine tutorial. Very briefly, here is how I did it. First find the oxygen level in the boat, which is a double. Then look at the code accessing it, from which to deduce it is a nested pointer, and should give us the base pointer to the player ship. Finally we can find what codes are accessing the player ship structure’s address, and do code injection from there.
You will notice that the crews’ skills are not accessible from the player ship structure. To find the skills, we need to use another Mono’s feature. In the tool bar menu of cheat engine, click on Mono, then Dissect Mono. Here we can do a search for keywords such as skill. I found that the search function within cheat engine to be slow, so what I tend to do is to save all the values to a text file and do string search in a different program. So we do File then Expand All, which should take a long time but will expand all fields of all data structures within this game. Then we do File then Save list to a plain text file. Open that plain text file in a text editor and do a search there. For example, you might discover that there is a struct called
UBOAT.Game.Scene.Characters.PlayableCharacterData
, which at
+20
is
availableSkills
, itself an array of
UBOAT.Game.Scene.Characters.Skills.CharacterSkill
. So looks like we should investigate that.
Turns out this particular feature is easier to experiment. We can hack the reputation points and see that it is a field of the player career structure (i.e.
UBOAT.Game.Sandbox.PlayerCareer
). At
+48
is a pointer to a list of
PlayableCharacterData
. So if we put this address into the struct viewer, we can see that
+10
is the pointer to the array of pointers each pointing to an element of the list, and
+18
is the size of the list stored as 4 bytes. The first entry is at
+20
, with an offset of 8 for each additional element. And we just walk down this nested pointers to find the actual skill we need. Luck has it, that the skills are stored as a list, so we can copy paste the pointer to an element within a list so that it points to a different skill. That way we can change the list without much effort on our side, and that’s how the skill editor is built.
Finally, the table forum’s rule is that when we upload our table we need to remove all unnecessary stuff such as the dissected structures we created in cheat engine. That’s why the released tables you found here are all clean, with no trace of the data structures I dissected.
Also note that for this particular game, due to long development cycles and changing requirements, some fields of data structures no longer functional despite not being deleted. That’s why you need additional tools such as dnSpy to read the actual code, to see how those fields are used by the game logic. For example, the crack threshold of a ship’s hull was used to be controlling if the ship should crack in half, and was essential in my nuclear torpedo cheat. But the 2025 update has completely changed its logic, denying me utilizing it to mimic nuclear torpedoes. This is not visible in cheat engine without spending great efforts reading assembly code, but is very clear in dnSpy inspecting the C# code.
Let me know if you have any questions. I am more than happy to share what I know!