Skip to content

Commit

Permalink
badge system: improvements and created talkactions to manage badges.
Browse files Browse the repository at this point in the history
friend system: wip.
  • Loading branch information
elsongabriel committed Apr 26, 2024
1 parent a820101 commit cbd9d44
Show file tree
Hide file tree
Showing 18 changed files with 473 additions and 79 deletions.
33 changes: 33 additions & 0 deletions data/scripts/talkactions/god/manage_badge.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
local addBadge = TalkAction("/addbadge")

function addBadge.onSay(player, words, param)
-- create log
logCommand(player, words, param)

if param == "" then
player:sendCancelMessage("Command param required.")
return true
end

local split = param:split(",")
if not split[2] then
player:sendCancelMessage("Insufficient parameters. Usage: /addbadge playerName, badgeID")
return true
end

local target = Player(split[1])
if not target then
player:sendCancelMessage("A player with that name is not online.")
return true
end

-- Trim left
split[2] = split[2]:gsub("^%s*(.-)$", "%1")
local id = tonumber(split[2])
target:addBadge(id)
return true
end

addBadge:separator(" ")
addBadge:groupType("god")
addBadge:register()
25 changes: 25 additions & 0 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,31 @@ CREATE TABLE IF NOT EXISTS `player_storage` (
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- Table structure `player_summary`
CREATE TABLE IF NOT EXISTS `player_summary` (
`player_id` int(11) NOT NULL,
`title` VARCHAR(250) NOT NULL DEFAULT '0',
`charms` VARCHAR(250) NOT NULL DEFAULT '0',
`goshnar` VARCHAR(250) NOT NULL DEFAULT '0',
`drome` VARCHAR(250) NOT NULL DEFAULT '0',
`xp_boosts` VARCHAR(250) NOT NULL DEFAULT '0',
`rewards_collection` VARCHAR(250) NOT NULL DEFAULT '0',
`prey_cards` VARCHAR(250) NOT NULL DEFAULT '0',
`hirelings` VARCHAR(250) NOT NULL DEFAULT '0',
`achievements_points` VARCHAR(250) NOT NULL DEFAULT '0',
`login_streak` VARCHAR(250) NOT NULL DEFAULT '0',
`task_points` VARCHAR(250) NOT NULL DEFAULT '0',
`map_area` VARCHAR(250) NOT NULL DEFAULT '0',
`hireling_outfits` BLOB NULL,
`hireling_jobs` BLOB NULL,
`house_items` BLOB NULL,
`blessings` BLOB NULL,
`achievements_unlockeds` BLOB NULL,
INDEX `player_id` (`player_id`),
FOREIGN KEY (`player_id`) REFERENCES `players` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- Table structure `store_history`
CREATE TABLE IF NOT EXISTS `store_history` (
`id` int(11) NOT NULL AUTO_INCREMENT,
Expand Down
1 change: 1 addition & 0 deletions src/canary_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ void CanaryServer::loadModules() {
g_game().loadBoostedCreature();
g_ioBosstiary().loadBoostedBoss();
g_ioprey().initializeTaskHuntOptions();
g_game().getCyclopediaStatistics();
}

void CanaryServer::modulesLoadHelper(bool loaded, std::string moduleName) {
Expand Down
1 change: 1 addition & 0 deletions src/creatures/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ target_sources(${PROJECT_NAME}_lib PRIVATE
players/storages/storages.cpp
players/player.cpp
players/achievement/player_achievement.cpp
players/cyclopedia/player_badge.cpp
players/wheel/player_wheel.cpp
players/wheel/wheel_gems.cpp
players/vocations/vocation.cpp
Expand Down
79 changes: 79 additions & 0 deletions src/creatures/players/cyclopedia/player_badge.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Canary - A free and open-source MMORPG server emulator
* Copyright (©) 2019-2024 OpenTibiaBR <[email protected]>
* Repository: https://github.com/opentibiabr/canary
* License: https://github.com/opentibiabr/canary/blob/main/LICENSE
* Contributors: https://github.com/opentibiabr/canary/graphs/contributors
* Website: https://docs.opentibiabr.com/
*/

#include "pch.hpp"

#include "player_badge.hpp"

#include "creatures/players/player.hpp"
#include "game/game.hpp"
#include "kv/kv.hpp"

PlayerBadge::PlayerBadge(Player &player) :
m_player(player) { }

bool PlayerBadge::hasBadge(uint8_t id) const {
if (id == 0) {
return false;
}

if (auto it = std::find_if(m_badgesUnlocked.begin(), m_badgesUnlocked.end(), [id](auto badge_it) {
return badge_it.first.m_id == id;
});
it != m_badgesUnlocked.end()) {
return true;
}

return false;
}

bool PlayerBadge::add(uint8_t id, uint32_t timestamp /* = 0*/) {
if (hasBadge(id)) {
return false;
}

const Badge &badge = g_game().getBadgeByIdOrName(id);
if (badge.m_id == 0) {
return false;
}

int toSaveTimeStamp = timestamp != 0 ? timestamp : (OTSYS_TIME() / 1000);
getUnlockedKV()->set(badge.m_name, toSaveTimeStamp);
m_badgesUnlocked.push_back({ badge, toSaveTimeStamp });
m_badgesUnlocked.shrink_to_fit();
return true;
}

std::vector<std::pair<Badge, uint32_t>> PlayerBadge::getUnlockedBadges() const {
return m_badgesUnlocked;
}

void PlayerBadge::loadUnlockedBadges() {
const auto &unlockedBadges = getUnlockedKV()->keys();
g_logger().debug("[{}] - Loading unlocked badges: {}", __FUNCTION__, unlockedBadges.size());
for (const auto &badgeName : unlockedBadges) {
const Badge &badge = g_game().getBadgeByIdOrName(0, badgeName);
if (badge.m_id == 0) {
g_logger().error("[{}] - Badge {} not found.", __FUNCTION__, badgeName);
continue;
}

g_logger().debug("[{}] - Badge {} found for player {}.", __FUNCTION__, badge.m_name, m_player.getName());

m_badgesUnlocked.push_back({ badge, getUnlockedKV()->get(badgeName)->getNumber() });
}
}

const std::shared_ptr<KV> &PlayerBadge::getUnlockedKV() {
if (m_badgeUnlockedKV == nullptr) {
m_badgeUnlockedKV = m_player.kv()->scoped("badges")->scoped("unlocked");
}

return m_badgeUnlockedKV;
}
55 changes: 55 additions & 0 deletions src/creatures/players/cyclopedia/player_badge.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Canary - A free and open-source MMORPG server emulator
* Copyright (©) 2019-2024 OpenTibiaBR <[email protected]>
* Repository: https://github.com/opentibiabr/canary
* License: https://github.com/opentibiabr/canary/blob/main/LICENSE
* Contributors: https://github.com/opentibiabr/canary/graphs/contributors
* Website: https://docs.opentibiabr.com/
*/

#pragma once

class Player;
class KV;

struct Badge {
uint8_t m_id = 0;
CyclopediaBadgeType_t m_type;
std::string m_name;
uint16_t m_amount = 0;

Badge() = default;

Badge(uint8_t id, CyclopediaBadgeType_t type, const std::string &name, uint16_t amount) :
m_id(id), m_type(type), m_name(name), m_amount(amount) { }

bool operator==(const Badge &other) const {
return m_id == other.m_id;
}
};

namespace std {
template <>
struct hash<Badge> {
std::size_t operator()(const Badge &b) const {
return hash<uint8_t>()(b.m_id); // Use o ID como base para o hash
}
};
}

class PlayerBadge {
public:
explicit PlayerBadge(Player &player);

[[nodiscard]] bool hasBadge(uint8_t id) const;
bool add(uint8_t id, uint32_t timestamp = 0);
[[nodiscard]] std::vector<std::pair<Badge, uint32_t>> getUnlockedBadges() const;
void loadUnlockedBadges();
const std::shared_ptr<KV> &getUnlockedKV();

private:
// {badge ID, time when it was unlocked}
std::shared_ptr<KV> m_badgeUnlockedKV;
std::vector<std::pair<Badge, uint32_t>> m_badgesUnlocked;
Player &m_player;
};
45 changes: 29 additions & 16 deletions src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "creatures/players/player.hpp"
#include "creatures/players/wheel/player_wheel.hpp"
#include "creatures/players/achievement/player_achievement.hpp"
#include "creatures/players/cyclopedia/player_badge.hpp"
#include "creatures/players/storages/storages.hpp"
#include "game/game.hpp"
#include "game/modal_window/modal_window.hpp"
Expand Down Expand Up @@ -49,6 +50,7 @@ Player::Player(ProtocolGame_ptr p) :
client(std::move(p)) {
m_wheelPlayer = std::make_unique<PlayerWheel>(*this);
m_playerAchievement = std::make_unique<PlayerAchievement>(*this);
m_playerBadge = std::make_unique<PlayerBadge>(*this);
}

Player::~Player() {
Expand Down Expand Up @@ -129,7 +131,7 @@ std::string Player::getDescription(int32_t lookDistance) {
s << " You have no vocation.";
}

if (loyaltyTitle.length() != 0) {
if (!loyaltyTitle.empty()) {
s << " You are a " << loyaltyTitle << ".";
}

Expand All @@ -141,9 +143,8 @@ std::string Player::getDescription(int32_t lookDistance) {
if (!group->access) {
s << " (Level " << level << ')';
}
s << '.';

s << " " << subjectPronoun;
s << ". " << subjectPronoun;

if (group->access) {
s << " " << getSubjectVerb() << " " << group->name << '.';
Expand All @@ -153,7 +154,7 @@ std::string Player::getDescription(int32_t lookDistance) {
s << " has no vocation.";
}

if (loyaltyTitle.length() != 0) {
if (!loyaltyTitle.empty()) {
std::string article = "a";
if (loyaltyTitle[0] == 'A' || loyaltyTitle[0] == 'E' || loyaltyTitle[0] == 'I' || loyaltyTitle[0] == 'O' || loyaltyTitle[0] == 'U') {
article = "an";
Expand Down Expand Up @@ -628,6 +629,19 @@ phmap::flat_hash_map<uint8_t, std::shared_ptr<Item>> Player::getAllSlotItems() c
return itemMap;
}

phmap::flat_hash_map<Blessings_t, std::string> Player::getBlessingNames() const {
return {
{ TWIST_OF_FATE, "Twist of Fate" },
{ WISDOM_OF_SOLITUDE, "The Wisdom of Solitude" },
{ SPARK_OF_THE_PHOENIX, "The Spark of the Phoenix" },
{ FIRE_OF_THE_SUNS, "The Fire of the Suns" },
{ SPIRITUAL_SHIELDING, "The Spiritual Shielding" },
{ EMBRACE_OF_TIBIA, "The Embrace of Tibia" },
{ BLOOD_OF_THE_MOUNTAIN, "Blood of the Mountain" },
{ HEARTH_OF_THE_MOUNTAIN, "Heart of the Mountain" },
};
}

void Player::setTraining(bool value) {
for (const auto &[key, player] : g_game().getPlayers()) {
if (!this->isInGhostMode() || player->isAccessPlayer()) {
Expand Down Expand Up @@ -1925,7 +1939,7 @@ void Player::onWalk(Direction &dir) {

Creature::onWalk(dir);
setNextActionTask(nullptr);

g_callbacks().executeCallback(EventCallback_t::playerOnWalk, &EventCallback::playerOnWalk, getPlayer(), dir);
}

Expand Down Expand Up @@ -6578,24 +6592,14 @@ void Player::initializeTaskHunting() {
}

std::string Player::getBlessingsName() const {
static const phmap::flat_hash_map<Blessings_t, std::string> BlessingNames = {
{ TWIST_OF_FATE, "Twist of Fate" },
{ WISDOM_OF_SOLITUDE, "The Wisdom of Solitude" },
{ SPARK_OF_THE_PHOENIX, "The Spark of the Phoenix" },
{ FIRE_OF_THE_SUNS, "The Fire of the Suns" },
{ SPIRITUAL_SHIELDING, "The Spiritual Shielding" },
{ EMBRACE_OF_TIBIA, "The Embrace of Tibia" },
{ BLOOD_OF_THE_MOUNTAIN, "Blood of the Mountain" },
{ HEARTH_OF_THE_MOUNTAIN, "Heart of the Mountain" },
};

uint8_t count = 0;
std::for_each(blessings.begin(), blessings.end(), [&count](uint8_t amount) {
if (amount != 0) {
count++;
}
});

auto BlessingNames = getBlessingNames();
std::ostringstream os;
for (uint8_t i = 1; i <= 8; i++) {
if (hasBlessing(i)) {
Expand Down Expand Up @@ -7953,6 +7957,15 @@ const std::unique_ptr<PlayerAchievement> &Player::achiev() const {
return m_playerAchievement;
}

// Badge interface
std::unique_ptr<PlayerBadge> &Player::badge() {
return m_playerBadge;
}

const std::unique_ptr<PlayerBadge> &Player::badge() const {
return m_playerBadge;
}

void Player::sendLootMessage(const std::string &message) const {
auto party = getParty();
if (!party) {
Expand Down
Loading

0 comments on commit cbd9d44

Please sign in to comment.