KAZESUI'S PROFILE

Doing Super Programming
on Super Computers
for Super Performance
The Curse of Cpt. Lovele...
Nautical-themed cephalopod-pirate-based action-shmup.

Search

Filter

[RM2K3] DynRPG Compiling Error

Scope here refers to variable life time, and is something which goes back to the stack vs heap thing again. Variables declared in a function generally have that function as their scope.

However, stuff on the heap (or global memory, like your std::map variables) can be changed at arbitrary points of the code. If you really showed the full function in that snippet, then there should be some issues with it, since the text images never get freed up or reset.
if(!myText[i] && RPG::monsters[i]) //Checks is there is a valid Image object and associated Monster exists.

once this you enter this if, and create an image for myText, it should never be entered again for that specific index (e.g. once you create a text for i = 0, it should never be entered whenever i = 0 the next time you enter the loop), since the image has already been created and will return always return true, which becomes false after the negation from '!'. Note that the negation operator will only be applied to
myText[i]
and not to both
myText[i] && RPG::monsters[i]
since the negation operator has a higher precedence than the 'logical and' operator '&&'. You could apply it to both by using a parenthesis, but then you get a memory leak instead. As mentioned, these variables are never destroyed automatically, since myText is global.
Unless these images are freed somewhere else in the code, then this should mean that you should be running into issues after having gone through one battle with one set of enemies and then encountering a new set of enemies. (If you have the same enemies, i.e. 4 king slimes in every battle, you'll probably not notice this issue).

If the images are freed, then that's not in the code snippet and that's why I'm not able to assess properly if there are issues with regards to memory management.

[RM2K3] DynRPG Compiling Error

Can't address too many of the earlier points mentioned, as scope is pretty important for these questions, and the scope is not contained in these snippets.
The code presented looks fine I would say. No need for optimization at this point.

And no, I don't see any obvious way to alter the damage itself, though instead of swapping the skill, you might be able to just alter the skill / spell itself directly for when a critical is supposed to happen, and change it back afterwards.
e.g. Do the check if it is supposed to cause a critical with onDoBattlerAction(), and change the skill if so, and then change it back to default with onBattlerActionDone(). This might even be safer than swapping skills during the battle, though I'm not sure how this would be best implemented, or the exact order in which calculations take place, so a different order of things might be required.

[RM2K3] DynRPG Compiling Error

It's perfectly fine to use if statements this way, and is even a standard way of doing it (I do this in my plugins as well). I'm just stressing the importance of setting it to NULL after calling destroy since the if statement won't generally work if you don't do this. The program itself does not know if there is an object or not at the end of the address during runtime, it just goes to wherever the address points and tries to access data as if it would be an object of the type defined by the pointer type. Upon trying to access data which the OS tells the program it does not have access to (because it has been de-allocated or never allocated in the first place), it will throw an exception and crash the program.
If you make sure to always reallocate memory for the image before this becomes a problem, you won't run into issues, but then you don't need the if statement either.

You do not need to initialize a pointer to NULL if you give it an address shortly after creation. You DO need to set it to NULL if you have an if statement checking the content of the pointer before it has been given an address however, because variables in c++ do not assume 0 or NULL as a default value.

e.g. if we alter the code from my previous example accordingly, we'll crash at the first if already
RPG::Image* img;
if(img)  // img is not NULL, which evaluates as true, so this branch will now be entered
    RPG::screen->canvas->draw(x, y, img);  // This should crash now
img = RPG::Image::create();
RPG::Image::destroy(img);
if(img)  //img is likely not NULL, which evaluates as true, so this branch will be entered
    RPG::screen->canvas->draw(x, y, img);  // This will likely crash


Telling you exactly what to do is hard without having the full source / context itself, but assuming that the if statements make sense, then you should probably also set the pointers to NULL after you destroy them.

[RM2K3] DynRPG Compiling Error

author=McBick
RPG::Image::destroy(myImage[0]);
...
myImage[0] = NULL; //Won't this crash?
...
Won't this try to change the value of an object that doesn't exist and crash?


Let me try to illustrate with code
RPG::Image* img;  // at this point, img contains random numbers
img = NULL;  // Sets the address in img to NULL, meaning it is not pointing at anything
img = RPG::Image::create();  // create() allocates memory for an image, and returns the address to that memory
RPG::Image::destroy(img);  // destroy() goes to the address stored in img, and deallocates the memory. Probably does not remove address at this point
img = NULL;  // Manually sets the address to NULL to reaffirm that the pointer is not pointing at anything.

Note that img never has the value of an object, but just the address to a point in memory which can be used to retrieve the object, and values within it. Of course, when you set a pointer to NULL, you cannot access anything from the object either (as there is no object), but you can't do that after destroying the image anyway (accessing deallocated memory will cause the program to crash).

The benefit of doing this is that
RPG::Image* img = NULL;
if(img)  // img is NULL, which evaluates as false, so this branch will never be entered
    RPG::screen->canvas->draw(x, y, img);  // This would normally crash, but here will never be executed
img = RPG::Image::create();
RPG::Image::destroy(img);
if(img)  //img is likely not NULL, which evaluates as true, so this branch will be entered
    RPG::screen->canvas->draw(x, y, img);  // This will likely crash

You can use if statements to check if the pointer has been allocated like this.
Also note that NULL is a value which can be set to every pointer variable, regardless of type.

[RM2K3] DynRPG Compiling Error

This is fine as long as you keep in mind what I mentioned, i.e.
RPG::Image* img = RPG::Image::create();
img->loadFromFile(loc);
RPG::Image::destroy(img);
if(img)
    RPG::screen->canvas->draw(x, y, img);  // This will probably crash


For this reason, and because it is a good habit as well, I would adjust your code to
if(RPG::battleData->battlePhase == RPG::BPHASE_END) //This would be in the callback onFrame
{
    RPG::Image::destroy(myImage[0]);
    RPG::Image::destroy(myImage[1]);
    ...  

    myImage[0] = NULL;
    myImage[1] = NULL;
    ...
}


since doing this, ensures that your if(img) will always be valid (or whatever other pointer you might be using at some point).

[RM2K3] DynRPG Compiling Error

author=McBick
I thought transitioning through scenes cleared the canvas and any graphical objects were also cleared away as well, but it seems I was wrong. If I were to do something like this:
if(myImage[i])
{
            RPG::Image::destroy(myImage[i]);
}
if(!myImage[i])
        {
            myImage[i] = RPG::Image::create();
            myImage[i]->loadFromFile(loc);
        }




This should essentially destroy the Image before creating a new one. The point of this would be if the battle ended without destroying the Image object then it would be destroyed during the next battle when the relevant Image is to be created again, so there won't be duplicates, or a memory leak.


Rather than doing this, maybe you use the callback "onInitFinished", Then you create all the images you intend to use (assuming you do not want to use too many different types of images). Doing this means you do not have to check with an if statement if there is an image in your array and you will not have to delete it. If RPG::Image::create is only used within this callback it will not cause any memory leaks. This is probably the easiest and safest way to do it (at the expense of eating a little bit more RAM than if you would allocate this dynamically as the need arises, but with the sizes used by rm2k3 and the setups of today, this is unlikely going to be a problem anyway).

You could try it your way as well, but that would be the point where you will start running it runtime errors, exactly because your if statement does not check if you've allocated a space for your image, it just checks if the pointer is set to 0 / NULL. It is possible that "RPG::Image::destroy" does this for you, but there is no guarantee of this, and there's a good chance the pointer will have the address of where the image used to be, but no is not anymore because it was destroyed. This address is not equal to 0 / NULL, so it will pass the if and try to destroy an image which has already been destroyed and will fail since there is no more image there.

You should add
myImage[i] = NULL;


or something similar after you call RPG::Image::destroy to be more certain.
Of course, I assume these two if statements of yours are not placed that close in the real code, as destroying and creating an image each frame is not good either, which is what would happen if they were this close.

[RM2K3] DynRPG Compiling Error

RPG::Image::create() allocates memory on the heap, not in the stack. Also transitioning from between scenes (for example from Battle to the Map) does not destroy any RPG::Image data. If you do not call free() before calling RPG::Image::create() again, you will create a memory leak.

That said, as long as you keep the image creation behind and if like this and you simply re-use the images already drawn with some logic as to when they are supposed be drawn, you do not really need to delete them manually. While scene changes will not remove any images, closing the game will free this memory as well.

If you want to test this a little bit more easily, find a really big picture which you load into your plugin, and then monitor the task manager. The bigger the better, as this will help you notice if there's anything there (as long as your logic is generic enough to allow for a test like this without becoming too artificial). That said, it might be safe as is as well, even if there is a memory leak if it is that tiny.

[RM2K3] DynRPG Compiling Error

Theoretically, if you want to store an array of pointers of different types, you can convert all of these pointer to "void*". But once you want to access the content of the pointer, you need to know the type of the pointer.

// Store relevant pointer types here
enum ptr_types{
    INT,
    BOOL
};

// Define struct which contains arbitrary pointer type and what type it is
struct t_ptr{
   void* ptr;
   ptr_types ptr_type;
};

int a = 42;
bool b = True

t_ptr ptr_a;
t_ptr ptr_b;

ptr_a.ptr = reinterpret_cast<void*>(&a);
ptr_b.ptr = reinterpret_cast<void*>(&b);
ptr_a.ptr_type = ptr_types::INT;
ptr_b.ptr_type = ptr_types::BOOL;

std::vector<t_ptr> ptr_list;  // declares a common array for all your funky pointers
ptr_list.append(ptr_a);
ptr_list.append(ptr_b);
int a2;
bool b2;
for(int i = 0; i < ptr_list.size(); i++)
{
    switch(ptr_list[i].ptr_type)  // The switch statement tells the code to continue to execute
    {
        case ptr_types::INT  // If ptr_type is an int, convert the void* to an int*
            a2 = *reinterpret_cast<int*>(ptr_list[i]);  // a2 should now be set to 42
            break;  // tells the code to exit the switch. Code will break otherwise
        case ptr_types::BOOL
            b2 = *reinterpret_cast<bool*>(ptr_list[i]);  // b2 should now be set to true
            break;
        default
            break; // in case the ptr_list[i].ptr_type contains a ptr_type not listed
    }        
}
Note that the example is more pedagogical than practical, but that would be a conceptual way to deal with arbitrary pointer types. Also, beware that there could be some syntax errors in there, as I don't have a compiler at hand to actually test the code at the moment. This is pretty much what Polymorphism is about as well, expect that this is a more common C way of doing things rather than C++.

Void pointers can be useful for writing more general functions, but keep in mind that you cannot dereference a void pointer. You need to convert it to something else before trying to read the value of the address (otherwise, the program won't know how many bytes are needed to interpret the value stored at that address).

Also note that if the value itself is const, it won't matter if you create a copy of the pointer pointing to the address of the value, as the value will still be const.
const int a = 42;
int* ptr_a = &a;
int* ptr_b = ptr_a;
*ptr_a = 1337; // This is not allowed
*ptr_b = 1337; // This is also not allowed for the same reason

int c = a; // This sets 'c' to 42
int* ptr_c = &c;
*ptr_c = 1337; // This is allowed and sets 'c' to 1337. 'a' remains 42

Edit: changed the first code snippet a little bit to make it somewhat more practical for the actual use-case

[RM2K3] DynRPG Compiling Error

ptrB is already a pointer so there's no need to use the & operator when wanting to copy the address. You can change from one type to another using reinterpret_cast, but the results might not be too intuitive.

A better approach might be to get into "why" you want to do this, or what you want to achieve with this

For the concrete example
bool b = true;
bool* ptrB = &b;
int* ptrA = reinterpret_cast<int*>(ptrB);
int a = *ptrA;  // if the step above works, 'a' might now be set to 1 (or something random)
Note that there is a good chance that the third line will fail, since a bool will generally have 1 byte allocated to it, while an integer expects 4 bytes, meaning it might try to read 4 bytes of data, which can lead to a segmentation fault, or to some very peculiar errors if the memory has been allocated.
Better yet would be to do (I think there's a good chance that it will compile, but that int a will receive an undefined value. I just don't remember exactly how this would play out)

bool b = true;
bool* ptrB = &b;
uint8* ptrA = reinterpret_cast<int*>(ptrB);
uint8 a = *ptrA;  // a should be set to 1

uint8 means an unsigned integer of 1 byte. That it is unsigned means that it can store no negative values (does not apply to float types) and int8 refers to 8 bits beings used for the integer.
Note however that uint8 can only store numbers from (and including) 0 to 255

[RM2K3] DynRPG Compiling Error

author=McBick
author=Kazesui
1. RPG::Battler is a class definition, not a variable. You cannot take the address of a class definition
This confuses me. To clarify, doing this would not work:
Namespace::Class* Ptr;
This would work (assuming namespace and class are both valid)
RPG::Battler* battler_ptr_a; // this should work
RPG::Battler* battler_ptr_b = battler_ptr_a; // this should also work
RPG::Battler* battler_ptr_c = &RPG::Battler  // this does not work
int* battler_ptr_d = &battle_ptr_a  // this does also not work
int* battler_ptr_e = battle_ptr_a  // nor should this
The third line is equivalent with the code you wrote

author=McBick
Would something like this be okay:
battler* actor; //I'm pretty sure this will error. 
// NOTE: (Correct, battler is not a class definition, but an object of RPG::Battler. You cannot declare a variable as the type of another variable)
int hp = actor->hp;

If you change the code to this, it should work (added callback for clarity)
bool onBattlerDrawn(RPG::Battler *battler, bool isMonster, int id)
{
    RPG::Battler* actor;
    actor = battler;
    int hp = actor->hp;
}
Side note here
RPG::Battler* battler;
RPG::Battler *battler;
These two things are exactly the same as seen from the compiler. I personally prefer putting the dereference operator next to the variable type when declaring pointers as I find that to be more clear, but a lot of people prefer putting it next to the variable during declaration. Either way, there are no differences between these two in terms of functionality

author=McBick
If not, is there a good way to copy pointers? For example making PtrA reference the same address as PtrB? I am guessing I would have to do something like this:
int n = 8;
int* PtrA = &n;
int* PtrB = &(*PtrA); //This should give PtrB the same address as PtrA?
No need for the extra layers or & and * since they negate each other. What I showed up there was to demonstrate the differences while also showing how they are kind of the inverse of each other.
int n = 8;
int* ptrA = &n;  // Again, these are variables, so they should start with a lowercase letter
int* ptrB = ptrA; //This this copies the address of ptrA into ptrB

author=Kazesui
Now, if you're really bent on converting it to something else, what you could do is

author=McBick
int* actor = reinterpret_cast<int*>(battler);
int actor_value = *actor;
This looks interesting. I will have to experiment with this to see if I can use this for anything or cut some of my code down with it.
If that "anything" is not memory mapping, then no, you should not use this. You're only going to confuse yourself further without gaining much from it, and it will most certainly not cut down code length. If anything, it will add code length and complexity and severely decrease readability of the code, even when done right.