CHERRY'S PROFILE

Search

Filter

[RM2K3] Does DynRPG support map manipulation?

It won't change pathing/terrain, this is stored somewhere else (haven't reverse-engingeered that yet). But yes, changing the graphic of a tile inside the chipset graphic of course will change its representation of the map in all places where the tile is used (as long as your change is still present in memory).

[RM2K3] Does DynRPG support map manipulation?

I guess the only useful way would be to modify the actual graphics of the chipset that is loaded (in memory).

After a bit more digging, I think I know how to get into the actual graphic - here are updated class definitions:

class TileMap {
  public:
    void **vTable;
    int width;
    int height;
    bool loopX;
    bool loopY;
    short *tiles;
    
    inline short *tile(int x, int y) {
        if (x < 0 || x >= width || y < 0 || y >= height) return NULL;
        return &tiles[y * width + x];
    };
};

class Chipset {
  public:
    void **vTable;
    int _unknown;
    RPG::DStringPtr filename;
    RPG::Image *image;
    RPG::Image *imageDarkened;
};

class DbMap {
  public:
    void **vTable;
    int _unknown[12];
    TileMap *lowerLayer;
    TileMap *upperLayer;
    int _unknown2;
    Chipset *chipset;
};

DbMap *&dbMap = (**reinterpret_cast<DbMap ***>(0x4CDD14));

// You can access the filename of the chipset, if you are interested, like this:
std::cout << (std::string) dbMap->chipset->filename << std::endl;
// You can get a pointer to the actual graphic
RPG::Image *chipsetGraphic = dbMap->chipset->image;
// You can change something there - let's change the pixel at (40, 50) in the graphic
// to palette color 10
chipsetGraphic->pixel(40, 50) = 10;

(RPG::Image and RPG::DStringPtr are documented in DynRPG.)

Note that there isn't only "image", there is also "imageDarkened", which seems to be another copy of the graphics data but with a plalette whose colors are darkened a bit. I'm not sure what it's used for, it's possible that it's not used in the game at all and maybe only used in the editor when you select one map layer and the other layer is drawn in a darkened fashion - in that case you can ignore it.

Also you will have to play with it and see how it behaves when changing maps, restarting the game and such, because I don't know if the chipset graphics are loaded from disk every time or maybe they are cached and reused if the same chipset file would be loaded again (in which case your changes would be applied to a different map as well and you'd have to undo them when you see the map changes even if the chipset filename were the same).

[RM2K3] Does DynRPG support map manipulation?

Yes, that's what PepsiOtaku has been doing too in his version 0.30+ of DynRPG

[RM2K3] Does DynRPG support map manipulation?

(Width and height are in tiles, it's just the size of the two-dimensional tiles array.)

The vTable has nothing to do with it, it's just a pointer to Delphi's virtual function table for each object, which is again included for completeness but it's not needed here, we could also put an "int _unknown" instead.

You are looking at this from a wrong point of view. Things being locked in their scope and what a variable logically "is" are just concepts put up by the compiler. At the end of the day, it's data in memory which the computer will read from and write to based on machine code instructions created by the compiler based on your code.

Most of what DynRPG's header files do (and what my definitions here do as well) is to create C++ structures which will look the same in memory (as in, have the same things at the same places relative to each other) as the equivalent Delphi structures do which the RPG Maker already uses. And then I use the "SomeType &someValue = *reinterpret_cast<SomeType *>(memoryAddressHere)" construct to force C++ to "put" someValue at memoryAddressHere, at which point a variable from the RPG Maker already exists, hence we then have the same underlying value now accessible by both of us (both readable and writable). (Plus, I also sometimes call an RPG Maker function directly, which requires inline assembly because Delphi uses a different calling convention which C++ doesn't support.)

So, this whole construct - the DbMap instance, the two TileMap instances whose pointers are saved inside the DbMap structure, and the tiles arrays whose pointers are saved in the TileMap structures - already exists, the RPG Maker created it internally. All we do (ignoring the double-derefencing for a moment, which is a technical implementation detail in Delphi not relevant to the overall technique) is telling the compiler that our variable "dbMap" is to be put at a specific memory address (it's actually the address that's already written at 0x4CDD14, which is 0x4D1F70 in RM, but that doesn't matter for this explanation) instead of allocating it on the stack or heap. This works because we are not actually assigning dbMap in this line, we are assigning &dbMap, it's a reference assignment, and the reference is thereby linked to the address, similarly how "int a = 1; int &b = a; b = 2; /* a is now also 2 */" works. This is the address at which RPG Maker has already put its own dbMap variable, so we now just got access to this piece of information in RM's memory by a name we can use (dbMap). Since the whole structure of the DbMap and its two TileMaps and their tiles arrays already exists in memory (RM has created it for itself), we don't have to worry about that, we just tapped into the existing structure. If the RPG Maker changes something, we'll see it, and the other way round as well, since we are just operating on the exact same data now.

Simplified example of how it works - we have an instance xyzThing of a structure XYZ defined by the RPG Maker, and it has some fields x, y, a, b, z in it, and our own code (which doesn't have access to RPG Maker's source code which can even be in another programming language) would like to get access to the fields a and b of this xyzThing instance - this is how it can be done:

// ***************************************
// This part of the code is in reality written in Delphi and not C++ and existing in the RPG Maker:

class XYZ {
  public: // Note: Would also work with "private:"! But it simplifies the example.
    int x;
    int y;
    int a;
    int b;
    int z;
};

XYZ xyzThing;
xyzThing.x = 111;
xyzThing.y = 222;
xyzThing.a = 333;
xyzThing.b = 444;
xyzThing.z = 555;

// ***************************************
// This part of the code is actually C++, in DynRPG or your own code, which is loaded into the
// same process as the RPG Maker's code so it has access to all its memory:

// We are now defining a structure whose memory layout is identical to XYZ, at least up to
// the last field we are interested in
class Something {
  public:
    // Let's say we don't know what the fields at the first 8 bytes in the structure are
    // (they were originally x and y) and we don't care about their value much
    int _unknown[2];
    // We know this field is interesting for us (it's originally called a, but we can't know that)
    int foo; 
    // And this field is interesting for us too (it's originally called b, but we can't know that)
    int bar; 
    
    // The original structure also had a field z at the end but we don't care about that one,
    // we may not even be aware of its existence, and there is nothing after it in
    // the structure which we are interested in, so it doesn't matter (unlike x and y for
    // which had to define *some* fields as padding because the a and b came afterwards
    // and had to be aligned at the right location).
};

// Let's say we know for sure that xyzThing is always located at address 0x12345678, then we can write
// this reference assignment to get access to the same "thing" that is known as xyzThing to the
// third-party code that we don't normally have access to:
//   Something &something = *reinterpret_cast<Something *>(0x12345678)
// However, to make this example actually compile, let's take advantage of the fact that this
// *is* an example where we *do* actually have xyzThing in scope. The following line does much
// the same as "Something &something = xyzThing;", but the latter wouldn't compile because according
// to the compiler, Something and XYZ are different types.
// Again - in reality we won't have access to "xyzThing" as variable, that's why
// we have to use its hardcoded memory address! This is just to make the example
// work. In reality we don't have access to anything of the code written above,
// the only "touching point" is that fact that both the RPG Maker's code and our
// own code live in the same process, i.e. memory space!
Something &something = *reinterpret_cast<Something *>(&xyzThing);

std::cout << something.foo << std::endl; // This will print 333, which is actually xyzThing.a!
std::cout << something.bar << std::endl; // This will print 444, which is actually xyzThing.b!

something.foo = 999; // Let's change something!

// ***************************************
// Now, the following part of the code is again RPG Maker's own code which wouldn't
// actually be C++ in reality. Let's say at some point later on, the RPG Maker
// now accesses xyzThing.a - which is known to us as something.foo above, i.e.
// we just changed this value!

std::cout << xyzThing.a << std::endl; // Prints 999!

// So, we achieved full read and write access to the data known as xyzThing to one
// part of the code from another part of the code, without having its source code or having
// access to its class definition or even variable definition (except for the line
// where we use &xyzThing but again that's just to make this example work smoothly.)

You can see it working here: https://onlinegdb.com/Bkj2hqqeu - you can see it prints 333, 444, 999 as expected.

[RM2K3] Does DynRPG support map manipulation?

Do you happen to know the order of the tile ids?
No, sorry, I don't know that. You will have to use trial and error to figure it out. EasyRPG should probably have some docs about that but I couldn't find it.

Also why is there std::cout in the code, does that work in DynRPG?
I wrote that just for demonstration purposes - but it will actually work if you allocate a console:
#include <stdio.h>

// in onStartup:
AllocConsole(); // No include needed because DynRPG already includes the WinAPI headers
freopen("CON", "w", stdout);

Are these variables getting their values somewhere else in DynRPG?
No, they are used by the RPG Maker itself (the pointer to the DbMap instance of the currently map is located at the address that you can get by dereferencing address 0x4CDD14 twice, as is written in the last line of my code).

Also if I change a tile, can I still retrieve its original value some how? It seems to be a volatile memory change, so the original value should still exist somewhere?
Well, in the map file itself on disk, but you won't have an easy way of accessing that ^^

//How is this being utilized?
Width and height are used in the function that I wrote, they are needed to know the array bounds and the size of the first dimension. About the looping flags (indicating whether the map horizontally and/or vertically loops), you probably won't need them, I just included them for completeness, since I already know what fields they are. I could have used "int _unknown" too.

//This is?
I don't know what fields exist at those 48 bytes, that's why the placeholder is called _unknown. There is probably other stuff about the map in there, such as its music or encounters or such. I just know that the tile map pointers start at offset 0x34 so this is padding to ensure our own definition also places them at that offset.

Also, I realized that while technically changes in those normally-not-saved-and-loaded structures do persist for the game session, in this case loading a new map will of course void any changes as well. But you still need to take care of saving and loading changes yourself so that you can reapply them when loading from a save file. (Loading a save file or starting a new game (re)loads the map as far as I know, so you don't have to worry about restoring the original tiles at least.)

RMN's Sexy Demographics Touch Encounters

[Poll] [RM2k3] Any interest in a CMS maker?

Maniacs Patch - Hm we have the patch EULA for this reason: https://forums.rpgmakerweb.com/index.php?threads/rpg-maker-2003-patch-eula.42021/

Modify rpg_rt.ldb: You can do that using liblcf: https://github.com/EasyRPG/liblcf

You can also go the route of using the clipboard as a way to import and export parts of RM data such as event code, like RMEventFactory does: https://rpgmaker.net/forums/topics/5811/ (if you are interested, I can go a bit more into detail about the clipboard formats)

[RM2K3] Does DynRPG support map manipulation?

Here is an interface for the map tiles:

class TileMap {
  public:
    void **vTable;
    int width;
    int height;
    bool loopX;
    bool loopY;
    short *tiles;
    
    inline short *tile(int x, int y) {
        if (x < 0 || x >= width || y < 0 || y >= height) return NULL;
        return &tiles[y * width + x];
    };
};

class DbMap {
  public:
    void **vTable;
    int _unknown[12];
    TileMap *lowerLayer;
    TileMap *upperLayer;
};

DbMap *&dbMap = (**reinterpret_cast<DbMap ***>(0x4CDD14));

// *** EXAMPLE: ***
// Change lower layer tile at (3, 5) to tile ID 5000
short *tile = dbMap->lowerLayer->tile(3, 5);
if (tile) {
  std::cout << "Current tile ID: " << *tile << std::endl;
  *tile = 5000;
}

Untested, but should work, let me know if it doesn't...

Note: Since this is data which is normally not changable ingame, it means any change here persists for the game session (until game is closed) and is not affected by saving/loading/resetting the game - you have to take care of this yourself.

(Solved) [DYNRPG] Setting a skill's Death condition in DynRPG doesn't work?

That's probably because PepsiOtaku defined it as RPG::DArray<bool> and not RPG::DArray<bool, 1> in Skill.h, but unfortunately this will now be very messy to fix because it would break existing code to change that, the only way would be to either use a define (but that can still get confusing) or a union with another correctly defined member but then the "good" member would have the unnecessarily weird name... welcome to the world of software interface compatibility.

[RM2K3] C++ Data Structures

Hm are you sure?

http://cpp.sh/8daae

As you can see here, the output is zero.



Maybe some other part of your code is the issue?