Browse Source

- 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)

Emagi 1 year ago
parent
commit
dc8b469de3

+ 26 - 22
EQ2/source/WorldServer/Entity.cpp

@@ -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();
 }

+ 1 - 0
EQ2/source/WorldServer/Entity.h

@@ -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);

+ 1 - 5
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -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;
 }

+ 35 - 10
EQ2/source/WorldServer/Player.cpp

@@ -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;
 }
 

+ 7 - 1
EQ2/source/WorldServer/Recipes/RecipeDB.cpp

@@ -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());

+ 2 - 0
EQ2/source/WorldServer/Rules/Rules.cpp

@@ -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...?

+ 2 - 0
EQ2/source/WorldServer/Rules/Rules.h

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

+ 3 - 9
EQ2/source/WorldServer/client.cpp

@@ -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());

+ 3 - 24
EQ2/source/WorldServer/zoneserver.cpp

@@ -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);
 						}
 					}
 				}