Skip to content

Commit

Permalink
[Feature] Item tiers and new skills (Onslaught, Ruse and Momentum) fr…
Browse files Browse the repository at this point in the history
…om 12.80 (#366)

• Tier system and new skills finished:
The forge will be finished later in the pull request: https://github.com/opentibiabr/canary/pull/543/files
If you find any bugs regarding this commit, please comment on pull 543 and not here.

• Useful information: 
Onslaught (Fatal): https://tibia.fandom.com/wiki/Onslaught
Ruse (block hits): https://tibia.fandom.com/wiki/Ruse
Momentum (cooldown reduction): https://tibia.fandom.com/wiki/Momentum

• For create item with tier, use the talkaction:
/i itemname, itemcount, tiercount
  • Loading branch information
omeranha authored Oct 27, 2022
1 parent addbbb7 commit 898cc6f
Show file tree
Hide file tree
Showing 29 changed files with 810 additions and 712 deletions.
3 changes: 3 additions & 0 deletions config.lua.dist
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ taskHuntingSelectListPrice = 1
taskHuntingBonusRerollPrice = 2
taskHuntingFreeRerollTime = 20 * 60 * 60

-- Forge system
forgeMaxItemTier = 10

-- NOTE: Access only for Premium Account
onlyPremiumAccount = false

Expand Down
7 changes: 4 additions & 3 deletions data/migrations/2.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-- return true = There are others migrations file
-- return false = This is the last migration file
function onUpdateDatabase()
return false
Spdlog.info("Updating database to version 3 (Add tier table to market_offers and market_history)")
db.query("ALTER TABLE `market_offers` ADD `tier` tinyint UNSIGNED NOT NULL DEFAULT '0';")
db.query("ALTER TABLE `market_history` ADD `tier` tinyint UNSIGNED NOT NULL DEFAULT '0';")
return true
end
5 changes: 5 additions & 0 deletions data/migrations/3.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- return true = There are others migrations file
-- return false = This is the last migration file
function onUpdateDatabase()
return false
end
10 changes: 9 additions & 1 deletion data/scripts/talkactions/god/create_item.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,15 @@ function createItem.onSay(player, words, param)
end
end

local result = player:addItem(itemType:getId(), count)
local tier = tonumber(split[3])
if tier then
if tier <= 0 or tier > 10 then
player:sendCancelMessage("Invalid tier count.")
return false
end
end

local result = player:addItem(itemType:getId(), count, true, 0, CONST_SLOT_WHEREEVER, tier)
if result then
if not itemType:isStackable() then
if type(result) == "table" then
Expand Down
4 changes: 3 additions & 1 deletion schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS `server_config` (
CONSTRAINT `server_config_pk` PRIMARY KEY (`config`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '2'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0');
INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '3'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0');

-- Table structure `accounts`
CREATE TABLE IF NOT EXISTS `accounts` (
Expand Down Expand Up @@ -427,6 +427,7 @@ CREATE TABLE IF NOT EXISTS `market_history` (
`expires_at` bigint(20) UNSIGNED NOT NULL,
`inserted` bigint(20) UNSIGNED NOT NULL,
`state` tinyint(1) UNSIGNED NOT NULL,
`tier` tinyint UNSIGNED NOT NULL DEFAULT '0',
INDEX `player_id` (`player_id`,`sale`),
CONSTRAINT `market_history_pk` PRIMARY KEY (`id`),
CONSTRAINT `market_history_players_fk`
Expand All @@ -444,6 +445,7 @@ CREATE TABLE IF NOT EXISTS `market_offers` (
`created` bigint(20) UNSIGNED NOT NULL,
`anonymous` tinyint(1) NOT NULL DEFAULT '0',
`price` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
`tier` tinyint UNSIGNED NOT NULL DEFAULT '0',
INDEX `sale` (`sale`,`itemtype`),
INDEX `created` (`created`),
INDEX `player_id` (`player_id`),
Expand Down
1 change: 1 addition & 0 deletions src/config/config_definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ enum integerConfig_t {
DEPOTCHEST,
CRITICALCHANCE,
ADVENTURERSBLESSING_LEVEL,
MAX_ITEM_FORGE_TIER,

LAST_INTEGER_CONFIG
};
Expand Down
1 change: 1 addition & 0 deletions src/config/configmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ bool ConfigManager::load()

boolean[INVENTORY_GLOW] = getGlobalBoolean(L, "inventoryGlowOnFiveBless", false);
integer[ADVENTURERSBLESSING_LEVEL] = getGlobalNumber(L, "adventurersBlessingLevel", 21);
integer[MAX_ITEM_FORGE_TIER] = getGlobalNumber(L, "forgeMaxItemTier", 10);

floating[RATE_HEALTH_REGEN] = getGlobalFloat(L, "rateHealthRegen", 1.0);
floating[RATE_HEALTH_REGEN_SPEED] = getGlobalFloat(L, "rateHealthRegenSpeed", 1.0);
Expand Down
90 changes: 60 additions & 30 deletions src/creatures/combat/combat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ void Combat::CombatConditionFunc(Creature* caster, Creature* target, const Comba

void Combat::CombatDispelFunc(Creature*, Creature* target, const CombatParams& params, CombatDamage*)
{
if(target) {
if (target) {
target->removeCombatCondition(params.dispelType);
}
}
Expand Down Expand Up @@ -838,14 +838,16 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat*
}
//
CombatDamage tmpDamage;
if(data) {
tmpDamage.origin = data->origin;
tmpDamage.primary.type = data->primary.type;
tmpDamage.primary.value = data->primary.value;
tmpDamage.secondary.type = data->secondary.type;
tmpDamage.secondary.value = data->secondary.value;
tmpDamage.critical = data->critical;
}
if (data) {
tmpDamage.origin = data->origin;
tmpDamage.primary.type = data->primary.type;
tmpDamage.primary.value = data->primary.value;
tmpDamage.secondary.type = data->secondary.type;
tmpDamage.secondary.value = data->secondary.value;
tmpDamage.critical = data->critical;
tmpDamage.fatal = data->fatal;
}

tmpDamage.affected = affected;
for (Tile* tile : tileList) {
if (canDoCombat(caster, tile, params.aggressive) != RETURNVALUE_NOERROR) {
Expand Down Expand Up @@ -893,12 +895,12 @@ void Combat::doCombatHealth(Creature* caster, Creature* target, CombatDamage& da
}

if (params.combatType == COMBAT_HEALING && target->getMonster()){
if (target != caster) {
if (target != caster) {
return;
}
}

if(caster && caster->getPlayer()){
if (caster && caster->getPlayer()) {
// Critical damage
uint16_t chance = caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE);
// Charm low blow rune)
Expand All @@ -919,6 +921,16 @@ void Combat::doCombatHealth(Creature* caster, Creature* target, CombatDamage& da
damage.primary.value += (damage.primary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
damage.secondary.value += (damage.secondary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
}

// fatal hit (onslaught)
if (caster->getPlayer()->getInventoryItem(CONST_SLOT_LEFT) != nullptr) {
double_t fatalChance = caster->getPlayer()->getInventoryItem(CONST_SLOT_LEFT)->getFatalChance();
if (damage.primary.type != COMBAT_HEALING && fatalChance > 0 && uniform_random(1, 100) <= fatalChance) {
damage.fatal = true;
damage.primary.value += static_cast<int32_t>(std::round(damage.primary.value * 0.6));
damage.secondary.value += static_cast<int32_t>(std::round(damage.secondary.value * 0.6));
}
}
}
if (canCombat) {
if (target && caster && params.distanceEffect != CONST_ANI_NONE) {
Expand All @@ -934,28 +946,43 @@ void Combat::doCombatHealth(Creature* caster, Creature* target, CombatDamage& da

void Combat::doCombatHealth(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params)
{
if(caster && caster->getPlayer()){
// Critical damage
uint16_t chance = caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE);
if (damage.primary.type != COMBAT_HEALING && chance != 0 && uniform_random(1, 100) <= chance) {
damage.critical = true;
damage.primary.value += (damage.primary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
damage.secondary.value += (damage.secondary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
if (caster && caster->getPlayer())
{
// Critical damage
if (uint16_t chance = caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE);
damage.primary.type != COMBAT_HEALING && chance != 0 && uniform_random(1, 100) <= chance)
{
damage.critical = true;
damage.primary.value += (damage.primary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
damage.secondary.value += (damage.secondary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
}

// fatal hit (onslaught)
if (caster->getPlayer()->getInventoryItem(CONST_SLOT_LEFT) != nullptr)
{
double_t fatalChance = caster->getPlayer()->getInventoryItem(CONST_SLOT_LEFT)->getFatalChance();
if (damage.primary.type != COMBAT_HEALING && fatalChance > 0 && uniform_random(1, 100) <= fatalChance) {
damage.fatal = true;
damage.primary.value += static_cast<int32_t>(std::round(damage.primary.value * 0.6));
damage.secondary.value += static_cast<int32_t>(std::round(damage.secondary.value * 0.6));
}
}
}
CombatFunc(caster, position, area, params, CombatHealthFunc, &damage);
}

void Combat::doCombatMana(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params)
{
bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target) == RETURNVALUE_NOERROR);
if ( (caster && target)
&& (caster == target || canCombat)
&& (params.impactEffect != CONST_ME_NONE)) {
if ((caster && target)
&& (caster == target || canCombat)
&& (params.impactEffect != CONST_ME_NONE))
{
g_game().addMagicEffect(target->getPosition(), params.impactEffect);
}

if(caster && caster->getPlayer()){
if (caster && caster->getPlayer())
{
// Critical damage
uint16_t chance = caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE);
if (chance != 0 && uniform_random(1, 100) <= chance) {
Expand All @@ -964,6 +991,7 @@ void Combat::doCombatMana(Creature* caster, Creature* target, CombatDamage& dama
damage.secondary.value += (damage.secondary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
}
}

if (canCombat) {
if (caster && params.distanceEffect != CONST_ANI_NONE) {
addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect);
Expand All @@ -978,15 +1006,17 @@ void Combat::doCombatMana(Creature* caster, Creature* target, CombatDamage& dama

void Combat::doCombatMana(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params)
{
if(caster && caster->getPlayer()){
// Critical damage
uint16_t chance = caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE);
if (chance != 0 && uniform_random(1, 100) <= chance) {
damage.critical = true;
damage.primary.value += (damage.primary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
damage.secondary.value += (damage.secondary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
}
if (caster && caster->getPlayer())
{
// Critical damage
uint16_t chance = caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE);
if (chance != 0 && uniform_random(1, 100) <= chance)
{
damage.critical = true;
damage.primary.value += (damage.primary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
damage.secondary.value += (damage.secondary.value * caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE ))/100;
}
}
CombatFunc(caster, position, area, params, CombatManaFunc, &damage);
}

Expand Down
13 changes: 10 additions & 3 deletions src/creatures/creatures_definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,8 @@ enum BlockType_t : uint8_t {
BLOCK_NONE,
BLOCK_DEFENSE,
BLOCK_ARMOR,
BLOCK_IMMUNITY
BLOCK_IMMUNITY,
BLOCK_DODGE
};

enum BestiaryType_t : uint8_t {
Expand Down Expand Up @@ -677,6 +678,7 @@ struct MarketOffer {
uint16_t amount;
uint16_t counter;
uint16_t itemId;
uint8_t tier;
std::string playerName;
};

Expand All @@ -691,6 +693,7 @@ struct MarketOfferEx {
counter(other.counter),
itemId(other.itemId),
type(other.type),
tier(other.tier),
playerName(std::move(other.playerName)) {}

uint32_t id;
Expand All @@ -701,6 +704,7 @@ struct MarketOfferEx {
uint16_t counter;
uint16_t itemId;
MarketAction_t type;
uint8_t tier;
std::string playerName;
};

Expand All @@ -709,6 +713,7 @@ struct HistoryMarketOffer {
uint64_t price;
uint16_t itemId;
uint16_t amount;
uint8_t tier;
MarketOfferState_t state;
};

Expand Down Expand Up @@ -766,6 +771,7 @@ struct CombatDamage {
int affected;
bool extension;
std::string exString;
bool fatal;

CombatDamage() {
origin = ORIGIN_NONE;
Expand All @@ -775,6 +781,7 @@ struct CombatDamage {
affected = 1;
extension = false;
exString = "";
fatal = false;
}
};

Expand Down Expand Up @@ -891,8 +898,8 @@ struct PartyAnalyzer {
uint64_t lootPrice = 0;
uint64_t supplyPrice = 0;

std::map<uint16_t, uint32_t> lootMap; // [itemID] = amount
std::map<uint16_t, uint32_t> supplyMap; // [itemID] = amount
std::map<uint16_t, uint64_t> lootMap; // [itemID] = amount
std::map<uint16_t, uint64_t> supplyMap; // [itemID] = amount
};

#endif // SRC_CREATURES_CREATURES_DEFINITIONS_HPP_
40 changes: 36 additions & 4 deletions src/creatures/npcs/npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,13 +312,45 @@ void Npc::onPlayerSellItem(Player* player, uint16_t itemId,
}
}

if(!player->removeItemOfType(itemId, amount, -1, ignore, false)) {
SPDLOG_ERROR("[Npc::onPlayerSellItem] - Player {} have a problem for sell item {} on shop for npc {}", player->getName(), itemId, getName());
auto removeAmount = amount;
auto inventoryItems = player->getInventoryItemsFromId(itemId, ignore);
uint16_t removedItems = 0;
for (auto item : inventoryItems) {
// Ignore item with tier highter than 0
if (!item || item->getTier() > 0) {
continue;
}

// Only remove if item has no imbuements
if (!item->hasImbuements()) {
auto removeCount = std::min<uint16_t>(removeAmount, item->getItemCount());
removeAmount -= removeCount;

if (auto ret = g_game().internalRemoveItem(item, removeCount);
ret != RETURNVALUE_NOERROR)
{
SPDLOG_ERROR("[Npc::onPlayerSellItem] - Player {} have a problem for sell item {} on shop for npc {}", player->getName(), item->getID(), getName());
continue;
}

// We will use it to check how many items have been removed to send totalCost
removedItems++;

if (removeAmount == 0) {
break;
}
}
}

uint64_t totalCost = 0;
// We will only add the money if any item has been removed from the player, to ensure that there is no possibility of cloning money
if (removedItems == 0) {
SPDLOG_ERROR("[Npc::onPlayerSellItem] - Player {} have a problem for remove items from id {} on shop for npc {}", player->getName(), itemId, getName());
return;
}

int64_t totalCost = sellPrice * amount;
g_game().addMoney(player, totalCost, 0);
totalCost = static_cast<uint64_t>(sellPrice * removedItems);
g_game().addMoney(player, totalCost);

// npc:onSellItem(player, itemId, subType, amount, ignore, itemName, totalCost)
CreatureCallback callback = CreatureCallback(npcType->info.scriptInterface, this);
Expand Down
4 changes: 2 additions & 2 deletions src/creatures/players/grouping/party.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ void Party::addPlayerLoot(const Player* player, const Item* item)
if (priceType == LEADER_PRICE) {
playerAnalyzer->lootPrice += leader->getItemCustomPrice(item->getID()) * count;
} else {
std::map<uint16_t, uint32_t> itemMap {{item->getID(), count}};
std::map<uint16_t, uint64_t> itemMap {{item->getID(), count}};
playerAnalyzer->lootPrice += g_game().getItemMarketPrice(itemMap, false);
}
updateTrackerAnalyzer();
Expand All @@ -618,7 +618,7 @@ void Party::addPlayerSupply(const Player* player, const Item* item)
if (priceType == LEADER_PRICE) {
playerAnalyzer->supplyPrice += leader->getItemCustomPrice(item->getID(), true);
} else {
std::map<uint16_t, uint32_t> itemMap {{item->getID(), 1}};
std::map<uint16_t, uint64_t> itemMap {{item->getID(), 1}};
playerAnalyzer->supplyPrice += g_game().getItemMarketPrice(itemMap, true);
}
updateTrackerAnalyzer();
Expand Down
Loading

0 comments on commit 898cc6f

Please sign in to comment.