From 2b943610598c193a349107fd9345d321724aff98 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 19 Jan 2014 14:20:57 +0200 Subject: [PATCH] Basic scoreboard implementation --- src/Entities/Player.cpp | 52 +++++++ src/Entities/Player.h | 11 +- src/Scoreboard.cpp | 301 ++++++++++++++++++++++++++++++++++++++++ src/Scoreboard.h | 207 +++++++++++++++++++++++++++ src/World.h | 6 + 5 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 src/Scoreboard.cpp create mode 100644 src/Scoreboard.h diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index c1f2456eb..4f3c6138b 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -74,6 +74,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) , m_IsChargingBow(false) , m_BowCharge(0) , m_FloaterID(-1) + , m_Team(NULL) { LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", a_PlayerName.c_str(), a_Client->GetIPString().c_str(), @@ -790,6 +791,20 @@ void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) return; } } + + if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer())) + { + cPlayer* Attacker = (cPlayer*) a_TDI.Attacker; + + if ((m_Team != NULL) && (m_Team == Attacker->m_Team)) + { + if (!m_Team->GetFriendlyFire()) + { + // Friendly fire is disabled + return; + } + } + } super::DoTakeDamage(a_TDI); @@ -836,6 +851,24 @@ void cPlayer::KilledBy(cEntity * a_Killer) GetWorld()->BroadcastChat(Printf("%s[DEATH] %s%s was killed by a %s", cChatColor::Red.c_str(), cChatColor::White.c_str(), GetName().c_str(), KillerClass.c_str())); } + + class cIncrementCounterCB + : public cObjectiveCallback + { + AString m_Name; + public: + cIncrementCounterCB(const AString & a_Name) : m_Name(a_Name) {} + + virtual bool Item(cObjective * a_Objective) override + { + a_Objective->AddScore(m_Name, 1); + } + } IncrementCounter (GetName()); + + cScoreboard* Scoreboard = m_World->GetScoreBoard(); + + // Update scoreboard objectives + Scoreboard->ForEachObjectiveWith(E_OBJECTIVE_DEATH_COUNT, IncrementCounter); } @@ -916,6 +949,25 @@ bool cPlayer::IsGameModeAdventure(void) const +void cPlayer::SetTeam(cTeam* a_Team) +{ + if (m_Team) + { + m_Team->RemovePlayer(this); + } + + m_Team = a_Team; + + if (m_Team) + { + m_Team->AddPlayer(this); + } +} + + + + + void cPlayer::OpenWindow(cWindow * a_Window) { if (a_Window != m_CurrentWindow) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index bf3ca08e8..52e629dc3 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -13,6 +13,7 @@ class cGroup; class cWindow; class cClientHandle; +class cTeam; @@ -153,6 +154,12 @@ public: AString GetIP(void) const { return m_IP; } // tolua_export + /// Returns the associated team, NULL if none + cTeam* GetTeam(void) { return m_Team; } // tolua_export + + /// Sets the player team, NULL if none + void SetTeam(cTeam* a_Team); + // tolua_end void SetIP(const AString & a_IP); @@ -456,6 +463,8 @@ protected: int m_FloaterID; + cTeam* m_Team; + void ResolvePermissions(void); @@ -463,7 +472,7 @@ protected: virtual void Destroyed(void); - /// Filters out damage for creative mode + /// Filters out damage for creative mode/friendly fire virtual void DoTakeDamage(TakeDamageInfo & TDI) override; /// Called in each tick to handle food-related processing diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp new file mode 100644 index 000000000..1d2711ca0 --- /dev/null +++ b/src/Scoreboard.cpp @@ -0,0 +1,301 @@ + +// Scoreboard.cpp + +// Implementation of a scoreboard that keeps track of specified objectives + +#include "Globals.h" + +#include "Scoreboard.h" + + + + + +cObjective::cObjective(eObjectiveType a_Type) : m_Type(a_Type) +{} + + + + + +void cObjective::SetDisplaySlot(eDisplaySlot a_Display) +{ + m_Display = a_Display; +} + + + + + +void cObjective::Reset(void) +{ + m_Scores.clear(); +} + + + + + +cObjective::Score cObjective::GetScore(const AString & a_Name) const +{ + ScoreMap::const_iterator it = m_Scores.find(a_Name); + + if (it == m_Scores.end()) + { + return 0; + } + else + { + return it->second; + } +} + + + + + +void cObjective::SetScore(const AString & a_Name, cObjective::Score a_Score) +{ + m_Scores[a_Name] = a_Score; +} + + + + + +void cObjective::ResetScore(const AString & a_Name) +{ + m_Scores.erase(a_Name); +} + + + + + +cObjective::Score cObjective::AddScore(const AString & a_Name, cObjective::Score a_Delta) +{ + // TODO 2014-01-19 xdot: Potential optimization - Reuse iterator + Score NewScore = m_Scores[a_Name] + a_Delta; + + m_Scores[a_Name] = NewScore; + + return NewScore; +} + + + + + +cObjective::Score cObjective::SubScore(const AString & a_Name, cObjective::Score a_Delta) +{ + // TODO 2014-01-19 xdot: Potential optimization - Reuse iterator + Score NewScore = m_Scores[a_Name] - a_Delta; + + m_Scores[a_Name] = NewScore; + + return NewScore; +} + + + + + +cTeam::cTeam(const AString & a_Name, const AString & a_DisplayName, + const AString & a_Prefix, const AString & a_Suffix) + : m_FriendlyFire(true) + , m_SeeFriendlyInvisible(false) + , m_Name(a_Name) + , m_DisplayName(a_DisplayName) + , m_Prefix(a_Prefix) + , m_Suffix(a_Suffix) +{} + + + + + +bool cTeam::AddPlayer(cPlayer * a_Player) +{ + return m_Players.insert(a_Player).second; +} + + + + + +bool cTeam::RemovePlayer(cPlayer * a_Player) +{ + return m_Players.erase(a_Player) > 0; +} + + + + + +void cTeam::Reset(void) +{ + m_Players.clear(); +} + + + + +unsigned int cTeam::GetNumPlayers(void) const +{ + return m_Players.size(); +} + + + + + +cScoreboard::~cScoreboard() +{ + for (ObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it) + { + delete it->second; + } + + for (TeamMap::iterator it = m_Teams.begin(); it != m_Teams.end(); ++it) + { + delete it->second; + } +} + + + + + +cObjective* cScoreboard::RegisterObjective(const AString & a_Name, eObjectiveType a_Type) +{ + cObjective* Objective = new cObjective(a_Type); + + bool Status = m_Objectives.insert(NamedObjective(a_Name, Objective)).second; + + if (Status) + { + return Objective; + } + else + { + delete Objective; + return NULL; + } +} + + + + + +bool cScoreboard::RemoveObjective(const AString & a_Name) +{ + ObjectiveMap::iterator it = m_Objectives.find(a_Name); + + if (it == m_Objectives.end()) + { + return false; + } + + m_Objectives.erase(it); + + return true; +} + + + + + +cObjective* cScoreboard::GetObjective(const AString & a_Name) +{ + ObjectiveMap::iterator it = m_Objectives.find(a_Name); + + if (it == m_Objectives.end()) + { + return NULL; + } + else + { + return it->second; + } +} + + + + + +cTeam* cScoreboard::RegisterTeam(const AString & a_Name, const AString & a_DisplayName, + const AString & a_Prefix, const AString & a_Suffix) +{ + cTeam* Team = new cTeam(a_Name, a_DisplayName, a_Prefix, a_Suffix); + + bool Status = m_Teams.insert(NamedTeam(a_Name, Team)).second; + + if (Status) + { + return Team; + } + else + { + delete Team; + return NULL; + } +} + + + + + +bool cScoreboard::RemoveTeam(const AString & a_Name) +{ + TeamMap::iterator it = m_Teams.find(a_Name); + + if (it == m_Teams.end()) + { + return false; + } + + m_Teams.erase(it); + + return true; +} + + + + + +cTeam* cScoreboard::GetTeam(const AString & a_Name) +{ + TeamMap::iterator it = m_Teams.find(a_Name); + + if (it == m_Teams.end()) + { + return NULL; + } + else + { + return it->second; + } +} + + + + + +void cScoreboard::ForEachObjectiveWith(eObjectiveType a_Type, cObjectiveCallback& a_Callback) +{ + for (ObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it) + { + if (it->second->GetType() == a_Type) + { + // Call callback + if (a_Callback.Item(it->second)) + { + return; + } + } + } +} + + + + diff --git a/src/Scoreboard.h b/src/Scoreboard.h new file mode 100644 index 000000000..8ab298a07 --- /dev/null +++ b/src/Scoreboard.h @@ -0,0 +1,207 @@ + +// Scoreboard.h + +// Implementation of a scoreboard that keeps track of specified objectives + + + + + +#pragma once + + + + + +class cPlayer; +class cObjective; + +typedef std::set< cPlayer * > cPlayerSet; +typedef cItemCallback cObjectiveCallback; + + + + + +enum eObjectiveType +{ + E_OBJECTIVE_DUMMY, + + E_OBJECTIVE_DEATH_COUNT, + E_OBJECTIVE_PLAYER_KILL_COUNT, + E_OBJECTIVE_TOTAL_KILL_COUNT, + E_OBJECTIVE_HEALTH, + + E_OBJECTIVE_ACHIEVEMENT, + + E_OBJECTIVE_STAT, + E_OBJECTIVE_STAT_ITEM_CRAFT, + E_OBJECTIVE_STAT_ITEM_USE, + E_OBJECTIVE_STAT_ITEM_BREAK, + + E_OBJECTIVE_STAT_BLOCK_MINE, + E_OBJECTIVE_STAT_ENTITY_KILL, + E_OBJECTIVE_STAT_ENTITY_KILLED_BY +}; + + + + + +enum eDisplaySlot +{ + E_DISPLAY_SLOT_LIST, + E_DISPLAY_SLOT_SIDEBAR, + E_DISPLAY_SLOT_NAME +}; + + + + + +class cObjective +{ +public: + typedef int Score; + +public: + cObjective(eObjectiveType a_Type); + + eObjectiveType GetType(void) const { return m_Type; } + + eDisplaySlot GetDisplaySlot(void) const { return m_Display; } + + void SetDisplaySlot(eDisplaySlot a_Display); + + /// Resets the objective + void Reset(void); + + /// Returns the score of the specified player + Score GetScore(const AString & a_Name) const; + + /// Sets the score of the specified player + void SetScore(const AString & a_Name, Score a_Score); + + /// Resets the score of the specified player + void ResetScore(const AString & a_Name); + + /// Adds a_Delta and returns the new score + Score AddScore(const AString & a_Name, Score a_Delta); + + /// Subtracts a_Delta and returns the new score + Score SubScore(const AString & a_Name, Score a_Delta); + +private: + typedef std::pair TrackedPlayer; + + typedef std::map ScoreMap; + + ScoreMap m_Scores; + + eObjectiveType m_Type; + + eDisplaySlot m_Display; +}; + + + + + +class cTeam +{ +public: + cTeam(const AString & a_Name, const AString & a_DisplayName, + const AString & a_Prefix, const AString & a_Suffix); + + /// Adds a new player to the team + bool AddPlayer(cPlayer * a_Player); + + /// Removes a player from the team + bool RemovePlayer(cPlayer * a_Player); + + /// Removes all registered players + void Reset(void); + + /// Returns the number of registered players + unsigned int GetNumPlayers(void) const; + + bool GetFriendlyFire(void) const { return m_FriendlyFire; } + bool GetCanSeeFriendlyInvisible(void) const { return m_SeeFriendlyInvisible; } + + const AString & GetDisplayName(void) const { return m_Name; } + const AString & GetName(void) const { return m_DisplayName; } + + const AString & GetPrefix(void) const { return m_Prefix; } + const AString & GetSuffix(void) const { return m_Suffix; } + + void SetFriendlyFire(bool a_Flag); + void SetCanSeeFriendlyInvisible(bool a_Flag); + + void SetDisplayName(const AString & a_Name); + + void SetPrefix(const AString & a_Prefix); + void SetSuffix(const AString & a_Suffix); + +private: + + bool m_FriendlyFire; + bool m_SeeFriendlyInvisible; + + AString m_DisplayName; + AString m_Name; + + AString m_Prefix; + AString m_Suffix; + + // TODO 2014-01-19 xdot: Potential optimization - vector/list + cPlayerSet m_Players; +}; + + + + + +class cScoreboard +{ +public: + cScoreboard() {} + virtual ~cScoreboard(); + + /// Registers a new scoreboard objective, returns the cObjective instance + cObjective* RegisterObjective(const AString & a_Name, eObjectiveType a_Type); + + /// Removes a registered objective, returns true if operation was successful + bool RemoveObjective(const AString & a_Name); + + /// Retrieves the objective with the specified name, NULL if not found + cObjective* GetObjective(const AString & a_Name); + + /// Registers a new team, returns the cTeam instance + cTeam* RegisterTeam(const AString & a_Name, const AString & a_DisplayName, + const AString & a_Prefix, const AString & a_Suffix); + + /// Removes a registered team, returns true if operation was successful + bool RemoveTeam(const AString & a_Name); + + /// Retrieves the team with the specified name, NULL if not found + cTeam* GetTeam(const AString & a_Name); + + /// Execute callback for each objective with the specified type + void ForEachObjectiveWith(eObjectiveType a_Type, cObjectiveCallback& a_Callback); + +private: + typedef std::pair NamedObjective; + typedef std::pair NamedTeam; + + typedef std::map ObjectiveMap; + typedef std::map TeamMap; + + // TODO 2014-01-19 xdot: Potential optimization - Sort objectives based on type + ObjectiveMap m_Objectives; + + TeamMap m_Teams; +} ; + + + + diff --git a/src/World.h b/src/World.h index 1a7ad0cb1..1dcbac8e4 100644 --- a/src/World.h +++ b/src/World.h @@ -22,6 +22,7 @@ #include "Item.h" #include "Mobs/Monster.h" #include "Entities/ProjectileEntity.h" +#include "Scoreboard.h" @@ -513,6 +514,9 @@ public: /// Returns the name of the world.ini file used by this world const AString & GetIniFileName(void) const {return m_IniFileName; } + + /// Returns the associated scoreboard instance + cScoreboard* GetScoreBoard(void) { return &m_Scoreboard; } // tolua_end @@ -757,6 +761,8 @@ private: sSetBlockList m_FastSetBlockQueue; cChunkGenerator m_Generator; + + cScoreboard m_Scoreboard; /** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */ cChunkGeneratorCallbacks m_GeneratorCallbacks;