- Strength damage now applicable to both low and high damage

* New rules:
 * R_Combat, StrengthNPC = 10
 * R_Combat, StrengthOther = 25
 * these are dividers, player_strength / 25 (or for NPCs a divide by 10), minimum value of 1 allowed
- ChangeLevel now properly called inside AddXP when player level changes (this is required!) else skill ups do not occur.
- Added weaponry to skillups (piercing, slashing, so on) since it wasn't skilling up
- Fixed crash upon not being able to load a recipe (if someone were to remove recipes from the DB that a character needed)
This commit is contained in:
Emagi 2022-08-20 07:52:10 -04:00
parent 27cc0415c0
commit dc8b469de3
9 changed files with 80 additions and 71 deletions

View file

@ -714,6 +714,7 @@ void Entity::ChangePrimaryWeapon(){
return;
}
int32 str_offset_dmg = GetStrengthDamage();
Item* item = equipment_list.GetItem(EQ2_PRIMARY_SLOT);
if(item && item->details.item_id > 0 && item->IsWeapon()){
GetInfoStruct()->set_primary_weapon_delay(item->weapon_info->delay * 100);
@ -728,8 +729,8 @@ void Entity::ChangePrimaryWeapon(){
effective_level = GetLevel();
GetInfoStruct()->set_primary_weapon_delay(2000);
GetInfoStruct()->set_primary_weapon_damage_low((int32)1 + effective_level * .2);
GetInfoStruct()->set_primary_weapon_damage_high((int32)(5 + effective_level * (effective_level/5)));
GetInfoStruct()->set_primary_weapon_damage_low((int32)1 + (effective_level * .2) + str_offset_dmg);
GetInfoStruct()->set_primary_weapon_damage_high((int32)(5 + effective_level * (effective_level/5)) + str_offset_dmg);
if(GetInfoStruct()->get_attack_type() > 0) {
GetInfoStruct()->set_primary_weapon_type(GetInfoStruct()->get_attack_type());
}
@ -738,14 +739,6 @@ void Entity::ChangePrimaryWeapon(){
}
GetInfoStruct()->set_wield_type(2);
}
int32 weapon_dmg_high = GetInfoStruct()->get_primary_weapon_damage_high();
if(IsNPC()) {
GetInfoStruct()->set_primary_weapon_damage_high(weapon_dmg_high + (int32)((GetInfoStruct()->get_str() / 10)));
}
else {
GetInfoStruct()->set_primary_weapon_damage_high(weapon_dmg_high + (int32)((GetInfoStruct()->get_str() / 25)));
}
}
void Entity::ChangeSecondaryWeapon(){
@ -753,11 +746,13 @@ void Entity::ChangeSecondaryWeapon(){
return;
}
int32 str_offset_dmg = GetStrengthDamage();
Item* item = equipment_list.GetItem(EQ2_SECONDARY_SLOT);
if(item && item->details.item_id > 0 && item->IsWeapon()){
GetInfoStruct()->set_secondary_weapon_delay(item->weapon_info->delay * 100);
GetInfoStruct()->set_secondary_weapon_damage_low(item->weapon_info->damage_low3);
GetInfoStruct()->set_secondary_weapon_damage_high(item->weapon_info->damage_high3);
GetInfoStruct()->set_secondary_weapon_damage_low(item->weapon_info->damage_low3 + str_offset_dmg);
GetInfoStruct()->set_secondary_weapon_damage_high(item->weapon_info->damage_high3 + str_offset_dmg);
GetInfoStruct()->set_secondary_weapon_type(item->GetWeaponType());
}
else{
@ -766,18 +761,10 @@ void Entity::ChangeSecondaryWeapon(){
effective_level = GetLevel();
GetInfoStruct()->set_secondary_weapon_delay(2000);
GetInfoStruct()->set_secondary_weapon_damage_low((int32)(1 + effective_level * .2));
GetInfoStruct()->set_secondary_weapon_damage_high((int32)(5 + effective_level * (effective_level/6)));
GetInfoStruct()->set_secondary_weapon_damage_low((int32)(1 + effective_level * .2) + str_offset_dmg);
GetInfoStruct()->set_secondary_weapon_damage_high((int32)(5 + effective_level * (effective_level/6)) + str_offset_dmg);
GetInfoStruct()->set_secondary_weapon_type(1);
}
int32 weapon_dmg_high = GetInfoStruct()->get_secondary_weapon_damage_high();
if(IsNPC()) {
GetInfoStruct()->set_secondary_weapon_damage_high(weapon_dmg_high + (int32)((GetInfoStruct()->get_str() / 10)));
}
else {
GetInfoStruct()->set_secondary_weapon_damage_high(weapon_dmg_high + (int32)((GetInfoStruct()->get_str() / 25)));
}
}
void Entity::ChangeRangedWeapon(){
@ -794,6 +781,23 @@ void Entity::ChangeRangedWeapon(){
}
}
int32 Entity::GetStrengthDamage() {
int32 str_offset = 1;
if(IsNPC()) {
str_offset = rule_manager.GetGlobalRule(R_Combat, StrengthNPC)->GetInt32();
if(str_offset < 1)
str_offset = 1;
}
else {
str_offset = rule_manager.GetGlobalRule(R_Combat, StrengthOther)->GetInt32();
if(str_offset < 1)
str_offset = 1;
}
int32 str_offset_dmg = (int32)((GetInfoStruct()->get_str() / str_offset));
return str_offset_dmg;
}
int32 Entity::GetPrimaryWeaponMinDamage(){
return GetInfoStruct()->get_primary_weapon_damage_low();
}

View file

@ -1360,6 +1360,7 @@ public:
void ChangePrimaryWeapon();
void ChangeSecondaryWeapon();
void ChangeRangedWeapon();
int32 GetStrengthDamage();
virtual Skill* GetSkillByName(const char* name, bool check_update = false);
virtual Skill* GetSkillByID(int32 id, bool check_update = false);
bool AttackAllowed(Entity* target, float distance = 0, bool range_attack = false);

View file

@ -10766,13 +10766,9 @@ int EQ2Emu_lua_GiveExp(lua_State* state) {
return 0;
Spawn* player = lua_interface->GetSpawn(state);
int32 amount = lua_interface->GetInt32Value(state, 2);
if (player && player->IsPlayer() && amount > 0) {
((Player*)player)->AddXP(amount);
((Player*)player)->SetCharSheetChanged(true);
Client* client = player->GetZone()->GetClientBySpawn(player);
if (client) {
client->SimpleMessage(CHANNEL_REWARD, "You gain experience!");
}
}
return 0;
}

View file

@ -1999,7 +1999,7 @@ vector<EQ2Packet*> Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
packets.push_back(bag->serialize(version, false, this));
}
if(send_item_updates)
if(send_item_updates && GetClient())
{
GetClient()->UpdateSentSpellList();
GetClient()->ClearSentSpellList();
@ -2155,20 +2155,25 @@ vector<EQ2Packet*> Player::EquipItem(int16 index, int16 version, int8 appearance
if(canEquip && !appearance_type && item->CheckFlag2(APPEARANCE_ONLY))
{
item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
GetClient()->SimpleMessage(CHANNEL_COLOR_RED, "This item is for appearance slots only.");
if(GetClient()) {
GetClient()->SimpleMessage(CHANNEL_COLOR_RED, "This item is for appearance slots only.");
}
return packets;
}
else if(canEquip && (conflictSlot = equipList->CheckSlotConflict(item)) > 0) {
bool abort = true;
switch(conflictSlot) {
case LORE:
GetClient()->SimpleMessage(CHANNEL_COLOR_RED, "Lore conflict, cannot equip this item.");
if(GetClient())
GetClient()->SimpleMessage(CHANNEL_COLOR_RED, "Lore conflict, cannot equip this item.");
break;
case LORE_EQUIP:
GetClient()->SimpleMessage(CHANNEL_COLOR_RED, "You already have this item equipped, you cannot equip another.");
if(GetClient())
GetClient()->SimpleMessage(CHANNEL_COLOR_RED, "You already have this item equipped, you cannot equip another.");
break;
case STACK_LORE:
GetClient()->SimpleMessage(CHANNEL_COLOR_RED, "Cannot equip as it exceeds lore stack.");
if(GetClient())
GetClient()->SimpleMessage(CHANNEL_COLOR_RED, "Cannot equip as it exceeds lore stack.");
break;
default:
abort = false;
@ -3472,8 +3477,12 @@ void Player::AddSpellEffect(LuaSpell* luaspell, int32 override_expire_time){
if(luaspell->caster && luaspell->caster->IsPlayer() && luaspell->caster != this)
{
GetClient()->TriggerSpellSave();
((Player*)luaspell->caster)->GetClient()->TriggerSpellSave();
if(GetClient()) {
GetClient()->TriggerSpellSave();
}
if(((Player*)luaspell->caster)->GetClient()) {
((Player*)luaspell->caster)->GetClient()->TriggerSpellSave();
}
}
}
}
@ -3916,7 +3925,7 @@ bool Player::SetSpawnSentState(Spawn* spawn, SpawnState state) {
if(index > 0 && (state == SpawnState::SPAWN_STATE_SENDING)) {
LogWrite(PLAYER__WARNING, 0, "Player", "Spawn ALREADY INDEXED for Player %s (%u). Spawn %s (index %u) attempted to state %u.",
GetName(), GetCharacterID(), spawn->GetName(), index, state);
if(GetClient()->IsReloadingZone()) {
if(GetClient() && GetClient()->IsReloadingZone()) {
spawn_packet_sent.insert(make_pair(spawn->GetID(), state));
val = false;
}
@ -3958,7 +3967,7 @@ bool Player::SetSpawnSentState(Spawn* spawn, SpawnState state) {
}
void Player::CheckSpawnStateQueue() {
if(!GetClient()->IsReadyForUpdates())
if(!GetClient() || !GetClient()->IsReadyForUpdates())
return;
spawn_mutex.writelock(__FUNCTION__, __LINE__);
@ -4400,6 +4409,9 @@ int32 Player::GetTSXP() {
}
bool Player::AddXP(int32 xp_amount){
if(!GetClient()) // potential linkdead player
return false;
MStats.lock();
xp_amount += (int32)(((float)xp_amount) * stats[ITEM_STAT_COMBATEXPMOD]) / 100;
MStats.unlock();
@ -4430,14 +4442,18 @@ bool Player::AddXP(int32 xp_amount){
}
// used up in xp debt
if(!xp_amount)
if(!xp_amount) {
SetCharSheetChanged(true);
return true;
}
int32 prev_level = GetLevel();
float current_xp_percent = ((float)GetXP()/(float)GetNeededXP())*100;
float miniding_min_percent = ((int)(current_xp_percent/10)+1)*10;
while((xp_amount + GetXP()) >= GetNeededXP()){
if (!CheckLevelStatus(GetLevel() + 1)) {
GetZone()->GetClientBySpawn(this)->SimpleMessage(CHANNEL_COLOR_RED, "You do not have the required status to level up anymore!");
SetCharSheetChanged(true);
return false;
}
xp_amount -= GetNeededXP() - GetXP();
@ -4451,6 +4467,15 @@ bool Player::AddXP(int32 xp_amount){
SetPower(GetTotalPower());
GetZone()->SendCastSpellPacket(332, this, this); //send mini level up spell effect
}
if(GetClient()) {
GetClient()->Message(CHANNEL_REWARD, "You gain %u experience!", (int32)xp_amount);
if (prev_level != GetLevel())
GetClient()->ChangeLevel(prev_level, GetLevel());
}
SetCharSheetChanged(true);
return true;
}

View file

@ -187,7 +187,13 @@ void WorldDatabase::LoadPlayerRecipes(Player *player){
res = query.RunQuery2(Q_SELECT, "SELECT recipe_id, highest_stage FROM character_recipes WHERE char_id = %u", player->GetCharacterID());
if (res) {
while ((row = mysql_fetch_row(res))){
recipe = new Recipe(master_recipe_list.GetRecipe(atoul(row[0])));
int32 recipe_id = atoul(row[0]);
Recipe* master_recipe = master_recipe_list.GetRecipe(recipe_id);
if(!master_recipe) {
LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error adding recipe %u to player '%s' - duplicate ID", atoul(row[0]), player->GetName());
continue;
}
recipe = new Recipe(master_recipe);
recipe->SetHighestStage(atoi(row[1]));
LogWrite(TRADESKILL__DEBUG, 5, "Recipes", "Loading recipe: %s (%u) for player: %s (%u)", recipe->GetName(), recipe->GetID(), player->GetName(), player->GetCharacterID());

View file

@ -244,6 +244,8 @@ void RuleManager::Init()
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
RULE_INIT(R_Combat, StrengthNPC, "10"); // divider for strength NPC only str/x = additional dmg to low/high dmg
RULE_INIT(R_Combat, StrengthOther, "25"); // divider for strength other than NPC str/x = additional dmg to low/high dmg
/* 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...?

View file

@ -103,6 +103,8 @@ enum RuleType {
MitigationLevelEffectivenessMin,
MaxMitigationAllowed,
MaxMitigationAllowedPVP,
StrengthNPC,
StrengthOther,
/* SPAWN */
SpeedMultiplier,

View file

@ -840,6 +840,7 @@ void Client::SendCharInfo() {
this->zoning_id = 0;
this->zoning_instance_id = 0;
SetZoningDestination(nullptr);
if (player->GetHP() < player->GetTotalHP() || player->GetPower() < player->GetTotalPower())
GetCurrentZone()->AddDamagedSpawn(player);
@ -4621,6 +4622,7 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) {
player_skills->SetSkillCapsByType(SKILL_TYPE_GENERAL, new_skill_cap);
player_skills->SetSkillCapsByType(SKILL_TYPE_SPELLCASTING, new_skill_cap);
player_skills->SetSkillCapsByType(SKILL_TYPE_AVOIDANCE, new_skill_cap);
player_skills->SetSkillCapsByType(SKILL_TYPE_WEAPONRY, new_skill_cap);
if (new_level > player->GetTSLevel())
player_skills->SetSkillCapsByType(SKILL_TYPE_HARVESTING, new_skill_cap);
@ -6423,14 +6425,8 @@ void Client::GiveQuestReward(Quest* quest, bool has_displayed) {
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);
}
player->AddXP(xp);
}
if (quest->GetTSExpReward() > 0) {
int8 ts_level = player->GetTSLevel();
@ -9733,10 +9729,8 @@ void Client::AcceptCollectionRewards(Collection* collection, int32 selectable_it
}
}
}
if (collection->GetRewardXP() > 0) {
player->AddXP((int32)collection->GetRewardXP());
SimpleMessage(CHANNEL_COLOR_YELLOW, "You gain experience!");
}
if (collection->GetRewardCoin() > 0) {
player->AddCoins(collection->GetRewardCoin());

View file

@ -4381,14 +4381,7 @@ void ZoneServer::SendCalculatedXP(Player* player, Spawn* victim){
if(group_member) {
float xp = group_member->CalculateXP(victim) / members->size();
if (xp > 0) {
int16 level = group_member->GetLevel();
if (group_member->AddXP((int32)xp)) {
gmi->client->Message(CHANNEL_REWARD, "You gain %u experience!", (int32)xp);
LogWrite(PLAYER__DEBUG, 0, "Player", "Player: %s earned %u experience (GroupID %u)", group_member->GetName(), (int32)xp, player->GetGroupMemberInfo()->group_id);
if (group_member->GetLevel() != level)
gmi->client->ChangeLevel(level, group_member->GetLevel());
group_member->SetCharSheetChanged(true);
}
group_member->AddXP((int32)xp);
}
}
}
@ -4404,14 +4397,7 @@ void ZoneServer::SendCalculatedXP(Player* player, Spawn* victim){
Client* client = GetClientBySpawn(player);
if(!client)
return;
int16 level = player->GetLevel();
if (player->AddXP((int32)xp)) {
client->Message(CHANNEL_REWARD, "You gain %u XP!", (int32)xp);
LogWrite(PLAYER__DEBUG, 0, "Player", "Player: %s earned %u experience.", player->GetName(), (int32)xp);
if(player->GetLevel() != level)
client->ChangeLevel(level, player->GetLevel());
player->SetCharSheetChanged(true);
}
player->AddXP((int32)xp);
}
}
}
@ -4660,14 +4646,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
float xp = ((Player*)spawn)->CalculateXP(dead) / size;
if (xp > 0) {
int16 level = spawn->GetLevel();
if (((Player*)spawn)->AddXP((int32)xp)) {
client->Message(CHANNEL_REWARD, "You gain %u XP!", (int32)xp);
LogWrite(PLAYER__DEBUG, 0, "Player", "Player: %s earned %u experience.", spawn->GetName(), (int32)xp);
if (spawn->GetLevel() != level)
client->ChangeLevel(level, spawn->GetLevel());
((Player*)spawn)->SetCharSheetChanged(true);
}
((Player*)spawn)->AddXP((int32)xp);
}
}
}