I have now finished the consumption code. It handles small and large races and mounts. Both of which can be turned off by rules in the database.
I made 3 characters on a live server and timed their eating & drinking. I can post the math for anyone interested, but the end result is I have an almost perfect live-like consumption rate (even with the rules turned off). I used unsigned integers which have no decimal place. To achieve a 'true' live-like rate, I would have to change this to use decimals. As it stands, food may only last a few extra seconds (42 to be exact), and I didn't think it was a big enough deal to worry with changing.
For all of this to work, 4 changes must be made. 3 to the code and 1 to the database.
In file \EQEmu-0.7.0-1119\common\ruletypes.h (Line 49) - ADD
Code:
RULE_BOOL( Character, RacialConsumption, true)
RULE_BOOL( Character, MountConsumption, true)
In file \EQEmu-0.7.0-1119\world\client.cpp (Line 957) - ADD
Code:
pp.hunger_level = 5000;
pp.thirst_level = 5000;
In file \EQEmu-0.7.0-1119\zone\client_packet.cpp (Line 1255 - 1327) - REPLACE
Code:
void Client::Handle_OP_Consume(const EQApplicationPacket *app)
{
//o----------------------------------------------------------------------------
//| Codemephit [2008.07.22] - Rewrote the consuption routine to handle racial
//| size and mounts.
//| Two rules were added. They are both Boolean.
//| Character:RacialConsumption - Turns on/off the racial size check.
//| Character:MountConsumption - Turns on/off the mount present check.
//o----------------------------------------------------------------------------
//o------------------------------------------------------------------------
//| Make sure the OP_Consume packet is valid and with no errors. If it is
//| not, then log the error and return to calling code.
//o------------------------------------------------------------------------
if (app->size != sizeof(Consume_Struct))
{
LogFile->write(EQEMuLog::Error, "OP size error: OP_Consume expected:%i got:%i", sizeof(Consume_Struct), app->size);
return;
}
Consume_Struct* pcs = (Consume_Struct*)app->pBuffer;
// This is the base Consumption Modifier.
uint16 intBaseConsMod = 107; //<-- This should be 106.5 but uint16 = no decimal.
// Apply the rule Character::ConsumptionModifier. We need to keep the base
// modifier seperate from the actual modifier to prevent compound calculations.
uint16 intConsMod = intBaseConsMod * RuleI(Character, ConsumptionMultiplier) / 100;
//o------------------------------------------------------------------------
//| Update intConsMod for Character Size. Only small or large characters
//| need to be updated, as there is no modifier for a medium sized player.
//o------------------------------------------------------------------------
if (RuleB(Character, RacialConsumption))
{
if (m_pp.race == 8 || m_pp.race == 11 || m_pp.race == 12)
{
// Dwarf = 8, Halfling = 11, Gnome = 12
// Player is a Small Race. (Modifier = +20%)
intConsMod = intConsMod + (intBaseConsMod * .20);
}
if (m_pp.race == 2 || m_pp.race == 9 || m_pp.race == 10 || m_pp.race == 128 || m_pp.race == 130)
{
// Barbarian = 2, Troll = 9, Ogre = 10, Iksar = 128, Vha Shir = 130
// Player is a Large Race. (Modifier = -20%)
intConsMod = intConsMod - (intBaseConsMod * .20);
}
}
//o------------------------------------------------------------------------
//| Update intConsMod for a summoned mount. Having this check before the
//| AA Innate Metabolism check will allow a mount to benifit from the AA
//| as well as the player. We caculate -25% from the 'base' modifer. This
//| ensures that mounts will eat the same ammount regardless of what size
//| thier owner is.
//o------------------------------------------------------------------------
if (RuleB(Character, MountConsumption))
{
if (GetHorseId() != 0)
{
// A mount is currently summoned.
intConsMod = intConsMod - (intBaseConsMod * .25);
}
}
//o------------------------------------------------------------------------
//| Update intConsMod for the Innate Metabolism AA.
//| All of these updates are done against the current modifer.
//o------------------------------------------------------------------------
switch(GetAA(aaInnateMetabolism))
{
case 1:
// Player has Innate Metabolism I (10% increase)
intConsMod = intConsMod + (intConsMod * .10);
break;
case 2:
// Player has Innate Metabolism II (25% increase)
intConsMod = intConsMod + (intConsMod * .25);
break;
case 3:
// Player has Innate Metabolism III (50% increase)
intConsMod = intConsMod + (intConsMod * .50);
break;
default:
// Player has no ranks in Innate Metabolism (0% increase)
break;
}
ItemInst *myitem = GetInv().GetItem(pcs->slot);
if(myitem == NULL)
{
LogFile->write(EQEMuLog::Error, "Consuming from empty slot %d", pcs->slot);
return;
}
const Item_Struct* eat_item = myitem->GetItem();
if (pcs->type == 0x01)
{
#if EQDEBUG >= 1
LogFile->write(EQEMuLog::Debug, "Eating from slot:%i", (int)pcs->slot);
#endif
m_pp.hunger_level += eat_item->CastTime*intConsMod;
DeleteItemInInventory(pcs->slot, 1, false);
if(pcs->auto_consumed != 0xffffffff) //no message if the client consumed for us
entity_list.MessageClose_StringID(this, true, 50, 0, EATING_MESSAGE, GetName(), eat_item->Name);
}
else if (pcs->type == 0x02)
{
#if EQDEBUG >= 1
LogFile->write(EQEMuLog::Debug, "Drinking from slot:%i", (int)pcs->slot);
#endif
m_pp.thirst_level += eat_item->CastTime*intConsMod;
DeleteItemInInventory(pcs->slot, 1, false);
if(pcs->auto_consumed != 0xffffffff) //no message if the client consumed for us
entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), eat_item->Name);
}
else
{
LogFile->write(EQEMuLog::Error, "OP_Consume: unknown type, type:%i", (int)pcs->type);
return;
}
if (m_pp.hunger_level > 6000)
m_pp.hunger_level = 6000;
if (m_pp.thirst_level > 6000)
m_pp.thirst_level = 6000;
EQApplicationPacket *outapp;
outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct));
Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer;
sta->food = m_pp.hunger_level;
sta->water = m_pp.thirst_level;
QueuePacket(outapp);
safe_delete(outapp);
return;
}
Database SQL
Code:
INSERT INTO rule_values (ruleset_id, rule_name, rule_value)
VALUES (1, 'Character:RacialConsumption', 'true');
INSERT INTO rule_values (ruleset_id, rule_name, rule_value)
VALUES (1, 'Character:MountConsumption', 'true');
With all of this in place, you can set these 2 rules in the database to true or false. This turn on or off the modifiers for race and mounts.
Also for the rule Character:ConsumptionMultiplier:
A value of 100 = Normal rate.
A lower value means faster eating. (ie. 50 would mean twice as fast)
A higher value means slower eating. (ie 200 would mean twice as slow)
Just keep in mind that everything is rounded to the nearest whole number during calculations.
Thanks for all the help and ideas guys =)
-Codemephit