Skip to content

Commit

Permalink
Merge branch 'main' into tibia1410
Browse files Browse the repository at this point in the history
  • Loading branch information
jprzimba committed Dec 21, 2024
2 parents 30f2f80 + 8840971 commit 8f46ad7
Show file tree
Hide file tree
Showing 17 changed files with 142 additions and 68 deletions.
1 change: 1 addition & 0 deletions config.lua.dist
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ orangeSkullDuration = 7
blackSkulledDeathHealth = 40
blackSkulledDeathMana = 0
fieldOwnershipDuration = 5 * 1000
loginProtectionPeriod = 10 * 1000

cleanProtectionZones = false

Expand Down
Binary file modified data-global/world/world.7z
Binary file not shown.
12 changes: 6 additions & 6 deletions data/libs/systems/hazard.lua
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,16 @@ function HazardMonster.onSpawn(monster, position)
if not zones then
return true
end

logger.debug("Monster {} spawned in hazard zone, position {}", monster:getName(), position:toString())
for _, zone in ipairs(zones) do
local hazard = Hazard.getByName(zone:getName())
if hazard then
monster:hazard(true)
if hazard then
monster:hazardCrit(hazard.crit)
monster:hazardDodge(hazard.dodge)
monster:hazardDamageBoost(hazard.damageBoost)
monster:hazardDefenseBoost(hazard.defenseBoost)
end
monster:hazardCrit(hazard.crit)
monster:hazardDodge(hazard.dodge)
monster:hazardDamageBoost(hazard.damageBoost)
monster:hazardDefenseBoost(hazard.defenseBoost)
end
end
return true
Expand Down
6 changes: 5 additions & 1 deletion data/scripts/lib/register_monster_type.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ end
registerMonsterType.name = function(mtype, mask)
if mask.name then
mtype:name(mask.name)
-- Try register hazard monsters
mtype.onSpawn = function(monster, spawnPosition)
HazardMonster.onSpawn(monster, spawnPosition)
end
end
end
registerMonsterType.description = function(mtype, mask)
Expand Down Expand Up @@ -194,7 +198,7 @@ registerMonsterType.flags = function(mtype, mask)
end
if mask.flags.rewardBoss then
mtype:isRewardBoss(mask.flags.rewardBoss)
mtype.onSpawn = function(monster)
mtype.onSpawn = function(monster, spawnPosition)
monster:setRewardBoss()
end
end
Expand Down
2 changes: 1 addition & 1 deletion markdowns/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
- New protocol 14.05 assets. ([Tryller](https://github.com/jprzimba))
- Fix gotoHouse talkaction. ([Tryller](https://github.com/jprzimba))
- Optimized the `onPlayerSellAllLoot` code to prevent prolonged freezes. ([Tryller](https://github.com/jprzimba))
- Add new configurable featurees in `config.lua`: `chainSystemVipOnly`, `fieldOwnershipDuration`, `bedsOnlyPremium`. ([Tryller](https://github.com/jprzimba))
- Add new configurable featurees in `config.lua`: `chainSystemVipOnly`, `fieldOwnershipDuration`, `bedsOnlyPremium`, `loginProtectionPeriod`. ([Tryller](https://github.com/jprzimba))

## Added files

Expand Down
1 change: 1 addition & 0 deletions src/config/config_enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,4 +348,5 @@ enum ConfigKey_t : uint16_t {
CHAIN_SYSTEM_VIP_ONLY,
FIELD_OWNERSHIP,
BEDS_ONLY_PREMIUM,
LOGIN_PROTECTION,
};
3 changes: 2 additions & 1 deletion src/config/configmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ bool ConfigManager::load() {
loadBoolConfig(L, HALF_LOSS_SKILL, "halfLossSkill", true);
loadBoolConfig(L, HALF_LOSS_MAGIC, "halfLossMagicLevel", true);
loadBoolConfig(L, CHAIN_SYSTEM_VIP_ONLY, "chainSystemVipOnly", false);
loadBoolConfig(L, BEDS_ONLY_PREMIUM, "bedsOnlyPremium", true);

loadFloatConfig(L, BESTIARY_RATE_CHARM_SHOP_PRICE, "bestiaryRateCharmShopPrice", 1.0);
loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_AXE, "combatChainSkillFormulaAxe", 0.9);
Expand Down Expand Up @@ -212,7 +213,6 @@ bool ConfigManager::load() {
loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_DISTANCE, "combatChainSkillFormulaDistance", 0.9);
loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_MISSILE, "combatChainSkillFormulaMissile", 0.9);
loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_WANDS_AND_RODS, "combatChainSkillFormulaWandsAndRods", 1.0);
loadFloatConfig(L, BEDS_ONLY_PREMIUM, "bedsOnlyPremium", true);

loadIntConfig(L, ACTIONS_DELAY_INTERVAL, "timeBetweenActions", 200);
loadIntConfig(L, ADVENTURERSBLESSING_LEVEL, "adventurersBlessingLevel", 21);
Expand Down Expand Up @@ -363,6 +363,7 @@ bool ConfigManager::load() {
loadIntConfig(L, BLACK_SKULL_DEATH_HEALTH, "blackSkulledDeathHealth", 40);
loadIntConfig(L, BLACK_SKULL_DEATH_MANA, "blackSkulledDeathMana", 0);
loadIntConfig(L, FIELD_OWNERSHIP, "fieldOwnershipDuration", 5 * 1000);
loadIntConfig(L, LOGIN_PROTECTION, "loginProtectionPeriod", 10 * 1000);

loadStringConfig(L, CORE_DIRECTORY, "coreDirectory", "data");
loadStringConfig(L, DATA_DIRECTORY, "dataPackDirectory", "data-global");
Expand Down
2 changes: 1 addition & 1 deletion src/creatures/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ bool Creature::dropCorpse(const std::shared_ptr<Creature> &lastHitCreature, cons
return true;
}

bool Creature::hasBeenAttacked(uint32_t attackerId) {
bool Creature::hasBeenAttacked(uint32_t attackerId) const {
auto it = damageMap.find(attackerId);
if (it == damageMap.end()) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion src/creatures/creature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ class Creature : virtual public Thing, public SharedObject {
void onDeath();
virtual uint64_t getGainedExperience(const std::shared_ptr<Creature> &attacker) const;
void addDamagePoints(const std::shared_ptr<Creature> &attacker, int32_t damagePoints);
bool hasBeenAttacked(uint32_t attackerId);
bool hasBeenAttacked(uint32_t attackerId) const;

// combat event functions
virtual void onAddCondition(ConditionType_t type);
Expand Down
17 changes: 14 additions & 3 deletions src/creatures/monsters/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,11 @@ void Monster::onCreatureMove(const std::shared_ptr<Creature> &creature, const st
}
}
} else if (isOpponent(creature)) {
auto player = std::dynamic_pointer_cast<Player>(creature);
if (player && player->checkLoginDelay(player->getID())) {
return;
}

// we have no target lets try pick this one
g_dispatcher().addEvent([selfWeak = std::weak_ptr(getMonster()), creatureWeak = std::weak_ptr(creature)] {
const auto &self = selfWeak.lock();
Expand Down Expand Up @@ -523,9 +528,9 @@ void Monster::onAttackedByPlayer(const std::shared_ptr<Player> &attackerPlayer)
}
}

void Monster::onSpawn() {
void Monster::onSpawn(const Position &position) {
if (mType->info.spawnEvent != -1) {
// onSpawn(self)
// onSpawn(self, spawnPosition)
LuaScriptInterface* scriptInterface = mType->info.scriptInterface;
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("Monster {} creature {}] Call stack overflow. Too many lua "
Expand All @@ -542,8 +547,9 @@ void Monster::onSpawn() {

LuaScriptInterface::pushUserdata<Monster>(L, getMonster());
LuaScriptInterface::setMetatable(L, -1, "Monster");
LuaScriptInterface::pushPosition(L, position);

scriptInterface->callVoidFunction(1);
scriptInterface->callVoidFunction(2);
}
}

Expand Down Expand Up @@ -954,6 +960,11 @@ bool Monster::selectTarget(const std::shared_ptr<Creature> &creature) {
return false;
}

auto player = std::dynamic_pointer_cast<Player>(creature);
if (player && player->checkLoginDelay(player->getID())) {
return false;
}

auto it = getTargetIterator(creature);
if (it == targetList.end()) {
// Target not found in our target list.
Expand Down
2 changes: 1 addition & 1 deletion src/creatures/monsters/monster.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class Monster final : public Creature {
void onCreatureMove(const std::shared_ptr<Creature> &creature, const std::shared_ptr<Tile> &newTile, const Position &newPos, const std::shared_ptr<Tile> &oldTile, const Position &oldPos, bool teleport) override;
void onCreatureSay(const std::shared_ptr<Creature> &creature, SpeakClasses type, const std::string &text) override;
void onAttackedByPlayer(const std::shared_ptr<Player> &attackerPlayer);
void onSpawn();
void onSpawn(const Position &position);

void drainHealth(const std::shared_ptr<Creature> &attacker, int32_t damage) override;
void changeHealth(int32_t healthChange, bool sendHealthChange = true) override;
Expand Down
2 changes: 1 addition & 1 deletion src/creatures/monsters/spawns/spawn_monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, spawnBlock_t &sb, const

spawnedMonsterMap[spawnMonsterId] = monster;
sb.lastSpawn = OTSYS_TIME();
monster->onSpawn();
monster->onSpawn(sb.pos);
return true;
}

Expand Down
143 changes: 94 additions & 49 deletions src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ MuteCountMap Player::muteCountMap;
Player::Player(std::shared_ptr<ProtocolGame> p) :
lastPing(OTSYS_TIME()),
lastPong(lastPing),
lastLoad(OTSYS_TIME()),
inbox(std::make_shared<Inbox>(ITEM_INBOX)),
client(std::move(p)) {
m_playerVIP = std::make_unique<PlayerVIP>(*this);
Expand Down Expand Up @@ -3803,19 +3804,42 @@ bool Player::dropCorpse(const std::shared_ptr<Creature> &lastHitCreature, const
}

std::shared_ptr<Item> Player::getCorpse(const std::shared_ptr<Creature> &lastHitCreature, const std::shared_ptr<Creature> &mostDamageCreature) {
const auto &corpse = Creature::getCorpse(lastHitCreature, mostDamageCreature);
if (corpse && corpse->getContainer()) {
std::ostringstream ss;
if (lastHitCreature) {
std::string subjectPronoun = getSubjectPronoun();
capitalizeWords(subjectPronoun);
ss << "You recognize " << getNameDescription() << ". " << subjectPronoun << " " << getSubjectVerb(true) << " killed by " << lastHitCreature->getNameDescription() << '.';
} else {
ss << "You recognize " << getNameDescription() << '.';
auto corpse = Creature::getCorpse(lastHitCreature, mostDamageCreature);
if (!corpse || !corpse->getContainer()) {
return nullptr;
}

std::ostringstream descriptionStream;
std::string subjectPronoun = getSubjectPronoun();
capitalizeWords(subjectPronoun);

if (damageMap.empty()) {
descriptionStream << fmt::format("You recognize {}.", getNameDescription());
} else {
descriptionStream << fmt::format("You recognize {}. {} was killed by ", getNameDescription(), subjectPronoun);

std::vector<std::string> killers;
for (const auto& [creatureId, damageInfo] : damageMap) {
auto damageDealer = g_game().getCreatureByID(creatureId);
if (damageDealer) {
killers.push_back(damageDealer->getNameDescription());
}
}

corpse->setAttribute(ItemAttribute_t::DESCRIPTION, ss.str());
if (killers.empty()) {
descriptionStream << "an unknown attacker";
} else {
for (size_t i = 0; i < killers.size(); ++i) {
if (i > 0) {
descriptionStream << (i == killers.size() - 1 ? " and " : ", ");
}
descriptionStream << killers[i];
}
}
descriptionStream << '.';
}

corpse->setAttribute(ItemAttribute_t::DESCRIPTION, descriptionStream.str());
return corpse;
}

Expand Down Expand Up @@ -5665,6 +5689,11 @@ void Player::onAttacked() {
addInFightTicks();
}

bool Player::checkLoginDelay(uint32_t playerId) const
{
return (OTSYS_TIME() <= (lastLoad + g_configManager().getNumber(LOGIN_PROTECTION)) && !hasBeenAttacked(playerId));
}

void Player::onIdleStatus() {
Creature::onIdleStatus();

Expand Down Expand Up @@ -8929,55 +8958,64 @@ void Player::forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemId, uint
history.success = success;
history.tierLoss = reduceTierLoss;

const auto &firstForgingItem = getForgeItemFromId(firstItemId, tier);
if (!firstForgingItem) {
g_logger().error("[Log 1] Player with name {} failed to fuse item with id {}", getName(), firstItemId);
const auto &exaltationChest = Item::CreateItem(ITEM_EXALTATION_CHEST, 1);
if (!exaltationChest) {
g_logger().error("Failed to create exaltation chest");
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
const auto &exaltationContainer = exaltationChest->getContainer();
if (!exaltationContainer) {
g_logger().error("Failed to create exaltation container");
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
auto returnValue = g_game().internalRemoveItem(firstForgingItem, 1);

auto returnValue = queryAdd(CONST_SLOT_BACKPACK, exaltationContainer, 1, 0);
if (returnValue != RETURNVALUE_NOERROR) {
g_logger().error("[Log 1] Failed to remove forge item {} from player with name {}", firstItemId, getName());
g_logger().error("[Log 1] Failed to add forge item {} from player with name {}", firstItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
const auto &secondForgingItem = getForgeItemFromId(secondItemId, tier);
if (!secondForgingItem) {
g_logger().error("[Log 2] Player with name {} failed to fuse item with id {}", getName(), secondItemId);

const auto &firstForgedItem = Item::CreateItem(firstItemId, 1);
if (!firstForgedItem) {
g_logger().error("[Log 3] Player with name {} failed to fuse item with id {}", getName(), firstItemId);
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
if (returnValue = g_game().internalRemoveItem(secondForgingItem, 1);
returnValue != RETURNVALUE_NOERROR) {
g_logger().error("[Log 2] Failed to remove forge item {} from player with name {}", secondItemId, getName());

returnValue = g_game().internalAddItem(exaltationContainer, firstForgedItem, INDEX_WHEREEVER);
if (returnValue != RETURNVALUE_NOERROR) {
g_logger().error("[Log 1] Failed to add forge item {} from player with name {}", firstItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}

const auto &exaltationChest = Item::CreateItem(ITEM_EXALTATION_CHEST, 1);
if (!exaltationChest) {
g_logger().error("Failed to create exaltation chest");
const auto &firstForgingItem = getForgeItemFromId(firstItemId, tier);
if (!firstForgingItem) {
g_logger().error("[Log 1] Player with name {} failed to fuse item with id {}", getName(), firstItemId);
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
const auto &exaltationContainer = exaltationChest->getContainer();
if (!exaltationContainer) {
g_logger().error("Failed to create exaltation container");
returnValue = g_game().internalRemoveItem(firstForgingItem, 1);
if (returnValue != RETURNVALUE_NOERROR) {
g_logger().error("[Log 1] Failed to remove forge item {} from player with name {}", firstItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}

const auto &firstForgedItem = Item::CreateItem(firstItemId, 1);
if (!firstForgedItem) {
g_logger().error("[Log 3] Player with name {} failed to fuse item with id {}", getName(), firstItemId);
const auto &secondForgingItem = getForgeItemFromId(secondItemId, tier);
if (!secondForgingItem) {
g_logger().error("[Log 2] Player with name {} failed to fuse item with id {}", getName(), secondItemId);
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
returnValue = g_game().internalAddItem(exaltationContainer, firstForgedItem, INDEX_WHEREEVER);
if (returnValue != RETURNVALUE_NOERROR) {
g_logger().error("[Log 1] Failed to add forge item {} from player with name {}", firstItemId, getName());
if (returnValue = g_game().internalRemoveItem(secondForgingItem, 1);
returnValue != RETURNVALUE_NOERROR) {
g_logger().error("[Log 2] Failed to remove forge item {} from player with name {}", secondItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
Expand Down Expand Up @@ -9144,7 +9182,7 @@ void Player::forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemId, uint

returnValue = g_game().internalAddItem(static_self_cast<Player>(), exaltationContainer, INDEX_WHEREEVER);
if (returnValue != RETURNVALUE_NOERROR) {
g_logger().error("Failed to add exaltation chest to player with name {}", getName());
g_logger().error("Failed to add exaltation chest to player with name {}", fmt::underlying(ITEM_EXALTATION_CHEST), getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
Expand All @@ -9171,13 +9209,33 @@ void Player::forgeTransferItemTier(ForgeAction_t actionType, uint16_t donorItemI
history.tier = tier;
history.success = true;

const auto &exaltationChest = Item::CreateItem(ITEM_EXALTATION_CHEST, 1);
if (!exaltationChest) {
g_logger().error("Exaltation chest is nullptr");
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
const auto &exaltationContainer = exaltationChest->getContainer();
if (!exaltationContainer) {
g_logger().error("Exaltation container is nullptr");
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}

auto returnValue = queryAdd(CONST_SLOT_BACKPACK, exaltationContainer, 1, 0);
if (returnValue != RETURNVALUE_NOERROR) {
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}

const auto &donorItem = getForgeItemFromId(donorItemId, tier);
if (!donorItem) {
g_logger().error("[Log 1] Player with name {} failed to transfer item with id {}", getName(), donorItemId);
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
auto returnValue = g_game().internalRemoveItem(donorItem, 1);
returnValue = g_game().internalRemoveItem(donorItem, 1);
if (returnValue != RETURNVALUE_NOERROR) {
g_logger().error("[Log 1] Failed to remove transfer item {} from player with name {}", donorItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
Expand All @@ -9199,19 +9257,6 @@ void Player::forgeTransferItemTier(ForgeAction_t actionType, uint16_t donorItemI
return;
}

const auto &exaltationChest = Item::CreateItem(ITEM_EXALTATION_CHEST, 1);
if (!exaltationChest) {
g_logger().error("Exaltation chest is nullptr");
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}
const auto &exaltationContainer = exaltationChest->getContainer();
if (!exaltationContainer) {
g_logger().error("Exaltation container is nullptr");
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
}

const auto &newReceiveItem = Item::CreateItem(receiveItemId, 1);
if (!newReceiveItem) {
g_logger().error("[Log 6] Player with name {} failed to fuse item with id {}", getName(), receiveItemId);
Expand Down
Loading

0 comments on commit 8f46ad7

Please sign in to comment.