Cursed Earths. Let’s change the game engine! Details + EXAMPLE of engine reversing

Hello everyone! Surely many have played the legendary game from Nival Interactive – Cursed Lands. I played it for the first time, it seems, in 2016, and since then I have had a dream – to make an addon for this wonderful game. And do it alone. From start to finish, including even the editors. And I will tell you some of this now.

If you’re not interested in the assembler part, just skip ahead. At the beginning and at the end I talk about what you can already feel.

The PZ engine even allows you to add chariots (with reservations, but still feasible)

The PZ engine even allows you to add chariots (with reservations, but still feasible)

Preface

In my last article, Cursed Earths. Let’s refresh the gameplay, I was able to talk about both the unusual features that are in the PZ, and demonstrate the creation and subsequent operation of some interesting modifications that are relatively easy to implement for the Cursed Lands. Modifications, such as spending your stamina depending on its amount, or unlocking hidden spells. Such modifications, which allow one way or another to experience the gameplay of this wonderful game more freshly, will always be relevant! And the more customization opportunities the creators of mods and addons for the game have, the better the quality of the mod or addon will be. And yes, I’m getting ready to make a Standalone Add-on for the Cursed Lands! And SpellAddon will help with this. But for now there is nothing to show.

This time I want to talk about other interesting features and non-obvious mechanics in the Cursed Lands, and at the same time as clearly as possible show how I disassembled the engine of this wonderful game (including to create my own add-on!) to help every novice developer understand how to use Cheat Engine or Interactive Disassembler (IDA) you can modify the engine of your favorite game, or at least break it… Let’s go!

Why Cursed Earths is cool

If in the last article the facts about the coolness of the PZ were not enough for someone to consider that this game is really worthy of reverse-engineering and making addons and modifications to it, then it’s time to learn even more amazing facts about the PZ!

Surely everyone who played PZ remembers the blow from the back to the head. This is such a powerful secret blow that smashes any enemy who has not yet detected the hero. The multiplier for backstabbing is written in the game config (Cursed of the Earth\Config\ai.reg). Do you recognize the “Genocide” project, in which the cannibal is killed on easy difficulty? He is! The blow from the back is so good that it can kill even the strongest enemies (and on easy difficulty, even if they have resistance to weapons).

76 - enough damage to win

76 – enough damage to win

A hit to the head deals critical damage. And this is not a special critical damage, but just another modifier. For example, 102% of the damage actually hits the arm of a humanoid, 101% hits the body, 100.5% hits the leg, and 303% hits the head. Vulnerability and health of body parts are recorded in the game database (Cursed Lands\Res\database.res), where the parameters of character prototypes and the characteristics of their races are located. And only the player can purposefully choose where to hit – opponents can’t do that (yet). But in PZ everything is much more interesting than just the ability to hit in the head or not in the head.

To begin with, it is worth saying that a character has from 1 to 6 conventional body parts – head, torso, left arm, left leg, right arm, right leg. Dealing damage affects movement and attack speed. A screenshot will be better than a bunch of words.

Dependence of actions on damage to the unit's hands.  At the 2nd stage, 49 HP, not 45, because this crossbowwoman managed to regenerate.

Dependence of actions on damage to the unit’s hands. At the 2nd stage, 49 HP, not 45, because this crossbowwoman managed to regenerate.

The screenshot shows how damage affects the unit’s attack speed. And it works almost proportionally! A humanoid unit with arm health less than 50% will attack slower by 8/15 of its base attack speed. And with a completely broken (not necessarily permanently broken) arm or with 2 arms whose health is less than 50%, the unit will attack as much as 3 times slower.

The situation is similar with the legs. And very logical. Tigers, boars, wolves, toads and other four-legged units attack by biting or spitting something out, so wounds to the front legs do not have a slowing effect on their attack. Instead of 2 arms, they have 2 more legs. If the leg’s health drops below 50%, the unit will no longer be able to run and the speed of each of its movement types will slow down slightly. So shooting at the legs is very useful even in the original, if you close your eyes to the fact that small arms do almost no damage. Fortunately, modern editors (of which there are a lot, including my own) make it easy to edit parameters in the software database.

It’s not just breaking a character’s legs that affects his ability to run. In this game, unfortunately, there is no such smooth change in speed depending on the load, as is presented in Allods, but there is a check for character overload. If the total weight of equipment is greater than the number that the hero can wear, then he will not be able to run. This works with heroes under player control. The greater the hero’s “strength”, the more weight he is able to withstand while running. The game also has the “Load” skill, which expands the limit, that is, it allows you to equip yourself with more weight.

Inscription "Too hard!" means that the character will not be able to run.  However, breaking the armor can easily fix this right on the battlefield.

The inscription “Too hard!” means that the character will not be able to run. However, breaking the armor can easily fix this right on the battlefield.

What about absolutely independent and very logically thought out types of perception, of which there are already 5 in the game?

Screenshot from the HxD hex editor.  Tracking by smell and tracking by footprints even work in the PZ, but, unfortunately, the reactions of the units are not prescribed.  That’s why they don’t do anything about the fact that, say, they smelled Khador or saw the traces of Eila.

Screenshot from the HxD hex editor. Tracking by smell and tracking by footprints even work in the PZ, but, unfortunately, the reactions of the units are not prescribed. That’s why they don’t do anything about the fact that, say, they smelled Khador or saw the traces of Eila.

The units’ hearing works great! As I already said in my last article, a walking unit makes a little sound, but a running one generally makes a terrible noise, as it should be. But probably not everyone knows that the crawling character also makes some noise. What is written in the tips is the absolute truth. And the fact that in the original Cursed Lands the crawling character is not heard is not because he is silent. Just opponents… They are a little… Deaf…

The abilities of the characters in the PZ are almost unique for the year 2000 (the year when the Cursed Lands was already released for sale).

The abilities of the characters in the PZ are almost unique for the year 2000 (the year when the Cursed Lands was already released for sale).

Modifiers that depend on the character’s position – be it audibility or visibility – are written in the game engine. On Cortana, a character is 1.5 times more invisible, while lying down – 2 times. But in the database with characters, you can register (as shown through HxD) modifiers for perception types. That is, if you prescribe good hearing for the characters (for example, orcs, opponents of the heroes), then they will be able to hear the crawling main character (or any of their opponents), but the creeping character, who is their enemy, will not be audible to them, although it will be noticeable by sight from a greater distance.

As for vision, the real PZ nerds probably know that cannibals and trolls can be seen from afar. And this is precisely because they have a visual detectability modifier of 1.5.

And life detection is the unconditional detection of opponents, which does not depend on the pose, clothing worn, and obstacles between two units. It can only be affected by the same detectability modifier and the Deadman spell (Lichdom in the game base).

By the way, it’s worth saying specifically about the undead in the Cursed Lands. Zombies, skeletons, lights and other magical not-quite-living creatures have both vision and life detection. So this returning dude told Babur the wrong information!

Dialogue with Babur "About Golden Valley"

Dialogue with Babur “About the Golden Valley”

Well, it’s okay, though. This just means that they see us casting a spell of elemental magic or astral magic from a greater distance than they detect with their senses. But the castes of the magic of feelings are neither heard nor seen.

By the way, about the magic of feelings. She can be quite combative. That is, to help not “not die,” but rather to “kill.” There are 2 wonderful spells: “Invisibility” and “Silent Step”. By casting the “Invisibility” spell on an enemy, you can kill him in front of the eyes of another enemy, and in this case, the second one will not activate GPS, which will allow him to pursue the hero! And “Silent Step” is not a very accurate name at all. This spell with any effect above 0 makes all the character’s actions inaudible to mobs on the map, including spell casting. Here is an example with treatment behind the back of an imperial guard.

The reaction (and lack thereof) of the guardsman

The reaction (and lack thereof) of the guardsman

This green sphere above the character is a visual representation of the current effect of Silent Step. Unfortunately, it is a rather weak spell, since running behind the enemy’s back can just lure enemies away, and fireworks will not take up a slot that is useful for some other spell.

What about online play? She is also in the Cursed Lands! Forget for a moment about the boring quests of the original network game with quests similar to each other (of which there are some 28 in the entire network game), a crooked balance for mages-players-bug users and clearly meager general content (no voice acting, few NPCs, the script of one the quest can literally be written on the palm of your hand)!

Screenshot of a player from Classic-Mod

Screenshot of a player from Classic-Mod

The most popular, large-scale and in-demand mod at the moment (from the network) Classic-mod corrects all these problems. Thanks to the fans of this wonderful game, it has voice acting and even completely new mechanics like mercenaries-necromancers (with which I helped the author Nikolai a little), story quests that open one after another as in a single player and transitions to other locations. It’s just that in the PZ, the nickname created for a character is limited to a measly 10 characters, and everything is sad with the parameters. “Strength”, “Dexterity” and “Intelligence” when creating a character are limited from 15 to 35, and even their total sum cannot differ from 75. And PZ, no matter what mod it is with, is not a donated MMORPG! Whoever wants a challenge, let him create a weak character, whoever wants a pampered Great Magician, let him create a 15 25 45 character. 0 problems.

Difficulty #1. We find and change the numbers visible to the eye.

As always, we will find it useful:

Let’s take a look at the parameters first. In fact, it is not always necessary to reverse the engine in order to achieve something good. To begin with, we will try, through HxD for free, to find the boundaries within which you can create a character for online play. Why not? It is quite possible that the treasured 35, 15, 100 and 75 (the sum of 3 characteristics – intelligence, strength and dexterity, called “Balance”) are written down there somewhere.

The HxD editor has a convenient search that can even search for floating point numbers. When I was doing this, I already knew that strength, agility, intelligence and height are fractional numbers, so it was a little easier for me in the process of finding them. It’s worth saying right away that such things are usually written down next to each other. There is a very low probability that the compiler or developer will decide to place 2 parameters responsible for the boundaries of the same thing in different parts of the file.

2 distant parameters equal to those we need.

2 distant parameters equal to those we need.

When the numbers we need, for example, 15 and 35, are so far apart (more than hundreds of bytes), we can assume that both are not what we are looking for…

Parameters similar to those needed.

Parameters similar to those needed.

But when they are so close, the hands are already reaching out to change them to 5 and 45 and see what comes of it! And it’s not in vain…

Everything worked out!

Everything worked out!

There is a result – let’s move on. Nicknames are next!

Difficulty #2. Let’s explore the power of Cheat Engine

Launch Cheat Engine. It doesn’t matter what version, the main thing is that we select the game.exe process (with the golden dragon icon) – this will be the Cursed Lands. If they are not running, then this must be corrected, and then again enter the process selection and select the software.

Cheat Engine itself shows you where to poke first.

Cheat Engine itself shows you where to poke first.

Let’s go into an online game and create a character for ourselves! Then we close the game, remembering the length of this Hero’s nickname. A hero with a capital “G”, because he will help us make the PZ a little better, at least I believe in him as much as possible!

Just in case, in Cheat Engine we will select Value Type = All and Scan Type = Unknown initial value. The line in which we could write something to search will disappear and the First Scan button will remain active, which we need to press, because we want to find the length of the character’s nickname.

The game will give us millions of different values ​​and this shouldn’t scare us. Let’s go into the game and change the character’s nickname, or rather its length. And after that, let’s run the scan in Cheat Engine again, but this time clicking on Next scan – that’s the whole point.

Perversions with a character's nickname to change its length

Perversions with a character’s nickname to change its length

For screening, you need to select some criteria. For example, if we shortened a character’s nickname, it would be rational to assume that its length has changed. And most likely it even decreased. Therefore, you should select Decreased value, and then run Next scan. Increased value – for a newly increased nickname. Running Unchanged value is also very important, because there are constantly changing values ​​that we would really like to discard, and preferably early.

After much sifting, a number will inevitably be found that changes in the same way as the character’s nickname changes. You need to click on this value and use the red arrow to fix it at the bottom. And then by right-clicking on it and selecting Find out what accesses this address (hotkey F5), we begin to check what reads the length of the nickname and which, accordingly, does not allow entering a long nickname. It can be established as easily as possible experimentally that when you enter a character in the character’s nickname field, the length is read out by 7 instructions. But when the nickname is already 10 characters, 2 instructions stop reading this length.

This is the picture I got.

This is the picture I got.

The command at address 0x0065CAE8 is located before the other one that failed, so let’s jump to it. For convenience, you can open the PZ engine game.exe in IDA.

Some check above may bypass this block of instructions.

Some check above may bypass this block of instructions.

Above, some conditional jump, obviously, can sometimes prevent those commands that read the length from being executed. It would seem, why do we need to read the length if we need a long nickname? We just need to make sure that the situation when there are 10 characters is no different from other situations, that is, remove the jump when checking for 10.

Jump Command 7D 20

Jump Command 7D 20

Let’s replace the command 7B 20 with 90 90 (90 is NOP, a command that does not create any new result). This way there will be no jumping through the code.

And it won’t work because the jump happens before that.

2 jumps.

2 jumps.

And there are only exactly 2 of them, as you can see if you open game.exe in IDA. This means that the earlier jump that we see in the disassembler is precisely the one that prevents characters from being entered further. Let’s remove him too. You will get the following code:

.text:0065CA9A                 nop
.text:0065CA9B                 nop
.text:0065CA9C                 mov     edx, [esi+0CCh]
.text:0065CAA2                 lea     eax, [esp+34h+var_24]
.text:0065CAA6                 push    edx             ; int
.text:0065CAA7                 push    ecx             ; lpString
.text:0065CAA8                 mov     ecx, esp
.text:0065CAAA                 mov     [esp+3Ch+arg_0], esp
.text:0065CAAE                 push    eax
.text:0065CAAF                 call    sub_7006E5
.text:0065CAB4                 lea     ecx, [esp+3Ch+var_14]
.text:0065CAB8                 push    ecx             ; int
.text:0065CAB9                 mov     ecx, [esi+4]
.text:0065CABC                 call    sub_5F8340
.text:0065CAC1                 mov     edx, [esi+0BCh]
.text:0065CAC7                 mov     ebp, [esi+0B4h]
.text:0065CACD                 mov     ecx, [eax]
.text:0065CACF                 sub     edx, ebp
.text:0065CAD1                 cmp     ecx, edx
.text:0065CAD3                 nop
.text:0065CAD4                 nop
.text:0065CAD5                 lea     eax, [esp+34h+var_24]
.text:0065CAD9                 mov     ecx, edi
.text:0065CADB                 push    eax
.text:0065CADC                 call    sub_700AA9
.text:0065CAE1                 mov     byte ptr [esi+0E6h], 1
.text:0065CAE8                 mov     eax, [esi+0E0h] ; check
.text:0065CAEE                 inc     eax
.text:0065CAEF                 mov     [esi+0E0h], eax

Old code:

Hidden text
.text:0065CA9A                 jge     short loc_65CAF5 ; jump
.text:0065CA9C                 mov     edx, [esi+0CCh]
.text:0065CAA2                 lea     eax, [esp+34h+var_24]
.text:0065CAA6                 push    edx             ; int
.text:0065CAA7                 push    ecx             ; lpString
.text:0065CAA8                 mov     ecx, esp
.text:0065CAAA                 mov     [esp+3Ch+arg_0], esp
.text:0065CAAE                 push    eax
.text:0065CAAF                 call    sub_7006E5
.text:0065CAB4                 lea     ecx, [esp+3Ch+var_14]
.text:0065CAB8                 push    ecx             ; int
.text:0065CAB9                 mov     ecx, [esi+4]
.text:0065CABC                 call    sub_5F8340
.text:0065CAC1                 mov     edx, [esi+0BCh]
.text:0065CAC7                 mov     ebp, [esi+0B4h]
.text:0065CACD                 mov     ecx, [eax]
.text:0065CACF                 sub     edx, ebp
.text:0065CAD1                 cmp     ecx, edx
.text:0065CAD3                 jge     short loc_65CAF5 ; jump
.text:0065CAD5                 lea     eax, [esp+34h+var_24]
.text:0065CAD9                 mov     ecx, edi
.text:0065CADB                 push    eax
.text:0065CADC                 call    sub_700AA9
.text:0065CAE1                 mov     byte ptr [esi+0E6h], 1
.text:0065CAE8                 mov     eax, [esi+0E0h] ; check
.text:0065CAEE                 inc     eax
.text:0065CAEF                 mov     [esi+0E0h], eax

Result:

Replaced bytes via Cheat Engine.

Replaced bytes via Cheat Engine.

We will also remove stupid checks for special characters. Why are they needed there, after all, if we want to write stubborn emoticons in our nickname?

Checks for different characters.

Checks for different characters.

Below there is a conditional transition that checks whether we have introduced such a game.

Jump if the symbol was incorrect.

Jump if the symbol was incorrect.

0F 84 3D 01 00 00 we also kick (we do it instead of jumping – 90 90 90 90 90 90). And the character’s nickname becomes like this:

Top character!

Top character!

Of course, I created such an amazingly cool guy as a keepsake.

PERSONAZH has already gone for a walk!

PERSONAZH has already gone for a walk!

And why was it so important to reduce the possibilities for creating a nickname for the beloved main character?.. Even Khador-Ruffnut is already longer than 10 characters… Everything is displayed normally and works great…

Difficulty #3. What is at the very end of the *.mob file?

The subtitle is the same question that I asked myself at the age of 16-17, when I first opened the *.mob file of the PZ. Ah, I still remember the delight of how I was able to literally read the section of the traffic graph without resorting to the help of other people…

A complete analysis of the *.mob file of the PZ would take at least one whole and separate article, and some other dudes already had these analyzes. And I’ll tell you about something that hasn’t been written about on the Internet yet. About cross-country graphs, which are also in the *.mob files of the software.

It was a very long time ago. I was still just a baby and I was very interested: what kind of crap is written at the end of each main *.mob file? Let’s go back to 2018 and open the mobile. We use HxD, of course.

Beginning of the *.mob file.

Beginning of the *.mob file.

At the very beginning, the signature of the *.mob file of the PZ, and then the address of the cross-country graph. In HxD you can move there very conveniently. Either copy the address (UInt32 on the right) and then, pressing Ctrl+G, paste it into the field, or simply click on that go to: and move there even faster. Go! For example, let’s take a file from the Cursed Lands: Lost in the Astral Plane! Map – “The city of Sadrok and its surroundings” (zone35.mob).

I allocated 16 bytes. These are 4 values ​​of 4 bytes. Let’s take a closer look:

  1. Passability graph signature.

  2. The size of the traffic graph section including the header.

  3. The size of the traversability graph along the X coordinate.

  4. The size of the traversability graph along the Y coordinate.

Below is His Majesty the cross-country ability graph, which helps the character walk 500 meters without compromising performance, clearly skirting all possible objects on the map!

Interesting fact: the longest path in the PZ is the path from the northwest corner to the southeast corner on the Portal map.

Let’s assume that we know nothing at all about the traversability graph. Well, that is, like me at 16-17 years old. We look at the mountain of bits in our favorite toy and think about what to do to understand what it all is.

A bunch of bytes.

A bunch of bytes.

It can be seen that only FF is “at the edge”, and not only higher up. If you try to fill everything with zeros, the game crashes when entering the City of Sadrok and its environs. If you fill everything with maximum values ​​(FF), you get this:

Kir walks at close range, since the game calculates everything that is specifically around the path – that is, a certain area is built around the trajectory line. And if the length of the trajectory is less than the region, then it turns out that the crippled traversability graph does not play a role. But in any case, this mechanism analyzes the entire area and makes it clear to the unit what the geometric shape of the objects is. And even trigonometric – after all, for different types of movement and for different hanging and lying objects there will be different cross-country ability! A troll can walk on logs, a prone hero can crawl under a fence, and these are not all interesting examples.

Thus, we can conclude that this is indeed a traversability graph. Knowing that the maps in the PP are made of sections (squares), the conclusion that the traversability graph also consists of squares suggests itself…

I analyzed this matter for a long time. While I was driving somewhere. While I was drinking on the street. And somewhere else, and somehow else. If you look carefully at the structure of the section with the traffic graph on each of the maps, you can see that the traffic graph itself consists of blocks, the contents of which are correlated as 16 to 1 to 2. In the screenshot above you can see that the “department” is not filled with zeros has a size of 1024 bytes, and to the next similar “department” – 192 bytes. Opening the maps of the Cursed Lands (not the addon PZ: ZvA) you can make sure that there are still 3 sections. And it is useless information that is filled with zeros (most likely used for editors by Nival). If you change it, then nothing will change in the behavior of anyone on the map.

Map traversability graph "Portal" from PZ.

Graph of the passability of the “Portal” map from the PZ.

The “structure” is clearly visible here. And since 3/19 are useless, then we don’t do anything with them. Next, you can logically think that around the 1st cell there should be passages to the cells surrounding it. 4 or 8. Now we look as closely as possible at the beginning of the cross-country graph. For example, we still take the City of Sadrok and its surroundings.

Graph of the passability of the map zone35.mob from the addon PZ: ZvA

Graph of the passability of the map zone35.mob from the addon PZ: ZvA

Knowing already that you cannot walk along FF FF (more precisely, our path is not considered valid), from the very beginning the conclusion arises that this is nothing more than a corner! The corner of the map that is the most impassable (on average) place in the PZ! After all, being strictly on the corner, you can only go away from the corner in the opposite direction, or along one of the sides. The other 5 sides are the path to a dead end, the edge of the map. At least this will be so if, after all, there are exactly 8 paths from each cell. And it’s hard for me to imagine any other situation.

But let’s not rush. In the title we saw the size of the map. There is no doubt that this is the size of the map, because on each map it coincides with its true size, only 4 times smaller. The portal, for example, says 128 by 128. But in reality its size is 512 by 512. And in sections – 16 by 16. The City of Sadrok – 64 by 64, 256 by 256, 8 by 8.

Let’s take the size of the traffic graph – 0x98000 or 622592. Divide this number by 64 and then again by 64 (by X and Y, respectively). We get 152. 152 is just divisible by 19, which gives us the opportunity to assume that there are still 8 passes from the cell, and not 4. FF FF are located too symmetrically without any “single” “FF”.

Having spoiled only one section of the traffic graph, you can come to the conclusion that nothing has changed at all. If you spoil 80% of the cross-country ability graph, then nothing will change either. But if you spoil it a little bit from the very beginning, you can still stumble upon the fact that part of the map becomes impassable.

If you look closely at the impassable bytes (FF FF), you can see that we first have 3 impassable cells, then 3 passable ones, and then 2 impassable ones. And after carefully testing a map with a damaged traffic graph, only one conclusion can be drawn!

The structure of the cell is a fundamental element of the graph of the patency of the PV.  The hex editor shows 6 cells from the top left corner.

The structure of the cell is a fundamental element of the graph of the patency of the PV. The hex editor shows 6 cells from the top left corner.

I decided to write test programs for converting a traffic graph into a picture and back. I don’t even know where they can fail, which doesn’t really matter. I even have my own map that uses a hand-drawn traffic graph, but again, too little has been done there yet to show it.

Hidden text

Code for converting the traffic graph into a picture:

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main () {
		char pppp[] = { 0x42, 0x4D, 0x1A, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
		char mainstr[256];
		printf("file name (must be pure AI graph file):\n");
		scanf("%s", mainstr);
		DWORD dwX, dwY;
		DWORD dwTemp, dwTemp2, dwLolKek = 0x31, dwSignature, dwFiles, dwFirSec, dwLasSec;
		char *bNum;
		bNum = (char*)malloc(20);
		HANDLE hFile = CreateFileA(mainstr,GENERIC_READ,4,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
		int a = strlen(mainstr);
		mainstr[a-4] = '-';
		mainstr[a] = '.';
		mainstr[a+1] = 'b';
		mainstr[a+2] = 'm';
		mainstr[a+3] = 'p';
		mainstr[a+4] = 0;
		printf("X & Y size (in *.SECs)\n");
		scanf("%d %d", &dwX, &dwY);
		dwX = dwX*8;
		dwY = dwY*8;
		
		char bFile[dwX*dwY*152], rFile[dwX*3*dwY*3*8*3+54], bTemp[20];
		HANDLE hFile4 = CreateFileA(mainstr,GENERIC_ALL,4,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
		ReadFile(hFile,bFile,dwX*dwY*152,&dwTemp,NULL);
		DWORD dwC1 = 0;
		*((DWORD*)(pppp+18)) = dwX*3;
		*((DWORD*)(pppp+22)) = dwY*3*8;
		WriteFile(hFile4, pppp, 0x36, &dwTemp2, NULL);
		register DWORD dwC3 = 0;

		for (register DWORD dwC = 0; dwC < dwY*8; dwC ++) {
			for (register DWORD dwC2 = dwC1; dwC2 < dwC1+dwX*16; dwC2 += 0x10) {
				*((BYTE*)(rFile+dwC3+3)) = (BYTE)*((BYTE*)(bFile+dwC2+0));
				*((BYTE*)(rFile+dwC3+4)) = (BYTE)*((BYTE*)(bFile+dwC2+0));
				*((BYTE*)(rFile+dwC3+5)) = (BYTE)*((BYTE*)(bFile+dwC2+0));
				
				*((BYTE*)(rFile+dwC3+0)) = (BYTE)*((BYTE*)(bFile+dwC2+2));
				*((BYTE*)(rFile+dwC3+1)) = (BYTE)*((BYTE*)(bFile+dwC2+2));
				*((BYTE*)(rFile+dwC3+2)) = (BYTE)*((BYTE*)(bFile+dwC2+2));
				
				*((BYTE*)(rFile+dwC3+0+9*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+4));
				*((BYTE*)(rFile+dwC3+1+9*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+4));
				*((BYTE*)(rFile+dwC3+2+9*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+4));
				
				*((BYTE*)(rFile+dwC3+0+18*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+6));
				*((BYTE*)(rFile+dwC3+1+18*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+6));
				*((BYTE*)(rFile+dwC3+2+18*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+6));
				
				*((BYTE*)(rFile+dwC3+3+18*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+8));
				*((BYTE*)(rFile+dwC3+4+18*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+8));
				*((BYTE*)(rFile+dwC3+5+18*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+8));
				
				*((BYTE*)(rFile+dwC3+6+18*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+10));
				*((BYTE*)(rFile+dwC3+7+18*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+10));
				*((BYTE*)(rFile+dwC3+8+18*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+10));
				
				*((BYTE*)(rFile+dwC3+6+9*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+12));
				*((BYTE*)(rFile+dwC3+7+9*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+12));
				*((BYTE*)(rFile+dwC3+8+9*dwX)) = (BYTE)*((BYTE*)(bFile+dwC2+12));
				
				*((BYTE*)(rFile+dwC3+6)) = (BYTE)*((BYTE*)(bFile+dwC2+14));
				*((BYTE*)(rFile+dwC3+7)) = (BYTE)*((BYTE*)(bFile+dwC2+14));
				*((BYTE*)(rFile+dwC3+8)) = (BYTE)*((BYTE*)(bFile+dwC2+14));
				
				*((BYTE*)(rFile+dwC3+3+9*dwX)) = 0;
				*((BYTE*)(rFile+dwC3+4+9*dwX)) = 0;
				*((BYTE*)(rFile+dwC3+5+9*dwX)) = 255;
				dwC3 = dwC3 + 9;
			}
			dwC1 += dwX*19;
			dwC3 += dwX*18;
		}		
		WriteFile(hFile4, rFile, (dwX*dwY*27*19)/2, &dwTemp2, NULL);
		CloseHandle(hFile4);
		CloseHandle(hFile);
	return 0;
}

Code for converting a picture into a traffic graph:

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main () {
		char pppp[] = { 0x42, 0x4D, 0x1A, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
		char mainstr[256];
		scanf("%s", mainstr);
		DWORD dwX, dwY;
		DWORD dwTemp, dwTemp2, dwLolKek = 0x31, dwSignature, dwFiles, dwFirSec, dwLasSec;
		char bFile[0x260000], rFile[0x402000], bTemp[20], *bNum;
		bNum = (char*)malloc(20);
		HANDLE hFile = CreateFileA(mainstr,GENERIC_READ,4,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
		int a = strlen(mainstr);
		mainstr[a-4] = '-';
		mainstr[a] = '.';
		mainstr[a+1] = 'A';
		mainstr[a+2] = 'I';
		mainstr[a+3] = 'G';
		mainstr[a+4] = 0;
		HANDLE hFile4 = CreateFileA(mainstr,GENERIC_ALL,4,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
		ReadFile(hFile,rFile,0x402000,&dwTemp,NULL);
		DWORD dwC1 = 0;
		scanf("%d %d", &dwX, &dwY);
		dwX = dwX*8;
		dwY = dwY*8;
		register DWORD dwC3 = 0x36;
		for (register DWORD dwC = 0; dwC < 1024; dwC ++) {
			for (register DWORD dwC2 = dwC1; dwC2 < dwC1+dwY*16; dwC2 += 0x10) {
				*((BYTE*)(bFile+dwC2+0)) = *((BYTE*)(rFile+dwC3+3));
				if ((BYTE)*((BYTE*)(bFile+dwC2+0)) == 0xff) *((BYTE*)(bFile+dwC2+1)) = 0xff; else *((BYTE*)(bFile+dwC2+1)) = 0x00;
				
				*((BYTE*)(bFile+dwC2+2)) = *((BYTE*)(rFile+dwC3+0));
				if ((BYTE)*((BYTE*)(bFile+dwC2+2)) == 0xff) *((BYTE*)(bFile+dwC2+3)) = 0xff; else *((BYTE*)(bFile+dwC2+3)) = 0x00;
				
				*((BYTE*)(bFile+dwC2+4)) = *((BYTE*)(rFile+dwC3+0+dwX*9));
				if ((BYTE)*((BYTE*)(bFile+dwC2+4)) == 0xff) *((BYTE*)(bFile+dwC2+5)) = 0xff; else *((BYTE*)(bFile+dwC2+5)) = 0x00;
				
				*((BYTE*)(bFile+dwC2+6)) = *((BYTE*)(rFile+dwC3+0+dwX*18));
				if ((BYTE)*((BYTE*)(bFile+dwC2+6)) == 0xff) *((BYTE*)(bFile+dwC2+7)) = 0xff; else *((BYTE*)(bFile+dwC2+7)) = 0x00;
				
				*((BYTE*)(bFile+dwC2+8)) = *((BYTE*)(rFile+dwC3+3+dwX*18));
				if ((BYTE)*((BYTE*)(bFile+dwC2+8)) == 0xff) *((BYTE*)(bFile+dwC2+9)) = 0xff; else *((BYTE*)(bFile+dwC2+9)) = 0x00;
				
				*((BYTE*)(bFile+dwC2+10)) = *((BYTE*)(rFile+dwC3+6+dwX*18));
				if ((BYTE)*((BYTE*)(bFile+dwC2+10)) == 0xff) *((BYTE*)(bFile+dwC2+11)) = 0xff; else *((BYTE*)(bFile+dwC2+11)) = 0x00;
				
				*((BYTE*)(bFile+dwC2+12)) = *((BYTE*)(rFile+dwC3+6+dwX*9));
				if ((BYTE)*((BYTE*)(bFile+dwC2+12)) == 0xff) *((BYTE*)(bFile+dwC2+13)) = 0xff; else *((BYTE*)(bFile+dwC2+13)) = 0x00;
				
				*((BYTE*)(bFile+dwC2+14)) = *((BYTE*)(rFile+dwC3+6));
				if ((BYTE)*((BYTE*)(bFile+dwC2+14)) == 0xff) *((BYTE*)(bFile+dwC2+15)) = 0xff; else *((BYTE*)(bFile+dwC2+15)) = 0x00;
				dwC3 = dwC3 + 9;
			}
			dwC1 += 19*dwX;
			dwC3 += 18*dwX;
		}
		WriteFile(hFile4, bFile, 152*dwX*dwY, &dwTemp2, NULL);
		CloseHandle(hFile4);
		CloseHandle(hFile);
	return 0;
}

You can try compiling the code yourself, just remember to increase the stack size.

Passability graphs for 3 PZ maps. Can you guess which ones?

Hidden text
PZ Map

PZ Map

Map from PZ: ZvA

Map from PZ: ZvA

PZ Map

PZ map

Then for me it was oh, how exciting, but what an epic result it turned out to be!

Conclusion and respect to modmakers

Recently, to the delight of many people, they have been working to improve the wonderful game Cursed of the Earth, while showing simply excellent results!

My friend PlayHard_GoPRo, my friend Igles, Pavel Shatov, Pyrsus Proskurin and many other wonderful modders are working on a real remake of the Cursed Lands right now! New territories, HD textures, perfectly adjusted balance, shields, staves, two-handed weapons, thoughtful scripts – this is not all that awaits you.

While cool guys are cutting a remake, I’m cutting an addon for the Cursed Lands… As soon as I have something to show you, I will definitely write a new article about this significant event. For now, I’ll just say mysteriously that SpellAddon is, in a sense, the basis for my addon. I never had a goal to get confused and make the PZ engine super universal and pull all the formulas from there into the configuration file, as Matthew and Demoth did, for example. Obviously, not all formulas make sense to change, but that’s a completely different story.

You can still find such developments as, for example, a long nickname, expanded parameters (for an online character), new spells for our wonderful game, rebalancing the expenditure of stamina, customization of skill prices, and the ability to reset the enemy’s aggro in my SpellAddon engine extension. This extension is also used in Jamevu, which is not surprising! I saw so many wishes for a normal reserve of strength, which is not spent equally quickly by a guy with 50 strength and with 300 strength, about the ability to run away from the enemy, that I simply could not leave it like that! And for me these innovations were also very welcome. Since 2018, it has become absolutely possible to escape from the enemy!

You can also see some of my developments on the YouTube channel on Cursed Lands and Allods from PlayHard_GoPro! This dude knows literally everything about the Cursed Lands and shares his knowledge not only about the original game, but also about various developments. Including our own.

Also, a couple of years ago I created XXX-MOD, which did not become very popular, probably because of its complexity. By the way, YouTuber Vismyt Play knows how to go through perverted modifications – and the other day you can check out how he goes through my ultra-complicated mod! He’s already done 1 stream.

This is the kind of vision everyone will have...

This is the kind of vision everyone will have…

One of the main features of the mod is that you can run away from the enemy, and the reserve of strength is spent one by one, and not as a percentage, which gives a feeling of freedom, and not of playing for a hero-smoker. A short list of what XXX-MOD brings to PZ: ZvA:

Hidden text

Changes compared to the original:
You can run away from the enemy (reset aggression).
You can play as a mage from the very beginning – the complexity of strong runes has been raised, but the complexity of elemental magic spells has been reduced.
Improved healing potions.
Enemies have been strengthened.
The player’s armor has been weakened.
Stages are decorated.
Playing as a warrior without magic has been made more difficult.
Spell balance has been changed.
Firez is no longer a cross-eyed fool.
Added experimental enemy respawn.
Now on Jigran the prices correspond to the plot.
The bottles have become more interesting.
Experience is given only for tasks – you no longer need to run after everyone all over the map.
Experience and money for tasks have been increased.
Batteries have been returned.
New materials have been added to Jigran.
Returned “Sands” to Gipat (just beat the mobs).
The alarm AI in the villages on Hypata has been replaced with an AI call for help (previously they attacked the player without any additional conditions).
Added Portal zone workers.
Elems on Suslanger drop stone.
Chests with pieces of thick leather have appeared in the “Cave of Wormfaces”.
Those who like to genocide mobs without experience and loot can now pit dragons against guards.
Secret missions have been changed!

A lot of people are engaged in modmaking for the game of 2000.

Let’s take a look at what the people’s remake team did! The Jamais Vu team is expected to provide quality that many game designers working today have never even dreamed of. Communicating with some of them, I got the impression that these people do not know what hack work is, just like those people who at one time created the most magnificent work “The Lord of the Rings”.

The work of programmer Pavel Shatov

The work of programmer Pavel Shatov

Weapon in 2 hands. An absolutely working character (not photoshopped, not an edited model with duplicate weapons), who can be given any combination of one-handed weapons. Naturally, this is not all that programmers who love the Cursed Lands, an article from one of which you are reading right now, are capable of.

The work of programmer Pavel Shatov

The work of programmer Pavel Shatov

No comments needed. It is a masterpiece! Just like what PlayHard_GoPro and Selenor did:

Minor-Mod from two benders of the Cursed Lands

Minor-Mod from two benders of the Cursed Lands

It’s funny that both of these dudes made it through the Cursed Lands without dying on their channels. Selenor is the game designer in this tandem, and PlayHard_GoPro is the programmer. Of course, this is not as grandiose a work as HG or Jamais Vu, but this is perhaps the best that a conservative can find for himself from the whole variety of mods.

These lanterns!

These lanterns!

I don’t know about you, but I’m just delighted with these lanterns!

I was very pleased to understand that people still haven’t forgotten about the Cursed Lands and the Allods universe in general! YouTuber CveaChannel actually sped up this game in 58 minutes, making several dozen attempts on his streams.

I hope that over time you and I will learn even more about what this world hides… The World of the Cursed Lands!

Dragon with a new texture from Jamevu!

Dragon with a new texture from Jamevu!

Links to interesting content:
https://evilislandsaddon.forumotion.com/t2-spelladdon – a forum where you can download the SpellAddon and XXX-MOD engine extension for PZ: ZvA
https://www.youtube.com/channel/UCrv33tEsSUhf3wVaht9y2oA – my friend, a cool YouTuber and just a wonderful person
https://vk.com/evil_islands_jamaisvu – remake mod group Jamais Vu
https://vk.com/hgmod – Honest Group fashion
http://gipatgroup.org/ – formerly the most popular forum of the Cursed Lands
https://www.gipat.ru/ – working forum of the Cursed Lands
https://vk.com/eiclassicmod – Classic-mod group
https://vk.com/evil.islands – group PZ
https://www.youtube.com/watch?v=6Z_sb_9dO14 – active streamer Vismyt Play
https://www.youtube.com/watch?v=TMiWd1UP100 – streamer and YouTuber Selenor
https://www.youtube.com/watch?v=HOHmZjwTfSg – speedrunner, YouTuber, CveaChannel tester
https://www.youtube.com/watch?v=f7Q8E2b2_zg – Pavel Shatov, developing Allods 2 Seal of Secrets on the Unreal Engine
http://honestgroup.net/forum/ – unfortunately abandoned Honest Group forum

Good luck to all!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *