Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions config.lua.dist
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ blackSkulledDeathMana = 0
fieldOwnershipDuration = 5 * 1000
loginProtectionPeriod = 10 * 1000

-- Expert PvP
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s incorrect: Expert PvP works on Open-PvP worlds. Optional-PvP worlds have different mechanics, as do Hardcore-PvP worlds.

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

-- Guild Wars
-- guildWarsDefaultFrags: Default frags for guild war invitations if not specified.
-- guildWarsMinimunFrags: Minimum frags required to initiate a guild war.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,125 +9,123 @@ local toPosition = Position(33464, 31481, 13)
local transformationDuration = 10000

local function isInArea(position, fromPos, toPos)
return position.x >= fromPos.x and position.x <= toPos.x and
position.y >= fromPos.y and position.y <= toPos.y and
position.z == fromPos.z
return position.x >= fromPos.x and position.x <= toPos.x and position.y >= fromPos.y and position.y <= toPos.y and position.z == fromPos.z
end

local function applyAreaEffect(player, transformationType)
if not player or not player:isPlayer() then
return
end
if not player or not player:isPlayer() then
return
end

local playerPos = player:getPosition()
if transformationType == "fire" then
for x = -2, 2 do
for y = -2, 2 do
local pos = Position(playerPos.x + x, playerPos.y + y, playerPos.z)
pos:sendMagicEffect(CONST_ME_HITBYFIRE)
end
end
elseif transformationType == "water" then
for x = -2, 2 do
for y = -2, 2 do
local pos = Position(playerPos.x + x, playerPos.y + y, playerPos.z)
pos:sendMagicEffect(CONST_ME_ICETORNADO)
end
end
end
local playerPos = player:getPosition()
if transformationType == "fire" then
for x = -2, 2 do
for y = -2, 2 do
local pos = Position(playerPos.x + x, playerPos.y + y, playerPos.z)
pos:sendMagicEffect(CONST_ME_HITBYFIRE)
end
end
elseif transformationType == "water" then
for x = -2, 2 do
for y = -2, 2 do
local pos = Position(playerPos.x + x, playerPos.y + y, playerPos.z)
pos:sendMagicEffect(CONST_ME_ICETORNADO)
end
end
end
end

function transformPlayers(position)
local players = Game.getSpectators(position, false, false, 30, 30, 30, 30)
local transformedPlayers = {}
for _, player in ipairs(players) do
if player:isPlayer() and isInArea(player:getPosition(), fromPosition, toPosition) then
local originalOutfit = player:getOutfit()
local randomTransformation = math.random(2)
if randomTransformation == 1 then
player:setOutfit({lookType = 49})
player:sendCancelMessage("You have been transformed into a Fire Elemental!")
transformedPlayers[player:getId()] = {type = "fire", originalOutfit = originalOutfit}
else
player:setOutfit({lookType = 286})
player:sendCancelMessage("You have been transformed into a Water Elemental!")
transformedPlayers[player:getId()] = {type = "water", originalOutfit = originalOutfit}
end
local players = Game.getSpectators(position, false, false, 30, 30, 30, 30)
local transformedPlayers = {}
for _, player in ipairs(players) do
if player:isPlayer() and isInArea(player:getPosition(), fromPosition, toPosition) then
local originalOutfit = player:getOutfit()
local randomTransformation = math.random(2)
if randomTransformation == 1 then
player:setOutfit({ lookType = 49 })
player:sendCancelMessage("You have been transformed into a Fire Elemental!")
transformedPlayers[player:getId()] = { type = "fire", originalOutfit = originalOutfit }
else
player:setOutfit({ lookType = 286 })
player:sendCancelMessage("You have been transformed into a Water Elemental!")
transformedPlayers[player:getId()] = { type = "water", originalOutfit = originalOutfit }
end

local function periodicEffect(playerId, transformationType)
local player = Player(playerId)
if player and player:getOutfit().lookType == (transformationType == "fire" and 49 or 286) then
applyAreaEffect(player, transformationType)
addEvent(periodicEffect, 2000, playerId, transformationType)
end
end
addEvent(periodicEffect, 2000, player:getId(), transformedPlayers[player:getId()].type)
local function periodicEffect(playerId, transformationType)
local player = Player(playerId)
if player and player:getOutfit().lookType == (transformationType == "fire" and 49 or 286) then
applyAreaEffect(player, transformationType)
addEvent(periodicEffect, 2000, playerId, transformationType)
end
end
addEvent(periodicEffect, 2000, player:getId(), transformedPlayers[player:getId()].type)

addEvent(function()
for _, p in ipairs(players) do
if p:isPlayer() and transformedPlayers[p:getId()] then
p:setOutfit(transformedPlayers[p:getId()].originalOutfit)
p:sendCancelMessage("You have returned to your normal form.")
p:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
transformedPlayers[p:getId()] = nil
end
end
end, transformationDuration)
addEvent(function()
for _, p in ipairs(players) do
if p:isPlayer() and transformedPlayers[p:getId()] then
p:setOutfit(transformedPlayers[p:getId()].originalOutfit)
p:sendCancelMessage("You have returned to your normal form.")
p:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
transformedPlayers[p:getId()] = nil
end
end
end, transformationDuration)

addEvent(checkProximityAndApplyDamage, 1000, transformedPlayers)
end
end
addEvent(checkProximityAndApplyDamage, 1000, transformedPlayers)
end
end
end

function checkProximityAndApplyDamage(transformedPlayers)
local currentTime = os.time()
local playerIds = {}
for playerId, _ in pairs(transformedPlayers) do
table.insert(playerIds, playerId)
end
for i = 1, #playerIds - 1 do
local player1 = Player(playerIds[i])
if player1 then
local outfit1 = transformedPlayers[player1:getId()].type
for j = i + 1, #playerIds do
local player2 = Player(playerIds[j])
if player2 and player1:getPosition():getDistance(player2:getPosition()) <= 3 then
local outfit2 = transformedPlayers[player2:getId()].type
if outfit1 ~= outfit2 then
local lastDamage1 = lastDamageTime[player1:getId()] or 0
local lastDamage2 = lastDamageTime[player2:getId()] or 0
if currentTime - lastDamage1 >= damageCooldown or currentTime - lastDamage2 >= damageCooldown then
if currentTime - lastDamage1 >= damageCooldown then
player1:addHealth(-damageAmount)
player1:sendCancelMessage("You took damage for being too close to an opposite elemental!")
player1:getPosition():sendMagicEffect(CONST_ME_BIGCLOUDS)
lastDamageTime[player1:getId()] = currentTime
end
if currentTime - lastDamage2 >= damageCooldown then
player2:addHealth(-damageAmount)
player2:sendCancelMessage("You took damage for being too close to an opposite elemental!")
player2:getPosition():sendMagicEffect(CONST_ME_BIGCLOUDS)
lastDamageTime[player2:getId()] = currentTime
end
end
end
end
end
end
end
local currentTime = os.time()
local playerIds = {}
for playerId, _ in pairs(transformedPlayers) do
table.insert(playerIds, playerId)
end
for i = 1, #playerIds - 1 do
local player1 = Player(playerIds[i])
if player1 then
local outfit1 = transformedPlayers[player1:getId()].type
for j = i + 1, #playerIds do
local player2 = Player(playerIds[j])
if player2 and player1:getPosition():getDistance(player2:getPosition()) <= 3 then
local outfit2 = transformedPlayers[player2:getId()].type
if outfit1 ~= outfit2 then
local lastDamage1 = lastDamageTime[player1:getId()] or 0
local lastDamage2 = lastDamageTime[player2:getId()] or 0
if currentTime - lastDamage1 >= damageCooldown or currentTime - lastDamage2 >= damageCooldown then
if currentTime - lastDamage1 >= damageCooldown then
player1:addHealth(-damageAmount)
player1:sendCancelMessage("You took damage for being too close to an opposite elemental!")
player1:getPosition():sendMagicEffect(CONST_ME_BIGCLOUDS)
lastDamageTime[player1:getId()] = currentTime
end
if currentTime - lastDamage2 >= damageCooldown then
player2:addHealth(-damageAmount)
player2:sendCancelMessage("You took damage for being too close to an opposite elemental!")
player2:getPosition():sendMagicEffect(CONST_ME_BIGCLOUDS)
lastDamageTime[player2:getId()] = currentTime
end
end
end
end
end
end
end

addEvent(checkProximityAndApplyDamage, 1000, transformedPlayers)
addEvent(checkProximityAndApplyDamage, 1000, transformedPlayers)
end

function dukeKruleTransformEvent.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
if creature and creature:isMonster() and creature:getName():lower() == "duke krule" then
local currentTime = os.time()
if currentTime - lastTransformationTime >= transformationCooldown then
transformPlayers(creature:getPosition())
lastTransformationTime = currentTime
end
end
return primaryDamage, primaryType, secondaryDamage, secondaryType
if creature and creature:isMonster() and creature:getName():lower() == "duke krule" then
local currentTime = os.time()
if currentTime - lastTransformationTime >= transformationCooldown then
transformPlayers(creature:getPosition())
lastTransformationTime = currentTime
end
end
return primaryDamage, primaryType, secondaryDamage, secondaryType
end

dukeKruleTransformEvent:register()
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
]]
14 changes: 7 additions & 7 deletions data/scripts/actions/items/potions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ function flaskPotion.onUse(player, item, fromPosition, target, toPosition, isHot
target:say("Aaaah...", MESSAGE_POTION)

local deactivatedFlasks = player:kv():get("talkaction.potions.flask") or false
if not deactivatedFlasks and configManager.getBoolean(configKeys.REMOVE_POTION_CHARGES) then
if fromPosition.x == CONTAINER_POSITION then
player:addItem(potion.flask, 1)
else
Game.createItem(potion.flask, 1, fromPosition)
end
end
if not deactivatedFlasks and configManager.getBoolean(configKeys.REMOVE_POTION_CHARGES) then
if fromPosition.x == CONTAINER_POSITION then
player:addItem(potion.flask, 1)
else
Game.createItem(potion.flask, 1, fromPosition)
end
end
end

player:getPosition():sendSingleSoundEffect(SOUND_EFFECT_TYPE_ITEM_USE_POTION, player:isInGhostMode() and nil or player)
Expand Down
3 changes: 3 additions & 0 deletions src/config/config_enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,4 +394,7 @@ 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,
};
3 changes: 3 additions & 0 deletions src/config/configmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,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);

loadFloatConfig(L, BESTIARY_RATE_CHARM_SHOP_PRICE, "bestiaryRateCharmShopPrice", 1.0);
loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_AXE, "combatChainSkillFormulaAxe", 0.9);
Expand Down
42 changes: 40 additions & 2 deletions src/creatures/combat/combat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,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 +435,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 +456,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 (g_game().getOwnerPlayer(target)) {
if (target->getZoneType() == ZONE_NOPVP) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
} else if (g_configManager().getBoolean(TOGGLE_EXPERT_PVP) && isProtected(attackerPlayer, targetPlayer)) {
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 (g_game().getOwnerPlayer(target)) {
if (target->getZoneType() == ZONE_NOPVP) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
} else if (isProtected(attackerPlayer, targetPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE;
} else if (!attackerPlayer->canCombat(target)) {
return RETURNVALUE_ADJUSTYOURCOMBAT;
}
}
}
}
} else if (target && target->getNpc()) {
Expand Down Expand Up @@ -2371,6 +2391,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 @@ -2583,6 +2608,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 @@ -328,6 +328,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
Loading