Added PVP based logic

This commit is contained in:
xackery 2021-01-14 10:09:47 -08:00
parent 0f5a7e1317
commit 64b64222d1
7 changed files with 149 additions and 44 deletions

View File

@ -4,10 +4,13 @@
"name": "Linux", "name": "Linux",
"includePath": [ "includePath": [
"${workspaceFolder}/**", "${workspaceFolder}/**",
"/usr/include/mysql" "/usr/include/mysql",
"/usr/local/include",
"/usr/include",
"/usr/include/lua5.1"
], ],
"defines": [], "defines": [],
"compilerPath": "/usr/bin/gcc", "compilerPath": "/usr/bin/c++",
"cStandard": "c11", "cStandard": "c11",
"cppStandard": "c++17" "cppStandard": "c++17"
} }

View File

@ -163,6 +163,7 @@ RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishin
RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted") RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted")
RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated") RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated")
RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared") RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared")
RULE_INT(Character, PVPRespawnManaPercent, 100, "How much mana to respawn with")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Mercs) RULE_CATEGORY(Mercs)
@ -239,6 +240,14 @@ RULE_INT(World, ExpansionSettings, 16383, "Sets the expansion settings for the s
RULE_BOOL(World, UseClientBasedExpansionSettings, true, "If true it will overrule World, ExpansionSettings and set someone's expansion based on the client they're using") RULE_BOOL(World, UseClientBasedExpansionSettings, true, "If true it will overrule World, ExpansionSettings and set someone's expansion based on the client they're using")
RULE_INT(World, PVPSettings, 0, "Sets the PVP settings for the server. 1=Rallos Zek RuleSet, 2=Tallon/Vallon Zek Ruleset, 4=Sullon Zek Ruleset, 6=Discord Ruleset, anything above 6 is the Discord Ruleset without the no-drop restrictions removed. NOTE: edit IsAttackAllowed in Zone-table to accomodate for these rules") RULE_INT(World, PVPSettings, 0, "Sets the PVP settings for the server. 1=Rallos Zek RuleSet, 2=Tallon/Vallon Zek Ruleset, 4=Sullon Zek Ruleset, 6=Discord Ruleset, anything above 6 is the Discord Ruleset without the no-drop restrictions removed. NOTE: edit IsAttackAllowed in Zone-table to accomodate for these rules")
RULE_INT(World, PVPMinLevel, 0, "Minimum level to pvp") RULE_INT(World, PVPMinLevel, 0, "Minimum level to pvp")
RULE_BOOL(World, PVPUseDeityBasedPVP, false, "In PvP, deity is used to determine if a player can attack another.")
RULE_BOOL(World, PVPUseTeamsBySizeBasedPVP, false, "In PVP, size of a race is used to determine if a player can attack another.")
RULE_INT(World, PVPLevelDifference, 0, "In PvP, if value is greater than 0, players with a difference greater than value will not be attackable")
RULE_INT(World, PVPLoseExperienceLevelDifference, 0, "In PvP, if value is greater than 0, players lose experience if killed by a player within level difference")
RULE_INT(World, PVPPetDamageMitigation, 100, "In PvP, pet damage is mitigated by this amount")
RULE_INT(World, PVPMeleeMitigation, 67, "In PvP, melee is mitigated by this amount")
RULE_INT(World, PVPSpellMitigation, 67, "In PvP, spells are mitigated by this amount")
RULE_INT(World, PVPRangedMitigation, 80, "In PvP, ranged attacks (archery/throwing) is mitigated by this amount")
RULE_BOOL (World, IsGMPetitionWindowEnabled, false, "Setting whether the GM petition window is available") RULE_BOOL (World, IsGMPetitionWindowEnabled, false, "Setting whether the GM petition window is available")
RULE_INT (World, FVNoDropFlag, 0, "Sets the Firiona Vie settings on the client, allowing trading of no-drop items. 1=for all players, 2=for GM only") RULE_INT (World, FVNoDropFlag, 0, "Sets the Firiona Vie settings on the client, allowing trading of no-drop items. 1=for all players, 2=for GM only")
RULE_BOOL (World, IPLimitDisconnectAll, false, "Disconnect all current clients by IP if they go over the IP limit. This should allow people to quickly reconnect in the case of dead sessions waiting to timeout") RULE_BOOL (World, IPLimitDisconnectAll, false, "Disconnect all current clients by IP if they go over the IP limit. This should allow people to quickly reconnect in the case of dead sessions waiting to timeout")
@ -390,6 +399,7 @@ RULE_BOOL(Spells, AllowItemTGB, false, "Target group buff (/tgb) doesn't work wi
RULE_BOOL(Spells, NPCInnateProcOverride, true, "NPC innate procs override the target type to single target") RULE_BOOL(Spells, NPCInnateProcOverride, true, "NPC innate procs override the target type to single target")
RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximum targets for rains") RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximum targets for rains")
RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs")
RULE_INT(Spells, PVPRootBreakFromSpells, 75, "Chance for root to break when cast on by a client")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Combat) RULE_CATEGORY(Combat)

View File

@ -527,22 +527,7 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
c1 = mob1->CastToClient(); c1 = mob1->CastToClient();
c2 = mob2->CastToClient(); c2 = mob2->CastToClient();
if // if both are pvp they can fight return c1->CanPvP(c2);
(
c1->GetPVP() &&
c2->GetPVP()
)
return true;
else if // if they're dueling they can go at it
(
c1->IsDueling() &&
c2->IsDueling() &&
c1->GetDuelTarget() == c2->GetID() &&
c2->GetDuelTarget() == c1->GetID()
)
return true;
else
return false;
} }
else if(_NPC(mob2)) // client vs npc else if(_NPC(mob2)) // client vs npc
{ {
@ -698,18 +683,7 @@ bool Mob::IsBeneficialAllowed(Mob *target)
c1 = mob1->CastToClient(); c1 = mob1->CastToClient();
c2 = mob2->CastToClient(); c2 = mob2->CastToClient();
if(c1->GetPVP() == c2->GetPVP()) return c1->CanPvP(c2);
return true;
else if // if they're dueling they can heal each other too
(
c1->IsDueling() &&
c2->IsDueling() &&
c1->GetDuelTarget() == c2->GetID() &&
c2->GetDuelTarget() == c1->GetID()
)
return true;
else
return false;
} }
else if(_NPC(mob2)) // client to npc else if(_NPC(mob2)) // client to npc
{ {

View File

@ -1574,11 +1574,15 @@ void Client::Damage(Mob* other, int32 damage, uint16 spell_id, EQ::skills::Skill
//should this be applied to all damage? comments sound like some is for spell DMG //should this be applied to all damage? comments sound like some is for spell DMG
//patch notes on PVP reductions only mention archery/throwing ... not normal dmg //patch notes on PVP reductions only mention archery/throwing ... not normal dmg
if (other && other->IsClient() && (other != this) && damage > 0) { if (other && other->IsClient() && (other != this) && damage > 0) {
int PvPMitigation = 100; int PvPMitigation = RuleI(World, PVPMeleeMitigation);
if (attack_skill == EQ::skills::SkillArchery || attack_skill == EQ::skills::SkillThrowing) if (attack_skill == EQ::skills::SkillAbjuration || //spells
PvPMitigation = 80; attack_skill == EQ::skills::SkillAlteration ||
else attack_skill == EQ::skills::SkillConjuration ||
PvPMitigation = 67; attack_skill == EQ::skills::SkillDivination ||
attack_skill == EQ::skills::SkillEvocation) PvPMitigation = RuleI(World, PVPSpellMitigation);
if (attack_skill == EQ::skills::SkillArchery || //ranged
attack_skill == EQ::skills::SkillThrowing) PvPMitigation = RuleI(World, PVPRangedMitigation);
damage = std::max((damage * PvPMitigation) / 100, 1); damage = std::max((damage * PvPMitigation) / 100, 1);
} }
@ -1754,7 +1758,27 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill
{ {
if (killerMob->IsClient()) if (killerMob->IsClient())
{ {
exploss = 0; int pvpleveldifference = 0;
if (RuleI(World, PVPSettings) == 4)
pvpleveldifference = 5; //Sullon Zek
if (RuleI(World, PVPLoseExperienceLevelDifference) > 0)
pvpleveldifference = RuleI(World, PVPLoseExperienceLevelDifference);
if (pvpleveldifference > 0) {
int level_difference = 0;
if (GetLevel() > killerMob->GetLevel())
level_difference = GetLevel() - killerMob->GetLevel();
else
level_difference = killerMob->GetLevel() - GetLevel();
if (level_difference > pvpleveldifference)
exploss = 0;
}
else
{
exploss = 0;
}
} }
else if (killerMob->GetOwner() && killerMob->GetOwner()->IsClient()) else if (killerMob->GetOwner() && killerMob->GetOwner()->IsClient())
{ {
@ -2068,14 +2092,11 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
other->AddToHateList(this, hate); other->AddToHateList(this, hate);
LogCombat("Final damage against [{}]: [{}]", other->GetName(), my_hit.damage_done);
if (other->IsClient() && IsPet() && GetOwner()->IsClient()) { if (other->IsClient() && IsPet() && GetOwner()->IsClient()) {
//pets do half damage to clients in pvp my_hit.damage_done = std::max(my_hit.damage_done * RuleI(World, PVPPetDamageMitigation) / 100, 1);
my_hit.damage_done /= 2;
if (my_hit.damage_done < 1)
my_hit.damage_done = 1;
} }
LogCombat("Final damage against [{}]: [{}]", other->GetName(), my_hit.damage_done);
} }
else { else {
my_hit.damage_done = DMG_INVULNERABLE; my_hit.damage_done = DMG_INVULNERABLE;
@ -4976,6 +4997,12 @@ bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) {
if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot) { if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot) {
int BreakChance = RuleI(Spells, RootBreakFromSpells); int BreakChance = RuleI(Spells, RootBreakFromSpells);
if (attacker && attacker->IsClient() && IsClient()) {
if (RuleI(World, PVPSettings) > 0) BreakChance = 75; //All PVP servers is default 75% chance for root to break
if (RuleI(Spells, PVPRootBreakFromSpells) > 0) BreakChance = RuleI(Spells, PVPRootBreakFromSpells);
}
BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance / 100; BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance / 100;
int level_diff = attacker->GetLevel() - GetLevel(); int level_diff = attacker->GetLevel() - GetLevel();

View File

@ -10009,4 +10009,83 @@ void Client::Fling(float value, float target_x, float target_y, float target_z,
outapp_fling->priority = 6; outapp_fling->priority = 6;
FastQueuePacket(&outapp_fling); FastQueuePacket(&outapp_fling);
} }
}
//CanPvP returns true if provided player can attack this player
bool Client::CanPvP(Client *c) {
if (c == nullptr)
return false;
//Dueling overrides normal PvP logic
if (IsDueling() && c->IsDueling() && GetDuelTarget() == c->GetID() && c->GetDuelTarget() == GetID())
return true;
//If PVPLevelDifference is enabled, only allow PVP if players are of proper range
int rule_level_diff = 0;
if (RuleI(World, PVPSettings) == 4)
rule_level_diff = 100; //Sullon Zek rules can attack anyone of opposing deity.
if (RuleI(World, PVPLevelDifference) > 0)
rule_level_diff = RuleI(World, PVPLevelDifference);
if (rule_level_diff > 0) {
int level_diff = 0;
if (c->GetLevel() > GetLevel())
level_diff = c->GetLevel() - GetLevel();
else
level_diff = GetLevel() - c->GetLevel();
if (level_diff > rule_level_diff)
return false;
}
//players need to be proper level for pvp
int rule_min_level = 0;
if (RuleI(World, PVPSettings) == 4)
rule_min_level = 6;
if (RuleI(World, PVPMinLevel) > 0)
rule_min_level = RuleI(World, PVPMinLevel);
if (rule_min_level > 0 && (GetLevel() < rule_min_level || c->GetLevel() < rule_min_level))
return false;
//is deity pvp rule enabled? If so, if we're same alignment, don't allow pvp
if ((RuleI(World, PVPSettings) == 4 || RuleB(World, PVPUseDeityBasedPVP)) && GetAlignment() == c->GetAlignment())
return false;
//VZTZ Zek PVP Setting
if ((RuleI(World, PVPSettings) == 2 || RuleB(World, PVPUseTeamsBySizeBasedPVP)) && GetPVPRaceTeamBySize() == c->GetPVPRaceTeamBySize())
return false;
//Check if players are flagged pvp. This may need to be removed later
if (!GetPVP() || !c->GetPVP()) return false;
return true;
}
//GetAlignment returns 0 = neutral, 1 = good, 2 = evil, used for pvp sullon zek rules
int Client::GetAlignment() {
if (GetDeity() == EQ::deity::DeityErollisiMarr ||
GetDeity() == EQ::deity::DeityMithanielMarr ||
GetDeity() == EQ::deity::DeityRodcetNife ||
GetDeity() == EQ::deity::DeityQuellious ||
GetDeity() == EQ::deity::DeityTunare) return 1; //good
if (GetDeity() == EQ::deity::DeityBertoxxulous ||
GetDeity() == EQ::deity::DeityCazicThule ||
GetDeity() == EQ::deity::DeityInnoruuk ||
GetDeity() == EQ::deity::DeityRallosZek) return 2; //evil
return 0; //neutral
}
//GetPVPRaceSize returns based on racial divisions
int Client::GetPVPRaceTeamBySize() {
if (GetRace() == HUMAN || GetRace() == BARBARIAN || GetRace() == ERUDITE || GetRace() == DRAKKIN)
return 1;
if (GetRace() == GNOME || GetRace() == HALFLING || GetRace() == DWARF || GetRace() == FROGLOK)
return 2;
if (GetRace() == HIGH_ELF || GetRace() == WOOD_ELF || GetRace() == HALF_ELF || GetRace() == VAHSHIR)
return 3;
if (GetRace() == DARK_ELF || GetRace() == OGRE || GetRace() == TROLL || GetRace() == IKSAR)
return 4;
return 1;
} }

View File

@ -770,6 +770,9 @@ public:
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec); bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, EQ::skills::SkillType tradeskill); void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, EQ::skills::SkillType tradeskill);
void InitInnates(); void InitInnates();
bool CanPvP(Client * c);
int GetAlignment();
int GetPVPRaceTeamBySize();
void GMKill(); void GMKill();
inline bool IsMedding() const {return medding;} inline bool IsMedding() const {return medding;}
@ -903,7 +906,7 @@ public:
void IncStats(uint8 type,int16 increase_val); void IncStats(uint8 type,int16 increase_val);
void DropItem(int16 slot_id, bool recurse = true); void DropItem(int16 slot_id, bool recurse = true);
void DropItemQS(EQ::ItemInstance* inst, bool pickup); void DropItemQS(EQ::ItemInstance* inst, bool pickup);
int GetItemLinkHash(const EQ::ItemInstance* inst); // move to ItemData..or make use of the pre-calculated database field int GetItemLinkHash(const EQ::ItemInstance* inst); // move to ItemData..or make use of the pre-calculated database field
void SendItemLink(const EQ::ItemInstance* inst, bool sendtoall=false); void SendItemLink(const EQ::ItemInstance* inst, bool sendtoall=false);

View File

@ -2077,7 +2077,16 @@ void Client::HandleRespawnFromHover(uint32 Option)
CalcBonuses(); CalcBonuses();
SetHP(GetMaxHP()); SetHP(GetMaxHP());
SetMana(GetMaxMana()); if (GetPVP()) {
int mana_pct = 100;
if (RuleI(World, PVPSettings) > 0)
mana_pct = 0; //All PVP servers spawn you with zero mana
if (RuleI(Character, PVPRespawnManaPercent) != 100) //override mana if it's not 100%
mana_pct = RuleI(Character, PVPRespawnManaPercent);
SetMana(std::max(GetMaxMana() * mana_pct / 100, 0));
} else {
SetMana(GetMaxMana());
}
SetEndurance(GetMaxEndurance()); SetEndurance(GetMaxEndurance());
m_Position.x = chosen->x; m_Position.x = chosen->x;