PEPSIOTAKU'S DYNRPG PLUGIN EMPORIUM!

Posts

What exactly did you do? Maybe I can spot the mistake.
Here's all of the code so far. Keep in mind this is still a work in progress. I haven't added the ability to change the actual "ATK/DEF/INT/AGL" text yet, but that's planned.


#define AUTO_DLLMAIN
#include <DynRPG/DynRPG.h>
#include <sstream> // For std::stringstream

std::map<std::string, std::string> configuration;

// This array stores the stat values for the current party members in battle (5 stats, 4 heroes max)
unsigned short int g_targetHero[5][4] = {
{0, 0, 0, 0}, // 0 - Hero MP
{0, 0, 0, 0}, // 1 - Hero Attack
{0, 0, 0, 0}, // 2 - Hero Defense
{0, 0, 0, 0}, // 3 - Hero Intelligence
{0, 0, 0, 0} // 4 - Hero Agility
};

// This array stores the stat values for the current monsters in battle (5 stats, 8 monsters max)
unsigned short int g_targetMonster[5][8] = {
{0, 0, 0, 0, 0, 0, 0, 0}, // 0 - Monster MP
{0, 0, 0, 0, 0, 0, 0, 0}, // 1 - Monster Attack
{0, 0, 0, 0, 0, 0, 0, 0}, // 2 - Monster Defense
{0, 0, 0, 0, 0, 0, 0, 0}, // 3 - Monster Intelligence
{0, 0, 0, 0, 0, 0, 0, 0}, // 4 - Monster Agility
};
bool g_isInitialized = false;

// This is used to store the stat values after they've been calculated. 5 stats. 6 conditionals (hero stat up/down, monsters 1-4 up/down, monsters 5-8 up/down)

unsigned short int battlerStat[5][6] = {
{0, 0, 0, 0, 0, 0}, // 0 - MP Storage
{0, 0, 0, 0, 0, 0}, // 1 - Attack Storage
{0, 0, 0, 0, 0, 0}, // 2 - Defense Storage
{0, 0, 0, 0, 0, 0}, // 3 - Intelligence Storage
{0, 0, 0, 0, 0, 0} // 4 - Agility Storage
};

// config options
unsigned char confColorMpPlus;
unsigned char confColorMpMinus;
bool confShowMpPlus;
bool confShowMpMinus;
bool confMonMpPlus;
bool confMonMpMinus;// This is the problematic option. If it's set to false, MonMpPlus doesn't show either
bool confStatsPlus;
bool confStatsMinus;
//std::stringstream popupText;

unsigned char frameTimer = 0;

bool onStartup (char *pluginName) {
// Get configuration settings
configuration = RPG::loadConfiguration(pluginName);
confColorMpPlus = atoi(configuration["ColorMpPlus"].c_str()); // Convert String to Int
confColorMpMinus = atoi(configuration["ColorMpMinus"].c_str());
confShowMpPlus = configuration["ShowMpPlus"] == "true";
confShowMpMinus = configuration["ShowMpMinus"] == "true";
confMonMpPlus = configuration["MonMpPlus"] == "true";
confMonMpMinus = configuration["MonMpMinus"] == "true"; //confMonMinus is declared
confStatsPlus = configuration["StatsPlus"] == "true";
confStatsMinus = configuration["StatsMinus"] == "true";
return true;
}

// the following functions are used to get the initial stat before a battler's action

void statInitHero ( int stat, int statId, int id) {
g_targetHero[statId][id] = stat;
}

void statInitMonster ( int stat, int statId, int id) {
g_targetMonster[statId][id] = stat;
}

// here is where the above funtions are run
bool __cdecl onDoBattlerAction (RPG::Battler *battler){
if (g_isInitialized == false) {
int i;
for (i=0; i<4; i++){
if (RPG::Actor::partyMember(i) != NULL) {
statInitHero(RPG::Actor::partyMember(i)->mp, 0, i);
statInitHero(RPG::Actor::partyMember(i)->getAttack(), 1, i);
statInitHero(RPG::Actor::partyMember(i)->getDefense(), 2, i);
statInitHero(RPG::Actor::partyMember(i)->getIntelligence(), 3, i);
statInitHero(RPG::Actor::partyMember(i)->getAgility(), 4, i);
}
if (RPG::monsters[i] != NULL) {
statInitMonster(RPG::monsters[i]->mp, 0, i);
statInitMonster(RPG::monsters[i]->getAttack(), 1, i);
statInitMonster(RPG::monsters[i]->getDefense(), 2, i);
statInitMonster(RPG::monsters[i]->getIntelligence(), 3, i);
statInitMonster(RPG::monsters[i]->getAgility(), 4, i);

}
if (RPG::monsters[4+i] != NULL) {
statInitMonster(RPG::monsters[4+i]->mp, 0, 4+i);
statInitMonster(RPG::monsters[4+i]->getAttack(), 1, 4+i);
statInitMonster(RPG::monsters[4+i]->getDefense(), 2, 4+i);
statInitMonster(RPG::monsters[4+i]->getIntelligence(), 3, 4+i);
statInitMonster(RPG::monsters[4+i]->getAgility(), 4, 4+i);
}
}
g_isInitialized = true;
}
return true;
}

// This is where it gets hairy. statPopupHero and statPopupMonster both do the exact same thing. The only difference is monsters vs heroes

void statPopupHero ( int stat, int statId, int id, int storeId, int colorPlus, int colorMinus, bool settingPlus, bool settingMinus, std::string statAbbr) {\
// stat increase
if (stat > g_targetHero[statId][id] && settingPlus == true) {
battlerStat[statId][storeId] = stat - g_targetHero[statId][id];
if (statId == 0) RPG::Actor::partyMember(id)->damagePopup(battlerStat[statId][storeId], colorPlus); // for mp
else { // for atk/def/int/agl
std::stringstream popupText;
popupText << "+" << battlerStat[statId][storeId] << " " << statAbbr;
RPG::Actor::partyMember(id)->damagePopup(popupText.str());
}
g_targetHero[statId][id] = stat;
// stat decrease
} else if (stat < g_targetHero[statId][id] && settingMinus == true) {
battlerStat[statId][storeId+1] = g_targetHero[statId][id] - stat;
if (statId == 0) RPG::Actor::partyMember(id)->damagePopup(battlerStat[statId][storeId+1], colorMinus); // for mp
else { // for atk/def/int/agl
std::stringstream popupText;
popupText << "-" << battlerStat[statId][storeId] << " " << statAbbr;
RPG::Actor::partyMember(id)->damagePopup(popupText.str());
}
g_targetHero[statId][id] = stat;
}
}

void statPopupMonster ( int stat, int statId, int id, int storeId, int colorPlus, int colorMinus, bool settingPlus, bool settingMinus, std::string statAbbr) {
// stat increase
if (stat > g_targetMonster[statId][id] && settingPlus == true) {
battlerStat[statId][storeId] = stat - g_targetMonster[statId][id];
if (statId == 0) RPG::monsters[id]->damagePopup(battlerStat[statId][storeId], colorPlus); // for mp
else { // for atk/def/int/agl
std::stringstream popupText;
popupText << "+" << battlerStat[statId][storeId] << " " << statAbbr;
RPG::monsters[id]->damagePopup(popupText.str());
}
g_targetMonster[statId][id] = stat;
// stat decrease
} else if (stat < g_targetMonster[statId][id] && settingMinus == true) {
battlerStat[statId][storeId+1] = g_targetMonster[statId][id] - stat;
if (statId == 0) RPG::monsters[id]->damagePopup(battlerStat[statId][storeId+1], colorMinus); // for mp
else { // for atk/def/int/agl
std::stringstream popupText;
popupText << "-" << battlerStat[statId][storeId] << " " << statAbbr;
RPG::monsters[id]->damagePopup(popupText.str());
}
g_targetMonster[statId][id] = stat;
}

}

// here's how those functions are actually called
void onFrame (RPG::Scene scene){
if (scene == RPG::SCENE_BATTLE && g_isInitialized == true) {
frameTimer++;
if (frameTimer > 10) {
frameTimer = 0;
int i;
for (i=0; i<4; i++){
if (RPG::Actor::partyMember(i) != NULL) {
//RPG::Actor::partyMember(i)->damagePopupTimer == 0
statPopupHero(RPG::Actor::partyMember(i)->mp, 0, i, 0, confColorMpPlus, confColorMpMinus, confShowMpPlus, confShowMpMinus, "");
statPopupHero(RPG::Actor::partyMember(i)->getAttack(), 1, i, 0, 0, 0, confStatsPlus, confStatsMinus, "ATK");
statPopupHero(RPG::Actor::partyMember(i)->getDefense(), 2, i, 0, 0, 0, confStatsPlus, confStatsMinus, "DEF");
statPopupHero(RPG::Actor::partyMember(i)->getIntelligence(), 3, i, 0, 0, 0, confStatsPlus, confStatsMinus, "INT");
statPopupHero(RPG::Actor::partyMember(i)->getAgility(), 4, i, 0, 0, 0, confStatsPlus, confStatsMinus, "AGL");
}
if (RPG::monsters[i] != NULL) {
// RPG::monsters[i]->damagePopupTimer == 0
statPopupMonster(RPG::monsters[i]->mp, 0, i, 2, confColorMpPlus, confColorMpMinus, confMonMpPlus, confMonMpMinus, "");
statPopupMonster(RPG::monsters[i]->getAttack(), 1, i, 2, 0, 0, confStatsPlus, confStatsMinus, "ATK");
statPopupMonster(RPG::monsters[i]->getDefense(), 2, i, 2, 0, 0, confStatsPlus, confStatsMinus, "DEF");
statPopupMonster(RPG::monsters[i]->getIntelligence(), 3, i, 2, 0, 0, confStatsPlus, confStatsMinus, "INT");
statPopupMonster(RPG::monsters[i]->getAgility(), 4, i, 2, 0, 0, confStatsPlus, confStatsMinus, "AGL");
}
if (RPG::monsters[4+i] != NULL) {
statPopupMonster(RPG::monsters[4+i]->mp, 0, 4+i, 4, confColorMpPlus, confColorMpMinus, confMonMpPlus, confMonMpMinus, "");
statPopupMonster(RPG::monsters[4+i]->getAttack(), 1, 4+i, 4, 0, 0, confStatsPlus, confStatsMinus, "ATK");
statPopupMonster(RPG::monsters[4+i]->getDefense(), 2, 4+i, 4, 0, 0, confStatsPlus, confStatsMinus, "DEF");
statPopupMonster(RPG::monsters[4+i]->getIntelligence(), 3, 4+i, 4, 0, 0, confStatsPlus, confStatsMinus, "INT");
statPopupMonster(RPG::monsters[4+i]->getAgility(), 4, 4+i, 4, 0, 0, confStatsPlus, confStatsMinus, "AGL");
}
}
}
} else if (g_isInitialized == true) g_isInitialized = false;
}
A few things:

1) You don't need to write the whole {{0, 0, 0, 0}, {0, 0,......}} array initialization. A special syntactic sugar in C++ allows you to write
myArray[5][4] = {0};
2) I don't see any point in the battlerStat array, you could just store the changed amount in a local variable. Would be less to write too.
3) Your big problem is that you have the
g_targetHero[statId][id] = stat;
line (same for monsters) inside of the if(setingsPlus), etc.! This is why MonMpMinus doesn't work correctly. Think about it for a minute!

My suggestions to simplify everything:

Create a function getBattler(id) which takes IDs from 0 to 11 where 0-3 are players and 4-11 are monsters:

RPG::Battler *getBattler(int id) {
return id < 4 ? RPG::Actor::partyMember(id) : RPG::monsters[id - 4];
}

The same thing can be done for stats:
First, we create an enum so it's easier to read:
enum Stat {
STAT_MP,
STAT_ATK,
STAT_DEF,
STAT_INT,
STAT_AGI
}

And then a function to retrieve the right stat value:

int getStat(RPG::Battler *battler, Stat stat) {
switch(stat) {
case STAT_MP: return battler->mp;
case STAT_ATK: return battler->getAttack();
case STAT_DEF: return battler->getDefense();
case STAT_INT: return battler->getIntelligence();
case STAT_AGI: return battler->getAgility();
default: return 0;
}
}

Then you just need one array like this ("short int" will slow things down, by the way. Always use "int" for numbers):

int g_battlerStats[11][5] = {0};
I have used the battlers as first index here, because it seems more logical to me.

Now this code to initialize the stats array:
for(int battlerId = 0; battlerId < 12; battlerId++) {
for(Stat statId = 0; statId < 5; statId++) {
RPG::Battler *battler = getBattler(battlerId);
g_battlerStats[battlerId][statId] = battler ? getStat(battler, statId) : 0;
}
}

Let's create another function to do the popup:

void showPopup(RPG::Battler *battler, Stat statId, int difference) {
if(statId == STAT_MP) {
battler->damagePopup(abs(difference), difference > 0 ? confColorPlus : confColorMinus);
} else {
std::string statNames[] = {"ATK", "DEF", "INT", "AGI"};
std::stringstream text;
if(difference > 0) text << "+";
text << difference << " " << statNames[statId - 1];
battler->damagePopup(text.str());
}
}


And a function which determines whether the popup should be shown (according to configuration) or not:

bool shouldPopupBeShown(RPG::Battler *battler, Stat statId, int difference) {
if(difference > 0) {
if(statId == STAT_MP) {
return battler->isMonster() ? confMonMpPlus : confShowMpPlus;
} else {
return confStatsPlus;
}
} else if(difference < 0) {
if(statId == STAT_MP) {
return battler->isMonster() ? confMonMpMinus : confShowMpMinus;
} else {
return confStatsMinus;
}
} else {
return false;
}
}

And this simple code to check and display changes (including the queueing I mentioned):

for(int battlerId = 0; battlerId < 12; battlerId++) {
RPG::Battler *battler = getBattler(battlerId);
if(!battler) continue;
for(Stat statId = 0; statId < 5; statId++) {
int newValue = getStat(battler, statId);
int difference = newValue - g_battlerStats[battlerId][statId];
if(shouldPopupBeDisplayed(battler, statId, difference)) {
if(battler->damagePopupTimer == 0) {
showPopup(battler, statId, difference);
g_battlerStats[battlerId][statId] = newValue;
}
} else {
g_battlerStats[battlerId][statId] = newValue;
}
}
}

This should make the code a lot more readable and expandable!

Disclaimer: I didn't test this code.


I'll test out that code out later today and hopefully get a better understanding of it! Thanks!
Could be further improved with this undocumented way to access the vocabulary:

static RPG::DStringPtr *&vocabulary = (**reinterpret_cast<RPG::System ***>(0x4CDCB4));

// indexes are:
// ATK: vocabulary[66]
// DEF: vocabulary[67]
// INT: vocabulary[68]
// AGI: vocabulary[69]
I couldn't get your code to work when I attempted to on Friday, so I just cleaned up mine further. I did fix this at least:

Note: There's a weird bug where if you set MonMpPlus=true & MonMpMinus=false, the monster's MP won't show at all. I've looked over my code numerous times, and couldn't find what was causing this, so if you think you can figure out why, feel free to modify my source!

The update has been added here:
http://rpgmaker.net/engines/rm2k3/utilities/35/

I could not fix the damagePopupTimer issue though. When I tried to implement it, I was getting bizarre results. There's some code commented out in the source if you want to take a look at it, but I basically burned myself out on Friday trying to get it to work, so I just gave up and worked on tileset mapping the rest of the weekend. :)


While I was working on that though, I found an ingenious way to control the ATB Speed of heroes/monsters independently from one another. I hereby dub this plugin Advanced Faster ATB 2.
http://rpgmaker.net/users/PepsiOtaku/locker/faster_atb_advanced2.rar

Here's how it works:

This patch allows you to control the speed calculations of party members and monsters independently, in turn controlling the speed of ATB bar. It uses the formula:

************************************
Base ATB Speed + AddValue * SpeedVar
************************************

The maximum value the ATB Speed can be is 30000, which will trigger the battler's next action. The AddValue
is a hard-coded number you set within the DynRPG.ini file. 2500 is the recommnded value, but you can
decrease/increase this value to your desired result. The SpeedVar is a multiplier that you can set via variable from within RPG Maker.

The formula gets calculated a few times a second, so if you set your AddValue to 2500 & the SpeedVar to 12 (multiplying to 30000) you would get an instantaneous ATB bar (which also makes things a little easy) forcing each party member's action. The lower the AddValue, the higher your SpeedVar value needs to be.

While you can't technically "stop" the ATB bar using that formula, you can set the SpeedVar to a negative value in RPG maker, which would make the ATB bar go backwards in game until it hits 0 and remains there until the SpeedVar is changed again.


See the readme for the rest of the documentation. You can only change the values of all monsters or all party members so far, but I plan on adding a way to change the multipliers of individual battlers in the future.
I couldn't get your code to work when I attempted to on Friday


What was the problem?
From what I tried, this wouldn't compile:
RPG::Battler *getBattler(int id) {
return id < 4 ? RPG::Actor::partyMember(id) : RPG::monsters[id - 4];
}

Neither would this:
static RPG::DStringPtr *&vocabulary = (**reinterpret_cast<RPG::System ***>(0x4CDCB4));

// indexes are:
// ATK: vocabulary[66]
// DEF: vocabulary[67]
// INT: vocabulary[68]
// AGI: vocabulary[69]
Two little oversights of mine.

Firstly, as the compiler message should have told you, there was a cast missing here.
RPG::Battler *getBattler(int id) {

return id < 4 ? reinterpret_cast<RPG::Battler *>(RPG::Actor::partyMember(id)) : reinterpret_cast<RPG::Battler *>(RPG::monsters[id - 4]);
}


Secondly, due to a copypasting error the right type is supposed to be RPG::DStringPtr instead of RPG::System here:
static RPG::DStringPtr *&vocabulary = (**reinterpret_cast<RPG::DStringPtr ***>(0x4CDCB4));


The question is: Do you understand what my code is doing? In this case it shouldn't be too hard to get it to work in case I made some mistakes. Otherwise, feel free to ask. My goal is not only optimizing this particular plugin, but your coding style in general, because I think it would make everything much easier for you (saving time and hassle for maintaining the code) if you would use short, clear and clean code as the example I gave you.
author=PepsiOtaku
See the readme for the rest of the documentation. You can only change the values of all monsters or all party members so far, but I plan on adding a way to change the multipliers of individual battlers in the future.


If I get this right: with that current plugin you can control the speed of the ATB of the player and the monster party using events that change those variables?
Yeah, so if you wanted to increase the speed of the party members, but keep the default speed of the monsters (like I did), you can do so with that plugin.
That's quite interesting. When you finish the version that allows for the controlling of individual player and monsters speeds it should be possible to create a very solid turn-based system that also allows for a lot of freedom.
For now I'm going to play around with the current version of Advanced Faster ATB 2.

edit: Damn, being able to control the speeds makes the ATB system actually kinda fun and interesting.xD
I was thinking that too, but the problem isn't making the ATB instantaneous, it's the repercussions of that. If you did that for both heroes and monsters, you would get constant monster attacks, and wouldn't be able to get a hero attack in, or would have to do so very fast. If you keep the monsters at their usual pace, and make the heroes instantaneous, you would be able to get 3-4 turns in (each hero) before a monster gets to makes a move. There's a lot of give/take. IMO there's no "good" way to make it turn-based aside from either rewriting the engine, or making a CBS.
author=PepsiOtaku
I was thinking that too, but the problem isn't making the ATB instantaneous, it's the repercussions of that. If you did that for both heroes and monsters, you would get constant monster attacks, and wouldn't be able to get a hero attack in, or would have to do so very fast. If you keep the monsters at their usual pace, and make the heroes instantaneous, you would be able to get 3-4 turns in (each hero) before a monster gets to makes a move. There's a lot of give/take. IMO there's no "good" way to make it turn-based aside from either rewriting the engine, or making a CBS.

Hm, I was thinking of doing it like this (once the plugin allows for changing individual speeds):

-Assuming player party gets first move. All monster variables are set to a negative number. All player variables are set to 30000. Have 2 variables, 1 tracking party size, other tracking mob size.
-If Hero # makes his move, set his variable to negative (to prevent a Hero from moving twice during a player turn).
-When all Heroes have made their moves set monster variables to 30000 (or near it).
-If Monster # makes his move, set his variable to negative.
-When all Monsters have made their moves, change Hero variables back to 30000.

It would require page tabs on all Monster Groups to make it work, though.

Even if that would not work, this plugin would still allow for enough interesting options to make the ATB system a lot better than it is in its original state. I like the screenshot that you included that shows an option screen that sets different ATB speeds, because that alone could make the ATB system a lot user-friendly (newbies can set it to slow speed, experienced RPG players can set it to fast).
http://share.cherrytree.at/showfile-1900/tbbpatch_demo.rar

If someone could tweak this patch, the only problem it appears to have is that it's completely incompatible with DynRPG.

In comparison to crashing under status (Cerberus, last I checked), or not being able to stop turns from happening, making poison annoying (mine, last I checked), this is really good.
author=bulmabriefs144
http://share.cherrytree.at/showfile-1900/tbbpatch_demo.rarIf someone could tweak this patch, the only problem it appears to have is that it's completely incompatible with DynRPG.

In comparison to crashing under status (Cerberus, last I checked), or not being able to stop turns from happening, making poison annoying (mine, last I checked), this is really good.


Is that the thing Cherry made some time ago? If so, I've tried it out before, and yeah, it does work really well. But incompatible with DynRPG unfortunately.
No idea if this will work, but try this:
Edit: NOPE

Patch DynRPG first, and then patch that over it & see if it runs. I'm guessing it'll screw up some of the DynRPG hooks though.
It uhhh didn't work. I have to rebuild the RTP from scratch now.
It'll screw up everything, Pepsi. That's because DynRPG partly uses the same areas in the RPG_RT.exe for its code as the Revolution Patch did (that's because this patch has been discontinued, was never actually released and was thus considered obsolete - and space in the RPG_RT.exe is rare anyway). Plus, creating an IPS patch won't work in the first place because IPS isn't meant to "mutate" a file by changing its size. You might have noticed that the tbbpatch's RPG_RT.exe is larger than a normal RPG_RT.exe, and there is code in the extra space which gets cut off and then we have lots of broken jumps and calls.
author=bulmabriefs144
It uhhh didn't work. I have to rebuild the RTP from scratch now.

What did you do?

author=Cherry
It'll screw up everything, Pepsi. That's because DynRPG partly uses the same areas in the RPG_RT.exe for its code as the Revolution Patch did (that's because this patch has been discontinued, was never actually released and was thus considered obsolete - and space in the RPG_RT.exe is rare anyway). Plus, creating an IPS patch won't work in the first place because IPS isn't meant to "mutate" a file by changing its size. You might have noticed that the tbbpatch's RPG_RT.exe is larger than a normal RPG_RT.exe, and there is code in the extra space which gets cut off and then we have lots of broken jumps and calls.

D'oh. I didn't notice the difference in filesizes. I assumed it was built from a 1.08 exe. I'll leave the patches to you. :)