Fix #451 - basic pvp mitigation / mitigation integration
Fix #458 - fixed memory leaks in lua quest step location/zone loc functions and also languages memory cleanup New Rules: RULE_INIT(R_PVP, PVPMitigationModByLevel, "25"); // gives a bonus to mitigation for PVP combat to offset the percentage level * mod (default 25) RULE_INIT(R_Combat, EffectiveMitigationCapLevel, "80"); // level multiplier for max effective cap, level * 80 (default) RULE_INIT(R_Combat, CalculatedMitigationCapLevel, "100"); // The cap to calculate your mitigation from is [level*100]. RULE_INIT(R_Combat, MitigationLevelEffectivenessMax, "1.5"); // ratio victim level / attacker level for max effectiveness, when victim is higher level cap can reach 1.5 RULE_INIT(R_Combat, MitigationLevelEffectivenessMin, ".5"); // ratio victim level / attacker level for min effectiveness RULE_INIT(R_Combat, MaxMitigationAllowed, ".75"); // percentage max mitigation allowed, eg. 75% of damage can be mitigated max in PVE RULE_INIT(R_Combat, MaxMitigationAllowedPVP, ".75"); // percentage max mitigation allowed, eg. 75% of damage can be mitigated max in PVP InfoStruct now has two unsigned int16 values that offer mitigation percentage values in integer formats, eg 155 = 15.5% mitigation mitigation_pve mitigation_pvp Updated formulas to use effective_level (mentor/actual level) vs just player level * Dodge * Block Bug fix for crash in non existent quest being called for /modify quest advance Bug fix for improperly trying to stack items that are not stackable (count of 0 items, stack count of 1) Bug fix for inventory updates, typically with overflow slots and after deleting items from inventory (packet count needs to be updated with current size in PlayerItemList::serialize) Collections/Rewards: - Display is now one reward at a time - Display of award now only allows the reward cash, status to be provided once - Database persistence of unaccepted rewards cross-zone with two new tables - Selectable collections now checks if either field provided in /accept_reward is a potential item id (this is likely due to a client versioning)
This commit is contained in:
parent
3cd6dcc16a
commit
1068849ef8
20 changed files with 585 additions and 114 deletions
17
DB/updates/character_quest_rewards_aug6th_2022.sql
Normal file
17
DB/updates/character_quest_rewards_aug6th_2022.sql
Normal file
|
@ -0,0 +1,17 @@
|
|||
CREATE TABLE `character_quest_rewards` (
|
||||
`char_id` int(10) unsigned NOT NULL default 0,
|
||||
`quest_id` int(10) unsigned NOT NULL default 0,
|
||||
`indexed` int(10) unsigned NOT NULL default 0,
|
||||
`is_temporary` tinyint(3) unsigned NOT NULL default 0,
|
||||
`is_collection` tinyint(3) unsigned NOT NULL default 0,
|
||||
`has_displayed` tinyint(3) unsigned NOT NULL default 0,
|
||||
`tmp_coin` bigint unsigned NOT NULL DEFAULT 0,
|
||||
`tmp_status` int(10) unsigned NOT NULL default 0,
|
||||
`description` text not null default ''
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
||||
CREATE TABLE `character_quest_temporary_rewards` (
|
||||
`char_id` int(10) unsigned NOT NULL default 0,
|
||||
`quest_id` int(10) unsigned NOT NULL default 0,
|
||||
`item_id` int(10) unsigned NOT NULL default 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
|
@ -912,6 +912,7 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_
|
|||
int8 hit_result = 0;
|
||||
int16 blow_type = 0;
|
||||
sint32 damage = 0;
|
||||
sint32 damage_before_crit = 0;
|
||||
bool crit = false;
|
||||
|
||||
if(low_damage > high_damage)
|
||||
|
@ -949,6 +950,7 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_
|
|||
crit = true;
|
||||
}
|
||||
if(crit){
|
||||
damage_before_crit = damage;
|
||||
//Apply total crit multiplier with crit bonus
|
||||
if(info_struct.get_crit_bonus() > 0)
|
||||
damage *= (1.3 + (info_struct.get_crit_bonus() / 100));
|
||||
|
@ -963,8 +965,21 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Mitigation equation from http://www.guildportal.com/Guild.aspx?GuildID=20881&TabID=189653&ForumID=95908&TopicID=9024250
|
||||
|
||||
if(type == DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE || type == DAMAGE_PACKET_TYPE_SIMPLE_CRIT_DMG || type == DAMAGE_PACKET_TYPE_RANGE_DAMAGE) {
|
||||
int16 effective_level_attacker = GetInfoStruct()->get_effective_level() != 0 ? GetInfoStruct()->get_effective_level() : GetLevel();
|
||||
float mit_percentage = CalculateMitigation(type, damage_type, effective_level_attacker, (IsPlayer() && victim->IsPlayer()));
|
||||
sint32 damage_to_reduce = (damage * mit_percentage);
|
||||
if(damage_to_reduce > damage)
|
||||
damage = 0;
|
||||
else
|
||||
damage -= damage_to_reduce;
|
||||
|
||||
// if we reduce damage back below crit level then its no longer a crit, but we don't go below base damage
|
||||
if(type == DAMAGE_PACKET_TYPE_SIMPLE_CRIT_DMG && damage <= damage_before_crit) {
|
||||
damage = damage_before_crit;
|
||||
type = DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogWrite(MISC__TODO, 3, "TODO", "Take players armor into account\nfile: %s, func: %s, line: %i)", __FILE__, __FUNCTION__, __LINE__);
|
||||
|
@ -1057,6 +1072,73 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_
|
|||
return crit;
|
||||
}
|
||||
|
||||
float Entity::CalculateMitigation(int8 type, int8 damage_type, int16 effective_level_attacker, bool for_pvp) {
|
||||
int16 effective_level_victim = GetInfoStruct()->get_effective_level() != 0 ? GetInfoStruct()->get_effective_level() : GetLevel();
|
||||
if(effective_level_attacker < 1 && effective_level_victim)
|
||||
effective_level_attacker = effective_level_victim;
|
||||
else
|
||||
effective_level_attacker = 1;
|
||||
|
||||
int32 effective_mit_cap = effective_level_victim * rule_manager.GetGlobalRule(R_Combat, EffectiveMitigationCapLevel)->GetInt32();
|
||||
float max_mit = (float)GetInfoStruct()->get_max_mitigation();
|
||||
if(max_mit == 0.0f)
|
||||
max_mit = effective_level_victim * 100.0f;
|
||||
|
||||
int32 mit_to_use = 0;
|
||||
switch(type) {
|
||||
|
||||
case DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE:
|
||||
case DAMAGE_PACKET_TYPE_RANGE_DAMAGE:
|
||||
mit_to_use = GetInfoStruct()->get_cur_mitigation();
|
||||
break;
|
||||
case DAMAGE_PACKET_TYPE_SIMPLE_CRIT_DMG:
|
||||
// since critical mitigation is a percentage we will reverse the mit value so we can add skill from specific types of weapons
|
||||
mit_to_use = (int32)((float)GetInfoStruct()->get_max_mitigation() * (float)(GetInfoStruct()->get_critical_mitigation()/100.0f));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
switch(damage_type) {
|
||||
case DAMAGE_PACKET_DAMAGE_TYPE_SLASH:
|
||||
mit_to_use += GetInfoStruct()->get_mitigation_skill1(); // slash
|
||||
break;
|
||||
case DAMAGE_PACKET_DAMAGE_TYPE_PIERCE:
|
||||
mit_to_use += GetInfoStruct()->get_mitigation_skill2(); // pierce
|
||||
break;
|
||||
case DAMAGE_PACKET_DAMAGE_TYPE_CRUSH:
|
||||
mit_to_use += GetInfoStruct()->get_mitigation_skill3(); // crush
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
if(for_pvp) {
|
||||
mit_to_use += effective_level_victim * rule_manager.GetGlobalRule(R_Combat, MitigationLevelEffectivenessMax)->GetInt32();
|
||||
}
|
||||
|
||||
if(mit_to_use > effective_mit_cap) {
|
||||
mit_to_use = effective_mit_cap;
|
||||
}
|
||||
float level_diff = ((float)effective_level_victim / (float)effective_level_attacker);
|
||||
if(level_diff > rule_manager.GetGlobalRule(R_Combat, MitigationLevelEffectivenessMax)->GetFloat()) {
|
||||
level_diff = rule_manager.GetGlobalRule(R_Combat, MitigationLevelEffectivenessMax)->GetFloat();
|
||||
}
|
||||
else if(level_diff < rule_manager.GetGlobalRule(R_Combat, MitigationLevelEffectivenessMax)->GetFloat()) {
|
||||
level_diff = rule_manager.GetGlobalRule(R_Combat, MitigationLevelEffectivenessMin)->GetFloat();
|
||||
}
|
||||
float mit_percentage = ((float)mit_to_use / max_mit) * level_diff;
|
||||
|
||||
if(!for_pvp && mit_percentage > rule_manager.GetGlobalRule(R_Combat, MaxMitigationAllowed)->GetFloat()) {
|
||||
mit_percentage = rule_manager.GetGlobalRule(R_Combat, MaxMitigationAllowed)->GetFloat();
|
||||
}
|
||||
else if(for_pvp && mit_percentage > rule_manager.GetGlobalRule(R_Combat, MaxMitigationAllowedPVP)->GetFloat()) {
|
||||
mit_percentage = rule_manager.GetGlobalRule(R_Combat, MaxMitigationAllowedPVP)->GetFloat();
|
||||
}
|
||||
|
||||
return mit_percentage;
|
||||
}
|
||||
|
||||
void Entity::AddHate(Entity* attacker, sint32 hate) {
|
||||
if(!attacker || GetHP() <= 0 || attacker->GetHP() <= 0)
|
||||
return;
|
||||
|
|
|
@ -4133,7 +4133,6 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
int32 selectable_item_id = 0;
|
||||
//Quest *quest = 0;
|
||||
Collection *collection = 0;
|
||||
|
||||
/* no idea what the first argument is for (faction maybe?)
|
||||
if the reward has a selectable item reward, it's sent as the second argument
|
||||
if neither of these are included in the reward, there is no sep
|
||||
|
@ -4144,10 +4143,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
selectable_item_id = atoul(sep->arg[1]);
|
||||
}
|
||||
|
||||
//if ((quest = player->GetPendingQuestReward()))
|
||||
// client->AcceptQuestRewards(quest, selectable_item_id);
|
||||
|
||||
/* the below needs to go away eventually and be redone */
|
||||
/* this logic here may seem unexpected, but the quest queue response for GetPendingQuestAcceptance is only populated if it is the current reward displayed to the client based on a quest
|
||||
** Otherwise it will likely be a DoF client scenario (pending item rewards, selectable item rewards) which is specifying an item id
|
||||
** lastly it will be a collection which also supplies an item id and you can only have one pending collection turn in at a time (they queue against Client::HandInCollections
|
||||
*/
|
||||
int32 item_id = 0;
|
||||
if(sep && sep->arg[0][0] && sep->IsNumber(0))
|
||||
item_id = atoul(sep->arg[0]);
|
||||
|
@ -4157,12 +4156,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
break;
|
||||
}
|
||||
bool collectedItems = false;
|
||||
if (collection = player->GetPendingCollectionReward())
|
||||
{
|
||||
client->AcceptCollectionRewards(collection, selectable_item_id);
|
||||
collectedItems = true;
|
||||
}
|
||||
else if (client->GetPlayer()->HasPendingItemRewards()) {
|
||||
if (client->GetPlayer()->HasPendingItemRewards()) {
|
||||
vector<Item*> items = client->GetPlayer()->GetPendingItemRewards();
|
||||
if (items.size() > 0) {
|
||||
collectedItems = true;
|
||||
|
@ -4170,6 +4164,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
client->GetPlayer()->AddItem(new Item(items[i]));
|
||||
}
|
||||
client->GetPlayer()->ClearPendingItemRewards();
|
||||
client->GetPlayer()->SetActiveReward(false);
|
||||
}
|
||||
map<int32, Item*> selectable_item = client->GetPlayer()->GetPendingSelectableItemReward(item_id);
|
||||
if (selectable_item.size() > 0) {
|
||||
|
@ -4179,8 +4174,15 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
client->GetPlayer()->AddItem(new Item(itr->second));
|
||||
client->GetPlayer()->ClearPendingSelectableItemRewards(itr->first);
|
||||
}
|
||||
client->GetPlayer()->SetActiveReward(false);
|
||||
}
|
||||
}
|
||||
else if (collection = player->GetPendingCollectionReward())
|
||||
{
|
||||
client->AcceptCollectionRewards(collection, (selectable_item_id > 0) ? selectable_item_id : item_id);
|
||||
collectedItems = true;
|
||||
}
|
||||
|
||||
if (collectedItems) {
|
||||
EQ2Packet* outapp = client->GetPlayer()->SendInventoryUpdate(client->GetVersion());
|
||||
if (outapp)
|
||||
|
@ -7729,6 +7731,10 @@ void Commands::Command_ModifyQuest(Client* client, Seperator* sep)
|
|||
quest_id = atoul(sep->arg[1]);
|
||||
Quest* quest = client->GetPlayer()->player_quests[quest_id];
|
||||
|
||||
if(!quest) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Quest not found!");
|
||||
return;
|
||||
}
|
||||
if (sep && sep->arg[2] && sep->IsNumber(1))
|
||||
{
|
||||
step = atoul(sep->arg[2]);
|
||||
|
|
|
@ -249,6 +249,8 @@ void Entity::MapInfoStruct()
|
|||
get_int16_funcs["mitigation_skill1"] = l::bind(&InfoStruct::get_mitigation_skill1, &info_struct);
|
||||
get_int16_funcs["mitigation_skill2"] = l::bind(&InfoStruct::get_mitigation_skill2, &info_struct);
|
||||
get_int16_funcs["mitigation_skill3"] = l::bind(&InfoStruct::get_mitigation_skill3, &info_struct);
|
||||
get_int16_funcs["mitigation_pve"] = l::bind(&InfoStruct::get_mitigation_pve, &info_struct);
|
||||
get_int16_funcs["mitigation_pvp"] = l::bind(&InfoStruct::get_mitigation_pvp, &info_struct);
|
||||
get_float_funcs["ability_modifier"] = l::bind(&InfoStruct::get_ability_modifier, &info_struct);
|
||||
get_float_funcs["critical_mitigation"] = l::bind(&InfoStruct::get_critical_mitigation, &info_struct);
|
||||
get_float_funcs["block_chance"] = l::bind(&InfoStruct::get_block_chance, &info_struct);
|
||||
|
@ -427,6 +429,8 @@ void Entity::MapInfoStruct()
|
|||
set_int16_funcs["mitigation_skill1"] = l::bind(&InfoStruct::set_mitigation_skill1, &info_struct, l::_1);
|
||||
set_int16_funcs["mitigation_skill2"] = l::bind(&InfoStruct::set_mitigation_skill2, &info_struct, l::_1);
|
||||
set_int16_funcs["mitigation_skill3"] = l::bind(&InfoStruct::set_mitigation_skill3, &info_struct, l::_1);
|
||||
set_int16_funcs["mitigation_pve"] = l::bind(&InfoStruct::set_mitigation_pve, &info_struct, l::_1);
|
||||
set_int16_funcs["mitigation_pvp"] = l::bind(&InfoStruct::set_mitigation_pvp, &info_struct, l::_1);
|
||||
set_float_funcs["ability_modifier"] = l::bind(&InfoStruct::set_ability_modifier, &info_struct, l::_1);
|
||||
set_float_funcs["critical_mitigation"] = l::bind(&InfoStruct::set_critical_mitigation, &info_struct, l::_1);
|
||||
set_float_funcs["block_chance"] = l::bind(&InfoStruct::set_block_chance, &info_struct, l::_1);
|
||||
|
@ -1289,6 +1293,15 @@ void Entity::CalculateBonuses(){
|
|||
CalculateSpellBonuses(values);
|
||||
|
||||
info->set_cur_mitigation(info->get_mitigation_base());
|
||||
|
||||
int32 calc_mit_cap = effective_level * rule_manager.GetGlobalRule(R_Combat, CalculatedMitigationCapLevel)->GetInt32();
|
||||
info->set_max_mitigation(calc_mit_cap);
|
||||
|
||||
int16 mit_percent = (int16)(CalculateMitigation() * 1000.0f);
|
||||
info->set_mitigation_pve(mit_percent);
|
||||
mit_percent = (int16)(CalculateMitigation(DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE,0,0,true) * 1000.0f);
|
||||
info->set_mitigation_pvp(mit_percent);
|
||||
|
||||
info->add_sta((float)values->sta);
|
||||
info->add_str((float)values->str);
|
||||
info->add_agi((float)values->agi);
|
||||
|
@ -1397,10 +1410,10 @@ void Entity::CalculateBonuses(){
|
|||
else if (skill->short_name.data == "buckler")
|
||||
baseBlock = 3.0f;
|
||||
}
|
||||
if(GetLevel() > mitigation)
|
||||
block_pct = log10f((float)mitigation/((float)GetLevel()*10.0f));
|
||||
if(effective_level > mitigation)
|
||||
block_pct = log10f((float)mitigation/((float)effective_level*10.0f));
|
||||
else
|
||||
block_pct = log10f(((float)GetLevel()/(float)mitigation)) * log10f(GetLevel()) * 2.0f;
|
||||
block_pct = log10f(((float)effective_level/(float)mitigation)) * log10f(effective_level) * 2.0f;
|
||||
|
||||
if(block_pct < 0.0f)
|
||||
block_pct *= -1.0f;
|
||||
|
@ -1441,7 +1454,7 @@ void Entity::CalculateBonuses(){
|
|||
|
||||
float dodge_actual = 0.0f;
|
||||
if(full_pct_hit > 0.0f)
|
||||
dodge_actual = dodge_pct * (full_pct_hit / 100.0f) + (log10f(GetLevel() * GetAgi()) / 100.0f);
|
||||
dodge_actual = dodge_pct * (full_pct_hit / 100.0f) + (log10f(effective_level * GetAgi()) / 100.0f);
|
||||
|
||||
info->set_avoidance_base(dodge_actual);
|
||||
|
||||
|
|
|
@ -190,6 +190,8 @@ struct InfoStruct{
|
|||
mitigation_skill1_ = 0;
|
||||
mitigation_skill2_ = 0;
|
||||
mitigation_skill3_ = 0;
|
||||
mitigation_pve_ = 0;
|
||||
mitigation_pvp_ = 0;
|
||||
ability_modifier_ = 0;
|
||||
critical_mitigation_ = 0;
|
||||
block_chance_ = 0;
|
||||
|
@ -370,6 +372,8 @@ struct InfoStruct{
|
|||
mitigation_skill1_ = oldStruct->get_mitigation_skill1();
|
||||
mitigation_skill2_ = oldStruct->get_mitigation_skill2();
|
||||
mitigation_skill3_ = oldStruct->get_mitigation_skill3();
|
||||
mitigation_pve_ = oldStruct->get_mitigation_pve();
|
||||
mitigation_pvp_ = oldStruct->get_mitigation_pvp();
|
||||
ability_modifier_ = oldStruct->get_ability_modifier();
|
||||
critical_mitigation_ = oldStruct->get_critical_mitigation();
|
||||
block_chance_ = oldStruct->get_block_chance();
|
||||
|
@ -557,6 +561,9 @@ struct InfoStruct{
|
|||
int16 get_mitigation_skill1() { std::lock_guard<std::mutex> lk(classMutex); return mitigation_skill1_; }
|
||||
int16 get_mitigation_skill2() { std::lock_guard<std::mutex> lk(classMutex); return mitigation_skill2_; }
|
||||
int16 get_mitigation_skill3() { std::lock_guard<std::mutex> lk(classMutex); return mitigation_skill3_; }
|
||||
|
||||
int16 get_mitigation_pve() { std::lock_guard<std::mutex> lk(classMutex); return mitigation_pve_; }
|
||||
int16 get_mitigation_pvp() { std::lock_guard<std::mutex> lk(classMutex); return mitigation_pvp_; }
|
||||
|
||||
float get_ability_modifier() { std::lock_guard<std::mutex> lk(classMutex); return ability_modifier_; }
|
||||
float get_critical_mitigation() { std::lock_guard<std::mutex> lk(classMutex); return critical_mitigation_; }
|
||||
|
@ -792,6 +799,9 @@ struct InfoStruct{
|
|||
void set_mitigation_skill1(int16 value) { std::lock_guard<std::mutex> lk(classMutex); mitigation_skill1_ = value; }
|
||||
void set_mitigation_skill2(int16 value) { std::lock_guard<std::mutex> lk(classMutex); mitigation_skill2_ = value; }
|
||||
void set_mitigation_skill3(int16 value) { std::lock_guard<std::mutex> lk(classMutex); mitigation_skill3_ = value; }
|
||||
|
||||
void set_mitigation_pve(int16 value) { std::lock_guard<std::mutex> lk(classMutex); mitigation_pve_ = value; }
|
||||
void set_mitigation_pvp(int16 value) { std::lock_guard<std::mutex> lk(classMutex); mitigation_pvp_ = value; }
|
||||
|
||||
void add_mitigation_skill1(int16 value) { std::lock_guard<std::mutex> lk(classMutex); mitigation_skill1_ += value; }
|
||||
void add_mitigation_skill2(int16 value) { std::lock_guard<std::mutex> lk(classMutex); mitigation_skill2_ += value; }
|
||||
|
@ -1037,6 +1047,8 @@ private:
|
|||
int16 mitigation_skill1_;
|
||||
int16 mitigation_skill2_;
|
||||
int16 mitigation_skill3_;
|
||||
int16 mitigation_pve_;
|
||||
int16 mitigation_pvp_;
|
||||
float ability_modifier_;
|
||||
float critical_mitigation_;
|
||||
float block_chance_;
|
||||
|
@ -1363,6 +1375,7 @@ public:
|
|||
float GetDamageTypeResistPercentage(int8 damage_type);
|
||||
Skill* GetSkillByWeaponType(int8 type, bool update);
|
||||
bool DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod = 0, bool is_tick = false, bool no_damage_calcs = false, bool ignore_attacker = false, LuaSpell* spell = 0);
|
||||
float CalculateMitigation(int8 type = DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, int8 damage_type = 0, int16 attacker_level = 0, bool for_pvp = false);
|
||||
void AddHate(Entity* attacker, sint32 hate);
|
||||
bool CheckInterruptSpell(Entity* attacker);
|
||||
bool CheckFizzleSpell(LuaSpell* spell);
|
||||
|
|
|
@ -3126,8 +3126,9 @@ bool PlayerItemList::GetFirstFreeSlot(sint32* bag_id, sint16* slot) {
|
|||
}
|
||||
|
||||
Item* PlayerItemList::CanStack(Item* item, bool include_bank){
|
||||
if(!item)
|
||||
if(!item || item->stack_count < 2)
|
||||
return 0;
|
||||
|
||||
Item* ret = 0;
|
||||
map<sint32, map<int8, map<int16, Item*>> >::iterator itr;
|
||||
map<int16, Item*>::iterator slot_itr;
|
||||
|
@ -3135,13 +3136,13 @@ Item* PlayerItemList::CanStack(Item* item, bool include_bank){
|
|||
for(itr = items.begin(); itr != items.end(); itr++){
|
||||
if(include_bank || (!include_bank && itr->first >= 0)){
|
||||
for(slot_itr=itr->second[0].begin();slot_itr!=itr->second[0].end(); slot_itr++){
|
||||
if(slot_itr->second && slot_itr->second->details.item_id == item->details.item_id && ((slot_itr->second->details.count + item->details.count) <= slot_itr->second->stack_count)){
|
||||
if(slot_itr->second && slot_itr->second->details.item_id == item->details.item_id && (((slot_itr->second->details.count ? slot_itr->second->details.count : 1) + (item->details.count > 0 ? item->details.count : 1)) <= slot_itr->second->stack_count)){
|
||||
ret = slot_itr->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(slot_itr=itr->second[1].begin();slot_itr!=itr->second[1].end(); slot_itr++){
|
||||
if(slot_itr->second && slot_itr->second->details.item_id == item->details.item_id && ((slot_itr->second->details.count + item->details.count) <= slot_itr->second->stack_count)){
|
||||
if(slot_itr->second && slot_itr->second->details.item_id == item->details.item_id && (((slot_itr->second->details.count ? slot_itr->second->details.count : 1) + (item->details.count > 0 ? item->details.count : 1)) <= slot_itr->second->stack_count)){
|
||||
ret = slot_itr->second;
|
||||
break;
|
||||
}
|
||||
|
@ -3416,9 +3417,10 @@ EQ2Packet* PlayerItemList::serialize(Player* player, int16 version){
|
|||
safe_delete_array(xor_packet);
|
||||
xor_packet = new uchar[packet_size * size];
|
||||
}
|
||||
packet_count = size;
|
||||
}
|
||||
|
||||
packet_count = size;
|
||||
|
||||
for(int16 i = 0; i < indexed_items.size(); i++){
|
||||
item = indexed_items[i];
|
||||
if (item && item->details.item_id > 0)
|
||||
|
@ -3574,9 +3576,10 @@ void PlayerItemList::AddItemToPacket(PacketStruct* packet, Player* player, Item*
|
|||
packet->setSubstructArrayDataByName("items", "menu_type", menu_data, 0, i);
|
||||
if (overflow)
|
||||
packet->setSubstructArrayDataByName("items", "index", 0xFFFF, 0, i);
|
||||
else
|
||||
else {
|
||||
packet->setSubstructArrayDataByName("items", "index", i, 0, i);
|
||||
item->details.index = i;
|
||||
item->details.index = i;
|
||||
}
|
||||
packet->setSubstructArrayDataByName("items", "icon", item->details.icon, 0, i);
|
||||
packet->setSubstructArrayDataByName("items", "slot_id", item->details.slot_id, 0, i);
|
||||
if (client->GetVersion() <= 1208) {
|
||||
|
|
|
@ -41,7 +41,15 @@ MasterLanguagesList::~MasterLanguagesList(){
|
|||
Clear();
|
||||
}
|
||||
|
||||
|
||||
// don't bother calling this beyond its deconstructor its not thread-safe
|
||||
void MasterLanguagesList::Clear(){
|
||||
list<Language*>::iterator itr;
|
||||
Language* language = 0;
|
||||
for(itr = languages_list.begin(); itr != languages_list.end(); itr++){
|
||||
language = *itr;
|
||||
safe_delete(language);
|
||||
}
|
||||
languages_list.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -4092,6 +4092,7 @@ int EQ2Emu_lua_AddQuestStepZoneLoc(lua_State* state) {
|
|||
i += 4;
|
||||
}
|
||||
QuestStep* quest_step = quest->AddQuestStep(step, QUEST_STEP_TYPE_LOCATION, description, 0, 1, taskgroup, locations, max_variation);
|
||||
safe_delete(locations); // gets duplicated into new table in QuestStep constructor
|
||||
if (quest_step && icon > 0)
|
||||
quest_step->SetIcon(icon);
|
||||
if (quest->GetPlayer()) {
|
||||
|
@ -4134,6 +4135,7 @@ int EQ2Emu_lua_AddQuestStepLocation(lua_State* state) {
|
|||
i += 3;
|
||||
}
|
||||
QuestStep* quest_step = quest->AddQuestStep(step, QUEST_STEP_TYPE_LOCATION, description, 0, 1, taskgroup, locations, max_variation);
|
||||
safe_delete(locations); // gets duplicated into new table in QuestStep constructor
|
||||
if (quest_step && icon > 0)
|
||||
quest_step->SetIcon(icon);
|
||||
if (quest->GetPlayer()) {
|
||||
|
@ -4407,9 +4409,7 @@ int EQ2Emu_lua_GiveQuestReward(lua_State* state) {
|
|||
if (quest && spawn) {
|
||||
if (spawn->IsPlayer()) {
|
||||
Client* client = spawn->GetZone()->GetClientBySpawn(spawn);
|
||||
if (client)
|
||||
{
|
||||
client->AddPendingQuestAcceptReward(quest);
|
||||
if (client) {
|
||||
client->AddPendingQuestReward(quest);
|
||||
}
|
||||
}
|
||||
|
@ -6034,8 +6034,7 @@ int EQ2Emu_lua_GiveQuestItem(lua_State* state)
|
|||
itemsAddedSuccessfully = false;
|
||||
}
|
||||
}
|
||||
client->AddPendingQuestAcceptReward(quest);
|
||||
client->DisplayQuestComplete(quest, true, description);
|
||||
client->AddPendingQuestReward(quest, true, true, description); // queue for display
|
||||
|
||||
lua_interface->SetBooleanValue(state, itemsAddedSuccessfully);
|
||||
return 1;
|
||||
|
|
|
@ -403,9 +403,9 @@ Mutex* LuaInterface::GetQuestMutex(Quest* quest) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
void LuaInterface::CallQuestFunction(Quest* quest, const char* function, Spawn* player, int32 step_id) {
|
||||
bool LuaInterface::CallQuestFunction(Quest* quest, const char* function, Spawn* player, int32 step_id, int32* returnValue) {
|
||||
if(shutting_down)
|
||||
return;
|
||||
return false;
|
||||
lua_State* state = 0;
|
||||
if(quest){
|
||||
LogWrite(LUA__DEBUG, 0, "LUA", "Quest: %s, function: %s", quest->GetName(), function);
|
||||
|
@ -413,6 +413,7 @@ void LuaInterface::CallQuestFunction(Quest* quest, const char* function, Spawn*
|
|||
mutex->lock();
|
||||
if(quest_states.count(quest->GetQuestID()) > 0)
|
||||
state = quest_states[quest->GetQuestID()];
|
||||
bool success = false; // if no state then we return false
|
||||
if(state){
|
||||
int8 arg_count = 3;
|
||||
lua_getglobal(state, function);
|
||||
|
@ -424,16 +425,14 @@ void LuaInterface::CallQuestFunction(Quest* quest, const char* function, Spawn*
|
|||
SetInt32Value(state, step_id);
|
||||
arg_count++;
|
||||
}
|
||||
if(lua_pcall(state, arg_count, 0, 0) != 0){
|
||||
LogError("%s: Error processing quest function '%s': %s ", GetScriptName(state), function, lua_tostring(state, -1));
|
||||
lua_pop(state, 1);
|
||||
mutex->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
success = CallScriptInt32(state, arg_count, returnValue);
|
||||
}
|
||||
mutex->unlock();
|
||||
LogWrite(LUA__DEBUG, 0, "LUA", "Done!");
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Quest* LuaInterface::LoadQuest(int32 id, const char* name, const char* type, const char* zone, int8 level, const char* description, char* script_name) {
|
||||
|
|
|
@ -276,7 +276,7 @@ public:
|
|||
void LogError(const char* error, ...);
|
||||
|
||||
|
||||
void CallQuestFunction(Quest* quest, const char* function, Spawn* player, int32 step_id = 0xFFFFFFFF);
|
||||
bool CallQuestFunction(Quest* quest, const char* function, Spawn* player, int32 step_id = 0xFFFFFFFF, int32* returnValue = 0);
|
||||
void RemoveDebugClients(Client* client);
|
||||
void UpdateDebugClients(Client* client);
|
||||
void ProcessErrorMessage(const char* message);
|
||||
|
|
|
@ -124,6 +124,7 @@ Player::Player(){
|
|||
reset_mentorship = false;
|
||||
all_spells_locked = false;
|
||||
current_language_id = 0;
|
||||
active_reward = false;
|
||||
}
|
||||
Player::~Player(){
|
||||
SetSaveSpellEffects(true);
|
||||
|
@ -703,8 +704,9 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal
|
|||
packet->setDataByName("stat_bonus_damage", 95); //stat_bonus_damage
|
||||
packet->setDataByName("mitigation_cur", info_struct->get_cur_mitigation());// confirmed DoV
|
||||
packet->setDataByName("mitigation_base", info_struct->get_mitigation_base());// confirmed DoV
|
||||
packet->setDataByName("mitigation_pct_pve", 392); // % calculation Mitigation % vs PvE 392 = 39.2%// confirmed DoV
|
||||
packet->setDataByName("mitigation_pct_pvp", 559); // % calculation Mitigation % vs PvP 559 = 55.9%// confirmed DoV
|
||||
|
||||
packet->setDataByName("mitigation_pct_pve", info_struct->get_mitigation_pve()); // % calculation Mitigation % vs PvE 392 = 39.2%// confirmed DoV
|
||||
packet->setDataByName("mitigation_pct_pvp", info_struct->get_mitigation_pvp()); // % calculation Mitigation % vs PvP 559 = 55.9%// confirmed DoV
|
||||
packet->setDataByName("toughness", 0);//toughness// confirmed DoV
|
||||
packet->setDataByName("toughness_resist_dmg_pvp", 0);//toughness_resist_dmg_pvp 73 = 7300% // confirmed DoV
|
||||
packet->setDataByName("avoidance_pct", (int16)info_struct->get_avoidance_display()*10.0f);//avoidance_pct 192 = 19.2% // confirmed DoV
|
||||
|
@ -5058,10 +5060,10 @@ map<int32, Quest*>* Player::GetCompletedPlayerQuests(){
|
|||
}
|
||||
|
||||
Quest* Player::GetAnyQuest(int32 quest_id) {
|
||||
if(completed_quests.count(quest_id) > 0)
|
||||
return completed_quests[quest_id];
|
||||
if(player_quests.count(quest_id) > 0)
|
||||
return player_quests[quest_id];
|
||||
if(completed_quests.count(quest_id) > 0)
|
||||
return completed_quests[quest_id];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -5120,7 +5122,12 @@ int8 Player::CheckQuestFlag(Spawn* spawn){
|
|||
}
|
||||
}
|
||||
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
|
||||
if (CanReceiveQuest(quests->at(i))){
|
||||
int8 flag = 0;
|
||||
if (CanReceiveQuest(quests->at(i), &flag)){
|
||||
if(flag) {
|
||||
ret = flag;
|
||||
break;
|
||||
}
|
||||
master_quest_list.LockQuests();
|
||||
quest = master_quest_list.GetQuest(quests->at(i), false);
|
||||
master_quest_list.UnlockQuests();
|
||||
|
@ -5155,7 +5162,7 @@ int8 Player::CheckQuestFlag(Spawn* spawn){
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool Player::CanReceiveQuest(int32 quest_id){
|
||||
bool Player::CanReceiveQuest(int32 quest_id, int8* ret){
|
||||
bool passed = true;
|
||||
int32 x;
|
||||
master_quest_list.LockQuests();
|
||||
|
@ -5266,6 +5273,18 @@ bool Player::CanReceiveQuest(int32 quest_id){
|
|||
passed = false;
|
||||
}
|
||||
}
|
||||
|
||||
int32 flag = 0;
|
||||
if(lua_interface->CallQuestFunction(quest, "ReceiveQuestCriteria", this, 0xFFFFFFFF, &flag)) {
|
||||
if(ret)
|
||||
*ret = flag;
|
||||
if(!flag) {
|
||||
passed = false;
|
||||
}
|
||||
else {
|
||||
passed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return passed;
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ public:
|
|||
|
||||
void RemoveEquipmentUpdates()
|
||||
{
|
||||
appearanceList->clear();
|
||||
appearanceList->clear();
|
||||
safe_delete(appearanceList);
|
||||
}
|
||||
|
||||
|
@ -865,7 +865,7 @@ public:
|
|||
PlayerLanguagesList* GetPlayerLanguages() { return &player_languages_list; }
|
||||
bool HasLanguage(int32 id);
|
||||
bool HasLanguage(const char* name);
|
||||
bool CanReceiveQuest(int32 quest_id);
|
||||
bool CanReceiveQuest(int32 quest_id, int8* ret = 0);
|
||||
float GetBoatX() { if (info) return info->GetBoatX(); return 0; }
|
||||
float GetBoatY() { if (info) return info->GetBoatY(); return 0; }
|
||||
float GetBoatZ() { if (info) return info->GetBoatZ(); return 0; }
|
||||
|
@ -1051,6 +1051,9 @@ public:
|
|||
int32 GetCurrentLanguage() { return current_language_id; }
|
||||
void SetCurrentLanguage(int32 language_id) { current_language_id = language_id; }
|
||||
|
||||
void SetActiveReward(bool val) { active_reward = val; }
|
||||
bool IsActiveReward() { return active_reward; }
|
||||
|
||||
Mutex MPlayerQuests;
|
||||
float pos_packet_speed;
|
||||
private:
|
||||
|
@ -1185,6 +1188,8 @@ private:
|
|||
vector<GMTagFilter> gm_visual_filters;
|
||||
|
||||
int32 current_language_id;
|
||||
|
||||
bool active_reward;
|
||||
};
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
|
|
@ -1475,6 +1475,13 @@ void Quest::AddTmpRewardItem(Item* item){
|
|||
tmp_reward_items.push_back(item);
|
||||
}
|
||||
|
||||
void Quest::GetTmpRewardItemsByID(std::vector<int32>* items) {
|
||||
if(!items)
|
||||
return;
|
||||
for(int32 i=0;i<tmp_reward_items.size();i++)
|
||||
items->push_back(tmp_reward_items[i]->details.item_id);
|
||||
}
|
||||
|
||||
void Quest::AddRewardCoins(int32 copper, int32 silver, int32 gold, int32 plat){
|
||||
reward_coins = copper + (silver*100) + (gold*10000) + ((int64)plat*1000000);
|
||||
}
|
||||
|
|
|
@ -140,6 +140,7 @@ public:
|
|||
|
||||
void AddRewardItem(Item* item);
|
||||
void AddTmpRewardItem(Item* item);
|
||||
void GetTmpRewardItemsByID(std::vector<int32>* items);
|
||||
void AddSelectableRewardItem(Item* item);
|
||||
void AddRewardCoins(int32 copper, int32 silver, int32 gold, int32 plat);
|
||||
void AddRewardCoinsMax(int64 coins);
|
||||
|
|
|
@ -224,6 +224,7 @@ void RuleManager::Init()
|
|||
RULE_INIT(R_PVP, AllowPVP, "0");
|
||||
RULE_INIT(R_PVP, LevelRange, "4");
|
||||
RULE_INIT(R_PVP, InvisPlayerDiscoveryRange, "20"); // value > 0 sets radius inner to see, = 0 means always seen, -1 = never seen
|
||||
RULE_INIT(R_PVP, PVPMitigationModByLevel, "25"); // gives a bonus to mitigation for PVP combat to offset the percentage level * mod (default 25)
|
||||
|
||||
/* COMBAT */
|
||||
RULE_INIT(R_Combat, MaxCombatRange, "4.0");
|
||||
|
@ -237,6 +238,12 @@ void RuleManager::Init()
|
|||
RULE_INIT(R_Combat, SpiritShardSpawnScript, "SpawnScripts/Generic/SpiritShard.lua");
|
||||
RULE_INIT(R_Combat, ShardDebtRecoveryPercent, "25.00"); // recovered percentage of debt upon obtainig shard, 25/100 means 25%. If there is .5 DeathExperienceDebt, .5*25% = .125, .5 - .125 = .375
|
||||
RULE_INIT(R_Combat, ShardRecoveryByRadius, "1"); // allow shards to auto pick up by radius, not requiring to click/right click the shard
|
||||
RULE_INIT(R_Combat, EffectiveMitigationCapLevel, "80"); // level multiplier for max effective cap, level * 80 (default)
|
||||
RULE_INIT(R_Combat, CalculatedMitigationCapLevel, "100"); // The cap to calculate your mitigation from is [level*100].
|
||||
RULE_INIT(R_Combat, MitigationLevelEffectivenessMax, "1.5"); // ratio victim level / attacker level for max effectiveness, when victim is higher level cap can reach 1.5
|
||||
RULE_INIT(R_Combat, MitigationLevelEffectivenessMin, ".5"); // ratio victim level / attacker level for min effectiveness
|
||||
RULE_INIT(R_Combat, MaxMitigationAllowed, ".75"); // percentage max mitigation allowed, eg. 75% of damage can be mitigated max in PVE
|
||||
RULE_INIT(R_Combat, MaxMitigationAllowedPVP, ".75"); // percentage max mitigation allowed, eg. 75% of damage can be mitigated max in PVP
|
||||
|
||||
/* SPAWN */
|
||||
RULE_INIT(R_Spawn, SpeedMultiplier, "300"); // note: this value was 1280 until 6/1/2009, then was 600 til Sep 2009, when it became 300...?
|
||||
|
|
|
@ -83,6 +83,7 @@ enum RuleType {
|
|||
AllowPVP,
|
||||
LevelRange,
|
||||
InvisPlayerDiscoveryRange,
|
||||
PVPMitigationModByLevel,
|
||||
|
||||
/* COMBAT */
|
||||
MaxCombatRange,
|
||||
|
@ -96,6 +97,12 @@ enum RuleType {
|
|||
SpiritShardSpawnScript,
|
||||
ShardDebtRecoveryPercent,
|
||||
ShardRecoveryByRadius,
|
||||
EffectiveMitigationCapLevel,
|
||||
CalculatedMitigationCapLevel,
|
||||
MitigationLevelEffectivenessMax,
|
||||
MitigationLevelEffectivenessMin,
|
||||
MaxMitigationAllowed,
|
||||
MaxMitigationAllowedPVP,
|
||||
|
||||
/* SPAWN */
|
||||
SpeedMultiplier,
|
||||
|
|
|
@ -1833,6 +1833,88 @@ SOGA chars looked ok in LoginServer screen tho... odd.
|
|||
return false;
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadCharacterQuestRewards(Client* client) {
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT indexed, quest_id, is_temporary, is_collection, has_displayed, tmp_coin, tmp_status, description FROM character_quest_rewards where char_id = %u ORDER BY indexed asc", client->GetCharacterID());
|
||||
int8 count = 0;
|
||||
if(result)
|
||||
{
|
||||
while(result && (row = mysql_fetch_row(result)))
|
||||
{
|
||||
int32 index = atoul(row[0]);
|
||||
int32 quest_id = atoul(row[1]);
|
||||
|
||||
bool is_temporary = atoul(row[2]);
|
||||
|
||||
bool is_collection = atoul(row[3]);
|
||||
|
||||
bool has_displayed = atoul(row[4]);
|
||||
|
||||
|
||||
int64 tmp_coin = 0;
|
||||
#ifdef WIN32
|
||||
tmp_coin = _strtoui64(row[5], NULL, 10);
|
||||
#else
|
||||
tmp_coin = strtoull(row[5], 0, 10);
|
||||
#endif
|
||||
|
||||
|
||||
int32 tmp_status = atoul(row[6]);
|
||||
|
||||
std::string description = std::string("");
|
||||
|
||||
if(row[7]) {
|
||||
std::string description = std::string(row[7]);
|
||||
}
|
||||
|
||||
if(is_collection) {
|
||||
map<int32, Collection*>* collections = client->GetPlayer()->GetCollectionList()->GetCollections();
|
||||
map<int32, Collection*>::iterator itr;
|
||||
Collection* collection = 0;
|
||||
for (itr = collections->begin(); itr != collections->end(); itr++) {
|
||||
collection = itr->second;
|
||||
if (collection->GetIsReadyToTurnIn()) {
|
||||
client->GetPlayer()->SetPendingCollectionReward(collection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(is_temporary) {
|
||||
LoadCharacterQuestTemporaryRewards(client, quest_id);
|
||||
}
|
||||
client->QueueQuestReward(quest_id, is_temporary, is_collection, has_displayed, tmp_coin, tmp_status, description, true, index);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if(count) {
|
||||
client->SetQuestUpdateState(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WorldDatabase::LoadCharacterQuestTemporaryRewards(Client* client, int32 quest_id) {
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT item_id FROM character_quest_temporary_rewards where char_id = %u and quest_id = %u", client->GetCharacterID(), quest_id);
|
||||
int8 count = 0;
|
||||
if(result)
|
||||
{
|
||||
while(result && (row = mysql_fetch_row(result)))
|
||||
{
|
||||
int32 item_id = atoul(row[0]);
|
||||
Quest* quest = client->GetPlayer()->GetAnyQuest(quest_id);
|
||||
if(quest) {
|
||||
Item* item = master_item_list.GetItem(item_id);
|
||||
if(item) {
|
||||
quest->AddTmpRewardItem(new Item(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WorldDatabase::InsertCharacterStats(int32 character_id, int8 class_id, int8 race_id){
|
||||
Query query1;
|
||||
Query query2;
|
||||
|
@ -4085,6 +4167,7 @@ void WorldDatabase::Save(Client* client){
|
|||
SavePlayerSpells(client);
|
||||
SavePlayerMail(client);
|
||||
SavePlayerCollections(client);
|
||||
client->SaveQuestRewardData();
|
||||
|
||||
LogWrite(PLAYER__INFO, 3, "Player", "Player '%s' (%u) data saved.", player->GetName(), player->GetCharacterID());
|
||||
}
|
||||
|
@ -4924,6 +5007,9 @@ bool WorldDatabase::DeleteCharacter(int32 account_id, int32 character_id){
|
|||
query.RunQuery2(Q_DELETE, "DELETE FROM characters WHERE id=%u AND account_id=%u", character_id, account_id);
|
||||
//delete languages
|
||||
query2.RunQuery2(Q_DELETE, "DELETE FROM character_languages WHERE char_id=%u", character_id);
|
||||
//delete quest rewards
|
||||
query2.RunQuery2(Q_DELETE, "DELETE FROM character_quest_rewards WHERE char_id=%u", character_id);
|
||||
query2.RunQuery2(Q_DELETE, "DELETE FROM character_quest_temporary_rewards WHERE char_id=%u", character_id);
|
||||
|
||||
if(!query.GetAffectedRows())
|
||||
{
|
||||
|
|
|
@ -296,6 +296,8 @@ public:
|
|||
void LoadCharacterItemList(int32 account_id, int32 char_id, Player* player, int16);
|
||||
bool loadCharacter(const char* name, int32 account_id, Client* client);
|
||||
bool LoadCharacterStats(int32 id, int32 account_id, Client* client);
|
||||
void LoadCharacterQuestRewards(Client* client);
|
||||
void LoadCharacterQuestTemporaryRewards(Client* client, int32 quest_id);
|
||||
bool InsertCharacterStats(int32 character_id, int8 class_id, int8 race_id);
|
||||
bool UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp);
|
||||
bool insertCharacterProperty(Client* client, char* propName, char* propValue);
|
||||
|
|
|
@ -402,6 +402,7 @@ void Client::SendLoginInfo() {
|
|||
}
|
||||
database.LoadPlayerFactions(this);
|
||||
database.LoadCharacterQuests(this);
|
||||
database.LoadCharacterQuestRewards(this);
|
||||
database.LoadPlayerMail(this);
|
||||
}
|
||||
LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
|
||||
|
@ -5444,14 +5445,75 @@ void Client::AddPendingQuestAcceptReward(Quest* quest)
|
|||
MPendingQuestAccept.unlock();
|
||||
}
|
||||
|
||||
void Client::AddPendingQuestReward(Quest* quest, bool update) {
|
||||
MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
|
||||
quest_pending_reward.push_back(quest->GetQuestID());
|
||||
void Client::AddPendingQuestReward(Quest* quest, bool update, bool is_temporary, std::string description) {
|
||||
QueueQuestReward(quest->GetQuestID(), is_temporary, false, false, (is_temporary ? quest->GetCoinTmpReward() : 0),
|
||||
(is_temporary ? quest->GetStatusTmpReward() : 0), description, false, 0);
|
||||
quest_updates = update;
|
||||
MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
|
||||
if(quest_updates) {
|
||||
SaveQuestRewardData(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Client::QueueQuestReward(int32 quest_id, bool is_temporary, bool is_collection, bool has_displayed, int64 tmp_coin, int32 tmp_status, std::string description, bool db_saved, int32 index) {
|
||||
if(HasQuestRewardQueued(quest_id, is_temporary, is_collection))
|
||||
return;
|
||||
|
||||
QuestRewardData data;
|
||||
data.quest_id = quest_id;
|
||||
data.is_temporary = is_temporary;
|
||||
data.is_collection = is_collection;
|
||||
data.has_displayed = has_displayed;
|
||||
data.tmp_coin = tmp_coin;
|
||||
data.tmp_status = tmp_status;
|
||||
data.description = std::string(description);
|
||||
data.db_saved = db_saved;
|
||||
data.db_index = index;
|
||||
MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
|
||||
quest_pending_reward.push_back(data);
|
||||
MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
bool Client::HasQuestRewardQueued(int32 quest_id, bool is_temporary, bool is_collection) {
|
||||
|
||||
bool success = false;
|
||||
MQuestPendingUpdates.readlock(__FUNCTION__, __LINE__);
|
||||
if (quest_pending_reward.size() > 0) {
|
||||
vector<QuestRewardData>::iterator itr;
|
||||
|
||||
for (itr = quest_pending_reward.begin(); itr != quest_pending_reward.end(); itr++) {
|
||||
int32 questID = (*itr).quest_id;
|
||||
bool temporary = (*itr).is_temporary;
|
||||
bool collection = (*itr).is_collection;
|
||||
if( questID == quest_id && is_temporary == temporary && is_collection == collection ) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
MQuestPendingUpdates.releasereadlock(__FUNCTION__, __LINE__);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void Client::RemoveQueuedQuestReward() {
|
||||
MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
|
||||
if(quest_pending_reward.size() > 0) {
|
||||
QuestRewardData data = quest_pending_reward.at(0);
|
||||
if(data.db_saved) {
|
||||
Query query;
|
||||
query.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete FROM character_quest_rewards where char_id = %u and indexed = %u", GetCharacterID(), data.db_index);
|
||||
if(data.is_temporary && data.quest_id) {
|
||||
query.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete FROM character_quest_temporary_rewards where char_id = %u and quest_id = %u", GetCharacterID(), data.quest_id);
|
||||
}
|
||||
}
|
||||
quest_pending_reward.erase(quest_pending_reward.begin());
|
||||
}
|
||||
MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
|
||||
|
||||
SaveQuestRewardData(true);
|
||||
}
|
||||
|
||||
void Client::AddPendingQuestUpdate(int32 quest_id, int32 step_id, int32 progress) {
|
||||
MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
|
||||
quest_pending_updates[quest_id][step_id] = progress;
|
||||
|
@ -5461,6 +5523,9 @@ void Client::AddPendingQuestUpdate(int32 quest_id, int32 step_id, int32 progress
|
|||
}
|
||||
|
||||
void Client::ProcessQuestUpdates() {
|
||||
if(!GetPlayer()->IsFullyLoggedIn())
|
||||
return;
|
||||
|
||||
if (quest_pending_updates.size() > 0) {
|
||||
map<int32, map<int32, int32> > tmp_quest_updates;
|
||||
MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
|
||||
|
@ -5483,17 +5548,41 @@ void Client::ProcessQuestUpdates() {
|
|||
MQuestPendingUpdates.readlock(__FUNCTION__, __LINE__);
|
||||
if (quest_pending_reward.size() > 0) {
|
||||
MQuestPendingUpdates.releasereadlock(__FUNCTION__, __LINE__);
|
||||
vector<int32>::iterator itr;
|
||||
vector<int32> tmp_quest_rewards;
|
||||
|
||||
// only able to display one reward at a time
|
||||
if(GetPlayer()->IsActiveReward())
|
||||
return;
|
||||
|
||||
Query query;
|
||||
vector<QuestRewardData>::iterator itr;
|
||||
vector<QuestRewardData> tmp_quest_rewards;
|
||||
MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
|
||||
tmp_quest_rewards.insert(tmp_quest_rewards.begin(), quest_pending_reward.begin(), quest_pending_reward.end());
|
||||
quest_pending_reward.clear();
|
||||
tmp_quest_rewards.insert(tmp_quest_rewards.begin(), quest_pending_reward.begin(), quest_pending_reward.begin()+1);
|
||||
MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
|
||||
|
||||
for (itr = tmp_quest_rewards.begin(); itr != tmp_quest_rewards.end(); itr++) {
|
||||
int32 questID = *itr;
|
||||
Quest* quest = GetPlayer()->GetAnyQuest(questID);
|
||||
if(quest) {
|
||||
GiveQuestReward(quest);
|
||||
int32 questID = (*itr).quest_id;
|
||||
Quest* quest = 0;
|
||||
if((*itr).is_collection && GetPlayer()->GetPendingCollectionReward()) {
|
||||
DisplayCollectionComplete(GetPlayer()->GetPendingCollectionReward());
|
||||
GetPlayer()->SetActiveReward(true);
|
||||
(*itr).has_displayed = true;
|
||||
|
||||
UpdateCharacterRewardData(&(*itr));
|
||||
}
|
||||
else if(questID > 0 && (quest = GetPlayer()->GetAnyQuest(questID))) {
|
||||
quest->SetQuestTemporaryState((*itr).is_temporary, (*itr).description);
|
||||
if((*itr).is_temporary) {
|
||||
quest->SetStatusTmpReward((*itr).tmp_status);
|
||||
quest->SetCoinTmpReward((*itr).tmp_coin);
|
||||
}
|
||||
GiveQuestReward(quest, (*itr).has_displayed);
|
||||
GetPlayer()->SetActiveReward(true);
|
||||
(*itr).has_displayed = true;
|
||||
|
||||
UpdateCharacterRewardData(&(*itr));
|
||||
// only able to display one reward at a time
|
||||
break;
|
||||
} else {
|
||||
LogWrite(CCLIENT__ERROR, 0, "Client", "Quest ID %u missing for Player %s, skipping quest id from tmp_quest_rewards.", questID, GetPlayer()->GetName());
|
||||
}
|
||||
|
@ -5501,7 +5590,15 @@ void Client::ProcessQuestUpdates() {
|
|||
} else {
|
||||
MQuestPendingUpdates.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
quest_updates = false;
|
||||
|
||||
MQuestPendingUpdates.readlock(__FUNCTION__, __LINE__);
|
||||
if (quest_pending_reward.size() > 0) {
|
||||
quest_updates = true;
|
||||
}
|
||||
else {
|
||||
quest_updates = false;
|
||||
}
|
||||
MQuestPendingUpdates.releasereadlock(__FUNCTION__, __LINE__);
|
||||
|
||||
}
|
||||
|
||||
|
@ -5961,7 +6058,11 @@ void Client::AcceptQuestReward(Quest* quest, int32 item_id) {
|
|||
totalItems += items->size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RemoveQueuedQuestReward();
|
||||
|
||||
GetPlayer()->SetActiveReward(false);
|
||||
|
||||
if (free_slots >= num_slots_needed || (player->item_list.HasFreeBagSlot() && master_item && master_item->IsBag() && master_item->bag_info->num_slots >= totalItems)) {
|
||||
if (master_item)
|
||||
AddItem(item_id);
|
||||
|
@ -5994,31 +6095,34 @@ void Client::AcceptQuestReward(Quest* quest, int32 item_id) {
|
|||
AwardCoins(total_coins, std::string("for completing ").append(quest->GetName()));
|
||||
|
||||
player->GetInfoStruct()->add_status_points(quest->GetStatusTmpReward());
|
||||
|
||||
quest->SetQuestTemporaryState(false);
|
||||
}
|
||||
else
|
||||
player->GetInfoStruct()->add_status_points(quest->GetStatusPoints());
|
||||
else {
|
||||
player->GetInfoStruct()->add_status_points(quest->GetStatusPoints());
|
||||
}
|
||||
|
||||
quest->SetQuestTemporaryState(false);
|
||||
player->SetCharSheetChanged(true);
|
||||
}
|
||||
else {
|
||||
MPendingQuestAccept.lock();
|
||||
pending_quest_accept.push_back(quest->GetQuestID());
|
||||
MPendingQuestAccept.unlock();
|
||||
GetPlayer()->SetActiveReward(true);
|
||||
AddPendingQuestAcceptReward(quest);
|
||||
SimpleMessage(CHANNEL_COLOR_RED, "You do not have enough free slots! Free some slots and try again.");
|
||||
DisplayQuestComplete(quest, quest->GetQuestTemporaryState(), quest->GetQuestTemporaryDescription());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* rewards, vector<Item*>* selectable_rewards, map<int32, sint32>* factions, const char* header, int32 status_points, const char* text) {
|
||||
void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* rewards, vector<Item*>* selectable_rewards, map<int32, sint32>* factions, const char* header, int32 status_points, const char* text, bool was_displayed) {
|
||||
if (coin == 0 && (!rewards || rewards->size() == 0) && (!selectable_rewards || selectable_rewards->size() == 0) && (!factions || factions->size() == 0) && status_points == 0 && text == 0 && (!quest || (quest->GetCoinsReward() == 0 && quest->GetCoinsRewardMax() == 0))) {
|
||||
/*if (quest)
|
||||
text = quest->GetName();
|
||||
else*/
|
||||
return;//nothing to give
|
||||
}
|
||||
|
||||
GetPlayer()->ClearPendingSelectableItemRewards(0, true);
|
||||
GetPlayer()->ClearPendingItemRewards();
|
||||
|
||||
PacketStruct* packet2 = configReader.getStruct("WS_QuestRewardPackMsg", GetVersion());
|
||||
if (packet2) {
|
||||
int32 source_id = 0;
|
||||
|
@ -6036,7 +6140,7 @@ void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* reward
|
|||
}
|
||||
if (rewarded_coin > coin)
|
||||
coin = rewarded_coin;
|
||||
if (!quest) { //this entire function is either for version <=546 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
if (!quest && !was_displayed) { //this entire function is either for version <=546 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
if (coin > 0) {
|
||||
player->AddCoins(coin);
|
||||
PlaySound("coin_cha_ching");
|
||||
|
@ -6045,7 +6149,7 @@ void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* reward
|
|||
packet2->setSubstructDataByName("reward_data", "unknown1", 255);
|
||||
packet2->setSubstructDataByName("reward_data", "reward", header);
|
||||
packet2->setSubstructDataByName("reward_data", "max_coin", coin);
|
||||
if (player->GetGuild()) {
|
||||
if (player->GetGuild() && !was_displayed) {
|
||||
if (!quest) { //this entire function is either for version <=546 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
|
||||
player->GetInfoStruct()->add_status_points(status_points);
|
||||
player->SetCharSheetChanged(true);
|
||||
|
@ -6107,16 +6211,12 @@ void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* reward
|
|||
}
|
||||
}
|
||||
|
||||
void Client::DisplayQuestComplete(Quest* quest, bool tempReward, std::string customDescription) {
|
||||
void Client::DisplayQuestComplete(Quest* quest, bool tempReward, std::string customDescription, bool was_displayed) {
|
||||
if (!quest)
|
||||
return;
|
||||
|
||||
quest->SetQuestTemporaryState(tempReward, customDescription);
|
||||
|
||||
AddPendingQuestAcceptReward(quest);
|
||||
|
||||
if (GetVersion() <= 546) {
|
||||
DisplayQuestRewards(quest, 0, quest->GetRewardItems(), quest->GetSelectableRewardItems(), quest->GetRewardFactions(), "Quest Complete!", quest->GetStatusPoints());
|
||||
DisplayQuestRewards(quest, 0, quest->GetRewardItems(), quest->GetSelectableRewardItems(), quest->GetRewardFactions(), "Quest Complete!", quest->GetStatusPoints(), customDescription.c_str(), was_displayed);
|
||||
return;
|
||||
}
|
||||
PacketStruct* packet = configReader.getStruct("WS_QuestComplete", GetVersion());
|
||||
|
@ -6289,51 +6389,59 @@ void Client::DisplayRandomizeFeatures(int32 flags) {
|
|||
|
||||
}
|
||||
|
||||
void Client::GiveQuestReward(Quest* quest) {
|
||||
void Client::GiveQuestReward(Quest* quest, bool has_displayed) {
|
||||
current_quest_id = 0;
|
||||
|
||||
if(!quest->GetQuestTemporaryState())
|
||||
if(!quest->GetQuestTemporaryState() && !has_displayed)
|
||||
{
|
||||
quest->IncrementCompleteCount();
|
||||
player->AddCompletedQuest(quest);
|
||||
}
|
||||
|
||||
AddPendingQuestAcceptReward(quest);
|
||||
|
||||
DisplayQuestComplete(quest, quest->GetQuestTemporaryState(), quest->GetQuestTemporaryDescription());
|
||||
LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
|
||||
SendQuestJournal();
|
||||
|
||||
if(quest->GetQuestTemporaryState())
|
||||
if(quest->GetQuestTemporaryState()) {
|
||||
return;
|
||||
|
||||
player->RemoveQuest(quest->GetQuestID(), false);
|
||||
if (quest->GetExpReward() > 0) {
|
||||
int16 level = player->GetLevel();
|
||||
int32 xp = quest->GetExpReward();
|
||||
if (player->AddXP(xp)) {
|
||||
Message(CHANNEL_REWARD, "You gain %u experience!", (int32)xp);
|
||||
if (player->GetLevel() != level)
|
||||
ChangeLevel(level, player->GetLevel());
|
||||
player->SetCharSheetChanged(true);
|
||||
}
|
||||
}
|
||||
if (quest->GetTSExpReward() > 0) {
|
||||
int8 ts_level = player->GetTSLevel();
|
||||
int32 xp = quest->GetTSExpReward();
|
||||
if (player->AddTSXP(xp)) {
|
||||
Message(CHANNEL_REWARD, "You gain %u tradeskill experience!", (int32)xp);
|
||||
if (player->GetTSLevel() != ts_level)
|
||||
ChangeTSLevel(ts_level, player->GetTSLevel());
|
||||
player->SetCharSheetChanged(true);
|
||||
|
||||
if(!has_displayed) {
|
||||
if (quest->GetExpReward() > 0) {
|
||||
int16 level = player->GetLevel();
|
||||
int32 xp = quest->GetExpReward();
|
||||
if (player->AddXP(xp)) {
|
||||
Message(CHANNEL_REWARD, "You gain %u experience!", (int32)xp);
|
||||
if (player->GetLevel() != level)
|
||||
ChangeLevel(level, player->GetLevel());
|
||||
player->SetCharSheetChanged(true);
|
||||
}
|
||||
}
|
||||
if (quest->GetTSExpReward() > 0) {
|
||||
int8 ts_level = player->GetTSLevel();
|
||||
int32 xp = quest->GetTSExpReward();
|
||||
if (player->AddTSXP(xp)) {
|
||||
Message(CHANNEL_REWARD, "You gain %u tradeskill experience!", (int32)xp);
|
||||
if (player->GetTSLevel() != ts_level)
|
||||
ChangeTSLevel(ts_level, player->GetTSLevel());
|
||||
player->SetCharSheetChanged(true);
|
||||
}
|
||||
}
|
||||
int64 total_coins = quest->GetGeneratedCoin();
|
||||
if (total_coins > 0)
|
||||
AwardCoins(total_coins, std::string("for completing ").append(quest->GetName()));
|
||||
|
||||
player->RemoveQuest(quest->GetQuestID(), false);
|
||||
}
|
||||
int64 total_coins = quest->GetGeneratedCoin();
|
||||
if (total_coins > 0)
|
||||
AwardCoins(total_coins, std::string("for completing ").append(quest->GetName()));
|
||||
|
||||
if (quest->GetQuestGiver() > 0)
|
||||
GetCurrentZone()->SendSpawnChangesByDBID(quest->GetQuestGiver(), this, false, true);
|
||||
|
||||
RemovePlayerQuest(quest->GetQuestID(), true, false);
|
||||
if(!has_displayed) {
|
||||
RemovePlayerQuest(quest->GetQuestID(), true, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::DisplayConversation(int32 conversation_id, int32 spawn_id, vector<ConversationOption>* conversations, const char* text, const char* mp3, int32 key1, int32 key2, int8 language, int8 can_close) {
|
||||
|
@ -8805,6 +8913,7 @@ void Client::SetReadyForSpawns(bool val) {
|
|||
world.GetGroupManager()->GroupMessage(GetPlayer()->GetGroupMemberInfo()->group_id, "%s has returned from Linkdead.", GetPlayer()->GetName());
|
||||
}
|
||||
}
|
||||
GetPlayer()->SetActiveReward(false);
|
||||
zone_list.CheckFriendZoned(this);
|
||||
|
||||
}
|
||||
|
@ -9043,9 +9152,8 @@ void Client::InspectPlayer(Player* player_to_inspect) {
|
|||
packet->setDataByName("gender", player_to_inspect->GetGender());
|
||||
packet->setDataByName("adventure_level", player_to_inspect->GetLevel());
|
||||
|
||||
LogWrite(MISC__TODO, 1, "TODO", "Put mentored level here (adventure_level_effective)\nfile: %s, func: %s, line: %i", __FILE__, __FUNCTION__, __LINE__);
|
||||
|
||||
packet->setDataByName("adventure_level_effective", player_to_inspect->GetLevel());
|
||||
int16 effective_level = player_to_inspect->GetInfoStruct()->get_effective_level() != 0 ? player_to_inspect->GetInfoStruct()->get_effective_level() : player_to_inspect->GetLevel();
|
||||
packet->setDataByName("adventure_level_effective", effective_level);
|
||||
packet->setDataByName("adventure_class", player_to_inspect->GetAdventureClass());
|
||||
packet->setDataByName("tradeskill_level", player_to_inspect->GetTSLevel());
|
||||
packet->setDataByName("tradeskill_class", player_to_inspect->GetTradeskillClass());
|
||||
|
@ -9543,11 +9651,26 @@ void Client::HandInCollections() {
|
|||
collection = itr->second;
|
||||
if (collection->GetIsReadyToTurnIn()) {
|
||||
player->SetPendingCollectionReward(collection);
|
||||
DisplayCollectionComplete(collection);
|
||||
|
||||
return;
|
||||
MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
|
||||
QuestRewardData data;
|
||||
data.quest_id = 0;
|
||||
data.is_temporary = false;
|
||||
data.description = std::string("");
|
||||
data.is_collection = true;
|
||||
data.has_displayed = false;
|
||||
data.tmp_coin = 0;
|
||||
data.tmp_status = 0;
|
||||
data.db_saved = false;
|
||||
data.db_index = 0;
|
||||
quest_pending_reward.push_back(data);
|
||||
MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
|
||||
quest_updates = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(quest_updates) {
|
||||
SaveQuestRewardData(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::AcceptCollectionRewards(Collection* collection, int32 selectable_item_id) {
|
||||
|
@ -9567,8 +9690,7 @@ void Client::AcceptCollectionRewards(Collection* collection, int32 selectable_it
|
|||
num_slots = player->GetPlayerItemList()->GetNumberOfFreeSlots();
|
||||
if (num_slots < num_slots_needed) {
|
||||
SimpleMessage(CHANNEL_COLOR_RED, "You do not have enough free slots. Free up some slots and try again");
|
||||
HandInCollections();
|
||||
|
||||
DisplayCollectionComplete(collection);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -9606,6 +9728,11 @@ void Client::AcceptCollectionRewards(Collection* collection, int32 selectable_it
|
|||
|
||||
/* reset the pending collection reward and check for my collections that the player needs to hand in */
|
||||
player->SetPendingCollectionReward(0);
|
||||
|
||||
RemoveQueuedQuestReward();
|
||||
|
||||
GetPlayer()->SetActiveReward(false);
|
||||
|
||||
HandInCollections();
|
||||
|
||||
}
|
||||
|
@ -11041,4 +11168,56 @@ void Client::SendPlayFlavor(Spawn* spawn, int8 language, VoiceOverStruct* non_ga
|
|||
if(resStruct) {
|
||||
GetPlayer()->GetZone()->PlayFlavor(this, spawn, resStruct->mp3_string.c_str(), resStruct->text_string.c_str(), resStruct->emote_string.c_str(), resStruct->key1, resStruct->key2, language);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SaveQuestRewardData(bool force_refresh) {
|
||||
Query query;
|
||||
if(force_refresh) {
|
||||
query.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_quest_rewards where char_id = %u",
|
||||
GetCharacterID());
|
||||
|
||||
query.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_quest_temporary_rewards where char_id = %u",
|
||||
GetCharacterID());
|
||||
}
|
||||
vector<QuestRewardData>::iterator itr;
|
||||
vector<QuestRewardData> tmp_quest_rewards;
|
||||
MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
|
||||
int index = 0;
|
||||
for (itr = quest_pending_reward.begin(); itr != quest_pending_reward.end(); itr++) {
|
||||
int32 questID = (*itr).quest_id;
|
||||
if(!(*itr).db_saved || force_refresh) {
|
||||
query.AddQueryAsync(GetCharacterID(), &database, Q_REPLACE, "replace into character_quest_rewards (char_id, indexed, quest_id, is_temporary, is_collection, has_displayed, tmp_coin, tmp_status, description) values(%u, %u, %u, %u, %u, %u, %I64u, %u, '%s')",
|
||||
GetCharacterID(), index, questID, (*itr).is_temporary, (*itr).is_collection, (*itr).has_displayed, (*itr).tmp_coin, (*itr).tmp_status, database.getSafeEscapeString((*itr).description.c_str()).c_str());
|
||||
(*itr).db_saved = true;
|
||||
(*itr).db_index = index;
|
||||
if((*itr).is_temporary) {
|
||||
std::vector<int32> items;
|
||||
Quest* quest = GetPlayer()->GetAnyQuest(questID);
|
||||
if(quest) {
|
||||
quest->GetTmpRewardItemsByID(&items);
|
||||
if(!force_refresh && items.size() > 0) {
|
||||
query.AddQueryAsync(GetCharacterID(), &database, Q_REPLACE, "delete from character_quest_temporary_rewards where char_id = %u and quest_id = %u",
|
||||
GetCharacterID(), questID);
|
||||
}
|
||||
for(int i=0;i<items.size();i++) {
|
||||
query.AddQueryAsync(GetCharacterID(), &database, Q_REPLACE, "replace into character_quest_temporary_rewards (char_id, quest_id, item_id) values(%u, %u, %u)",
|
||||
GetCharacterID(), questID, items[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void Client::UpdateCharacterRewardData(QuestRewardData* data) {
|
||||
|
||||
if(!data)
|
||||
return;
|
||||
if(data->db_saved) {
|
||||
Query query;
|
||||
query.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "update character_quest_rewards set is_temporary = %u, is_collection = %u, has_displayed = %u, tmp_coin = %I64u, tmp_status = %u, description = '%s' where char_id=%u and indexed=%u and quest_id=%u",
|
||||
data->is_temporary, data->is_collection, data->has_displayed, data->tmp_coin, data->tmp_status, database.getSafeEscapeString(data->description.c_str()).c_str(), GetCharacterID(), data->db_index, data->quest_id);
|
||||
}
|
||||
}
|
|
@ -142,6 +142,18 @@ struct WaypointInfo {
|
|||
int8 type;
|
||||
};
|
||||
|
||||
struct QuestRewardData {
|
||||
int32 quest_id;
|
||||
bool is_temporary;
|
||||
std::string description;
|
||||
bool is_collection;
|
||||
bool has_displayed;
|
||||
int64 tmp_coin;
|
||||
int32 tmp_status;
|
||||
bool db_saved;
|
||||
int32 db_index;
|
||||
};
|
||||
|
||||
class Client {
|
||||
public:
|
||||
Client(EQStream* ieqs);
|
||||
|
@ -280,8 +292,8 @@ public:
|
|||
void SendQuestFailure(Quest* quest);
|
||||
void SendQuestUpdateStep(Quest* quest, int32 step, bool display_quest_helper = true);
|
||||
void SendQuestUpdateStepImmediately(Quest* quest, int32 step, bool display_quest_helper = true);
|
||||
void DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* rewards=0, vector<Item*>* selectable_rewards=0, map<int32, sint32>* factions=0, const char* header="Quest Reward!", int32 status_points=0, const char* text=0);
|
||||
void DisplayQuestComplete(Quest* quest, bool tempReward = false, std::string customDescription = string(""));
|
||||
void DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* rewards=0, vector<Item*>* selectable_rewards=0, map<int32, sint32>* factions=0, const char* header="Quest Reward!", int32 status_points=0, const char* text=0, bool was_displayed = false);
|
||||
void DisplayQuestComplete(Quest* quest, bool tempReward = false, std::string customDescription = string(""), bool was_displayed = false);
|
||||
void DisplayRandomizeFeatures(int32 features);
|
||||
void AcceptQuestReward(Quest* quest, int32 item_id);
|
||||
Quest* GetPendingQuestAcceptance(int32 item_id);
|
||||
|
@ -348,7 +360,10 @@ public:
|
|||
void SetPlayer(Player* new_player);
|
||||
|
||||
void AddPendingQuestAcceptReward(Quest* quest);
|
||||
void AddPendingQuestReward(Quest* quest, bool update=true);
|
||||
void AddPendingQuestReward(Quest* quest, bool update=true, bool is_temporary = false, std::string description = std::string(""));
|
||||
bool HasQuestRewardQueued(int32 quest_id, bool is_temporary, bool is_collection);
|
||||
void QueueQuestReward(int32 quest_id, bool is_temporary, bool is_collection, bool has_displayed, int64 tmp_coin, int32 tmp_status, std::string description, bool db_saved=false, int32 index=0);
|
||||
void RemoveQueuedQuestReward();
|
||||
void AddPendingQuestUpdate(int32 quest_id, int32 step_id, int32 progress = 0xFFFFFFFF);
|
||||
void ProcessQuestUpdates();
|
||||
void AddWaypoint(const char* waypoint_name, int8 waypoint_category, int32 spawn_id);
|
||||
|
@ -543,15 +558,18 @@ public:
|
|||
bool UseItem(Item* item, Spawn* target = nullptr);
|
||||
|
||||
void SendPlayFlavor(Spawn* spawn, int8 language, VoiceOverStruct* non_garble, VoiceOverStruct* garble, bool success = false, bool garble_success = false);
|
||||
void SaveQuestRewardData(bool force_refresh = false);
|
||||
void UpdateCharacterRewardData(QuestRewardData* data);
|
||||
void SetQuestUpdateState(bool val) { quest_updates = val; }
|
||||
private:
|
||||
void SavePlayerImages();
|
||||
void SkillChanged(Skill* skill, int16 previous_value, int16 new_value);
|
||||
void GiveQuestReward(Quest* quest);
|
||||
void GiveQuestReward(Quest* quest, bool has_displayed = false);
|
||||
void SetStepComplete(int32 quest_id, int32 step);
|
||||
void AddStepProgress(int32 quest_id, int32 step, int32 progress);
|
||||
map<int32, map<int32, int32> > quest_pending_updates;
|
||||
vector<QueuedQuest*> quest_queue;
|
||||
vector<int32> quest_pending_reward;
|
||||
vector<QuestRewardData> quest_pending_reward;
|
||||
volatile bool quest_updates;
|
||||
Mutex MQuestPendingUpdates;
|
||||
Mutex MQuestQueue;
|
||||
|
|
Loading…
Reference in a new issue