diff --git a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua
index b2ba681d251..f8f7ddc5c6b 100644
--- a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua
+++ b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua
@@ -57,7 +57,7 @@ function deathEvent.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller)
end
end
-- Minotaurs
- killCheck(player, targetName, Storage.KillingInTheNameOf.BudrikMinos, 0, tasks.Budrik[1].creatures, nil, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.MinotaurCount)
+ killCheck(player, targetName, Storage.KillingInTheNameOf.BudrikMinos, 0, tasks.Budrik[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.MinotaurCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.MinotaurCount)
-- Necromancers and Priestesses
killCheck(player, targetName, Storage.KillingInTheNameOf.LugriNecromancers, 0, tasks.Lugri[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.NecromancerCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.NecromancerCount)
killCheck(player, targetName, Storage.KillingInTheNameOf.LugriNecromancers, 3, tasks.Lugri[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.NecromancerCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.NecromancerCount)
diff --git a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua b/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua
index 61984209287..36be8dc38b4 100644
--- a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua
+++ b/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua
@@ -16,7 +16,7 @@ combat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
function onGetFormulaValues(player, skill, attack, factor)
local distanceSkill = player:getEffectiveSkillLevel(SKILL_DISTANCE)
local min = (player:getLevel() / 5)
- local max = (0.09 * factor) * distanceSkill * 37 + (player:getLevel() / 5)
+ local max = (0.09 * factor) * distanceSkill * attack + (player:getLevel() / 5)
return -min, -max
end
diff --git a/data/items/items.xml b/data/items/items.xml
index 0bc1c782452..421e981ad02 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -53105,6 +53105,9 @@ hands of its owner. Granted by TibiaRoyal.com"/>
+
+
+
-
diff --git a/data/libs/functions/functions.lua b/data/libs/functions/functions.lua
index 9eb1b0b57b5..c2349ce7fd6 100644
--- a/data/libs/functions/functions.lua
+++ b/data/libs/functions/functions.lua
@@ -67,17 +67,28 @@ end
function getTimeInWords(secsParam)
local secs = tonumber(secsParam)
+ local days = math.floor(secs / (24 * 3600))
+ secs = secs - (days * 24 * 3600)
local hours, minutes, seconds = getHours(secs), getMinutes(secs), getSeconds(secs)
local timeStr = ""
+ if days > 0 then
+ timeStr = days .. (days > 1 and " days" or " day")
+ end
+
if hours > 0 then
- timeStr = hours .. (hours > 1 and " hours" or " hour")
+ if timeStr ~= "" then
+ timeStr = timeStr .. ", "
+ end
+
+ timeStr = timeStr .. hours .. (hours > 1 and " hours" or " hour")
end
if minutes > 0 then
if timeStr ~= "" then
timeStr = timeStr .. ", "
end
+
timeStr = timeStr .. minutes .. (minutes > 1 and " minutes" or " minute")
end
@@ -85,9 +96,9 @@ function getTimeInWords(secsParam)
if timeStr ~= "" then
timeStr = timeStr .. " and "
end
+
timeStr = timeStr .. seconds .. (seconds > 1 and " seconds" or " second")
end
-
return timeStr
end
diff --git a/data/scripts/eventcallbacks/README.md b/data/scripts/eventcallbacks/README.md
index bdafcb41b33..601653574bd 100644
--- a/data/scripts/eventcallbacks/README.md
+++ b/data/scripts/eventcallbacks/README.md
@@ -47,6 +47,7 @@ Event callbacks are available for several categories of game entities, such as `
- `(void)` `playerOnCombat`
- `(void)` `playerOnInventoryUpdate`
- `(bool)` `playerOnRotateItem`
+- `(void)` `playerOnWalk`
- `(void)` `monsterOnDropLoot`
- `(void)` `monsterPostDropLoot`
- `(void)` `monsterOnSpawn`
diff --git a/data/scripts/runes/destroy_field_rune.lua b/data/scripts/runes/destroy_field_rune.lua
index 024dbc1bcd7..8752be7eb51 100644
--- a/data/scripts/runes/destroy_field_rune.lua
+++ b/data/scripts/runes/destroy_field_rune.lua
@@ -4,6 +4,13 @@ local fields = { 105, 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2132
local rune = Spell("rune")
function rune.onCastSpell(creature, variant, isHotkey)
+ local inPz = creature:getTile():hasFlag(TILESTATE_PROTECTIONZONE)
+ if inPz then
+ creature:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
+ creature:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
local position = Variant.getPosition(variant)
local tile = Tile(position)
local field = tile and tile:getItemByType(ITEM_TYPE_MAGICFIELD)
diff --git a/data/scripts/spells/house/kick.lua b/data/scripts/spells/house/kick.lua
index b4b583c1007..265ac48f796 100644
--- a/data/scripts/spells/house/kick.lua
+++ b/data/scripts/spells/house/kick.lua
@@ -4,6 +4,7 @@ function spell.onCastSpell(player, variant)
local targetPlayer = Player(variant:getString()) or player
local guest = targetPlayer:getTile():getHouse()
local owner = player:getTile():getHouse()
+
-- Owner kick yourself from house
if targetPlayer == player then
player:getPosition():sendMagicEffect(CONST_ME_POFF)
@@ -11,6 +12,13 @@ function spell.onCastSpell(player, variant)
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
return true
end
+
+ if not owner:canEditAccessList(GUEST_LIST, player) then
+ player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
if not owner or not guest or not guest:kickPlayer(player, targetPlayer) then
player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
player:getPosition():sendMagicEffect(CONST_ME_POFF)
diff --git a/data/scripts/talkactions/gm/afk.lua b/data/scripts/talkactions/gm/afk.lua
new file mode 100644
index 00000000000..6167a2b6068
--- /dev/null
+++ b/data/scripts/talkactions/gm/afk.lua
@@ -0,0 +1,94 @@
+local afk = TalkAction("/afk")
+
+playersAFKs = {}
+
+local function checkIsAFK(id)
+ for index, item in pairs(playersAFKs) do
+ if id == item.id then
+ return { afk = true, index = index }
+ end
+ end
+ return { afk = false }
+end
+
+local function showAfkMessage(playerPosition)
+ local spectators = Game.getSpectators(playerPosition, false, true, 8, 8, 8, 8)
+ if #spectators > 0 then
+ for _, spectator in ipairs(spectators) do
+ spectator:say("AFK !", TALKTYPE_MONSTER_SAY, false, spectator, playerPosition)
+ end
+ end
+end
+
+function afk.onSay(player, words, param)
+ if param == "" then
+ player:sendCancelMessage("You need to specify on/off param.")
+ return true
+ end
+
+ local id, playerPosition = player:getId(), player:getPosition()
+ local isAfk = checkIsAFK(id)
+ if param == "on" then
+ if isAfk.afk then
+ player:sendCancelMessage("You are already AFK!")
+ return true
+ end
+
+ table.insert(playersAFKs, { id = id, position = playerPosition })
+ if player:isInGhostMode() then
+ player:setGhostMode(false)
+ end
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are now AFK!")
+ playerPosition:sendMagicEffect(CONST_ME_REDSMOKE)
+ showAfkMessage(playerPosition)
+ elseif param == "off" then
+ if isAfk.afk then
+ table.remove(playersAFKs, isAfk.index)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are no longer AFK!")
+ playerPosition:sendMagicEffect(CONST_ME_REDSMOKE)
+ end
+ end
+
+ return true
+end
+
+afk:separator(" ")
+afk:groupType("gamemaster")
+afk:register()
+
+------------------ AFK Effect Message ------------------
+local afkEffect = GlobalEvent("GodAfkEffect")
+function afkEffect.onThink(interval)
+ for _, player in ipairs(playersAFKs) do
+ showAfkMessage(player.position)
+ end
+ return true
+end
+
+afkEffect:interval(5000)
+afkEffect:register()
+
+------------------ Stop AFK Message when moves ------------------
+local callback = EventCallback()
+function callback.playerOnWalk(player, creature, creaturePos, toPos)
+ local isAfk = checkIsAFK(player:getId())
+ if isAfk.afk then
+ table.remove(playersAFKs, isAfk.index)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are no longer AFK!")
+ end
+ return true
+end
+
+callback:register()
+
+------------------ Player Logout ------------------
+local godAfkLogout = CreatureEvent("GodAfkLogout")
+function godAfkLogout.onLogout(player)
+ local isAfk = checkIsAFK(player:getId())
+ if isAfk.afk then
+ table.remove(playersAFKs, isAfk.index)
+ end
+ return true
+end
+
+godAfkLogout:register()
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index 4f6b5d29294..73dba80b929 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -1926,6 +1926,8 @@ void Player::onWalk(Direction &dir) {
Creature::onWalk(dir);
setNextActionTask(nullptr);
setNextAction(OTSYS_TIME() + getStepDuration(dir));
+
+ g_callbacks().executeCallback(EventCallback_t::playerOnWalk, &EventCallback::playerOnWalk, getPlayer(), dir);
}
void Player::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) {
@@ -2918,6 +2920,7 @@ bool Player::spawn() {
getParent()->postAddNotification(static_self_cast(), nullptr, 0);
g_game().addCreatureCheck(static_self_cast());
g_game().addPlayer(static_self_cast());
+ static_self_cast()->onChangeZone(static_self_cast()->getZoneType());
return true;
}
@@ -4673,6 +4676,8 @@ void Player::onPlacedCreature() {
removePlayer(true);
}
+ this->onChangeZone(this->getZoneType());
+
sendUnjustifiedPoints();
}
@@ -6692,7 +6697,7 @@ void Player::triggerTranscendance() {
outfit.lookType = getVocation()->getAvatarLookType();
outfitCondition->setOutfit(outfit);
addCondition(outfitCondition);
- wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR, OTSYS_TIME() + duration);
+ wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_FORGE, OTSYS_TIME() + duration);
g_game().addMagicEffect(getPosition(), CONST_ME_AVATAR_APPEAR);
sendTextMessage(MESSAGE_ATTENTION, "Transcendance was triggered.");
sendSkills();
diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp
index 77a15f7cbf7..20e8f683bf9 100644
--- a/src/creatures/players/wheel/player_wheel.cpp
+++ b/src/creatures/players/wheel/player_wheel.cpp
@@ -2410,21 +2410,25 @@ int32_t PlayerWheel::checkBattleHealingAmount() const {
}
int32_t PlayerWheel::checkAvatarSkill(WheelAvatarSkill_t skill) const {
- if (skill == WheelAvatarSkill_t::NONE || getOnThinkTimer(WheelOnThink_t::AVATAR) <= OTSYS_TIME()) {
+ if (skill == WheelAvatarSkill_t::NONE || (getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL) <= OTSYS_TIME() && getOnThinkTimer(WheelOnThink_t::AVATAR_FORGE) <= OTSYS_TIME())) {
return 0;
}
uint8_t stage = 0;
- if (getInstant("Avatar of Light")) {
- stage = getStage(WheelStage_t::AVATAR_OF_LIGHT);
- } else if (getInstant("Avatar of Steel")) {
- stage = getStage(WheelStage_t::AVATAR_OF_STEEL);
- } else if (getInstant("Avatar of Nature")) {
- stage = getStage(WheelStage_t::AVATAR_OF_NATURE);
- } else if (getInstant("Avatar of Storm")) {
- stage = getStage(WheelStage_t::AVATAR_OF_STORM);
+ if (getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL) > OTSYS_TIME()) {
+ if (getInstant("Avatar of Light")) {
+ stage = getStage(WheelStage_t::AVATAR_OF_LIGHT);
+ } else if (getInstant("Avatar of Steel")) {
+ stage = getStage(WheelStage_t::AVATAR_OF_STEEL);
+ } else if (getInstant("Avatar of Nature")) {
+ stage = getStage(WheelStage_t::AVATAR_OF_NATURE);
+ } else if (getInstant("Avatar of Storm")) {
+ stage = getStage(WheelStage_t::AVATAR_OF_STORM);
+ } else {
+ return 0;
+ }
} else {
- return 0;
+ stage = 3;
}
if (skill == WheelAvatarSkill_t::DAMAGE_REDUCTION) {
diff --git a/src/creatures/players/wheel/wheel_definitions.hpp b/src/creatures/players/wheel/wheel_definitions.hpp
index 8432e9dca21..c23d2adf53f 100644
--- a/src/creatures/players/wheel/wheel_definitions.hpp
+++ b/src/creatures/players/wheel/wheel_definitions.hpp
@@ -105,9 +105,10 @@ enum class WheelOnThink_t : uint8_t {
FOCUS_MASTERY = 4,
GIFT_OF_LIFE = 5,
DIVINE_EMPOWERMENT = 6,
- AVATAR = 7,
+ AVATAR_SPELL = 7,
+ AVATAR_FORGE = 8,
- TOTAL_COUNT = 8
+ TOTAL_COUNT = 9
};
enum class WheelStat_t : uint8_t {
diff --git a/src/lua/callbacks/callbacks_definitions.hpp b/src/lua/callbacks/callbacks_definitions.hpp
index 521a2c4cda7..3b8016f5f5b 100644
--- a/src/lua/callbacks/callbacks_definitions.hpp
+++ b/src/lua/callbacks/callbacks_definitions.hpp
@@ -56,6 +56,7 @@ enum class EventCallback_t : uint16_t {
playerOnCombat,
playerOnInventoryUpdate,
playerOnRotateItem,
+ playerOnWalk,
// Monster
monsterOnDropLoot,
monsterPostDropLoot,
diff --git a/src/lua/callbacks/event_callback.cpp b/src/lua/callbacks/event_callback.cpp
index 51c03131fa9..d2b88c30f3a 100644
--- a/src/lua/callbacks/event_callback.cpp
+++ b/src/lua/callbacks/event_callback.cpp
@@ -985,6 +985,29 @@ bool EventCallback::playerOnRotateItem(std::shared_ptr player, std::shar
return getScriptInterface()->callFunction(3);
}
+void EventCallback::playerOnWalk(std::shared_ptr player, Direction &dir) const {
+ if (!getScriptInterface()->reserveScriptEnv()) {
+ g_logger().error("[EventCallback::eventOnWalk - "
+ "Player {}] "
+ "Call stack overflow. Too many lua script calls being nested.",
+ player->getName());
+ return;
+ }
+
+ ScriptEnvironment* scriptEnvironment = getScriptInterface()->getScriptEnv();
+ scriptEnvironment->setScriptId(getScriptId(), getScriptInterface());
+
+ lua_State* L = getScriptInterface()->getLuaState();
+ getScriptInterface()->pushFunction(getScriptId());
+
+ LuaScriptInterface::pushUserdata(L, player);
+ LuaScriptInterface::setMetatable(L, -1, "Player");
+
+ lua_pushnumber(L, dir);
+
+ getScriptInterface()->callVoidFunction(2);
+}
+
void EventCallback::playerOnStorageUpdate(std::shared_ptr player, const uint32_t key, const int32_t value, int32_t oldValue, uint64_t currentTime) const {
if (!getScriptInterface()->reserveScriptEnv()) {
g_logger().error("[EventCallback::eventOnStorageUpdate - "
diff --git a/src/lua/callbacks/event_callback.hpp b/src/lua/callbacks/event_callback.hpp
index d9dc4a9b110..9141235a028 100644
--- a/src/lua/callbacks/event_callback.hpp
+++ b/src/lua/callbacks/event_callback.hpp
@@ -116,6 +116,7 @@ class EventCallback : public Script {
void playerOnCombat(std::shared_ptr player, std::shared_ptr target, std::shared_ptr
- item, CombatDamage &damage) const;
void playerOnInventoryUpdate(std::shared_ptr player, std::shared_ptr
- item, Slots_t slot, bool equip) const;
bool playerOnRotateItem(std::shared_ptr player, std::shared_ptr
- item, const Position &position) const;
+ void playerOnWalk(std::shared_ptr player, Direction &dir) const;
// Monster
void monsterOnDropLoot(std::shared_ptr monster, std::shared_ptr corpse) const;
diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp
index 3ba2c43841d..621023e5c52 100644
--- a/src/lua/functions/creatures/player/player_functions.cpp
+++ b/src/lua/functions/creatures/player/player_functions.cpp
@@ -3997,9 +3997,9 @@ int PlayerFunctions::luaPlayerAvatarTimer(lua_State* L) {
}
if (lua_gettop(L) == 1) {
- lua_pushnumber(L, (lua_Number)player->wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR));
+ lua_pushnumber(L, (lua_Number)player->wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL));
} else {
- player->wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR, getNumber(L, 2));
+ player->wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_SPELL, getNumber(L, 2));
pushBoolean(L, true);
}
return 1;