Skip to content
6 changes: 6 additions & 0 deletions config.lua.dist
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ blackSkulledDeathMana = 0
fieldOwnershipDuration = 5 * 1000
loginProtectionPeriod = 10 * 1000

-- Expert PvP
-- NOTE: toggleExpertPvp enables the PvP frames, similar to Tibia RL.
toggleExpertPvp = true
canWalkThroughOtherPlayers = false
canWalkThroughMagicWalls = false

-- Guilds
-- ingameGuildManagement: set true to enable in-game guild commands.
-- guildWarsDefaultFrags: Default frags for guild war invitations if not specified.
Expand Down
15 changes: 0 additions & 15 deletions data/modules/lib/modules.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,3 @@ function addPlayerEvent(callable, delay, playerId, ...)
end
end, delay, callable, player.uid, ...)
end

--[[
function Player.updateFightModes(self)
local msg = NetworkMessage()

msg:addByte(0xA7)

msg:addByte(self:getFightMode())
msg:addByte(self:getChaseMode())
msg:addByte(self:getSecureMode() and 1 or 0)
msg:addByte(self:getPvpMode())

msg:sendToPlayer(self)
end
]]
3 changes: 3 additions & 0 deletions src/config/config_enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,9 @@ enum ConfigKey_t : uint16_t {
TOGGLE_GUILD_WARS,
GUILD_WARS_MINIMUM_FRAGS,
GUILD_WARS_DEFAULT_FRAGS,
TOGGLE_EXPERT_PVP,
EXPERT_PVP_CANWALKTHROUGHOTHERPLAYERS,
EXPERT_PVP_CANWALKTHROUGHMAGICWALLS,
INGAME_GUILD_MANAGEMENT,
LEVEL_TO_FORM_GUILD,
CREATE_GUILD_ONLY_PREMIUM,
Expand Down
3 changes: 3 additions & 0 deletions src/config/configmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ bool ConfigManager::load() {
loadBoolConfig(L, TOGGLE_GUILDHALL_NEED_GUILD, "toggleGuildHallNeedGuild", true);
loadBoolConfig(L, TOGGLE_MAX_CONNECTIONS_BY_IP, "toggleMaxConnectionsByIP", false);
loadBoolConfig(L, TOGGLE_GUILD_WARS, "toggleGuildWars", false);
loadBoolConfig(L, TOGGLE_EXPERT_PVP, "toggleExpertPvp", false);
loadBoolConfig(L, EXPERT_PVP_CANWALKTHROUGHOTHERPLAYERS, "canWalkThroughOtherPlayers", false);
loadBoolConfig(L, EXPERT_PVP_CANWALKTHROUGHMAGICWALLS, "canWalkThroughMagicWalls", false);
loadBoolConfig(L, INGAME_GUILD_MANAGEMENT, "ingameGuildManagement", true);
loadBoolConfig(L, CREATE_GUILD_ONLY_PREMIUM, "createGuildOnlyPremium", true);
loadBoolConfig(L, EXIVA_RESTRICTIONS_ONLY_OPTIONAL_WORLDS, "exivaRestrictionsOnlyOptionalWorlds", true);
Expand Down
51 changes: 47 additions & 4 deletions src/creatures/combat/combat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,11 @@ bool Combat::isPlayerCombat(const std::shared_ptr<Creature> &target) {
return true;
}

if (target->isSummon() && target->getMaster()->getPlayer()) {
return true;
if (target->isSummon()) {
const auto &master = target->getMaster();
if (master && master->getPlayer()) {
return true;
}
}

return false;
Expand Down Expand Up @@ -389,10 +392,12 @@ ReturnValue Combat::canDoCombat(const std::shared_ptr<Creature> &attacker, const
const auto &masterAttackerPlayer = attackerMaster ? attackerMaster->getPlayer() : nullptr;
const auto &masterAttackerMonster = attackerMaster ? attackerMaster->getMonster() : nullptr;
const auto &attackerPlayer = attacker->getPlayer();
const auto &effectiveAttackerPlayer = attackerPlayer ? attackerPlayer : masterAttackerPlayer;
const auto &attackerMonster = attacker->getMonster();
const auto &targetMonster = target ? target->getMonster() : nullptr;
const auto &targetMaster = target ? target->getMaster() : nullptr;
const auto &targetMasterPlayer = targetMaster ? targetMaster->getPlayer() : nullptr;
const auto &targetOwnerPlayer = target ? g_game().getOwnerPlayer(target) : nullptr;
if (targetPlayer) {
if (targetPlayer->hasFlag(PlayerFlags_t::CannotBeAttacked)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
Expand All @@ -406,6 +411,8 @@ ReturnValue Combat::canDoCombat(const std::shared_ptr<Creature> &attacker, const

if (isProtected(attackerPlayer, targetPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
} else if (!attackerPlayer->canCombat(targetPlayer)) {
return RETURNVALUE_ADJUSTYOURCOMBAT;
}

// nopvp-zone
Expand Down Expand Up @@ -433,6 +440,8 @@ ReturnValue Combat::canDoCombat(const std::shared_ptr<Creature> &attacker, const

if (isProtected(masterAttackerPlayer, targetPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
} else if (!masterAttackerPlayer->canCombat(targetPlayer)) {
return RETURNVALUE_ADJUSTYOURCOMBAT;
}
}
}
Expand All @@ -452,14 +461,30 @@ ReturnValue Combat::canDoCombat(const std::shared_ptr<Creature> &attacker, const
return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE;
}

if (target->isSummon() && targetMasterPlayer && target->getZoneType() == ZONE_NOPVP) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
if (targetOwnerPlayer) {
if (target->getZoneType() == ZONE_NOPVP) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
} else if (g_configManager().getBoolean(TOGGLE_EXPERT_PVP) && effectiveAttackerPlayer && isProtected(effectiveAttackerPlayer, targetOwnerPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE;
} else if (!attackerPlayer->canCombat(target)) {
return RETURNVALUE_ADJUSTYOURCOMBAT;
}
}
} else if (attackerMonster) {
if ((!targetMaster || !targetMasterPlayer) && attacker->getFaction() == FACTION_DEFAULT) {
if (!attackerMaster || !masterAttackerPlayer) {
return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE;
}
} else if (g_configManager().getBoolean(TOGGLE_EXPERT_PVP)) {
if (targetOwnerPlayer) {
if (target->getZoneType() == ZONE_NOPVP) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
} else if (effectiveAttackerPlayer && isProtected(effectiveAttackerPlayer, targetOwnerPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE;
} else if (effectiveAttackerPlayer && !effectiveAttackerPlayer->canCombat(target)) {
return RETURNVALUE_ADJUSTYOURCOMBAT;
}
}
}
}
} else if (target && target->getNpc()) {
Expand Down Expand Up @@ -2503,6 +2528,11 @@ void AreaCombat::setupExtArea(const std::list<uint32_t> &list, uint32_t rows) {
//**********************************************************//

void MagicField::onStepInField(const std::shared_ptr<Creature> &creature) {
const auto &target = g_game().getOwnerPlayer(creature);
if (target && !isAggressive(target)) {
return;
}

// remove magic walls/wild growth
if ((!isBlocking() && g_game().getWorldType() == WORLDTYPE_OPTIONAL && id == ITEM_MAGICWALL_SAFE) || id == ITEM_WILDGROWTH_SAFE) {
if (!creature->isInGhostMode()) {
Expand Down Expand Up @@ -2757,6 +2787,19 @@ int32_t MagicField::getDamage() const {
return 0;
}

bool MagicField::isAggressive(const std::shared_ptr<Player> &player) const {
if (!g_configManager().getBoolean(TOGGLE_EXPERT_PVP) && g_configManager().getBoolean(EXPERT_PVP_CANWALKTHROUGHMAGICWALLS)) {
return true;
}

const auto &caster = g_game().getOwnerPlayer(getOwnerId());
if (!caster || pvpMode == PVP_MODE_RED_FIST) {
return true;
}

return caster->isAggressiveCreature(player, pvpMode == PVP_MODE_WHITE_HAND, createTime) || pvpMode == PVP_MODE_YELLOW_HAND && player->getSkull() != SKULL_NONE;
}

MatrixArea::MatrixArea(uint32_t initRows, uint32_t initCols) :
centerX(0), centerY(0), rows(initRows), cols(initCols) {
data_ = new bool*[rows];
Expand Down
3 changes: 3 additions & 0 deletions src/creatures/combat/combat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,9 @@ class MagicField final : public Item {
CombatType_t getCombatType() const;
int32_t getDamage() const;
void onStepInField(const std::shared_ptr<Creature> &creature);
bool isAggressive(const std::shared_ptr<Player> &player) const;

PvpMode_t pvpMode = PVP_MODE_DOVE;

private:
int64_t createTime;
Expand Down
2 changes: 1 addition & 1 deletion src/creatures/combat/condition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1850,7 +1850,7 @@ bool ConditionDamage::doDamage(const std::shared_ptr<Creature> &creature, int32_
}

if (!creature->isAttackable() || Combat::canDoCombat(attacker, creature, damage.primary.type != COMBAT_HEALING) != RETURNVALUE_NOERROR) {
if (!creature->isInGhostMode() && !creature->getNpc()) {
if (!creature->isInGhostMode() && !creature->getNpc() && !g_configManager().getBoolean(TOGGLE_EXPERT_PVP)) {
g_game().addMagicEffect(creature->getPosition(), CONST_ME_POFF);
}
return false;
Expand Down
7 changes: 6 additions & 1 deletion src/creatures/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,12 @@ void Creature::onDeath() {
const uint64_t gainExp = getGainedExperience(attacker);
const auto &attackerMaster = attacker->getMaster() ? attacker->getMaster() : attacker;
if (auto attackerPlayer = attackerMaster->getPlayer()) {
attackerPlayer->removeAttacked(getPlayer());
if (auto thisPlayer = getPlayer()) {
attackerPlayer->removeAttacked(thisPlayer);
thisPlayer->removeAttackedBy(attackerPlayer);
}

g_game().updateCreatureSquare(attackerPlayer);

const auto &party = attackerPlayer->getParty();
killers.insert(attackerPlayer);
Expand Down
Loading
Loading