introduce changes from streamside fork

This commit is contained in:
ErikasKontenis 2019-09-16 20:38:16 +03:00
parent f2cbc2dc1c
commit ad03b0eb3e
162 changed files with 2485 additions and 698 deletions

View File

@ -69,7 +69,9 @@ freePremium = false
kickIdlePlayerAfterMinutes = 15
maxMessageBuffer = 4
showMonsterLoot = false
queryPlayerContainers = false
blockHeight = false
dropItems = false
-- Character Rooking
-- Level threshold is the level requirement to teleport players back to newbie town

View File

@ -2,7 +2,12 @@ local drunk = Condition(CONDITION_DRUNK)
drunk:setParameter(CONDITION_PARAM_TICKS, 60000)
local poison = Condition(CONDITION_POISON)
poison:setTiming(100)
poison:setParameter(CONDITION_PARAM_DELAYED, true)
poison:setParameter(CONDITION_PARAM_MINVALUE, -50)
poison:setParameter(CONDITION_PARAM_MAXVALUE, -120)
poison:setParameter(CONDITION_PARAM_STARTVALUE, -5)
poison:setParameter(CONDITION_PARAM_TICKINTERVAL, 5000)
poison:setParameter(CONDITION_PARAM_FORCEUPDATE, true)
local messages = {
[FLUID_WATER] = "Gulp.",

View File

@ -1,7 +1,5 @@
function onLogin(player)
if player:getLastLoginSaved() <= 0 or player:getStorageValue(30017) == 1 then
player:setStorageValue(30017, 0) -- reset storage for first items
if player:getLastLoginSaved() <= 0 then
-- Items
if player:getSex() == PLAYERSEX_FEMALE then
player:addItem(3562, 1, true, -1, CONST_SLOT_ARMOR)

28
data/events/events.xml Normal file
View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<events>
<!-- Creature methods -->
<event class="Creature" method="onChangeOutfit" enabled="0" />
<event class="Creature" method="onAreaCombat" enabled="0" />
<event class="Creature" method="onTargetCombat" enabled="0" />
<!-- Party methods -->
<event class="Party" method="onJoin" enabled="0" />
<event class="Party" method="onLeave" enabled="0" />
<event class="Party" method="onDisband" enabled="0" />
<event class="Party" method="onShareExperience" enabled="1" />
<!-- Player methods -->
<event class="Player" method="onLook" enabled="1" />
<event class="Player" method="onLookInBattleList" enabled="1" />
<event class="Player" method="onLookInTrade" enabled="1" />
<event class="Player" method="onMoveItem" enabled="1" />
<event class="Player" method="onItemMoved" enabled="0" />
<event class="Player" method="onMoveCreature" enabled="0" />
<event class="Player" method="onReportBug" enabled="1" />
<event class="Player" method="onTurn" enabled="0" />
<event class="Player" method="onTradeRequest" enabled="0" />
<event class="Player" method="onTradeAccept" enabled="0" />
<event class="Player" method="onGainExperience" enabled="1" />
<event class="Player" method="onLoseExperience" enabled="0" />
<event class="Player" method="onGainSkillTries" enabled="1" />
</events>

View File

@ -0,0 +1,11 @@
function Creature:onChangeOutfit(outfit)
return true
end
function Creature:onAreaCombat(tile, isAggressive)
return RETURNVALUE_NOERROR
end
function Creature:onTargetCombat(target)
return RETURNVALUE_NOERROR
end

View File

@ -0,0 +1,35 @@
function Party:onJoin(player)
return true
end
function Party:onLeave(player)
return true
end
function Party:onDisband()
return true
end
function Party:onShareExperience(exp)
local sharedExperienceMultiplier = 1.20 --20%
local vocationsIds = {}
local vocationId = self:getLeader():getVocation():getBase():getId()
if vocationId ~= VOCATION_NONE then
table.insert(vocationsIds, vocationId)
end
for _, member in ipairs(self:getMembers()) do
vocationId = member:getVocation():getBase():getId()
if not table.contains(vocationsIds, vocationId) and vocationId ~= VOCATION_NONE then
table.insert(vocationsIds, vocationId)
end
end
local size = #vocationsIds
if size > 1 then
sharedExperienceMultiplier = 1.0 + ((size * (5 * (size - 1) + 10)) / 100)
end
return (exp * sharedExperienceMultiplier) / (#self:getMembers() + 1)
end

View File

@ -0,0 +1,169 @@
function Player:onLook(thing, position, distance)
local description = "You see " .. thing:getDescription(distance)
if self:getGroup():getAccess() then
if thing:isItem() then
description = string.format("%s\nItem ID: %d", description, thing:getId())
local actionId = thing:getActionId()
if actionId ~= 0 then
description = string.format("%s, Action ID: %d", description, actionId)
end
local uniqueId = thing:getAttribute(ITEM_ATTRIBUTE_MOVEMENTID)
if uniqueId > 0 and uniqueId < 65536 then
description = string.format("%s, Movement ID: %d", description, uniqueId)
end
local itemType = thing:getType()
local transformEquipId = itemType:getTransformEquipId()
local transformDeEquipId = itemType:getTransformDeEquipId()
if transformEquipId ~= 0 then
description = string.format("%s\nTransforms to: %d (onEquip)", description, transformEquipId)
elseif transformDeEquipId ~= 0 then
description = string.format("%s\nTransforms to: %d (onDeEquip)", description, transformDeEquipId)
end
local decayId = itemType:getDecayId()
if decayId ~= -1 then
description = string.format("%s\nDecays to: %d", description, decayId)
end
if thing:getAttribute(ITEM_ATTRIBUTE_DECAYSTATE) == 1 then
description = string.format("%s\nDecaying in %d minutes (%d seconds).", description, thing:getAttribute(ITEM_ATTRIBUTE_DURATION) / 1000 / 60, thing:getAttribute(ITEM_ATTRIBUTE_DURATION) / 1000)
end
elseif thing:isCreature() then
local str = "%s\nHealth: %d / %d"
if thing:isPlayer() and thing:getMaxMana() > 0 then
str = string.format("%s, Mana: %d / %d", str, thing:getMana(), thing:getMaxMana())
end
description = string.format(str, description, thing:getHealth(), thing:getMaxHealth()) .. "."
end
local position = thing:getPosition()
description = string.format(
"%s\nPosition: %d, %d, %d",
description, position.x, position.y, position.z
)
if thing:isCreature() then
if thing:isPlayer() then
description = string.format("%s\nIP: %s.", description, Game.convertIpToString(thing:getIp()))
end
end
end
self:sendTextMessage(MESSAGE_INFO_DESCR, description)
end
function Player:onLookInBattleList(creature, distance)
local description = "You see " .. creature:getDescription(distance)
if self:getGroup():getAccess() then
local str = "%s\nHealth: %d / %d"
if creature:isPlayer() and creature:getMaxMana() > 0 then
str = string.format("%s, Mana: %d / %d", str, creature:getMana(), creature:getMaxMana())
end
description = string.format(str, description, creature:getHealth(), creature:getMaxHealth()) .. "."
local position = creature:getPosition()
description = string.format(
"%s\nPosition: %d, %d, %d",
description, position.x, position.y, position.z
)
if creature:isPlayer() then
description = string.format("%s\nIP: %s", description, Game.convertIpToString(creature:getIp()))
end
end
self:sendTextMessage(MESSAGE_INFO_DESCR, description)
end
function Player:onLookInTrade(partner, item, distance)
self:sendTextMessage(MESSAGE_INFO_DESCR, "You see " .. item:getDescription(distance))
end
function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder, toCylinder)
return true
end
function Player:onItemMoved(item, count, fromPosition, toPosition, fromCylinder, toCylinder)
end
function Player:onMoveCreature(creature, fromPosition, toPosition)
return true
end
function Player:onReportBug(message, position, category)
if self:getAccountType() == ACCOUNT_TYPE_NORMAL then
return false
end
local name = self:getName()
local file = io.open("data/reports/bugs/" .. name .. " report.txt", "a")
if not file then
self:sendTextMessage(MESSAGE_EVENT_DEFAULT, "There was an error when processing your report, please contact a gamemaster.")
return true
end
io.output(file)
io.write("------------------------------\n")
io.write("Name: " .. name)
if category == BUG_CATEGORY_MAP then
io.write(" [Map position: " .. position.x .. ", " .. position.y .. ", " .. position.z .. "]")
end
local playerPosition = self:getPosition()
io.write(" [Player Position: " .. playerPosition.x .. ", " .. playerPosition.y .. ", " .. playerPosition.z .. "]\n")
io.write("Comment: " .. message .. "\n")
io.close(file)
self:sendTextMessage(MESSAGE_EVENT_DEFAULT, "Your report has been sent to " .. configManager.getString(configKeys.SERVER_NAME) .. ".")
return true
end
function Player:onTurn(direction)
return true
end
function Player:onTradeRequest(target, item)
return true
end
function Player:onTradeAccept(target, item, targetItem)
return true
end
local soulCondition = Condition(CONDITION_SOUL, CONDITIONID_DEFAULT)
soulCondition:setTicks(4 * 60 * 1000)
soulCondition:setParameter(CONDITION_PARAM_SOULGAIN, 1)
function Player:onGainExperience(source, exp, rawExp)
if not source or source:isPlayer() then
return exp
end
-- Soul regeneration
local vocation = self:getVocation()
if self:getSoul() < vocation:getMaxSoul() and exp >= self:getLevel() then
soulCondition:setParameter(CONDITION_PARAM_SOULTICKS, vocation:getSoulGainTicks() * 1000)
self:addCondition(soulCondition)
end
-- Apply experience stage multiplier
exp = exp * Game.getExperienceStage(self:getLevel())
return exp
end
function Player:onLoseExperience(exp)
return exp
end
function Player:onGainSkillTries(skill, tries)
if APPLY_SKILL_MULTIPLIER == false then
return tries
end
if skill == SKILL_MAGLEVEL then
return tries * configManager.getNumber(configKeys.RATE_MAGIC)
end
return tries * configManager.getNumber(configKeys.RATE_SKILL)
end

View File

@ -40,4 +40,8 @@ table.contains = function(array, value)
end
end
return false
end
function isNumber(str)
return tonumber(str) ~= nil
end

View File

@ -9,3 +9,4 @@ dofile('data/lib/core/player.lua')
dofile('data/lib/core/position.lua')
dofile('data/lib/core/teleport.lua')
dofile('data/lib/core/tile.lua')
dofile('data/lib/core/vocation.lua')

View File

@ -9,7 +9,7 @@ function Game.removeItemsOnMap(position)
local i = 0
while i < tileCount do
local tileItem = tile:getThing(i)
if tileItem and not tileItem:isCreature() and ItemType(tileItem:getId()):isMovable() then
if tileItem and tileItem:getType():isMovable() then
tileItem:remove()
else
i = i + 1
@ -24,10 +24,8 @@ function Game.transformItemOnMap(position, itemId, toItemId, subtype)
local tile = Tile(position)
local item = tile:getItemById(itemId)
if item ~= nil then
item:transform(toItemId, subtype)
item:decay()
end
item:transform(toItemId, subtype)
item:decay()
return item
end

View File

@ -21,3 +21,45 @@ end
function Tile.isTile(self)
return true
end
function Tile.relocateTo(self, toPosition, pushMove, monsterPosition)
if self:getPosition() == toPosition then
return false
end
if not Tile(toPosition) then
return false
end
for i = self:getThingCount() - 1, 0, -1 do
local thing = self:getThing(i)
if thing then
if thing:isItem() then
if ItemType(thing.itemid):isMovable() then
thing:moveTo(toPosition)
end
elseif thing:isCreature() then
if monsterPosition and thing:isMonster() then
thing:teleportTo(monsterPosition, pushMove)
else
thing:teleportTo(toPosition, pushMove)
end
end
end
end
return true
end
function Tile:getPlayers()
local players = {}
local creatures = self:getCreatures()
if (creatures) then
for i = 1, #creatures do
if (creatures[i]:isPlayer()) then
table.insert(players, creatures[i])
end
end
end
return players
end

View File

@ -0,0 +1,7 @@
function Vocation.getBase(self)
local base = self
while base:getDemotion() do
base = base:getDemotion()
end
return base
end

View File

@ -81,8 +81,7 @@ function onStepIn(creature, item, position, fromPosition)
return false
end
doRelocate(position, relPos)
Tile(item:getPosition()):relocateTo(relPos)
if item:getId() == 293 then
item:transform(294)
item:decay()

View File

@ -0,0 +1,65 @@
local reloadTypes = {
["all"] = { targetType = RELOAD_TYPE_ALL, name = "all" },
["action"] = { targetType = RELOAD_TYPE_ACTIONS, name = "actions" },
["actions"] = { targetType = RELOAD_TYPE_ACTIONS, name = "actions" },
["chat"] = { targetType = RELOAD_TYPE_CHAT, name = "chatchannels" },
["channel"] = { targetType = RELOAD_TYPE_CHAT, name = "chatchannels" },
["chatchannels"] = { targetType = RELOAD_TYPE_CHAT, name = "chatchannels" },
["config"] = { targetType = RELOAD_TYPE_CONFIG, name = "config" },
["configuration"] = { targetType = RELOAD_TYPE_CONFIG, name = "config" },
["creaturescript"] = { targetType = RELOAD_TYPE_CREATURESCRIPTS, name = "creature scripts" },
["creaturescripts"] = { targetType = RELOAD_TYPE_CREATURESCRIPTS, name = "creature scripts" },
["events"] = { targetType = RELOAD_TYPE_EVENTS, name = "events" },
["global"] = { targetType = RELOAD_TYPE_GLOBAL, name = "global.lua" },
["globalevent"] = { targetType = RELOAD_TYPE_GLOBALEVENTS, name = "globalevents" },
["globalevents"] = { targetType = RELOAD_TYPE_GLOBALEVENTS, name = "globalevents" },
["items"] = { targetType = RELOAD_TYPE_ITEMS, name = "items" },
["monster"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" },
["monsters"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" },
["move"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
["movement"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
["movements"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
["npc"] = { targetType = RELOAD_TYPE_NPCS, name = "npcs" },
["npcs"] = { targetType = RELOAD_TYPE_NPCS, name = "npcs" },
["raid"] = { targetType = RELOAD_TYPE_RAIDS, name = "raids" },
["raids"] = { targetType = RELOAD_TYPE_RAIDS, name = "raids" },
["spell"] = { targetType = RELOAD_TYPE_SPELLS, name = "spells" },
["spells"] = { targetType = RELOAD_TYPE_SPELLS, name = "spells" },
["talk"] = { targetType = RELOAD_TYPE_TALKACTIONS, name = "talk actions" },
["talkaction"] = { targetType = RELOAD_TYPE_TALKACTIONS, name = "talk actions" },
["talkactions"] = { targetType = RELOAD_TYPE_TALKACTIONS, name = "talk actions" }
}
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GAMEMASTER then
return false
end
local reloadType = reloadTypes[param and param:lower()]
if not reloadType then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Reload type not found.")
return false
end
Game.reload(reloadType.targetType)
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Reloaded %s.", reloadType.name))
return true
end

View File

@ -0,0 +1,23 @@
function onSay(player, words, param)
local function timeSave(delay, msg)
broadcastMessage(msg, MESSAGE_STATUS_WARNING)
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, msg)
addEvent(saveServer, delay)
end
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
if isNumber(param) then
local delay = tonumber(param) * 1000
timeSave(delay, "Saving server in " .. tonumber(param) .. " seconds.")
else
saveServer()
end
end

View File

@ -0,0 +1,19 @@
function onSay(player, words, param)
local tradePartner = Player(param)
if not tradePartner or tradePartner == player then
player:sendCancelMessage("Trade player not found.")
return false
end
local house = player:getTile():getHouse()
if not house then
player:sendCancelMessage("You must stand in your house to initiate the trade.")
return false
end
local returnValue = house:startTrade(player, tradePartner)
if returnValue ~= RETURNVALUE_NOERROR then
player:sendCancelMessage(returnValue)
end
return false
end

View File

@ -27,7 +27,9 @@
<talkaction words="/removetutor" separator=" " script="remove_tutor.lua" />
<talkaction words="/looktype" separator=" " script="looktype.lua" />
<talkaction words="/summon" separator=" " script="place_summon.lua" />
<talkaction words="/chameleon" separator=" " script="chameleon.lua" />
<talkaction words="/reload" separator=" " script="reload.lua" />
<talkaction words="/save" script="save.lua" />
<talkaction words="/chameleon" separator=" " script="chameleon.lua" />
<talkaction words="/addskill" separator=" " script="add_skill.lua" />
<talkaction words="/mccheck" script="mccheck.lua" />
<talkaction words="/ghost" script="ghost.lua" />
@ -37,6 +39,7 @@
<!-- player talkactions -->
<talkaction words="!buypremium" script="buyprem.lua"/>
<talkaction words="!buyhouse" script="buyhouse.lua"/>
<talkaction words="!sellhouse" separator=" " script="sellhouse.lua" />
<talkaction words="!leavehouse" script="leavehouse.lua"/>
<talkaction words="!changesex" script="changesex.lua"/>
<talkaction words="!uptime" script="uptime.lua"/>

View File

@ -7,7 +7,6 @@ set(tfs_SRC
${CMAKE_CURRENT_LIST_DIR}/behaviourdatabase.cpp
${CMAKE_CURRENT_LIST_DIR}/chat.cpp
${CMAKE_CURRENT_LIST_DIR}/combat.cpp
${CMAKE_CURRENT_LIST_DIR}/commands.cpp
${CMAKE_CURRENT_LIST_DIR}/condition.cpp
${CMAKE_CURRENT_LIST_DIR}/configmanager.cpp
${CMAKE_CURRENT_LIST_DIR}/connection.cpp
@ -19,6 +18,7 @@ set(tfs_SRC
${CMAKE_CURRENT_LIST_DIR}/databasemanager.cpp
${CMAKE_CURRENT_LIST_DIR}/databasetasks.cpp
${CMAKE_CURRENT_LIST_DIR}/depotlocker.cpp
${CMAKE_CURRENT_LIST_DIR}/events.cpp
${CMAKE_CURRENT_LIST_DIR}/fileloader.cpp
${CMAKE_CURRENT_LIST_DIR}/game.cpp
${CMAKE_CURRENT_LIST_DIR}/globalevent.cpp

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -820,10 +820,6 @@ void BehaviourDatabase::checkAction(const NpcBehaviourAction* action, Player* pl
}
} while (amount);
} else {
if (it.charges) {
data = it.charges;
}
for (int32_t i = 0; i < std::max<int32_t>(1, amount); i++) {
Item* item = Item::CreateItem(itemId, data);
if (!item) {
@ -937,7 +933,7 @@ void BehaviourDatabase::checkAction(const NpcBehaviourAction* action, Player* pl
}
case BEHAVIOUR_TYPE_EXPERIENCE: {
int32_t experience = evaluate(action->expression, player, message);
player->addExperience(experience, true, false);
player->addExperience(nullptr, experience, false);
break;
}
case BEHAVIOUR_TYPE_WITHDRAW: {

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,13 +24,16 @@
#include "game.h"
#include "configmanager.h"
#include "monster.h"
#include "events.h"
extern Game g_game;
extern ConfigManager g_config;
extern Events* g_events;
CombatDamage Combat::getCombatDamage(Creature* creature) const
{
CombatDamage damage;
damage.origin = params.origin;
damage.type = params.combatType;
if (formulaType == COMBAT_FORMULA_DAMAGE) {
damage.min = static_cast<int32_t>(mina);
@ -175,11 +178,7 @@ ReturnValue Combat::canDoCombat(Creature* caster, Tile* tile, bool aggressive)
return RETURNVALUE_NOTENOUGHROOM;
}
/*if (tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID) && tile->hasProperty(CONST_PROP_UNLAY)) {
return RETURNVALUE_NOTENOUGHROOM;
}*/
if (tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKPATH) && tile->hasProperty(CONST_PROP_IMMOVABLENOFIELDBLOCKPATH)) {
if (tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID) && tile->hasProperty(CONST_PROP_UNLAY)) {
return RETURNVALUE_NOTENOUGHROOM;
}
@ -192,7 +191,8 @@ ReturnValue Combat::canDoCombat(Creature* caster, Tile* tile, bool aggressive)
const Position& tilePosition = tile->getPosition();
if (casterPosition.z < tilePosition.z) {
return RETURNVALUE_FIRSTGODOWNSTAIRS;
} else if (casterPosition.z > tilePosition.z) {
}
else if (casterPosition.z > tilePosition.z) {
return RETURNVALUE_FIRSTGOUPSTAIRS;
}
@ -208,7 +208,7 @@ ReturnValue Combat::canDoCombat(Creature* caster, Tile* tile, bool aggressive)
return RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE;
}
return RETURNVALUE_NOERROR;
return g_events->eventCreatureOnAreaCombat(caster, tile, aggressive);
}
bool Combat::isInPvpZone(const Creature* attacker, const Creature* target)
@ -232,74 +232,78 @@ bool Combat::isProtected(const Player* attacker, const Player* target)
ReturnValue Combat::canDoCombat(Creature* attacker, Creature* target)
{
if (attacker) {
if (const Player* targetPlayer = target->getPlayer()) {
if (targetPlayer->hasFlag(PlayerFlag_CannotBeAttacked)) {
if (!attacker) {
return g_events->eventCreatureOnTargetCombat(attacker, target);
}
if (const Player* targetPlayer = target->getPlayer()) {
if (targetPlayer->hasFlag(PlayerFlag_CannotBeAttacked)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
if (const Player* attackerPlayer = attacker->getPlayer()) {
if (attackerPlayer->hasFlag(PlayerFlag_CannotAttackPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
if (const Player* attackerPlayer = attacker->getPlayer()) {
if (attackerPlayer->hasFlag(PlayerFlag_CannotAttackPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
if (isProtected(attackerPlayer, targetPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
//nopvp-zone
const Tile* targetPlayerTile = targetPlayer->getTile();
if (targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE)) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
} else if (attackerPlayer->getTile()->hasFlag(TILESTATE_NOPVPZONE) && !targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE | TILESTATE_PROTECTIONZONE)) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
}
if (isProtected(attackerPlayer, targetPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
if (attacker->isSummon()) {
if (const Player* masterAttackerPlayer = attacker->getMaster()->getPlayer()) {
if (masterAttackerPlayer->hasFlag(PlayerFlag_CannotAttackPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
if (targetPlayer->getTile()->hasFlag(TILESTATE_NOPVPZONE)) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
}
if (isProtected(masterAttackerPlayer, targetPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
}
//nopvp-zone
const Tile* targetPlayerTile = targetPlayer->getTile();
if (targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE)) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
}
} else if (target->getMonster()) {
if (const Player* attackerPlayer = attacker->getPlayer()) {
if (attackerPlayer->hasFlag(PlayerFlag_CannotAttackMonster)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE;
}
if (target->isSummon() && target->getMaster()->getPlayer() && target->getZone() == ZONE_NOPVP) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
}
else if (attackerPlayer->getTile()->hasFlag(TILESTATE_NOPVPZONE) && !targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE | TILESTATE_PROTECTIONZONE)) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
}
}
if (g_game.getWorldType() == WORLD_TYPE_NO_PVP) {
if (attacker->getPlayer() || (attacker->isSummon() && attacker->getMaster()->getPlayer())) {
if (target->getPlayer()) {
if (!isInPvpZone(attacker, target)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
if (attacker->isSummon()) {
if (const Player* masterAttackerPlayer = attacker->getMaster()->getPlayer()) {
if (masterAttackerPlayer->hasFlag(PlayerFlag_CannotAttackPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
if (target->isSummon() && target->getMaster()->getPlayer()) {
if (!isInPvpZone(attacker, target)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE;
}
if (targetPlayer->getTile()->hasFlag(TILESTATE_NOPVPZONE)) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
}
if (isProtected(masterAttackerPlayer, targetPlayer)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
}
}
}
return RETURNVALUE_NOERROR;
else if (target->getMonster()) {
if (const Player* attackerPlayer = attacker->getPlayer()) {
if (attackerPlayer->hasFlag(PlayerFlag_CannotAttackMonster)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE;
}
if (target->isSummon() && target->getMaster()->getPlayer() && target->getZone() == ZONE_NOPVP) {
return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE;
}
}
}
if (g_game.getWorldType() == WORLD_TYPE_NO_PVP) {
if (attacker->getPlayer() || (attacker->isSummon() && attacker->getMaster()->getPlayer())) {
if (target->getPlayer()) {
if (!isInPvpZone(attacker, target)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER;
}
}
if (target->isSummon() && target->getMaster()->getPlayer()) {
if (!isInPvpZone(attacker, target)) {
return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE;
}
}
}
}
return g_events->eventCreatureOnTargetCombat(attacker, target);
}
void Combat::setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb)
@ -422,7 +426,7 @@ CallBack* Combat::getCallback(CallBackParam_t key)
return nullptr;
}
bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data)
void Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data)
{
assert(data);
CombatDamage damage = *data;
@ -439,18 +443,16 @@ bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatPa
}
if (g_game.combatBlockHit(damage, caster, target, params.blockedByShield, params.blockedByArmor, params.itemId != 0)) {
return false;
return;
}
if (g_game.combatChangeHealth(caster, target, damage)) {
CombatConditionFunc(caster, target, params, nullptr);
CombatConditionFunc(caster, target, params, &damage);
CombatDispelFunc(caster, target, params, nullptr);
}
return true;
}
bool Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data)
void Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data)
{
assert(data);
CombatDamage damage = *data;
@ -465,16 +467,18 @@ bool Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatPara
}
}
if (g_game.combatChangeMana(caster, target, damage.value)) {
if (g_game.combatChangeMana(caster, target, damage)) {
CombatConditionFunc(caster, target, params, nullptr);
CombatDispelFunc(caster, target, params, nullptr);
}
return true;
}
bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage*)
void Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data)
{
if (params.origin == ORIGIN_MELEE && data && data->value == 0) {
return;
}
for (const auto& condition : params.conditionList) {
if (caster == target || !target->isImmune(condition->getType())) {
Condition* conditionCopy = condition->clone();
@ -486,21 +490,17 @@ bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const Comba
target->addCombatCondition(conditionCopy);
}
}
return true;
}
bool Combat::CombatDispelFunc(Creature*, Creature* target, const CombatParams& params, CombatDamage*)
void Combat::CombatDispelFunc(Creature*, Creature* target, const CombatParams& params, CombatDamage*)
{
target->removeCombatCondition(params.dispelType);
return true;
}
bool Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage*)
void Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage*)
{
CombatConditionFunc(caster, target, params, nullptr);
CombatDispelFunc(caster, target, params, nullptr);
return true;
}
void Combat::combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params)
@ -632,6 +632,8 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat*
const int32_t rangeY = maxY + Map::maxViewportY;
g_game.map.getSpectators(list, pos, true, true, rangeX, rangeX, rangeY, rangeY);
postCombatEffects(caster, pos, params);
uint16_t decreasedDamage = 0;
const uint16_t maximumDecreasedDamage = params.maximumDecreasedDamage;
@ -721,6 +723,8 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat*
continue;
}
combatTileEffects(list, caster, tile, params);
if (CreatureVector* creatures = tile->getCreatures()) {
const Creature* topCreature = tile->getTopCreature();
for (Creature* creature : *creatures) {
@ -746,9 +750,7 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat*
}
}
}
combatTileEffects(list, caster, tile, params);
}
postCombatEffects(caster, pos, params);
}
void Combat::doCombat(Creature* caster, Creature* target) const
@ -834,7 +836,7 @@ bool Combat::attack(Creature* attacker, Creature* target)
return false;
}
bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode, bool fist)
bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode)
{
const Position& attackerPos = attacker->getPosition();
const Position& targetPos = target->getPosition();
@ -859,7 +861,7 @@ bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fight
uint32_t skillValue = 0;
uint8_t skill = SKILL_FIST;
Combat::getAttackValue(attacker, attackValue, skillValue, skill, fist);
Combat::getAttackValue(attacker, attackValue, skillValue, skill);
int32_t defense = target->getDefense();
@ -876,6 +878,7 @@ bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fight
combatDamage.type = combatParams.combatType;
int32_t totalDamage = Combat::getTotalDamage(skillValue, attackValue, fightMode);
combatDamage.value = totalDamage;
combatDamage.origin = ORIGIN_MELEE;
bool hit = Combat::doCombatHealth(attacker, target, combatDamage, combatParams);
@ -884,7 +887,7 @@ bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fight
if (poison) {
int32_t randTest = rand();
if (hit || -totalDamage > defense && (randTest == 5 * (randTest / 5))) {
if (hit || ((-totalDamage > defense) && (randTest == 5 * (randTest / 5)))) {
poison = normal_random(poison / 2, poison);
if (poison) {
ConditionDamage* condition = static_cast<ConditionDamage*>(Condition::createCondition(CONDITIONID_COMBAT, CONDITION_POISON, 0, 0));
@ -962,9 +965,9 @@ bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fight
if (weapon->getWeaponType() == WEAPON_DISTANCE) {
ammunition = player->getAmmunition();
if (weapon->getAmmoType() != AMMO_NONE) {
if (!ammunition || ammunition->getWeaponType() != WEAPON_AMMO || weapon->getAmmoType() != ammunition->getAmmoType()) {
if (!ammunition || weapon->getAmmoType() != ammunition->getAmmoType()) {
// redirect to fist fighting
return closeAttack(attacker, target, fightMode, true);
return closeAttack(attacker, target, fightMode);
}
distanceEffect = ammunition->getMissileType();
@ -991,6 +994,7 @@ bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fight
CombatDamage combatDamage;
combatDamage.type = combatParams.combatType;
combatDamage.value = Combat::getTotalDamage(skillValue, attackValue, fightMode);
combatDamage.origin = ORIGIN_RANGED;
if (weapon) {
hitChance = 75; // throwables and such
@ -1011,7 +1015,7 @@ bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fight
}
}
if (ammunition && ammunition->getWeaponType() == WEAPON_AMMO && weapon->getAmmoType() != AMMO_NONE && weapon->getAmmoType() == ammunition->getAmmoType()) {
if (ammunition && weapon->getAmmoType() != AMMO_NONE && weapon->getAmmoType() == ammunition->getAmmoType()) {
hitChance = 90; // bows and crossbows
specialEffect = ammunition->getWeaponSpecialEffect();
attackStrength = ammunition->getAttackStrength();
@ -1037,10 +1041,12 @@ bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fight
bool hit = false;
if (rand() % distance <= skillValue) {
hit = rand() % 100 <= hitChance;
int32_t random = rand();
if (random % distance <= static_cast<int32_t>(skillValue)) {
hit = random % 100 <= hitChance;
}
if (Player* player = attacker->getPlayer()) {
if (player->getAddAttackSkill()) {
switch (player->getLastAttackBlockType()) {
@ -1130,6 +1136,7 @@ bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fight
CombatDamage combatDamage;
combatDamage.type = combatParams.combatType;
combatDamage.value = -(variation + weapon->getAttackStrength());
combatDamage.origin = ORIGIN_RANGED;
g_game.addDistanceEffect(attackerPos, targetPos, distanceEffect);
Combat::doCombatHealth(attacker, target, combatDamage, combatParams);
@ -1182,13 +1189,13 @@ void Combat::circleShapeSpell(Creature* attacker, const Position& toPos, int32_t
}
}
void Combat::getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill, bool fist)
void Combat::getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill)
{
skill = SKILL_FIST;
if (Player* player = creature->getPlayer()) {
Item* weapon = player->getWeapon();
if (weapon && !fist) {
if (weapon) {
switch (weapon->getWeaponType()) {
case WEAPON_AXE: {
skill = SKILL_AXE;
@ -1243,7 +1250,7 @@ bool Combat::canUseWeapon(Player* player, Item* weapon)
return false;
}
if (!player->hasFlag(PlayerFlag_HasInfiniteMana) && player->getMana() < weapon->getManaConsumption()) {
if (!player->hasFlag(PlayerFlag_HasInfiniteMana) && static_cast<int32_t>(player->getMana()) < weapon->getManaConsumption()) {
return false;
}
@ -1278,14 +1285,14 @@ bool Combat::doCombatHealth(Creature* caster, Creature* target, CombatDamage& da
}
if (canCombat) {
canCombat = CombatHealthFunc(caster, target, params, &damage);
if (params.targetCallback) {
params.targetCallback->onTargetCombat(caster, target);
}
if (caster && params.distanceEffect != CONST_ANI_NONE) {
addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect);
}
CombatHealthFunc(caster, target, params, &damage);
if (params.targetCallback) {
params.targetCallback->onTargetCombat(caster, target);
}
}
return canCombat;
@ -1304,14 +1311,14 @@ void Combat::doCombatMana(Creature* caster, Creature* target, CombatDamage& dama
}
if (canCombat) {
if (caster && params.distanceEffect != CONST_ANI_NONE) {
addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect);
}
CombatManaFunc(caster, target, params, &damage);
if (params.targetCallback) {
params.targetCallback->onTargetCombat(caster, target);
}
if (caster && params.distanceEffect != CONST_ANI_NONE) {
addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect);
}
}
}
@ -1333,14 +1340,14 @@ void Combat::doCombatCondition(Creature* caster, Creature* target, const CombatP
}
if (canCombat) {
if (caster && params.distanceEffect != CONST_ANI_NONE) {
addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect);
}
CombatConditionFunc(caster, target, params, nullptr);
if (params.targetCallback) {
params.targetCallback->onTargetCombat(caster, target);
}
if (caster && params.distanceEffect != CONST_ANI_NONE) {
addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect);
}
}
}

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -73,6 +73,7 @@ struct CombatParams {
ConditionType_t dispelType = CONDITION_NONE;
CombatType_t combatType = COMBAT_NONE;
CombatOrigin origin = ORIGIN_SPELL;
uint8_t impactEffect = CONST_ME_NONE;
uint8_t distanceEffect = CONST_ANI_NONE;
@ -116,7 +117,7 @@ struct DunkenImpact : Impact
void handleCreature(Creature* target) final;
};
typedef bool (*COMBATFUNC)(Creature*, Creature*, const CombatParams&, CombatDamage*);
typedef void(*COMBATFUNC)(Creature*, Creature*, const CombatParams&, CombatDamage*);
class MatrixArea
{
@ -285,12 +286,12 @@ class Combat
static int32_t getTotalDamage(int32_t attackSkill, int32_t attackValue, fightMode_t fightMode);
static bool attack(Creature* attacker, Creature* target);
static bool closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode, bool fist = false);
static bool closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode);
static bool rangeAttack(Creature* attacker, Creature* target, fightMode_t fightMode);
static void circleShapeSpell(Creature* attacker, const Position& toPos, int32_t range, int32_t animation, int32_t radius, DamageImpact* impact, int32_t effect);
static void getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill, bool fist = false);
static void getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill);
static bool doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params);
static void doCombatHealth(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params);
@ -339,6 +340,10 @@ class Combat
postCombatEffects(caster, pos, params);
}
void setOrigin(CombatOrigin origin) {
params.origin = origin;
}
protected:
static bool canUseWeapon(Player* player, Item* weapon);
static void postWeaponEffects(Player* player, Item* weapon);
@ -347,11 +352,11 @@ class Combat
static void CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, COMBATFUNC func, CombatDamage* data);
static bool CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
static bool CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage);
static bool CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
static bool CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
static bool CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
static void CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
static void CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage);
static void CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
static void CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
static void CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
static void combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params);
CombatDamage getCombatDamage(Creature* creature) const;

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -227,7 +227,7 @@ bool Condition::isPersistent() const
return false;
}
if (!(id == CONDITIONID_DEFAULT || id == CONDITIONID_COMBAT)) {
if (!(id == CONDITIONID_DEFAULT || id == CONDITIONID_COMBAT || conditionType == CONDITION_MUTED)) {
return false;
}
@ -655,7 +655,9 @@ bool ConditionRegeneration::executeCondition(Creature* creature, int32_t interva
if (internalManaTicks >= manaTicks) {
internalManaTicks = 0;
creature->changeMana(manaGain);
if (Player* player = creature->getPlayer()) {
player->changeMana(manaGain);
}
}
}
@ -756,8 +758,6 @@ bool ConditionSoul::setParam(ConditionParam_t param, int32_t value)
bool ConditionDamage::setParam(ConditionParam_t param, int32_t value)
{
Condition::setParam(param, value);
switch (param) {
case CONDITION_PARAM_OWNER:
owner = value;
@ -927,6 +927,7 @@ bool ConditionDamage::doDamage(Creature* creature, int32_t healthChange)
}
CombatDamage damage;
damage.origin = ORIGIN_CONDITION;
damage.value = healthChange;
damage.type = Combat::ConditionToDamageType(conditionType);

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -77,7 +77,9 @@ bool ConfigManager::load()
boolean[CONVERT_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "convertUnsafeScripts", true);
boolean[TELEPORT_NEWBIES] = getGlobalBoolean(L, "teleportNewbies", true);
boolean[STACK_CUMULATIVES] = getGlobalBoolean(L, "autoStackCumulatives", false);
boolean[QUERY_PLAYER_CONTAINERS] = getGlobalBoolean(L, "queryPlayerContainers", false);
boolean[BLOCK_HEIGHT] = getGlobalBoolean(L, "blockHeight", false);
boolean[DROP_ITEMS] = getGlobalBoolean(L, "dropItems", false);
string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high");
string[SERVER_NAME] = getGlobalString(L, "serverName", "");

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -40,7 +40,8 @@ class ConfigManager
CONVERT_UNSAFE_SCRIPTS,
TELEPORT_NEWBIES,
STACK_CUMULATIVES,
QUERY_PLAYER_CONTAINERS,
BLOCK_HEIGHT,
DROP_ITEMS,
LAST_BOOLEAN_CONFIG /* this must be the last one */
};

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -308,6 +308,28 @@ enum PlayerFlags : uint64_t {
PlayerFlag_SpecialMoveUse = static_cast<uint64_t>(1) << 38,
};
enum ReloadTypes_t : uint8_t {
RELOAD_TYPE_ALL,
RELOAD_TYPE_ACTIONS,
RELOAD_TYPE_CHAT,
RELOAD_TYPE_COMMANDS,
RELOAD_TYPE_CONFIG,
RELOAD_TYPE_CREATURESCRIPTS,
RELOAD_TYPE_EVENTS,
RELOAD_TYPE_GLOBAL,
RELOAD_TYPE_GLOBALEVENTS,
RELOAD_TYPE_ITEMS,
RELOAD_TYPE_MONSTERS,
RELOAD_TYPE_MOUNTS,
RELOAD_TYPE_MOVEMENTS,
RELOAD_TYPE_NPCS,
RELOAD_TYPE_QUESTS,
RELOAD_TYPE_RAIDS,
RELOAD_TYPE_SPELLS,
RELOAD_TYPE_TALKACTIONS,
RELOAD_TYPE_WEAPONS,
};
static constexpr int32_t CHANNEL_GUILD = 0x00;
static constexpr int32_t CHANNEL_PARTY = 0x01;
static constexpr int32_t CHANNEL_RULE_REP = 0x02;

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -370,7 +370,7 @@ ReturnValue Container::queryRemove(const Thing& thing, uint32_t count, uint32_t
}
Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item** destItem,
uint32_t& flags)
uint32_t& flags)
{
if (index == 254 /*move up*/) {
index = INDEX_WHEREEVER;
@ -386,7 +386,8 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item**
if (index == 255 /*add wherever*/) {
index = INDEX_WHEREEVER;
*destItem = nullptr;
} else if (index >= static_cast<int32_t>(capacity())) {
}
else if (index >= static_cast<int32_t>(capacity())) {
/*
if you have a container, maximize it to show all 20 slots
then you open a bag that is inside the container you will have a bag with 8 slots
@ -403,6 +404,20 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item**
return this;
}
if (index != INDEX_WHEREEVER) {
Item* itemFromIndex = getItemByIndex(index);
if (itemFromIndex) {
*destItem = itemFromIndex;
}
Cylinder* subCylinder = dynamic_cast<Cylinder*>(*destItem);
if (subCylinder) {
index = INDEX_WHEREEVER;
*destItem = nullptr;
return subCylinder;
}
}
if (g_config.getBoolean(ConfigManager::STACK_CUMULATIVES)) {
bool autoStack = !hasBitSet(FLAG_IGNOREAUTOSTACK, flags);
if (autoStack && item->isStackable() && item->getParent() != this) {
@ -419,19 +434,6 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item**
}
}
if (index != INDEX_WHEREEVER) {
Item* itemFromIndex = getItemByIndex(index);
if (itemFromIndex) {
*destItem = itemFromIndex;
}
Cylinder* subCylinder = dynamic_cast<Cylinder*>(*destItem);
if (subCylinder) {
index = INDEX_WHEREEVER;
*destItem = nullptr;
return subCylinder;
}
}
return this;
}

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -619,7 +619,8 @@ void Creature::onDeath()
if (lastHitCreature) {
lastHitUnjustified = lastHitCreature->onKilledCreature(this);
lastHitCreatureMaster = lastHitCreature->getMaster();
} else {
}
else {
lastHitCreatureMaster = nullptr;
}
@ -639,8 +640,9 @@ void Creature::onDeath()
if (attacker != this) {
uint64_t gainExp = getGainedExperience(attacker);
if (Player* player = attacker->getPlayer()) {
Party* party = player->getParty();
if (Player* attackerPlayer = attacker->getPlayer()) {
attackerPlayer->removeAttacked(getPlayer());
Party* party = attackerPlayer->getParty();
if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
attacker = party->getLeader();
}
@ -649,7 +651,8 @@ void Creature::onDeath()
auto tmpIt = experienceMap.find(attacker);
if (tmpIt == experienceMap.end()) {
experienceMap[attacker] = gainExp;
} else {
}
else {
tmpIt->second += gainExp;
}
}
@ -752,15 +755,6 @@ void Creature::changeHealth(int32_t healthChange, bool sendHealthChange/* = true
}
}
void Creature::changeMana(int32_t manaChange)
{
if (manaChange > 0) {
mana += std::min<int32_t>(manaChange, getMaxMana() - mana);
} else {
mana = std::max<int32_t>(0, mana + manaChange);
}
}
void Creature::gainHealth(Creature* healer, int32_t healthGain)
{
changeHealth(healthGain);
@ -778,16 +772,6 @@ void Creature::drainHealth(Creature* attacker, int32_t damage)
}
}
void Creature::drainMana(Creature* attacker, int32_t manaLoss)
{
onAttacked();
changeMana(-manaLoss);
if (attacker) {
addDamagePoints(attacker, manaLoss);
}
}
BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage,
bool checkDefense /* = false */, bool checkArmor /* = false */, bool /* field = false */)
{

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -220,12 +220,6 @@ class Creature : virtual public Thing
virtual int32_t getMaxHealth() const {
return healthMax;
}
uint32_t getMana() const {
return mana;
}
virtual uint32_t getMaxMana() const {
return 0;
}
const Outfit_t getCurrentOutfit() const {
return currentOutfit;
@ -320,11 +314,9 @@ class Creature : virtual public Thing
}
virtual void changeHealth(int32_t healthChange, bool sendHealthChange = true);
virtual void changeMana(int32_t manaChange);
void gainHealth(Creature* attacker, int32_t healthGain);
virtual void drainHealth(Creature* attacker, int32_t damage);
virtual void drainMana(Creature* attacker, int32_t manaLoss);
virtual bool challengeCreature(Creature*) {
return false;
@ -494,7 +486,6 @@ class Creature : virtual public Thing
uint32_t blockTicks = 0;
uint32_t lastStepCost = 1;
uint32_t baseSpeed = 70;
uint32_t mana = 0;
uint32_t latestKillEvent = 0;
int32_t varSpeed = 0;
int32_t health = 1000;

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -177,6 +177,10 @@ bool CreatureEvent::configureEvent(const pugi::xml_node& node)
type = CREATURE_EVENT_KILL;
} else if (tmpStr == "advance") {
type = CREATURE_EVENT_ADVANCE;
} else if (tmpStr == "healthchange") {
type = CREATURE_EVENT_HEALTHCHANGE;
} else if (tmpStr == "manachange") {
type = CREATURE_EVENT_MANACHANGE;
} else if (tmpStr == "extendedopcode") {
type = CREATURE_EVENT_EXTENDED_OPCODE;
} else {
@ -213,6 +217,12 @@ std::string CreatureEvent::getScriptEventName() const
case CREATURE_EVENT_ADVANCE:
return "onAdvance";
case CREATURE_EVENT_HEALTHCHANGE:
return "onHealthChange";
case CREATURE_EVENT_MANACHANGE:
return "onManaChange";
case CREATURE_EVENT_EXTENDED_OPCODE:
return "onExtendedOpcode";
@ -409,6 +419,85 @@ void CreatureEvent::executeOnKill(Creature* creature, Creature* target)
scriptInterface->callVoidFunction(2);
}
void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage)
{
//onHealthChange(creature, attacker, value, type, min, max, origin)
if (!scriptInterface->reserveScriptEnv()) {
std::cout << "[Error - CreatureEvent::executeHealthChange] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface->getScriptEnv();
env->setScriptId(scriptId, scriptInterface);
lua_State* L = scriptInterface->getLuaState();
scriptInterface->pushFunction(scriptId);
LuaScriptInterface::pushUserdata(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
if (attacker) {
LuaScriptInterface::pushUserdata(L, attacker);
LuaScriptInterface::setCreatureMetatable(L, -1, attacker);
}
else {
lua_pushnil(L);
}
LuaScriptInterface::pushCombatDamage(L, damage);
if (scriptInterface->protectedCall(L, 7, 4) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
}
else {
damage.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -4));
damage.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
damage.min = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2));
damage.max = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
lua_pop(L, 4);
if (damage.type != COMBAT_HEALING) {
damage.value = -damage.value;
}
}
scriptInterface->resetScriptEnv();
}
void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage) {
//onManaChange(creature, attacker, value, type, min, max, origin)
if (!scriptInterface->reserveScriptEnv()) {
std::cout << "[Error - CreatureEvent::executeManaChange] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface->getScriptEnv();
env->setScriptId(scriptId, scriptInterface);
lua_State* L = scriptInterface->getLuaState();
scriptInterface->pushFunction(scriptId);
LuaScriptInterface::pushUserdata(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
if (attacker) {
LuaScriptInterface::pushUserdata(L, attacker);
LuaScriptInterface::setCreatureMetatable(L, -1, attacker);
}
else {
lua_pushnil(L);
}
LuaScriptInterface::pushCombatDamage(L, damage);
if (scriptInterface->protectedCall(L, 7, 4) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
}
else {
damage = LuaScriptInterface::getCombatDamage(L);
}
scriptInterface->resetScriptEnv();
}
void CreatureEvent::executeExtendedOpcode(Player* player, uint8_t opcode, const std::string& buffer)
{
//onExtendedOpcode(player, opcode, buffer)

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -33,6 +33,8 @@ enum CreatureEventType_t {
CREATURE_EVENT_DEATH,
CREATURE_EVENT_KILL,
CREATURE_EVENT_ADVANCE,
CREATURE_EVENT_HEALTHCHANGE,
CREATURE_EVENT_MANACHANGE,
CREATURE_EVENT_EXTENDED_OPCODE, // otclient additional network opcodes
};
@ -97,6 +99,8 @@ class CreatureEvent final : public Event
bool executeOnDeath(Creature* creature, Item* corpse, Creature* killer, Creature* mostDamageKiller, bool lastHitUnjustified, bool mostDamageUnjustified);
void executeOnKill(Creature* creature, Creature* target);
bool executeAdvance(Player* player, skills_t, uint32_t, uint32_t);
void executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage);
void executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage);
void executeExtendedOpcode(Player* player, uint8_t opcode, const std::string& buffer);
//

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -20,9 +20,9 @@
#ifndef FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963
#define FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963
static constexpr auto STATUS_SERVER_NAME = "Nostalrius";
static constexpr auto STATUS_SERVER_VERSION = "3.0";
static constexpr auto STATUS_SERVER_DEVELOPERS = "Alejandro Mujica";
static constexpr auto STATUS_SERVER_NAME = "Sabrehaven";
static constexpr auto STATUS_SERVER_VERSION = "1.0";
static constexpr auto STATUS_SERVER_DEVELOPERS = "Sabrehaven Developers Team";
static constexpr auto CLIENT_VERSION_MIN = 772;
static constexpr auto CLIENT_VERSION_MAX = 772;
@ -57,7 +57,7 @@ static constexpr auto CLIENT_VERSION_STR = "7.72";
#pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
#pragma warning(disable:4351) // new behavior: elements of array will be default initialized
#pragma warning(disable:4458) // declaration hides class member
#pragma warning(disable:4018) // signed/unsigned mismatch
#pragma warning(disable:4996) // inetpton warning
#endif
#define strcasecmp _stricmp

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -345,6 +345,11 @@ enum ReturnValue {
RETURNVALUE_CANONLYUSEONESHIELD,
RETURNVALUE_NOPARTYMEMBERSINRANGE,
RETURNVALUE_YOUARENOTTHEOWNER,
RETURNVALUE_TRADEPLAYERFARAWAY,
RETURNVALUE_YOUDONTOWNTHISHOUSE,
RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE,
RETURNVALUE_TRADEPLAYERHIGHESTBIDDER,
RETURNVALUE_YOUCANNOTTRADETHISHOUSE,
};
struct Outfit_t {
@ -363,15 +368,26 @@ struct LightInfo {
constexpr LightInfo(uint8_t level, uint8_t color) : level(level), color(color) {}
};
enum CombatOrigin
{
ORIGIN_NONE,
ORIGIN_CONDITION,
ORIGIN_SPELL,
ORIGIN_MELEE,
ORIGIN_RANGED,
};
struct CombatDamage
{
CombatType_t type;
int32_t value;
int32_t min;
int32_t max;
CombatOrigin origin;
CombatDamage()
{
origin = ORIGIN_NONE;
type = COMBAT_NONE;
value = 0;
min = 0;

827
src/events.cpp Normal file
View File

@ -0,0 +1,827 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2016 Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "otpch.h"
#include "events.h"
#include "tools.h"
#include "item.h"
#include "player.h"
#include <set>
Events::Events() :
scriptInterface("Event Interface")
{
clear();
scriptInterface.initState();
}
void Events::clear()
{
// Creature
creatureOnChangeOutfit = -1;
creatureOnAreaCombat = -1;
creatureOnTargetCombat = -1;
// Party
partyOnJoin = -1;
partyOnLeave = -1;
partyOnDisband = -1;
// Player
playerOnLook = -1;
playerOnLookInBattleList = -1;
playerOnLookInTrade = -1;
playerOnMoveItem = -1;
playerOnItemMoved = -1;
playerOnMoveCreature = -1;
playerOnTurn = -1;
playerOnTradeRequest = -1;
playerOnTradeAccept = -1;
playerOnGainExperience = -1;
playerOnLoseExperience = -1;
playerOnGainSkillTries = -1;
playerOnReportBug = -1;
}
bool Events::load()
{
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file("data/events/events.xml");
if (!result) {
printXMLError("Error - Events::load", "data/events/events.xml", result);
return false;
}
std::set<std::string> classes;
for (auto eventNode : doc.child("events").children()) {
if (!eventNode.attribute("enabled").as_bool()) {
continue;
}
const std::string& className = eventNode.attribute("class").as_string();
auto res = classes.insert(className);
if (res.second) {
const std::string& lowercase = asLowerCaseString(className);
if (scriptInterface.loadFile("data/events/scripts/" + lowercase + ".lua") != 0) {
std::cout << "[Warning - Events::load] Can not load script: " << lowercase << ".lua" << std::endl;
std::cout << scriptInterface.getLastLuaError() << std::endl;
}
}
const std::string& methodName = eventNode.attribute("method").as_string();
const int32_t event = scriptInterface.getMetaEvent(className, methodName);
if (className == "Creature") {
if (methodName == "onChangeOutfit") {
creatureOnChangeOutfit = event;
}
else if (methodName == "onAreaCombat") {
creatureOnAreaCombat = event;
}
else if (methodName == "onTargetCombat") {
creatureOnTargetCombat = event;
}
else {
std::cout << "[Warning - Events::load] Unknown creature method: " << methodName << std::endl;
}
}
else if (className == "Party") {
if (methodName == "onJoin") {
partyOnJoin = event;
}
else if (methodName == "onLeave") {
partyOnLeave = event;
}
else if (methodName == "onDisband") {
partyOnDisband = event;
}
else if (methodName == "onShareExperience") {
partyOnShareExperience = event;
}
else {
std::cout << "[Warning - Events::load] Unknown party method: " << methodName << std::endl;
}
}
else if (className == "Player") {
if (methodName == "onLook") {
playerOnLook = event;
}
else if (methodName == "onLookInBattleList") {
playerOnLookInBattleList = event;
}
else if (methodName == "onLookInTrade") {
playerOnLookInTrade = event;
}
else if (methodName == "onTradeRequest") {
playerOnTradeRequest = event;
}
else if (methodName == "onTradeAccept") {
playerOnTradeAccept = event;
}
else if (methodName == "onMoveItem") {
playerOnMoveItem = event;
}
else if (methodName == "onItemMoved") {
playerOnItemMoved = event;
}
else if (methodName == "onMoveCreature") {
playerOnMoveCreature = event;
}
else if (methodName == "onReportBug") {
playerOnReportBug = event;
}
else if (methodName == "onTurn") {
playerOnTurn = event;
}
else if (methodName == "onGainExperience") {
playerOnGainExperience = event;
}
else if (methodName == "onLoseExperience") {
playerOnLoseExperience = event;
}
else if (methodName == "onGainSkillTries") {
playerOnGainSkillTries = event;
}
else {
std::cout << "[Warning - Events::load] Unknown player method: " << methodName << std::endl;
}
}
else {
std::cout << "[Warning - Events::load] Unknown class: " << className << std::endl;
}
}
return true;
}
// Creature
bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit)
{
// Creature:onChangeOutfit(outfit) or Creature.onChangeOutfit(self, outfit)
if (creatureOnChangeOutfit == -1) {
return true;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventCreatureOnChangeOutfit] Call stack overflow" << std::endl;
return false;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(creatureOnChangeOutfit, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(creatureOnChangeOutfit);
LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
LuaScriptInterface::pushOutfit(L, outfit);
return scriptInterface.callFunction(2);
}
ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive)
{
// Creature:onAreaCombat(tile, aggressive) or Creature.onAreaCombat(self, tile, aggressive)
if (creatureOnAreaCombat == -1) {
return RETURNVALUE_NOERROR;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventCreatureOnAreaCombat] Call stack overflow" << std::endl;
return RETURNVALUE_NOTPOSSIBLE;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(creatureOnAreaCombat, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(creatureOnAreaCombat);
if (creature) {
LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
}
else {
lua_pushnil(L);
}
LuaScriptInterface::pushUserdata<Tile>(L, tile);
LuaScriptInterface::setMetatable(L, -1, "Tile");
LuaScriptInterface::pushBoolean(L, aggressive);
ReturnValue returnValue;
if (scriptInterface.protectedCall(L, 3, 1) != 0) {
returnValue = RETURNVALUE_NOTPOSSIBLE;
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
}
else {
returnValue = LuaScriptInterface::getNumber<ReturnValue>(L, -1);
lua_pop(L, 1);
}
scriptInterface.resetScriptEnv();
return returnValue;
}
ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* target)
{
// Creature:onTargetCombat(target) or Creature.onTargetCombat(self, target)
if (creatureOnTargetCombat == -1) {
return RETURNVALUE_NOERROR;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventCreatureOnTargetCombat] Call stack overflow" << std::endl;
return RETURNVALUE_NOTPOSSIBLE;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(creatureOnTargetCombat, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(creatureOnTargetCombat);
if (creature) {
LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
}
else {
lua_pushnil(L);
}
LuaScriptInterface::pushUserdata<Creature>(L, target);
LuaScriptInterface::setCreatureMetatable(L, -1, target);
ReturnValue returnValue;
if (scriptInterface.protectedCall(L, 2, 1) != 0) {
returnValue = RETURNVALUE_NOTPOSSIBLE;
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
}
else {
returnValue = LuaScriptInterface::getNumber<ReturnValue>(L, -1);
lua_pop(L, 1);
}
scriptInterface.resetScriptEnv();
return returnValue;
}
// Party
bool Events::eventPartyOnJoin(Party* party, Player* player)
{
// Party:onJoin(player) or Party.onJoin(self, player)
if (partyOnJoin == -1) {
return true;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPartyOnJoin] Call stack overflow" << std::endl;
return false;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(partyOnJoin, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(partyOnJoin);
LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party");
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
return scriptInterface.callFunction(2);
}
bool Events::eventPartyOnLeave(Party* party, Player* player)
{
// Party:onLeave(player) or Party.onLeave(self, player)
if (partyOnLeave == -1) {
return true;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPartyOnLeave] Call stack overflow" << std::endl;
return false;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(partyOnLeave, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(partyOnLeave);
LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party");
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
return scriptInterface.callFunction(2);
}
bool Events::eventPartyOnDisband(Party* party)
{
// Party:onDisband() or Party.onDisband(self)
if (partyOnDisband == -1) {
return true;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPartyOnDisband] Call stack overflow" << std::endl;
return false;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(partyOnDisband, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(partyOnDisband);
LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party");
return scriptInterface.callFunction(1);
}
void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp)
{
// Party:onShareExperience(exp) or Party.onShareExperience(self, exp)
if (partyOnShareExperience == -1) {
return;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPartyOnShareExperience] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(partyOnShareExperience, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(partyOnShareExperience);
LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party");
lua_pushnumber(L, exp);
if (scriptInterface.protectedCall(L, 2, 1) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
}
else {
exp = LuaScriptInterface::getNumber<uint64_t>(L, -1);
lua_pop(L, 1);
}
scriptInterface.resetScriptEnv();
}
void Events::eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance)
{
// Player:onLook(thing, position, distance) or Player.onLook(self, thing, position, distance)
if (playerOnLook == -1) {
return;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnLook] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnLook, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnLook);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
if (Creature* creature = thing->getCreature()) {
LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
}
else if (Item* item = thing->getItem()) {
LuaScriptInterface::pushUserdata<Item>(L, item);
LuaScriptInterface::setItemMetatable(L, -1, item);
}
else {
lua_pushnil(L);
}
LuaScriptInterface::pushPosition(L, position, stackpos);
lua_pushnumber(L, lookDistance);
scriptInterface.callVoidFunction(4);
}
void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance)
{
// Player:onLookInBattleList(creature, position, distance) or Player.onLookInBattleList(self, creature, position, distance)
if (playerOnLookInBattleList == -1) {
return;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnLookInBattleList] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnLookInBattleList, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnLookInBattleList);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
lua_pushnumber(L, lookDistance);
scriptInterface.callVoidFunction(3);
}
void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance)
{
// Player:onLookInTrade(partner, item, distance) or Player.onLookInTrade(self, partner, item, distance)
if (playerOnLookInTrade == -1) {
return;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnLookInTrade] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnLookInTrade, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnLookInTrade);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<Player>(L, partner);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<Item>(L, item);
LuaScriptInterface::setItemMetatable(L, -1, item);
lua_pushnumber(L, lookDistance);
scriptInterface.callVoidFunction(4);
}
bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder)
{
// Player:onMoveItem(item, count, fromPosition, toPosition) or Player.onMoveItem(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder)
if (playerOnMoveItem == -1) {
return true;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnMoveItem] Call stack overflow" << std::endl;
return false;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnMoveItem, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnMoveItem);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<Item>(L, item);
LuaScriptInterface::setItemMetatable(L, -1, item);
lua_pushnumber(L, count);
LuaScriptInterface::pushPosition(L, fromPosition);
LuaScriptInterface::pushPosition(L, toPosition);
LuaScriptInterface::pushCylinder(L, fromCylinder);
LuaScriptInterface::pushCylinder(L, toCylinder);
return scriptInterface.callFunction(7);
}
void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder)
{
// Player:onItemMoved(item, count, fromPosition, toPosition) or Player.onItemMoved(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder)
if (playerOnItemMoved == -1) {
return;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnItemMoved] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnItemMoved, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnItemMoved);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<Item>(L, item);
LuaScriptInterface::setItemMetatable(L, -1, item);
lua_pushnumber(L, count);
LuaScriptInterface::pushPosition(L, fromPosition);
LuaScriptInterface::pushPosition(L, toPosition);
LuaScriptInterface::pushCylinder(L, fromCylinder);
LuaScriptInterface::pushCylinder(L, toCylinder);
scriptInterface.callVoidFunction(7);
}
bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition)
{
// Player:onMoveCreature(creature, fromPosition, toPosition) or Player.onMoveCreature(self, creature, fromPosition, toPosition)
if (playerOnMoveCreature == -1) {
return true;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnMoveCreature] Call stack overflow" << std::endl;
return false;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnMoveCreature, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnMoveCreature);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
LuaScriptInterface::pushPosition(L, fromPosition);
LuaScriptInterface::pushPosition(L, toPosition);
return scriptInterface.callFunction(4);
}
bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position)
{
// Player:onReportBug(message, position, category)
if (playerOnReportBug == -1) {
return true;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnReportBug] Call stack overflow" << std::endl;
return false;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnReportBug, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnReportBug);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushString(L, message);
LuaScriptInterface::pushPosition(L, position);
return scriptInterface.callFunction(3);
}
bool Events::eventPlayerOnTurn(Player* player, Direction direction)
{
// Player:onTurn(direction) or Player.onTurn(self, direction)
if (playerOnTurn == -1) {
return true;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnTurn] Call stack overflow" << std::endl;
return false;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnTurn, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnTurn);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
lua_pushnumber(L, direction);
return scriptInterface.callFunction(2);
}
bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* item)
{
// Player:onTradeRequest(target, item)
if (playerOnTradeRequest == -1) {
return true;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnTradeRequest] Call stack overflow" << std::endl;
return false;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnTradeRequest, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnTradeRequest);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<Player>(L, target);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<Item>(L, item);
LuaScriptInterface::setItemMetatable(L, -1, item);
return scriptInterface.callFunction(3);
}
bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem)
{
// Player:onTradeAccept(target, item, targetItem)
if (playerOnTradeAccept == -1) {
return true;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnTradeAccept] Call stack overflow" << std::endl;
return false;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnTradeAccept, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnTradeAccept);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<Player>(L, target);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<Item>(L, item);
LuaScriptInterface::setItemMetatable(L, -1, item);
LuaScriptInterface::pushUserdata<Item>(L, targetItem);
LuaScriptInterface::setItemMetatable(L, -1, targetItem);
return scriptInterface.callFunction(4);
}
void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp)
{
// Player:onGainExperience(source, exp, rawExp)
// rawExp gives the original exp which is not multiplied
if (playerOnGainExperience == -1) {
return;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnGainExperience] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnGainExperience, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnGainExperience);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
if (source) {
LuaScriptInterface::pushUserdata<Creature>(L, source);
LuaScriptInterface::setCreatureMetatable(L, -1, source);
}
else {
lua_pushnil(L);
}
lua_pushnumber(L, exp);
lua_pushnumber(L, rawExp);
if (scriptInterface.protectedCall(L, 4, 1) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
}
else {
exp = LuaScriptInterface::getNumber<uint64_t>(L, -1);
lua_pop(L, 1);
}
scriptInterface.resetScriptEnv();
}
void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp)
{
// Player:onLoseExperience(exp)
if (playerOnLoseExperience == -1) {
return;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnLoseExperience] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnLoseExperience, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnLoseExperience);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
lua_pushnumber(L, exp);
if (scriptInterface.protectedCall(L, 2, 1) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
}
else {
exp = LuaScriptInterface::getNumber<uint64_t>(L, -1);
lua_pop(L, 1);
}
scriptInterface.resetScriptEnv();
}
void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries)
{
// Player:onGainSkillTries(skill, tries)
if (playerOnGainSkillTries == -1) {
return;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnGainSkillTries] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnGainSkillTries, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnGainSkillTries);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
lua_pushnumber(L, skill);
lua_pushnumber(L, tries);
if (scriptInterface.protectedCall(L, 3, 1) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
}
else {
tries = LuaScriptInterface::getNumber<uint64_t>(L, -1);
lua_pop(L, 1);
}
scriptInterface.resetScriptEnv();
}

95
src/events.h Normal file
View File

@ -0,0 +1,95 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2016 Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef FS_EVENTS_H_BD444CC0EE167E5777E4C90C766B36DC
#define FS_EVENTS_H_BD444CC0EE167E5777E4C90C766B36DC
#include "luascript.h"
class Party;
class ItemType;
class Tile;
class Events
{
public:
Events();
void clear();
bool load();
// Creature
bool eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit);
ReturnValue eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive);
ReturnValue eventCreatureOnTargetCombat(Creature* creature, Creature* target);
// Party
bool eventPartyOnJoin(Party* party, Player* player);
bool eventPartyOnLeave(Party* party, Player* player);
bool eventPartyOnDisband(Party* party);
void eventPartyOnShareExperience(Party* party, uint64_t& exp);
// Player
void eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance);
void eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance);
void eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance);
bool eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder);
void eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder);
bool eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition);
void eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation);
bool eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position);
bool eventPlayerOnTurn(Player* player, Direction direction);
bool eventPlayerOnTradeRequest(Player* player, Player* target, Item* item);
bool eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem);
void eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp);
void eventPlayerOnLoseExperience(Player* player, uint64_t& exp);
void eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries);
private:
LuaScriptInterface scriptInterface;
// Creature
int32_t creatureOnChangeOutfit;
int32_t creatureOnAreaCombat;
int32_t creatureOnTargetCombat;
// Party
int32_t partyOnJoin;
int32_t partyOnLeave;
int32_t partyOnDisband;
int32_t partyOnShareExperience;
// Player
int32_t playerOnLook;
int32_t playerOnLookInBattleList;
int32_t playerOnLookInTrade;
int32_t playerOnMoveItem;
int32_t playerOnItemMoved;
int32_t playerOnMoveCreature;
int32_t playerOnReportRuleViolation;
int32_t playerOnReportBug;
int32_t playerOnTurn;
int32_t playerOnTradeRequest;
int32_t playerOnTradeAccept;
int32_t playerOnGainExperience;
int32_t playerOnLoseExperience;
int32_t playerOnGainSkillTries;
};
#endif

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,9 +22,9 @@
#include "pugicast.h"
#include "items.h"
#include "commands.h"
#include "creature.h"
#include "monster.h"
#include "events.h"
#include "game.h"
#include "actions.h"
#include "iologindata.h"
@ -36,6 +36,7 @@
#include "bed.h"
#include "scheduler.h"
#include "databasetasks.h"
#include "movement.h"
extern ConfigManager g_config;
extern Actions* g_actions;
@ -44,6 +45,10 @@ extern TalkActions* g_talkActions;
extern Spells* g_spells;
extern Vocations g_vocations;
extern GlobalEvents* g_globalEvents;
extern Events* g_events;
extern CreatureEvents* g_creatureEvents;
extern Monsters g_monsters;
extern MoveEvents* g_moveEvents;
Game::~Game()
{
@ -84,8 +89,6 @@ void Game::setGameState(GameState_t newState)
gameState = newState;
switch (newState) {
case GAME_STATE_INIT: {
commands.loadFromXml();
loadExperienceStages();
groups.load();
@ -219,7 +222,7 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index
}
case STACKPOS_USEITEM: {
thing = tile->getUseItem();
thing = tile->getUseItem(index);
break;
}
@ -231,7 +234,7 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index
case STACKPOS_USETARGET: {
thing = tile->getTopCreature();
if (!thing) {
thing = tile->getUseItem();
thing = tile->getUseItem(index);
}
break;
}
@ -651,6 +654,13 @@ void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Po
player->setNextActionTask(nullptr);
if (g_config.getBoolean(ConfigManager::BLOCK_HEIGHT)) {
if (toTile->getHeight() > 1) {
player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
return;
}
}
if (!Position::areInRange<1, 1, 0>(movingCreatureOrigPos, player->getPosition())) {
//need to walk to the creature first before moving it
std::forward_list<Direction> listDir;
@ -705,6 +715,10 @@ void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Po
}
}
if (!g_events->eventPlayerOnMoveCreature(player, movingCreature, movingCreaturePos, toPos)) {
return;
}
ReturnValue ret = internalMoveCreature(*movingCreature, *toTile);
if (ret != RETURNVALUE_NOERROR) {
player->sendCancelMessage(ret);
@ -729,7 +743,8 @@ ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction,
internalCreatureTurn(creature, DIRECTION_NORTH);
}
}
} else {
}
else {
//try go down
Tile* tmpTile = map.getTile(destPos.x, destPos.y, destPos.z);
if (currentPos.z != 7 && (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID)))) {
@ -742,11 +757,37 @@ ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction,
}
}
ReturnValue ret = RETURNVALUE_NOTPOSSIBLE;
Tile* toTile = map.getTile(destPos);
if (!toTile) {
return RETURNVALUE_NOTPOSSIBLE;
Tile* toPos = map.getTile(destPos.x, destPos.y, destPos.z);
Tile* fromPos = map.getTile(currentPos.x, currentPos.y, currentPos.z);
if (g_config.getBoolean(ConfigManager::BLOCK_HEIGHT)) {
if (toTile) {
if (currentPos.z > destPos.z && toPos->getHeight() > 1);
// not possible
else if ((((toPos->getHeight() - fromPos->getHeight()) < 2)) ||
(fromPos->hasHeight(3) && (currentPos.z == destPos.z)) ||
((currentPos.z < destPos.z) && (toPos->hasHeight(3) && (fromPos->getHeight() < 2))))
ret = internalMoveCreature(*creature, *toTile, flags);
}
if (ret != RETURNVALUE_NOERROR) {
if (Player* player = creature->getPlayer()) {
player->sendCancelMessage(ret);
player->sendCancelWalk();
}
}
return ret;
}
else {
if (!toTile) {
return RETURNVALUE_NOTPOSSIBLE;
}
return internalMoveCreature(*creature, *toTile, flags);
}
return internalMoveCreature(*creature, *toTile, flags);
}
ReturnValue Game::internalMoveCreature(Creature& creature, Tile& toTile, uint32_t flags /*= 0*/)
@ -964,6 +1005,10 @@ void Game::playerMoveItem(Player* player, const Position& fromPos,
return;
}
if (!g_events->eventPlayerOnMoveItem(player, item, count, fromPos, toPos, fromCylinder, toCylinder)) {
return;
}
uint8_t toIndex = 0;
if (toPos.x == 0xFFFF) {
if (toPos.y & 0x40) {
@ -976,6 +1021,8 @@ void Game::playerMoveItem(Player* player, const Position& fromPos,
ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, count, nullptr, 0, player);
if (ret != RETURNVALUE_NOERROR) {
player->sendCancelMessage(ret);
} else {
g_events->eventPlayerOnItemMoved(player, item, count, fromPos, toPos, fromCylinder, toCylinder);
}
}
@ -2316,6 +2363,10 @@ void Game::playerRequestTrade(uint32_t playerId, const Position& pos, uint8_t st
return;
}
if (!g_events->eventPlayerOnTradeRequest(player, tradePartner, tradeItem)) {
return;
}
internalStartTrade(player, tradePartner, tradeItem);
}
@ -2379,6 +2430,11 @@ void Game::playerAcceptTrade(uint32_t playerId)
Item* tradeItem1 = player->tradeItem;
Item* tradeItem2 = tradePartner->tradeItem;
if (!g_events->eventPlayerOnTradeAccept(player, tradePartner, tradeItem1, tradeItem2)) {
internalCloseTrade(player);
return;
}
player->setTradeState(TRADE_TRANSFER);
tradePartner->setTradeState(TRADE_TRANSFER);
@ -2508,10 +2564,8 @@ void Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t
int32_t lookDistance = std::max<int32_t>(Position::getDistanceX(playerPosition, tradeItemPosition),
Position::getDistanceY(playerPosition, tradeItemPosition));
std::stringstream ss;
if (index == 0) {
ss << "You see " << tradeItem->getDescription(lookDistance);
player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
g_events->eventPlayerOnLookInTrade(player, tradePartner, tradeItem, lookDistance);
return;
}
@ -2531,8 +2585,7 @@ void Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t
}
if (--index == 0) {
ss << "You see " << item->getDescription(lookDistance);
player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
g_events->eventPlayerOnLookInTrade(player, tradePartner, item, lookDistance);
return;
}
}
@ -2624,52 +2677,7 @@ void Game::playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos
lookDistance = -1;
}
std::ostringstream ss;
ss << "You see " << thing->getDescription(lookDistance);
if (player->isAccessPlayer()) {
Item* item = thing->getItem();
if (item) {
ss << std::endl << "ItemID: [" << item->getID() << ']';
uint16_t actionId = item->getActionId();
if (actionId != 0) {
ss << ", ActionID: [" << actionId << ']';
}
uint16_t movementID = item->getMovementId();
if (movementID != 0) {
ss << ", MovementID: [" << movementID << ']';
}
ss << '.';
const ItemType& it = Item::items[item->getID()];
if (it.transformEquipTo) {
ss << std::endl << "TransformTo: [" << it.transformEquipTo << "] (onEquip).";
} else if (it.transformDeEquipTo) {
ss << std::endl << "TransformTo: [" << it.transformDeEquipTo << "] (onDeEquip).";
}
if (it.decayTo != -1) {
ss << std::endl << "DecayTo: [" << it.decayTo << "].";
}
}
if (const Creature* creature = thing->getCreature()) {
ss << std::endl << "Health: [" << creature->getHealth() << " / " << creature->getMaxHealth() << ']';
if (creature->getMaxMana() > 0) {
ss << ", Mana: [" << creature->getMana() << " / " << creature->getMaxMana() << ']';
}
ss << '.';
}
ss << std::endl << "Position: [X: " << thingPos.x << "] [Y: " << thingPos.y << "] [Z: " << thingPos.getZ() << "].";
}
player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
g_events->eventPlayerOnLook(player, pos, thing, stackPos, lookDistance);
}
void Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId)
@ -2704,21 +2712,7 @@ void Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId)
lookDistance = -1;
}
std::ostringstream ss;
ss << "You see " << creature->getDescription(lookDistance);
if (player->isAccessPlayer()) {
ss << std::endl << "Health: [" << creature->getHealth() << " / " << creature->getMaxHealth() << ']';
if (creature->getMaxMana() > 0) {
ss << ", Mana: [" << creature->getMana() << " / " << creature->getMaxMana() << ']';
}
ss << '.' << std::endl;
ss << "Position: [X: " << creaturePos.x << "] [Y: " << creaturePos.y << "] [Z: " << creaturePos.getZ() << "].";
}
player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
g_events->eventPlayerOnLookInBattleList(player, creature, lookDistance);
}
void Game::playerCancelAttackAndFollow(uint32_t playerId)
@ -2847,6 +2841,10 @@ void Game::playerTurn(uint32_t playerId, Direction dir)
return;
}
if (!g_events->eventPlayerOnTurn(player, dir)) {
return;
}
player->resetIdleTime();
internalCreatureTurn(player, dir);
}
@ -2897,6 +2895,10 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type,
player->resetIdleTime();
if (playerSaySpell(player, type, text)) {
return;
}
uint32_t muteTime = player->isMuted();
if (muteTime > 0) {
std::ostringstream ss;
@ -2905,14 +2907,6 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type,
return;
}
if (playerSayCommand(player, text)) {
return;
}
if (playerSaySpell(player, type, text)) {
return;
}
if (!text.empty() && text.front() == '/' && player->isAccessPlayer()) {
return;
}
@ -2966,23 +2960,6 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type,
}
}
bool Game::playerSayCommand(Player* player, const std::string& text)
{
if (text.empty()) {
return false;
}
char firstCharacter = text.front();
for (char commandTag : commandTags) {
if (commandTag == firstCharacter) {
if (commands.exeCommand(*player, text)) {
return true;
}
}
}
return false;
}
bool Game::playerSaySpell(Player* player, SpeakClasses type, const std::string& text)
{
std::string words = text;
@ -3234,6 +3211,10 @@ void Game::changeSpeed(Creature* creature, int32_t varSpeedDelta)
void Game::internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit)
{
if (!g_events->eventCreatureOnChangeOutfit(creature, outfit)) {
return;
}
creature->setCurrentOutfit(outfit);
if (creature->isInvisible()) {
@ -3403,6 +3384,17 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage
return false;
}
if (damage.origin != ORIGIN_NONE) {
const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
if (!events.empty()) {
for (CreatureEvent* creatureEvent : events) {
creatureEvent->executeHealthChange(target, attacker, damage);
}
damage.origin = ORIGIN_NONE;
return combatChangeHealth(attacker, target, damage);
}
}
int32_t realHealthChange = target->getHealth();
target->gainHealth(attacker, damage.value);
realHealthChange = target->getHealth() - realHealthChange;
@ -3410,7 +3402,8 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage
if (realHealthChange > 0 && !target->isInGhostMode()) {
addMagicEffect(targetPos, CONST_ME_MAGIC_BLUE);
}
} else {
}
else {
if (Monster* monster = target->getMonster()) {
// makes monsters aggressive when damaged
// basically stands for UNDERATTACK stance under CipSoft servers
@ -3431,7 +3424,8 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage
Player* attackerPlayer;
if (attacker) {
attackerPlayer = attacker->getPlayer();
} else {
}
else {
attackerPlayer = nullptr;
}
damage.value = std::abs(damage.value);
@ -3440,25 +3434,39 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage
if (healthChange == 0) {
return true;
}
Player* targetPlayer = target->getPlayer();
SpectatorVec list;
if (target->hasCondition(CONDITION_MANASHIELD) && damage.type != COMBAT_UNDEFINEDDAMAGE) {
int32_t manaDamage = std::min<int32_t>(target->getMana(), healthChange);
int32_t manaDamage = std::min<int32_t>(targetPlayer->getMana(), healthChange);
if (manaDamage != 0) {
target->drainMana(attacker, manaDamage);
if (damage.origin != ORIGIN_NONE) {
const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
if (!events.empty()) {
for (CreatureEvent* creatureEvent : events) {
creatureEvent->executeManaChange(target, attacker, damage);
}
healthChange = damage.value;
if (healthChange == 0) {
return true;
}
manaDamage = std::min<int32_t>(targetPlayer->getMana(), healthChange);
}
}
targetPlayer->drainMana(attacker, manaDamage);
map.getSpectators(list, targetPos, true, true);
addMagicEffect(list, targetPos, CONST_ME_LOSEENERGY);
std::string damageString = std::to_string(manaDamage);
Player* targetPlayer = target->getPlayer();
if (targetPlayer) {
std::stringstream ss;
if (!attacker) {
ss << "You lose " << damageString << " mana.";
} else if (targetPlayer == attackerPlayer) {
}
else if (targetPlayer == attackerPlayer) {
ss << "You lose " << damageString << " mana due to your own attack.";
} else {
}
else {
ss << "You lose " << damageString << " mana due to an attack by " << attacker->getNameDescription() << '.';
}
targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str());
@ -3489,19 +3497,11 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage
realDamage = damage.value;
if (realDamage == 0) {
return true;
} else if (realDamage >= targetHealth) {
for (CreatureEvent* creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) {
if (!creatureEvent->executeOnPrepareDeath(target, attacker)) {
return false;
}
}
}
target->drainHealth(attacker, realDamage);
if (list.empty()) {
map.getSpectators(list, targetPos, true, true);
}
addCreatureHealth(list, target);
TextColor_t color = TEXTCOLOR_NONE;
uint8_t hitEffect;
@ -3514,15 +3514,15 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage
if (color != TEXTCOLOR_NONE) {
std::string damageString = std::to_string(realDamage) + (realDamage != 1 ? " hitpoints" : " hitpoint");
Player* targetPlayer = target->getPlayer();
if (targetPlayer) {
std::stringstream ss;
if (!attacker) {
ss << "You lose " << damageString << ".";
} else if (targetPlayer == attackerPlayer) {
}
else if (targetPlayer == attackerPlayer) {
ss << "You lose " << damageString << " due to your own attack.";
} else {
}
else {
ss << "You lose " << damageString << " due to an attack by " << attacker->getNameDescription() << '.';
}
targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str());
@ -3534,16 +3534,43 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage
tmpPlayer->sendAnimatedText(targetPos, color, realDamageStr);
}
}
if (realDamage >= targetHealth) {
for (CreatureEvent* creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) {
if (!creatureEvent->executeOnPrepareDeath(target, attacker)) {
return false;
}
}
}
target->drainHealth(attacker, realDamage);
addCreatureHealth(list, target);
}
return true;
}
bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange)
bool Game::combatChangeMana(Creature* attacker, Creature* target, CombatDamage& damage)
{
Player* targetPlayer = target->getPlayer();
if (!targetPlayer) {
return true;
}
int32_t manaChange = damage.value;
if (manaChange > 0) {
target->changeMana(manaChange);
} else {
if (damage.origin != ORIGIN_NONE) {
const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
if (!events.empty()) {
for (CreatureEvent* creatureEvent : events) {
creatureEvent->executeManaChange(target, attacker, damage);
}
damage.origin = ORIGIN_NONE;
return combatChangeMana(attacker, target, damage);
}
}
targetPlayer->changeMana(manaChange);
}
else {
const Position& targetPos = target->getPosition();
if (!target->isAttackable()) {
if (!target->isInGhostMode()) {
@ -3555,11 +3582,12 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaCh
Player* attackerPlayer;
if (attacker) {
attackerPlayer = attacker->getPlayer();
} else {
}
else {
attackerPlayer = nullptr;
}
int32_t manaLoss = std::min<int32_t>(target->getMana(), -manaChange);
int32_t manaLoss = std::min<int32_t>(targetPlayer->getMana(), -manaChange);
BlockType_t blockType = target->blockHit(attacker, COMBAT_MANADRAIN, manaLoss);
if (blockType != BLOCK_NONE) {
addMagicEffect(targetPos, CONST_ME_POFF);
@ -3570,11 +3598,20 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaCh
return true;
}
target->drainMana(attacker, manaLoss);
if (damage.origin != ORIGIN_NONE) {
const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
if (!events.empty()) {
for (CreatureEvent* creatureEvent : events) {
creatureEvent->executeManaChange(target, attacker, damage);
}
damage.origin = ORIGIN_NONE;
return combatChangeMana(attacker, target, damage);
}
}
targetPlayer->drainMana(attacker, manaLoss);
std::string damageString = std::to_string(manaLoss);
Player* targetPlayer = target->getPlayer();
SpectatorVec list;
map.getSpectators(list, targetPos, false, true);
@ -3582,9 +3619,11 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaCh
std::stringstream ss;
if (!attacker) {
ss << "You lose " << damageString << " mana.";
} else if (targetPlayer == attackerPlayer) {
}
else if (targetPlayer == attackerPlayer) {
ss << "You lose " << damageString << " mana due to your own attack.";
} else {
}
else {
ss << "You lose " << damageString << " mana due to an attack by " << attacker->getNameDescription() << '.';
}
targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str());
@ -3814,23 +3853,9 @@ void Game::getWorldLightInfo(LightInfo& lightInfo) const
lightInfo.color = 0xD7;
}
void Game::addCommandTag(char tag)
{
for (char commandTag : commandTags) {
if (commandTag == tag) {
return;
}
}
commandTags.push_back(tag);
}
void Game::resetCommandTag()
{
commandTags.clear();
}
void Game::shutdown()
{
saveGameState();
std::cout << "Shutting down..." << std::flush;
g_scheduler.shutdown();
@ -4324,22 +4349,8 @@ void Game::playerReportBug(uint32_t playerId, const std::string& message)
return;
}
if (player->getAccountType() == ACCOUNT_TYPE_NORMAL) {
return;
}
std::string fileName = "data/reports/" + player->getName() + " report.txt";
FILE* file = fopen(fileName.c_str(), "a");
if (!file) {
player->sendTextMessage(MESSAGE_EVENT_DEFAULT, "There was an error when processing your report, please contact a gamemaster.");
return;
}
const Position& playerPosition = player->getPosition();
fprintf(file, "------------------------------\nName: %s [Player Position: %u, %u, %u]\nComment: %s\n", player->getName().c_str(), playerPosition.x, playerPosition.y, playerPosition.z, message.c_str());
fclose(file);
player->sendTextMessage(MESSAGE_EVENT_DEFAULT, "Your report has been sent to " + g_config.getString(ConfigManager::SERVER_NAME) + ".");
const Position& position = player->getPosition();
g_events->eventPlayerOnReportBug(player, message, position);
}
void Game::playerDebugAssert(uint32_t playerId, const std::string& assertLine, const std::string& date, const std::string& description, const std::string& comment)
@ -4534,3 +4545,64 @@ void Game::removeBedSleeper(uint32_t guid)
bedSleepersMap.erase(it);
}
}
bool Game::reload(ReloadTypes_t reloadType)
{
switch (reloadType) {
case RELOAD_TYPE_ACTIONS: return g_actions->reload();
case RELOAD_TYPE_CHAT: return g_chat->load();
case RELOAD_TYPE_CONFIG: return g_config.reload();
case RELOAD_TYPE_CREATURESCRIPTS: return g_creatureEvents->reload();
case RELOAD_TYPE_EVENTS: return g_events->load();
case RELOAD_TYPE_GLOBALEVENTS: return g_globalEvents->reload();
case RELOAD_TYPE_ITEMS: return Item::items.reload();
case RELOAD_TYPE_MONSTERS: return g_monsters.reload();
case RELOAD_TYPE_MOVEMENTS: return g_moveEvents->reload();
case RELOAD_TYPE_NPCS: {
Npcs::reload();
return true;
}
case RELOAD_TYPE_RAIDS: return raids.reload() && raids.startup();
case RELOAD_TYPE_SPELLS: {
if (!g_spells->reload()) {
std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl;
std::terminate();
}
else if (!g_monsters.reload()) {
std::cout << "[Error - Game::reload] Failed to reload monsters." << std::endl;
std::terminate();
}
return true;
}
case RELOAD_TYPE_TALKACTIONS: return g_talkActions->reload();
default: {
if (!g_spells->reload()) {
std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl;
std::terminate();
return false;
}
else if (!g_monsters.reload()) {
std::cout << "[Error - Game::reload] Failed to reload monsters." << std::endl;
std::terminate();
return false;
}
g_actions->reload();
g_config.reload();
g_creatureEvents->reload();
g_monsters.reload();
g_moveEvents->reload();
Npcs::reload();
raids.reload() && raids.startup();
g_talkActions->reload();
Item::items.reload();
g_globalEvents->reload();
g_events->load();
g_chat->load();
return true;
}
}
}

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,7 +22,6 @@
#include "account.h"
#include "combat.h"
#include "commands.h"
#include "groups.h"
#include "map.h"
#include "position.h"
@ -436,7 +435,7 @@ class Game
void combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColor_t& color, uint8_t& effect);
bool combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage);
bool combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange);
bool combatChangeMana(Creature* attacker, Creature* target, CombatDamage& damage);
//animation help functions
void addCreatureHealth(const Creature* target);
@ -449,9 +448,6 @@ class Game
static void addAnimatedText(const SpectatorVec& list, const Position& pos, uint8_t color, const std::string& text);
void addMonsterSayText(const Position& pos, const std::string& text);
void addCommandTag(char tag);
void resetCommandTag();
void startDecay(Item* item);
int32_t getLightHour() const {
return lightHour;
@ -488,13 +484,13 @@ class Game
BedItem* getBedBySleeper(uint32_t guid) const;
void setBedSleeper(BedItem* bed, uint32_t guid);
void removeBedSleeper(uint32_t guid);
bool reload(ReloadTypes_t reloadType);
Groups groups;
Map map;
Raids raids;
protected:
bool playerSayCommand(Player* player, const std::string& text);
bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text);
void playerWhisper(Player* player, const std::string& text);
bool playerYell(Player* player, const std::string& text);
@ -518,7 +514,6 @@ class Game
std::vector<Creature*> ToReleaseCreatures;
std::vector<Item*> ToReleaseItems;
std::vector<char> commandTags;
size_t lastBucket = 0;
@ -532,8 +527,6 @@ class Game
std::map<uint32_t, BedItem*> bedSleepersMap;
Commands commands;
static constexpr int32_t LIGHT_LEVEL_DAY = 250;
static constexpr int32_t LIGHT_LEVEL_NIGHT = 40;
static constexpr int32_t SUNSET = 1305;

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -82,7 +82,7 @@ void House::setOwner(uint32_t guid, bool updateDatabase/* = true*/, Player* play
setAccessList(SUBOWNER_LIST, "");
setAccessList(GUEST_LIST, "");
for (Door* door : doorList) {
for (Door* door : doorSet) {
door->setAccessList("");
}
@ -116,7 +116,7 @@ void House::updateDoorDescription() const
}
}
for (const auto& it : doorList) {
for (const auto& it : doorSet) {
it->setSpecialDescription(ss.str());
}
}
@ -277,17 +277,17 @@ bool House::isInvited(const Player* player)
void House::addDoor(Door* door)
{
door->incrementReferenceCounter();
doorList.push_back(door);
doorSet.insert(door);
door->setHouse(this);
updateDoorDescription();
}
void House::removeDoor(Door* door)
{
auto it = std::find(doorList.begin(), doorList.end(), door);
if (it != doorList.end()) {
auto it = doorSet.find(door);
if (it != doorSet.end()) {
door->decrementReferenceCounter();
doorList.erase(it);
doorSet.erase(it);
}
}
@ -299,7 +299,7 @@ void House::addBed(BedItem* bed)
Door* House::getDoorByNumber(uint32_t doorId) const
{
for (Door* door : doorList) {
for (Door* door : doorSet) {
if (door->getDoorId() == doorId) {
return door;
}
@ -309,7 +309,7 @@ Door* House::getDoorByNumber(uint32_t doorId) const
Door* House::getDoorByPosition(const Position& pos)
{
for (Door* door : doorList) {
for (Door* door : doorSet) {
if (door->getPosition() == pos) {
return door;
}

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,6 +21,7 @@
#define FS_HOUSE_H_EB9732E7771A438F9CD0EFA8CB4C58C4
#include <regex>
#include <set>
#include "container.h"
#include "housetile.h"
@ -212,10 +213,11 @@ class House
return houseTiles;
}
const std::list<Door*>& getDoors() const {
return doorList;
const std::set<Door*>& getDoors() const {
return doorSet;
}
void addBed(BedItem* bed);
const HouseBedItemList& getBeds() const {
return bedsList;
@ -234,7 +236,7 @@ class House
Container transfer_container{ITEM_LOCKER1};
HouseTileList houseTiles;
std::list<Door*> doorList;
std::set<Door*> doorSet;
HouseBedItemList bedsList;
std::string houseName;

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -676,6 +676,18 @@ void LuaScriptInterface::setCreatureMetatable(lua_State* L, int32_t index, const
}
// Get
CombatDamage LuaScriptInterface::getCombatDamage(lua_State* L)
{
CombatDamage damage;
damage.value = getNumber<int32_t>(L, -4);
damage.type = getNumber<CombatType_t>(L, -3);
damage.min = getNumber<int32_t>(L, -2);
damage.max = getNumber<CombatType_t>(L, -1);
lua_pop(L, 4);
return damage;
}
std::string LuaScriptInterface::getString(lua_State* L, int32_t arg)
{
size_t len;
@ -839,6 +851,16 @@ void LuaScriptInterface::pushBoolean(lua_State* L, bool value)
lua_pushboolean(L, value ? 1 : 0);
}
void LuaScriptInterface::pushCombatDamage(lua_State* L, const CombatDamage& damage)
{
lua_pushnumber(L, damage.value);
lua_pushnumber(L, damage.type);
lua_pushnumber(L, damage.min);
lua_pushnumber(L, damage.max);
lua_pushnumber(L, damage.origin);
}
void LuaScriptInterface::pushPosition(lua_State* L, const Position& position, int32_t stackpos/* = 0*/)
{
lua_createtable(L, 0, 4);
@ -1465,6 +1487,13 @@ void LuaScriptInterface::registerFunctions()
registerEnum(SLOTP_DEPOT)
registerEnum(SLOTP_TWO_HAND)
// Use with combat functions
registerEnum(ORIGIN_NONE)
registerEnum(ORIGIN_CONDITION)
registerEnum(ORIGIN_SPELL)
registerEnum(ORIGIN_MELEE)
registerEnum(ORIGIN_RANGED)
// Use with house:getAccessList, house:setAccessList
registerEnum(GUEST_LIST)
registerEnum(SUBOWNER_LIST)
@ -1533,6 +1562,31 @@ void LuaScriptInterface::registerFunctions()
registerEnum(RETURNVALUE_CANONLYUSEONESHIELD)
registerEnum(RETURNVALUE_NOPARTYMEMBERSINRANGE)
registerEnum(RETURNVALUE_YOUARENOTTHEOWNER)
registerEnum(RETURNVALUE_TRADEPLAYERFARAWAY)
registerEnum(RETURNVALUE_YOUDONTOWNTHISHOUSE)
registerEnum(RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE)
registerEnum(RETURNVALUE_TRADEPLAYERHIGHESTBIDDER)
registerEnum(RETURNVALUE_YOUCANNOTTRADETHISHOUSE)
registerEnum(RELOAD_TYPE_ALL)
registerEnum(RELOAD_TYPE_ACTIONS)
registerEnum(RELOAD_TYPE_CHAT)
registerEnum(RELOAD_TYPE_COMMANDS)
registerEnum(RELOAD_TYPE_CONFIG)
registerEnum(RELOAD_TYPE_CREATURESCRIPTS)
registerEnum(RELOAD_TYPE_EVENTS)
registerEnum(RELOAD_TYPE_GLOBAL)
registerEnum(RELOAD_TYPE_GLOBALEVENTS)
registerEnum(RELOAD_TYPE_ITEMS)
registerEnum(RELOAD_TYPE_MONSTERS)
registerEnum(RELOAD_TYPE_MOUNTS)
registerEnum(RELOAD_TYPE_MOVEMENTS)
registerEnum(RELOAD_TYPE_NPCS)
registerEnum(RELOAD_TYPE_QUESTS)
registerEnum(RELOAD_TYPE_RAIDS)
registerEnum(RELOAD_TYPE_SPELLS)
registerEnum(RELOAD_TYPE_TALKACTIONS)
registerEnum(RELOAD_TYPE_WEAPONS)
// _G
registerGlobalVariable("INDEX_WHEREEVER", INDEX_WHEREEVER);
@ -1608,6 +1662,8 @@ void LuaScriptInterface::registerFunctions()
registerEnumIn("configKeys", ConfigManager::MAX_PACKETS_PER_SECOND)
registerEnumIn("configKeys", ConfigManager::NEWBIE_TOWN)
registerEnumIn("configKeys", ConfigManager::NEWBIE_LEVEL_THRESHOLD)
registerEnumIn("configKeys", ConfigManager::BLOCK_HEIGHT)
registerEnumIn("configKeys", ConfigManager::DROP_ITEMS)
// os
registerMethod("os", "mtime", LuaScriptInterface::luaSystemTime);
@ -1646,6 +1702,8 @@ void LuaScriptInterface::registerFunctions()
registerMethod("Game", "startRaid", LuaScriptInterface::luaGameStartRaid);
registerMethod("Game", "reload", LuaScriptInterface::luaGameReload);
// Variant
registerClass("Variant", "", LuaScriptInterface::luaVariantCreate);
@ -1853,10 +1911,6 @@ void LuaScriptInterface::registerFunctions()
registerMethod("Creature", "getMaxHealth", LuaScriptInterface::luaCreatureGetMaxHealth);
registerMethod("Creature", "setMaxHealth", LuaScriptInterface::luaCreatureSetMaxHealth);
registerMethod("Creature", "getMana", LuaScriptInterface::luaCreatureGetMana);
registerMethod("Creature", "addMana", LuaScriptInterface::luaCreatureAddMana);
registerMethod("Creature", "getMaxMana", LuaScriptInterface::luaCreatureGetMaxMana);
registerMethod("Creature", "getSkull", LuaScriptInterface::luaCreatureGetSkull);
registerMethod("Creature", "setSkull", LuaScriptInterface::luaCreatureSetSkull);
@ -1914,6 +1968,9 @@ void LuaScriptInterface::registerFunctions()
registerMethod("Player", "getMagicLevel", LuaScriptInterface::luaPlayerGetMagicLevel);
registerMethod("Player", "getBaseMagicLevel", LuaScriptInterface::luaPlayerGetBaseMagicLevel);
registerMethod("Player", "getMana", LuaScriptInterface::luaPlayerGetMana);
registerMethod("Player", "addMana", LuaScriptInterface::luaPlayerAddMana);
registerMethod("Player", "getMaxMana", LuaScriptInterface::luaPlayerGetMaxMana);
registerMethod("Player", "setMaxMana", LuaScriptInterface::luaPlayerSetMaxMana);
registerMethod("Player", "getManaSpent", LuaScriptInterface::luaPlayerGetManaSpent);
registerMethod("Player", "addManaSpent", LuaScriptInterface::luaPlayerAddManaSpent);
@ -2122,6 +2179,7 @@ void LuaScriptInterface::registerFunctions()
registerMethod("House", "getOwnerGuid", LuaScriptInterface::luaHouseGetOwnerGuid);
registerMethod("House", "setOwnerGuid", LuaScriptInterface::luaHouseSetOwnerGuid);
registerMethod("House", "startTrade", LuaScriptInterface::luaHouseStartTrade);
registerMethod("House", "getBeds", LuaScriptInterface::luaHouseGetBeds);
registerMethod("House", "getBedCount", LuaScriptInterface::luaHouseGetBedCount);
@ -2196,6 +2254,7 @@ void LuaScriptInterface::registerFunctions()
registerMethod("Combat", "setArea", LuaScriptInterface::luaCombatSetArea);
registerMethod("Combat", "setCondition", LuaScriptInterface::luaCombatSetCondition);
registerMethod("Combat", "setCallback", LuaScriptInterface::luaCombatSetCallback);
registerMethod("Combat", "setOrigin", LuaScriptInterface::luaCombatSetOrigin);
registerMethod("Combat", "execute", LuaScriptInterface::luaCombatExecute);
@ -2794,7 +2853,7 @@ int LuaScriptInterface::luaCreateCombatArea(lua_State* L)
int LuaScriptInterface::luaDoAreaCombatHealth(lua_State* L)
{
//doAreaCombatHealth(cid, type, pos, area, min, max, effect)
//doAreaCombatHealth(cid, type, pos, area, min, max, effect[, origin = ORIGIN_SPELL])
Creature* creature = getCreature(L, 1);
if (!creature && (!isNumber(L, 1) || getNumber<uint32_t>(L, 1) != 0)) {
reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
@ -2812,6 +2871,7 @@ int LuaScriptInterface::luaDoAreaCombatHealth(lua_State* L)
params.impactEffect = getNumber<uint8_t>(L, 7);
CombatDamage damage;
damage.origin = getNumber<CombatOrigin>(L, 8, ORIGIN_SPELL);
damage.type = combatType;
damage.value = normal_random(getNumber<int32_t>(L, 6), getNumber<int32_t>(L, 5));
@ -2826,7 +2886,7 @@ int LuaScriptInterface::luaDoAreaCombatHealth(lua_State* L)
int LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L)
{
//doTargetCombatHealth(cid, target, type, min, max, effect)
//doTargetCombatHealth(cid, target, type, min, max, effect[, origin = ORIGIN_SPELL])
Creature* creature = getCreature(L, 1);
if (!creature && (!isNumber(L, 1) || getNumber<uint32_t>(L, 1) != 0)) {
reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
@ -2848,6 +2908,7 @@ int LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L)
params.impactEffect = getNumber<uint8_t>(L, 6);
CombatDamage damage;
damage.origin = getNumber<CombatOrigin>(L, 7, ORIGIN_SPELL);
damage.type = combatType;
damage.value = normal_random(getNumber<int32_t>(L, 4), getNumber<int32_t>(L, 5));
@ -2858,7 +2919,7 @@ int LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L)
int LuaScriptInterface::luaDoAreaCombatMana(lua_State* L)
{
//doAreaCombatMana(cid, pos, area, min, max, effect)
//doAreaCombatMana(cid, pos, area, min, max, effect[, origin = ORIGIN_SPELL])
Creature* creature = getCreature(L, 1);
if (!creature && (!isNumber(L, 1) || getNumber<uint32_t>(L, 1) != 0)) {
reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
@ -2873,6 +2934,7 @@ int LuaScriptInterface::luaDoAreaCombatMana(lua_State* L)
params.impactEffect = getNumber<uint8_t>(L, 6);
CombatDamage damage;
damage.origin = getNumber<CombatOrigin>(L, 7, ORIGIN_SPELL);
damage.type = COMBAT_MANADRAIN;
damage.value = normal_random(getNumber<int32_t>(L, 4), getNumber<int32_t>(L, 5));
@ -2888,7 +2950,7 @@ int LuaScriptInterface::luaDoAreaCombatMana(lua_State* L)
int LuaScriptInterface::luaDoTargetCombatMana(lua_State* L)
{
//doTargetCombatMana(cid, target, min, max, effect)
//doTargetCombatMana(cid, target, min, max, effect[, origin = ORIGIN_SPELL)
Creature* creature = getCreature(L, 1);
if (!creature && (!isNumber(L, 1) || getNumber<uint32_t>(L, 1) != 0)) {
reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
@ -2907,6 +2969,7 @@ int LuaScriptInterface::luaDoTargetCombatMana(lua_State* L)
params.impactEffect = getNumber<uint8_t>(L, 5);
CombatDamage damage;
damage.origin = getNumber<CombatOrigin>(L, 6, ORIGIN_SPELL);
damage.type = COMBAT_MANADRAIN;
damage.value = normal_random(getNumber<int32_t>(L, 3), getNumber<int32_t>(L, 4));
@ -4144,6 +4207,25 @@ int LuaScriptInterface::luaGameStartRaid(lua_State* L)
return 1;
}
int LuaScriptInterface::luaGameReload(lua_State* L)
{
// Game.reload(reloadType)
ReloadTypes_t reloadType = getNumber<ReloadTypes_t>(L, 1);
if (!reloadType) {
lua_pushnil(L);
return 1;
}
if (reloadType == RELOAD_TYPE_GLOBAL) {
pushBoolean(L, g_luaEnvironment.loadFile("data/global.lua") == 0);
}
else {
pushBoolean(L, g_game.reload(reloadType));
}
lua_gc(g_luaEnvironment.getLuaState(), LUA_GCCOLLECT, 0);
return 1;
}
// Variant
int LuaScriptInterface::luaVariantCreate(lua_State* L)
{
@ -6575,50 +6657,6 @@ int LuaScriptInterface::luaCreatureSetMaxHealth(lua_State* L)
return 1;
}
int LuaScriptInterface::luaCreatureGetMana(lua_State* L)
{
// creature:getMana()
const Creature* creature = getUserdata<const Creature>(L, 1);
if (creature) {
lua_pushnumber(L, creature->getMana());
} else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaCreatureAddMana(lua_State* L)
{
// creature:addMana(manaChange[, animationOnLoss = false])
Creature* creature = getUserdata<Creature>(L, 1);
if (!creature) {
lua_pushnil(L);
return 1;
}
int32_t manaChange = getNumber<int32_t>(L, 2);
bool animationOnLoss = getBoolean(L, 3, false);
if (!animationOnLoss && manaChange < 0) {
creature->changeMana(manaChange);
} else {
g_game.combatChangeMana(nullptr, creature, manaChange);
}
pushBoolean(L, true);
return 1;
}
int LuaScriptInterface::luaCreatureGetMaxMana(lua_State* L)
{
// creature:getMaxMana()
const Creature* creature = getUserdata<const Creature>(L, 1);
if (creature) {
lua_pushnumber(L, creature->getMaxMana());
} else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaCreatureGetSkull(lua_State* L)
{
// creature:getSkull()
@ -7191,15 +7229,15 @@ int LuaScriptInterface::luaPlayerGetExperience(lua_State* L)
int LuaScriptInterface::luaPlayerAddExperience(lua_State* L)
{
// player:addExperience(experience[, sendText = false[, applyStages = true])
// player:addExperience(experience[, sendText = false])
Player* player = getUserdata<Player>(L, 1);
if (player) {
int64_t experience = getNumber<int64_t>(L, 2);
bool sendText = getBoolean(L, 3, false);
bool applyStages = getBoolean(L, 4, true);
player->addExperience(experience, sendText, applyStages);
player->addExperience(nullptr, experience, sendText);
pushBoolean(L, true);
} else {
}
else {
lua_pushnil(L);
}
return 1;
@ -7255,6 +7293,56 @@ int LuaScriptInterface::luaPlayerGetBaseMagicLevel(lua_State* L)
return 1;
}
int LuaScriptInterface::luaPlayerGetMana(lua_State* L)
{
// player:getMana()
const Player* player = getUserdata<const Player>(L, 1);
if (player) {
lua_pushnumber(L, player->getMana());
}
else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaPlayerAddMana(lua_State* L)
{
// player:addMana(manaChange[, animationOnLoss = false])
Player* player = getUserdata<Player>(L, 1);
if (!player) {
lua_pushnil(L);
return 1;
}
int32_t manaChange = getNumber<int32_t>(L, 2);
bool animationOnLoss = getBoolean(L, 3, false);
if (!animationOnLoss && manaChange < 0) {
player->changeMana(manaChange);
}
else {
CombatDamage damage;
damage.value = manaChange;
damage.origin = ORIGIN_NONE;
g_game.combatChangeMana(nullptr, player, damage);
}
pushBoolean(L, true);
return 1;
}
int LuaScriptInterface::luaPlayerGetMaxMana(lua_State* L)
{
// player:getMaxMana()
const Player* player = getUserdata<const Player>(L, 1);
if (player) {
lua_pushnumber(L, player->getMaxMana());
}
else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaPlayerSetMaxMana(lua_State* L)
{
// player:setMaxMana(maxMana)
@ -7723,12 +7811,20 @@ int LuaScriptInterface::luaPlayerSetBankBalance(lua_State* L)
{
// player:setBankBalance(bankBalance)
Player* player = getUserdata<Player>(L, 1);
if (player) {
player->setBankBalance(getNumber<uint64_t>(L, 2));
pushBoolean(L, true);
} else {
if (!player) {
lua_pushnil(L);
return 1;
}
int64_t balance = getNumber<int64_t>(L, 2);
if (balance < 0) {
reportErrorFunc("Invalid bank balance value.");
lua_pushnil(L);
return 1;
}
player->setBankBalance(balance);
pushBoolean(L, true);
return 1;
}
@ -9473,6 +9569,53 @@ int LuaScriptInterface::luaHouseSetOwnerGuid(lua_State* L)
return 1;
}
int LuaScriptInterface::luaHouseStartTrade(lua_State* L)
{
// house:startTrade(player, tradePartner)
House* house = getUserdata<House>(L, 1);
Player* player = getUserdata<Player>(L, 2);
Player* tradePartner = getUserdata<Player>(L, 3);
if (!player || !tradePartner || !house) {
lua_pushnil(L);
return 1;
}
if (!Position::areInRange<2, 2, 0>(tradePartner->getPosition(), player->getPosition())) {
lua_pushnumber(L, RETURNVALUE_TRADEPLAYERFARAWAY);
return 1;
}
if (house->getOwner() != player->getGUID()) {
lua_pushnumber(L, RETURNVALUE_YOUDONTOWNTHISHOUSE);
return 1;
}
if (g_game.map.houses.getHouseByPlayerId(tradePartner->getGUID())) {
lua_pushnumber(L, RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE);
return 1;
}
if (IOLoginData::hasBiddedOnHouse(tradePartner->getGUID())) {
lua_pushnumber(L, RETURNVALUE_TRADEPLAYERHIGHESTBIDDER);
return 1;
}
Item* transferItem = house->getTransferItem();
if (!transferItem) {
lua_pushnumber(L, RETURNVALUE_YOUCANNOTTRADETHISHOUSE);
return 1;
}
transferItem->getParent()->setParent(player);
if (!g_game.internalStartTrade(player, tradePartner, transferItem)) {
house->resetTransferItem();
}
lua_pushnumber(L, RETURNVALUE_NOERROR);
return 1;
}
int LuaScriptInterface::luaHouseGetBeds(lua_State* L)
{
// house:getBeds()
@ -10225,6 +10368,20 @@ int LuaScriptInterface::luaCombatSetCallback(lua_State* L)
return 1;
}
int LuaScriptInterface::luaCombatSetOrigin(lua_State* L)
{
// combat:setOrigin(origin)
Combat* combat = getUserdata<Combat>(L, 1);
if (combat) {
combat->setOrigin(getNumber<CombatOrigin>(L, 2));
pushBoolean(L, true);
}
else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaCombatExecute(lua_State* L)
{
// combat:execute(creature, variant)
@ -11222,7 +11379,7 @@ int LuaScriptInterface::luaPartyShareExperience(lua_State* L)
uint64_t experience = getNumber<uint64_t>(L, 2);
Party* party = getUserdata<Party>(L, 1);
if (party) {
party->shareExperience(experience);
party->shareExperience(experience, nullptr);
pushBoolean(L, true);
} else {
lua_pushnil(L);

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -320,6 +320,7 @@ class LuaScriptInterface
}
static std::string getString(lua_State* L, int32_t arg);
static CombatDamage getCombatDamage(lua_State* L);
static Position getPosition(lua_State* L, int32_t arg, int32_t& stackpos);
static Position getPosition(lua_State* L, int32_t arg);
static Outfit_t getOutfit(lua_State* L, int32_t arg);
@ -368,6 +369,7 @@ class LuaScriptInterface
// Push
static void pushBoolean(lua_State* L, bool value);
static void pushCombatDamage(lua_State* L, const CombatDamage& damage);
static void pushPosition(lua_State* L, const Position& position, int32_t stackpos = 0);
static void pushOutfit(lua_State* L, const Outfit_t& outfit);
@ -549,6 +551,8 @@ class LuaScriptInterface
static int luaGameStartRaid(lua_State* L);
static int luaGameReload(lua_State* L);
// Variant
static int luaVariantCreate(lua_State* L);
@ -749,10 +753,6 @@ class LuaScriptInterface
static int luaCreatureGetMaxHealth(lua_State* L);
static int luaCreatureSetMaxHealth(lua_State* L);
static int luaCreatureGetMana(lua_State* L);
static int luaCreatureAddMana(lua_State* L);
static int luaCreatureGetMaxMana(lua_State* L);
static int luaCreatureGetSkull(lua_State* L);
static int luaCreatureSetSkull(lua_State* L);
@ -809,6 +809,9 @@ class LuaScriptInterface
static int luaPlayerGetMagicLevel(lua_State* L);
static int luaPlayerGetBaseMagicLevel(lua_State* L);
static int luaPlayerGetMana(lua_State* L);
static int luaPlayerAddMana(lua_State* L);
static int luaPlayerGetMaxMana(lua_State* L);
static int luaPlayerSetMaxMana(lua_State* L);
static int luaPlayerGetManaSpent(lua_State* L);
static int luaPlayerAddManaSpent(lua_State* L);
@ -1011,6 +1014,7 @@ class LuaScriptInterface
static int luaHouseGetOwnerGuid(lua_State* L);
static int luaHouseSetOwnerGuid(lua_State* L);
static int luaHouseStartTrade(lua_State* L);
static int luaHouseGetBeds(lua_State* L);
static int luaHouseGetBedCount(lua_State* L);
@ -1082,6 +1086,7 @@ class LuaScriptInterface
static int luaCombatSetArea(lua_State* L);
static int luaCombatSetCondition(lua_State* L);
static int luaCombatSetCallback(lua_State* L);
static int luaCombatSetOrigin(lua_State* L);
static int luaCombatExecute(lua_State* L);

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -744,23 +744,12 @@ void Monster::setIdle(bool idle)
void Monster::updateIdleStatus()
{
bool idle = true;
bool idle = false;
if (!isSummon()) {
if (!targetList.empty()) {
// visible target
idle = false;
} else {
for (Condition* condition : conditions) {
if (condition->getType() >= CONDITION_ENERGY && condition->getType() <= CONDITION_ENERGY) {
// monsters with aggressive conditions never become idle
idle = false;
break;
}
}
if (conditions.empty()) {
if (!isSummon() && targetList.empty()) {
idle = true;
}
} else {
idle = false;
}
setIdle(idle);
@ -887,7 +876,7 @@ void Monster::doAttacking(uint32_t)
for (spellBlock_t& spellBlock : mType->info.attackSpells) {
if (spellBlock.range != 0 && std::max<uint32_t>(Position::getDistanceX(myPos, targetPos), Position::getDistanceY(myPos, targetPos)) <= spellBlock.range) {
if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) {
if (normal_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || normal_random(1, 3) == 1)) {
updateLookDirection();
minCombatValue = spellBlock.minCombatValue;
@ -988,7 +977,7 @@ void Monster::onThinkTarget(uint32_t interval)
void Monster::onThinkDefense(uint32_t)
{
for (const spellBlock_t& spellBlock : mType->info.defenseSpells) {
if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) {
if (normal_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || normal_random(1, 3) == 1)) {
minCombatValue = spellBlock.minCombatValue;
maxCombatValue = spellBlock.maxCombatValue;
spellBlock.spell->castSpell(this, this);
@ -1012,7 +1001,7 @@ void Monster::onThinkDefense(uint32_t)
continue;
}
if (uniform_random(0, summonBlock.chance) == 0 && (health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) {
if (normal_random(0, summonBlock.chance) == 0 && (health > mType->info.runAwayHealth || normal_random(1, 3) == 1)) {
Monster* summon = Monster::createMonster(summonBlock.name);
if (summon) {
const Position& summonPos = getPosition();
@ -1038,16 +1027,15 @@ void Monster::onThinkYell(uint32_t)
}
int32_t randomResult = rand();
if (randomResult == 50 * (randomResult / 50)) {
if (!mType->info.voiceVector.empty()) {
uint32_t index = uniform_random(0, mType->info.voiceVector.size() - 1);
const voiceBlock_t& vb = mType->info.voiceVector[index];
if (vb.yellText) {
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, vb.text, false);
} else {
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, vb.text, false);
}
if (rand() == 50 * (randomResult / 50)) {
int32_t totalVoices = mType->info.voiceVector.size();
const voiceBlock_t& voice = mType->info.voiceVector[rand() % totalVoices + 1];
if (voice.yellText) {
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, voice.text, false);
}
else {
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, voice.text, false);
}
}
}
@ -1172,7 +1160,7 @@ bool Monster::getNextStep(Direction& direction, uint32_t& flags)
if ((!followCreature || !hasFollowPath) && (!isSummon() || !isMasterInRange)) {
if (OTSYS_TIME() >= nextDanceStepRound) {
updateLookDirection();
nextDanceStepRound = OTSYS_TIME() + 200 + getStepDuration();
nextDanceStepRound = OTSYS_TIME() + getStepDuration();
//choose a random direction
result = getRandomStep(getPosition(), direction);
@ -1235,6 +1223,7 @@ bool Monster::getNextStep(Direction& direction, uint32_t& flags)
egibleToDance = false;
earliestWakeUpTime = OTSYS_TIME() + 1000;
earliestDanceTime = OTSYS_TIME() + 1000 + getStepDuration();
earliestAttackTime += 200;
}
}
}
@ -1276,6 +1265,7 @@ bool Monster::getNextStep(Direction& direction, uint32_t& flags)
egibleToDance = false;
earliestWakeUpTime = OTSYS_TIME() + 1000;
earliestDanceTime = OTSYS_TIME() + 1000 + getStepDuration();
earliestAttackTime += 200;
}
}
}
@ -2002,40 +1992,71 @@ void Monster::updateLookDirection()
//look EAST/WEST
if (offsetx < 0) {
newDir = DIRECTION_WEST;
} else {
}
else {
newDir = DIRECTION_EAST;
}
} else if (dx < dy) {
}
else if (dx < dy) {
//look NORTH/SOUTH
if (offsety < 0) {
newDir = DIRECTION_NORTH;
} else {
}
else {
newDir = DIRECTION_SOUTH;
}
} else {
}
else {
Direction dir = getDirection();
if (offsetx < 0 && offsety < 0) {
if (dir == DIRECTION_SOUTH || dir == DIRECTION_NORTH) {
if (offsetx == -1 && offsety == -1) {
if (dir == DIRECTION_NORTH) {
newDir = DIRECTION_WEST;
}
}
if (dir == DIRECTION_SOUTH) {
newDir = DIRECTION_WEST;
} else if (dir == DIRECTION_EAST) {
}
else if (dir == DIRECTION_EAST) {
newDir = DIRECTION_NORTH;
}
} else if (offsetx < 0 && offsety > 0) {
if (dir == DIRECTION_NORTH || dir == DIRECTION_SOUTH) {
}
else if (offsetx < 0 && offsety > 0) {
if (offsetx == -1 && offsety == 1) {
if (dir == DIRECTION_SOUTH) {
newDir = DIRECTION_WEST;
}
}
if (dir == DIRECTION_NORTH) {
newDir = DIRECTION_WEST;
} else if (dir == DIRECTION_EAST) {
}
else if (dir == DIRECTION_EAST) {
newDir = DIRECTION_SOUTH;
}
} else if (offsetx > 0 && offsety < 0) {
if (dir == DIRECTION_SOUTH || dir == DIRECTION_NORTH) {
}
else if (offsetx > 0 && offsety < 0) {
if (offsetx == 1 && offsety == -1) {
if (dir == DIRECTION_NORTH) {
newDir = DIRECTION_EAST;
}
}
if (dir == DIRECTION_SOUTH) {
newDir = DIRECTION_EAST;
} else if (dir == DIRECTION_WEST) {
}
else if (dir == DIRECTION_WEST) {
newDir = DIRECTION_NORTH;
}
} else {
if (dir == DIRECTION_NORTH || dir == DIRECTION_SOUTH) {
}
else {
if (offsetx == 1 && offsety == 1) {
if (dir == DIRECTION_SOUTH) {
newDir = DIRECTION_EAST;
}
}
if (dir == DIRECTION_NORTH) {
newDir = DIRECTION_EAST;
} else if (dir == DIRECTION_WEST) {
}
else if (dir == DIRECTION_WEST) {
newDir = DIRECTION_SOUTH;
}
}

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -372,6 +372,18 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co
combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE);
combat->setParam(COMBAT_PARAM_BLOCKARMOR, 1);
combat->setParam(COMBAT_PARAM_BLOCKSHIELD, 1);
uint32_t tD = this->getMonsterType(description)->info.targetDistance;
if (tD == 1) {
if (sb.range > 1) {
combat->setOrigin(ORIGIN_RANGED);
}
else {
combat->setOrigin(ORIGIN_MELEE);
}
}
else if (tD > 1 && sb.range > 1) {
combat->setOrigin(ORIGIN_RANGED);
}
} else if (tmpName == "bleed") {
combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE);
} else if (tmpName == "poison" || tmpName == "earth") {

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2017 Alejandro Mujica <alejandrodemujica@gmail.com>
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

Some files were not shown because too many files have changed in this diff Show More