[colobot] 58/100: Add basics of scoreboard implementation; better support for multiple teams

Didier Raboud odyx at moszumanska.debian.org
Thu Jun 1 18:10:19 UTC 2017


This is an automated email from the git hooks/post-receive script.

odyx pushed a commit to branch debian/master
in repository colobot.

commit 880f31a7c66927420537aeecd546e8da0562a237
Author: krzys-h <krzys_h at interia.pl>
Date:   Wed May 17 18:22:27 2017 +0200

    Add basics of scoreboard implementation; better support for multiple teams
---
 src/CMakeLists.txt                        |   2 +
 src/graphics/engine/lightning.cpp         |   2 +-
 src/graphics/engine/particle.cpp          |  12 +--
 src/graphics/engine/pyro.cpp              |   2 +
 src/level/robotmain.cpp                   |  82 ++++++++++++++++--
 src/level/robotmain.h                     |  11 +++
 src/level/scene_conditions.cpp            | 137 +++++++++++++++++-------------
 src/level/scene_conditions.h              |  23 ++++-
 src/level/scoreboard.cpp                  |  94 ++++++++++++++++++++
 src/level/scoreboard.h                    | 120 ++++++++++++++++++++++++++
 src/object/interface/damageable_object.h  |   4 +-
 src/object/interface/destroyable_object.h |   3 +-
 src/object/object_manager.cpp             |   4 +-
 src/object/object_manager.h               |   3 +-
 src/object/old_object.cpp                 |  19 +++--
 src/object/old_object.h                   |   4 +-
 src/physics/physics.cpp                   |   5 ++
 17 files changed, 436 insertions(+), 91 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 117648f..e085750 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -241,6 +241,8 @@ set(BASE_SOURCES
     level/robotmain.h
     level/scene_conditions.cpp
     level/scene_conditions.h
+    level/scoreboard.cpp
+    level/scoreboard.h
     math/all.h
     math/const.h
     math/func.h
diff --git a/src/graphics/engine/lightning.cpp b/src/graphics/engine/lightning.cpp
index 18192fe..190f5db 100644
--- a/src/graphics/engine/lightning.cpp
+++ b/src/graphics/engine/lightning.cpp
@@ -131,7 +131,7 @@ bool CLightning::EventFrame(const Event &event)
                 else
                 {
                     assert(obj->Implements(ObjectInterfaceType::Destroyable));
-                    dynamic_cast<CDestroyableObject*>(obj)->DamageObject(DamageType::Lightning);
+                    dynamic_cast<CDestroyableObject*>(obj)->DamageObject(DamageType::Lightning, std::numeric_limits<float>::infinity());
                 }
             }
 
diff --git a/src/graphics/engine/particle.cpp b/src/graphics/engine/particle.cpp
index 6bd310e..e63a8c6 100644
--- a/src/graphics/engine/particle.cpp
+++ b/src/graphics/engine/particle.cpp
@@ -954,7 +954,7 @@ void CParticle::FrameParticle(float rTime)
             m_particle[i].goal = m_particle[i].pos;
             if (object != nullptr && object->Implements(ObjectInterfaceType::Damageable))
             {
-                dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Phazer, 0.002f);
+                dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Phazer, 0.002f, m_particle[i].objFather);
             }
 
             m_particle[i].zoom = 1.0f-(m_particle[i].time-m_particle[i].duration);
@@ -1157,7 +1157,7 @@ void CParticle::FrameParticle(float rTime)
                 {
                     if (object->Implements(ObjectInterfaceType::Damageable))
                     {
-                        dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Fire, 0.001f);
+                        dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Fire, 0.001f, m_particle[i].objFather);
                     }
 
                     m_exploGunCounter++;
@@ -1241,7 +1241,7 @@ void CParticle::FrameParticle(float rTime)
 
                         if (object->Implements(ObjectInterfaceType::Damageable))
                         {
-                            dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Organic, 0.1f);  // starts explosion
+                            dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Organic, 0.1f, m_particle[i].objFather);  // starts explosion
                         }
                     }
                 }
@@ -1286,7 +1286,7 @@ void CParticle::FrameParticle(float rTime)
                     {
                         if (object->Implements(ObjectInterfaceType::Damageable))
                         {
-                            dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Fire);  // starts explosion
+                            dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Fire, std::numeric_limits<float>::infinity(), m_particle[i].objFather);  // starts explosion
                         }
                     }
                 }
@@ -1345,7 +1345,7 @@ void CParticle::FrameParticle(float rTime)
                 {
                     if (object->Implements(ObjectInterfaceType::Damageable))
                     {
-                        dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Organic, 0.001f);
+                        dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Organic, 0.001f, m_particle[i].objFather);
                     }
 
                     m_exploGunCounter ++;
@@ -2423,7 +2423,7 @@ void CParticle::FrameParticle(float rTime)
                 if (object != nullptr)
                 {
                     assert(object->Implements(ObjectInterfaceType::Damageable));
-                    dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Tower);
+                    dynamic_cast<CDamageableObject*>(object)->DamageObject(DamageType::Tower, std::numeric_limits<float>::infinity(), m_particle[i].objFather);
                 }
             }
 
diff --git a/src/graphics/engine/pyro.cpp b/src/graphics/engine/pyro.cpp
index 8ac406e..6f3961a 100644
--- a/src/graphics/engine/pyro.cpp
+++ b/src/graphics/engine/pyro.cpp
@@ -2296,6 +2296,7 @@ void CPyro::FallProgress(float rTime)
                 if (floor)  // reaches the ground?
                 {
                     assert(m_object->Implements(ObjectInterfaceType::Destroyable));
+                    // TODO: implement "killer"?
                     dynamic_cast<CDestroyableObject*>(m_object)->DestroyObject(DestructionType::Explosion);
                 }
             }
@@ -2319,6 +2320,7 @@ void CPyro::FallProgress(float rTime)
                     else
                     {
                         assert(m_object->Implements(ObjectInterfaceType::Destroyable));
+                        // TODO: implement "killer"?
                         dynamic_cast<CDestroyableObject*>(m_object)->DestroyObject(DestructionType::Explosion);
                     }
                 }
diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp
index 2797e3d..b723b58 100644
--- a/src/level/robotmain.cpp
+++ b/src/level/robotmain.cpp
@@ -55,6 +55,7 @@
 #include "level/mainmovie.h"
 #include "level/player_profile.h"
 #include "level/scene_conditions.h"
+#include "level/scoreboard.h"
 
 #include "level/parser/parser.h"
 
@@ -2518,7 +2519,7 @@ bool CRobotMain::EventFrame(const Event &event)
 
     if (m_phase == PHASE_SIMUL)
     {
-        if (!m_editLock)
+        if (!m_editLock && !m_engine->GetPause())
         {
             CheckEndMission(true);
             UpdateAudio(true);
@@ -2695,6 +2696,8 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
         m_endTakeResearch = 0;
         m_endTakeWinDelay = 2.0f;
         m_endTakeLostDelay = 2.0f;
+        m_teamFinished.clear();
+        m_scoreboard.reset();
         m_globalMagnifyDamage = 1.0f;
         m_obligatoryTokens.clear();
         m_mapShow = true;
@@ -3552,6 +3555,34 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
                 continue;
             }
 
+            if (line->GetCommand() == "Scoreboard" && !resetObject)
+            {
+                if (line->GetParam("enable")->AsBool(false))
+                {
+                    // Create the scoreboard
+                    m_scoreboard = MakeUnique<CScoreboard>();
+                }
+                continue;
+            }
+            if (line->GetCommand() == "ScoreboardKillRule" && !resetObject)
+            {
+                if (!m_scoreboard)
+                    throw CLevelParserException("ScoreboardKillRule encountered but scoreboard is not enabled");
+                auto rule = MakeUnique<CScoreboard::CScoreboardKillRule>();
+                rule->Read(line.get());
+                m_scoreboard->AddKillRule(std::move(rule));
+                continue;
+            }
+            if (line->GetCommand() == "ScoreboardEndTakeRule" && !resetObject)
+            {
+                if (!m_scoreboard)
+                    throw CLevelParserException("ScoreboardEndTakeRule encountered but scoreboard is not enabled");
+                auto rule = MakeUnique<CScoreboard::CScoreboardEndTakeRule>();
+                rule->Read(line.get());
+                m_scoreboard->AddEndTakeRule(std::move(rule));
+                continue;
+            }
+
             if (line->GetCommand() == "ObligatoryToken" && !resetObject)
             {
                 std::string token = line->GetParam("text")->AsString();
@@ -4909,7 +4940,7 @@ Error CRobotMain::ProcessEndMissionTake()
         int team = it.first;
         if (team == 0) continue;
         usesTeamConditions = true;
-        if (!m_objMan->TeamExists(team)) continue;
+        if (m_teamFinished[team]) continue;
         teamCount++;
     }
 
@@ -4924,8 +4955,32 @@ Error CRobotMain::ProcessEndMissionTake()
 
         if (teamCount == 0)
         {
-            GetLogger()->Info("All teams died, mission ended with failure\n");
-            m_missionResult = INFO_LOST;
+            GetLogger()->Info("All teams died, mission ended\n");
+            if (m_scoreboard)
+            {
+                std::string details = "";
+                for (auto it : teams)
+                {
+                    int team = it.first;
+                    if (team == 0) continue;
+                    details += "Team "+boost::lexical_cast<std::string>(team)+": "+boost::lexical_cast<std::string>(m_scoreboard->GetScore(team))+" points\n";
+                }
+                m_ui->GetDialog()->StartInformation(
+                    "Results",
+                    "The battle has ended",
+                    details,
+                    false, true,
+                    [&]() {
+                        ChangePhase(PHASE_WIN);
+                    }
+                );
+                m_endTakeWinDelay = 0.0f;
+                m_missionResult = ERR_OK;
+            }
+            else
+            {
+                m_missionResult = INFO_LOST;
+            }
         }
         else
         {
@@ -4933,7 +4988,7 @@ Error CRobotMain::ProcessEndMissionTake()
             {
                 int team = it.first;
                 if (team == 0) continue;
-                if (!m_objMan->TeamExists(team)) continue;
+                if (m_teamFinished[team]) continue;
 
                 Error result = ProcessEndMissionTakeForGroup(it.second);
                 if (result == INFO_LOST || result == INFO_LOSTq)
@@ -4944,10 +4999,12 @@ Error CRobotMain::ProcessEndMissionTake()
                     m_displayText->SetEnable(false); // To prevent "bot destroyed" messages
                     m_objMan->DestroyTeam(team);
                     m_displayText->SetEnable(true);
+
+                    m_teamFinished[team] = true;
                 }
                 else if (result == ERR_OK)
                 {
-                    if (m_winDelay == 0.0f)
+                    /*if (m_winDelay == 0.0f)
                     {
                         GetLogger()->Info("Team %d won\n", team);
 
@@ -4963,7 +5020,13 @@ Error CRobotMain::ProcessEndMissionTake()
                         m_displayText->SetEnable(false);
                     }
                     m_missionResult = ERR_OK;
-                    return ERR_OK;
+                    return ERR_OK;*/
+                    GetLogger()->Info("Team %d finished\n", team);
+                    m_displayText->DisplayText(("<<< Team "+boost::lexical_cast<std::string>(team)+" finished >>>").c_str(), Math::Vector(0.0f,0.0f,0.0f));
+                    if (m_scoreboard)
+                        m_scoreboard->ProcessEndTake(team);
+                    m_objMan->DestroyTeam(team, DestructionType::Win);
+                    m_teamFinished[team] = true;
                 }
             }
         }
@@ -5817,3 +5880,8 @@ std::string CRobotMain::GetPreviousFromCommandHistory()
         return "";
     return m_commandHistory[--m_commandHistoryIndex];
 }
+
+CScoreboard* CRobotMain::GetScoreboard()
+{
+    return m_scoreboard.get();
+}
diff --git a/src/level/robotmain.h b/src/level/robotmain.h
index dad1bed..6bde5f6 100644
--- a/src/level/robotmain.h
+++ b/src/level/robotmain.h
@@ -87,6 +87,7 @@ class CInput;
 class CObjectManager;
 class CSceneEndCondition;
 class CAudioChangeCondition;
+class CScoreboard;
 class CPlayerProfile;
 class CSettings;
 class COldObject;
@@ -307,6 +308,10 @@ public:
     //! Return list of scripts to load to robot created in BotFactory
     std::vector<std::string> GetNewScriptNames(ObjectType type);
 
+    //! Return the scoreboard manager
+    //! Note: this may return nullptr if the scoreboard is not enabled!
+    CScoreboard* GetScoreboard();
+
     void        SelectPlayer(std::string playerName);
     CPlayerProfile* GetPlayerProfile();
 
@@ -655,9 +660,15 @@ protected:
     long            m_endTakeResearch = 0;
     float           m_endTakeWinDelay = 0.0f;
     float           m_endTakeLostDelay = 0.0f;
+    //! Set to true for teams that have already finished
+    std::map<int, bool> m_teamFinished;
 
     std::vector<std::unique_ptr<CAudioChangeCondition>> m_audioChange;
 
+    //! The scoreboard
+    //! If the scoreboard is not enabled for this level, this will be null
+    std::unique_ptr<CScoreboard> m_scoreboard;
+
     std::map<std::string, MinMax> m_obligatoryTokens;
 
     //! Enabled buildings
diff --git a/src/level/scene_conditions.cpp b/src/level/scene_conditions.cpp
index e475dee..d4ed1bf 100644
--- a/src/level/scene_conditions.cpp
+++ b/src/level/scene_conditions.cpp
@@ -29,11 +29,13 @@
 #include "object/interface/powered_object.h"
 #include "object/interface/transportable_object.h"
 
+#include <limits>
 
-void CSceneCondition::Read(CLevelParserLine* line)
+
+void CObjectCondition::Read(CLevelParserLine* line)
 {
     this->pos      = line->GetParam("pos")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f))*g_unit;
-    this->dist     = line->GetParam("dist")->AsFloat(8.0f)*g_unit;
+    this->dist     = line->GetParam("dist")->AsFloat(std::numeric_limits<float>::infinity())*g_unit;
     this->type     = line->GetParam("type")->AsObjectType(OBJECT_NULL);
     this->powermin = line->GetParam("powermin")->AsFloat(-1);
     this->powermax = line->GetParam("powermax")->AsFloat(100);
@@ -41,81 +43,95 @@ void CSceneCondition::Read(CLevelParserLine* line)
     this->drive    = line->GetParam("drive")->AsDriveType(DriveType::Other);
     this->countTransported = line->GetParam("countTransported")->AsBool(true);
     this->team     = line->GetParam("team")->AsInt(0);
-
-    this->min      = line->GetParam("min")->AsInt(1);
-    this->max      = line->GetParam("max")->AsInt(9999);
 }
 
-int CSceneCondition::CountObjects()
+bool CObjectCondition::CheckForObject(CObject* obj)
 {
-    Math::Vector bPos = this->pos;
-    bPos.y = 0.0f;
+    if (!this->countTransported)
+    {
+        if (IsObjectBeingTransported(obj)) return false;
+    }
 
-    Math::Vector oPos;
+    ObjectType type = obj->GetType();
 
-    int nb = 0;
-    for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects())
-    {
-        if (!obj->GetActive()) continue;
+    ToolType tool = GetToolFromObject(type);
+    DriveType drive = GetDriveFromObject(type);
+    if (this->tool != ToolType::Other &&
+        tool != this->tool)
+        return false;
+
+    if (this->drive != DriveType::Other &&
+        drive != this->drive)
+        return false;
+
+    if (this->tool == ToolType::Other &&
+        this->drive == DriveType::Other &&
+        type != this->type &&
+        this->type != OBJECT_NULL)
+        return false;
+
+    if ((this->team > 0 && obj->GetTeam() != this->team) ||
+        (this->team < 0 && (obj->GetTeam() == -(this->team) || obj->GetTeam() == 0)))
+        return false;
 
-        if (!this->countTransported)
+    float energyLevel = -1;
+    CPowerContainerObject* power = nullptr;
+    if (obj->Implements(ObjectInterfaceType::PowerContainer))
+    {
+        power = dynamic_cast<CPowerContainerObject*>(obj);
+    }
+    else if (obj->Implements(ObjectInterfaceType::Powered))
+    {
+        CObject* powerObj = dynamic_cast<CPoweredObject*>(obj)->GetPower();
+        if(powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer))
         {
-            if (IsObjectBeingTransported(obj)) continue;
+            power = dynamic_cast<CPowerContainerObject*>(powerObj);
         }
+    }
 
-        ObjectType type = obj->GetType();
-
-        ToolType tool = GetToolFromObject(type);
-        DriveType drive = GetDriveFromObject(type);
-        if (this->tool != ToolType::Other &&
-            tool != this->tool)
-            continue;
+    if (power != nullptr)
+    {
+        energyLevel = power->GetEnergy();
+        if (power->GetCapacity() > 1.0f) energyLevel *= 10; // TODO: Who designed it like that ?!?!
+    }
+    if (energyLevel < this->powermin || energyLevel > this->powermax) return false;
 
-        if (this->drive != DriveType::Other &&
-            drive != this->drive)
-            continue;
+    Math::Vector oPos;
+    if (IsObjectBeingTransported(obj))
+        oPos = dynamic_cast<CTransportableObject*>(obj)->GetTransporter()->GetPosition();
+    else
+        oPos = obj->GetPosition();
+    oPos.y = 0.0f;
 
-        if (this->tool == ToolType::Other &&
-            this->drive == DriveType::Other &&
-            type != this->type &&
-            this->type != OBJECT_NULL)
-            continue;
+    Math::Vector bPos = this->pos;
+    bPos.y = 0.0f;
 
-        if ((this->team > 0 && obj->GetTeam() != this->team) ||
-            (this->team < 0 && (obj->GetTeam() == -(this->team) || obj->GetTeam() == 0)))
-            continue;
+    if (Math::DistanceProjected(oPos, bPos) <= this->dist)
+        return true;
 
-        float energyLevel = -1;
-        CPowerContainerObject* power = nullptr;
-        if (obj->Implements(ObjectInterfaceType::PowerContainer))
-        {
-            power = dynamic_cast<CPowerContainerObject*>(obj);
-        }
-        else if (obj->Implements(ObjectInterfaceType::Powered))
-        {
-            CObject* powerObj = dynamic_cast<CPoweredObject*>(obj)->GetPower();
-            if(powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer))
-            {
-                power = dynamic_cast<CPowerContainerObject*>(powerObj);
-            }
-        }
+    return false;
+}
 
-        if (power != nullptr)
-        {
-            energyLevel = power->GetEnergy();
-            if (power->GetCapacity() > 1.0f) energyLevel *= 10; // TODO: Who designed it like that ?!?!
-        }
-        if (energyLevel < this->powermin || energyLevel > this->powermax) continue;
+void CSceneCondition::Read(CLevelParserLine* line)
+{
+    CObjectCondition::Read(line);
 
-        if (IsObjectBeingTransported(obj))
-            oPos = dynamic_cast<CTransportableObject*>(obj)->GetTransporter()->GetPosition();
-        else
-            oPos = obj->GetPosition();
+    // Scene conditions STILL use a different default value
+    // See issue #759
+    this->dist     = line->GetParam("dist")->AsFloat(8.0f)*g_unit;
 
-        oPos.y = 0.0f;
+    this->min      = line->GetParam("min")->AsInt(1);
+    this->max      = line->GetParam("max")->AsInt(9999);
+}
 
-        if (Math::DistanceProjected(oPos, bPos) <= this->dist)
-            nb ++;
+int CSceneCondition::CountObjects()
+{
+    int nb = 0;
+    for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects())
+    {
+        if (!obj->GetActive()) continue;
+        if (!CheckForObject(obj)) continue;
+        nb ++;
     }
     return nb;
 }
@@ -126,7 +142,6 @@ bool CSceneCondition::Check()
     return nb >= this->min && nb <= this->max;
 }
 
-
 void CSceneEndCondition::Read(CLevelParserLine* line)
 {
     CSceneCondition::Read(line);
diff --git a/src/level/scene_conditions.h b/src/level/scene_conditions.h
index 258cc7e..721c486 100644
--- a/src/level/scene_conditions.h
+++ b/src/level/scene_conditions.h
@@ -34,12 +34,13 @@
 #include "object/tool_type.h"
 
 class CLevelParserLine;
+class CObject;
 
 /**
- * \class CSceneCondition
- * \brief Base scene condition structure
+ * \class CObjectCondition
+ * \brief Base object condition structure
  */
-class CSceneCondition
+class CObjectCondition
 {
 public:
     Math::Vector  pos = Math::Vector(0.0f, 0.0f, 0.0f)*g_unit;
@@ -52,11 +53,25 @@ public:
     bool          countTransported = true;
     int           team = 0;
 
+    //! Read from line in scene file
+    virtual void Read(CLevelParserLine* line);
+
+    //! Checks if this condition is met
+    bool CheckForObject(CObject* obj);
+};
+
+/**
+ * \class CSceneCondition
+ * \brief Base scene condition structure
+ */
+class CSceneCondition : public CObjectCondition
+{
+public:
     int           min = 1;        // wins if >
     int           max = 9999;     // wins if <
 
     //! Read from line in scene file
-    virtual void Read(CLevelParserLine* line);
+    void Read(CLevelParserLine* line) override;
 
     //! Checks if this condition is met
     bool Check();
diff --git a/src/level/scoreboard.cpp b/src/level/scoreboard.cpp
new file mode 100644
index 0000000..7208dd4
--- /dev/null
+++ b/src/level/scoreboard.cpp
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the Colobot: Gold Edition source code
+ * Copyright (C) 2001-2017, Daniel Roux, EPSITEC SA & TerranovaTeam
+ * http://epsitec.ch; http://colobot.info; http://github.com/colobot
+ *
+ * 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 3 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, see http://gnu.org/licenses
+ */
+
+#include "level/scoreboard.h"
+
+#include "level/parser/parserline.h"
+
+#include "level/robotmain.h"
+
+#include "object/object.h"
+
+#include "ui/displaytext.h"
+
+#include <boost/lexical_cast.hpp>
+
+void CScoreboard::CScoreboardRule::Read(CLevelParserLine* line)
+{
+    this->score = line->GetParam("score")->AsInt();
+}
+
+void CScoreboard::CScoreboardKillRule::Read(CLevelParserLine* line)
+{
+    CScoreboardRule::Read(line);
+    CObjectCondition::Read(line);
+}
+
+void CScoreboard::CScoreboardEndTakeRule::Read(CLevelParserLine* line)
+{
+    CScoreboardRule::Read(line);
+    this->team = line->GetParam("team")->AsInt(0);
+}
+
+void CScoreboard::AddKillRule(std::unique_ptr<CScoreboardKillRule> rule)
+{
+    m_rulesKill.push_back(std::move(rule));
+}
+
+void CScoreboard::AddEndTakeRule(std::unique_ptr<CScoreboardEndTakeRule> rule)
+{
+    m_rulesEndTake.push_back(std::move(rule));
+}
+
+void CScoreboard::ProcessKill(CObject* target, CObject* killer)
+{
+    if (killer == nullptr) return;
+    if (killer->GetTeam() == 0) return;
+    for (auto& rule : m_rulesKill)
+    {
+        if ((rule->team == killer->GetTeam() || rule->team == 0) &&
+            rule->CheckForObject(target))
+        {
+            AddPoints(killer->GetTeam(), rule->score);
+        }
+    }
+}
+
+void CScoreboard::ProcessEndTake(int team)
+{
+    for (auto& rule : m_rulesEndTake)
+    {
+        if (rule->team == team || rule->team == 0)
+        {
+            AddPoints(team, rule->score);
+        }
+    }
+}
+
+void CScoreboard::AddPoints(int team, int points)
+{
+    GetLogger()->Info("Team %d earned %d points\n", team, points);
+    CRobotMain::GetInstancePointer()->GetDisplayText()->DisplayText(("<<< Team "+boost::lexical_cast<std::string>(team)+" recieved "+boost::lexical_cast<std::string>(points)+" points! >>>").c_str(), Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 10.0f, Ui::TT_WARNING);
+    m_score[team] += points;
+}
+
+int CScoreboard::GetScore(int team)
+{
+    return m_score[team];
+}
diff --git a/src/level/scoreboard.h b/src/level/scoreboard.h
new file mode 100644
index 0000000..c0c2512
--- /dev/null
+++ b/src/level/scoreboard.h
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the Colobot: Gold Edition source code
+ * Copyright (C) 2001-2017, Daniel Roux, EPSITEC SA & TerranovaTeam
+ * http://epsitec.ch; http://colobot.info; http://github.com/colobot
+ *
+ * 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 3 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, see http://gnu.org/licenses
+ */
+
+/**
+ * \file level/scoreboard.h
+ * \brief Code responsible for managing the scoreboard used to score complex code battles
+ */
+
+#pragma once
+
+#include "level/scene_conditions.h"
+
+#include <memory>
+#include <vector>
+#include <map>
+
+class CObject;
+
+/**
+ * \class CScoreboard
+ * \brief Scoreboard used to score complex code battles
+ *
+ * \todo This is pretty much a work-in-progress hack for Diversity. Be wary of possible API changes.
+ *
+ * \todo Proper documentation
+ *
+ * \see CRobotMain::GetScoreboard()
+ *
+ * \section example Usage example
+ * \code{.scene}
+ * Scoreboard enable=true // enable the scoreboard
+ * ScoreboardKillRule type=WheeledShooter team=1 score=500 // destruction of team 1's WheeledShooter gives 100 points to the team that destroyed it
+ * ScoreboardKillRule type=TargetBot score=100 // destruction of TargetBot (any team) gives 100 points
+ * ScoreboardEndTakeRule score=1000 // completion of EndMissionTake objectives for any team results in 1000 points for that team
+ * \endcode
+ */
+class CScoreboard
+{
+public:
+    //! Creates the scoreboard
+    //! The scoreboard exists only if enabled in level file
+    CScoreboard() {};
+    //! Destroys the scoreboard
+    ~CScoreboard() {};
+
+public:
+    /**
+     * \class CScoreboardRule
+     * \brief Base class for scoreboard rules
+     */
+    class CScoreboardRule
+    {
+    public:
+        int score = 0;
+
+        //! Read from line in scene file
+        virtual void Read(CLevelParserLine* line);
+    };
+
+    /**
+     * \class CScoreboardKillRule
+     * \brief Scoreboard rule for destroying other objects
+     * \see CScoreboard::AddKillRule()
+     */
+    class CScoreboardKillRule : public CScoreboardRule, public CObjectCondition
+    {
+    public:
+        //! Read from line in scene file
+        void Read(CLevelParserLine* line) override;
+    };
+
+    /**
+     * \class CScoreboardEndTakeRule
+     * \brief Scoreboard rule for EndMissionTake rewards
+     * \see CScoreboard::AddEndTakeRule()
+     */
+    class CScoreboardEndTakeRule : public CScoreboardRule
+    {
+    public:
+        int team = 0;
+
+        //! Read from line in scene file
+        void Read(CLevelParserLine* line) override;
+    };
+
+public:
+    void AddKillRule(std::unique_ptr<CScoreboardKillRule> rule);
+    void AddEndTakeRule(std::unique_ptr<CScoreboardEndTakeRule> rule);
+
+    //! Called after an object is destroyed by another object
+    //! \param target The object that has just been destroyed
+    //! \param killer The object that caused the destruction, can be null
+    void ProcessKill(CObject* target, CObject* killer = nullptr);
+    //! Called after EndTake contition has been met, used to handle ScoreboardEndTakeRule
+    void ProcessEndTake(int team);
+
+    void AddPoints(int team, int points);
+    int GetScore(int team);
+
+private:
+    std::vector<std::unique_ptr<CScoreboardKillRule>> m_rulesKill = {};
+    std::vector<std::unique_ptr<CScoreboardEndTakeRule>> m_rulesEndTake = {};
+    std::map<int, int> m_score;
+};
\ No newline at end of file
diff --git a/src/object/interface/damageable_object.h b/src/object/interface/damageable_object.h
index 27b35ca..0813a28 100644
--- a/src/object/interface/damageable_object.h
+++ b/src/object/interface/damageable_object.h
@@ -23,6 +23,8 @@
 
 #include <limits>
 
+class CObject;
+
 /**
  * \enum DamageType
  * \brief Type of damage, for use in CDamageableObject::DamageObject
@@ -56,5 +58,5 @@ public:
 
     //! Damage the object, with the given force. Returns true if the object has been fully destroyed (assuming the object is destroyable, of course). If force == infinity, destroy immediately (this is the default value)
     /** NOTE: You should never assume that after this function exits, the object is destroyed, unless it returns true. Even if you specify force = infinity, if may still sometimes decide not to destroy the object. */
-    virtual bool DamageObject(DamageType type, float force = std::numeric_limits<float>::infinity()) = 0;
+    virtual bool DamageObject(DamageType type, float force = std::numeric_limits<float>::infinity(), CObject* killer = nullptr) = 0;
 };
diff --git a/src/object/interface/destroyable_object.h b/src/object/interface/destroyable_object.h
index d3e6ee0..48f4736 100644
--- a/src/object/interface/destroyable_object.h
+++ b/src/object/interface/destroyable_object.h
@@ -32,6 +32,7 @@ enum class DestructionType
     ExplosionWater = 2, //!< explosion underwater
     Burn           = 3, //!< burning
     Drowned        = 4, //!< drowned (only for Me)
+    Win            = 5, //!< used when removing objects from a team that won
 };
 
 /**
@@ -65,7 +66,7 @@ public:
 
     //! Destroy the object immediately. Use this only if you are 100% sure this is what you want, because object with magnifyDamage=0 should be able to bypass all damage. It's recommended to use CDamageableObject::DamageObject() instead.
     /** NOTE: After this function exits, you can assume the object has been definetly destroyed */
-    virtual void DestroyObject(DestructionType type) = 0;
+    virtual void DestroyObject(DestructionType type, CObject* killer = nullptr) = 0;
 
     //! Returns the distance modifier for CLightning, used to modify hit probability. Value in range [0..1], where 0 is never and 1 is normal probability
     virtual float GetLightningHitProbability() = 0;
diff --git a/src/object/object_manager.cpp b/src/object/object_manager.cpp
index a903a3a..9483936 100644
--- a/src/object/object_manager.cpp
+++ b/src/object/object_manager.cpp
@@ -199,7 +199,7 @@ bool CObjectManager::TeamExists(int team)
     return false;
 }
 
-void CObjectManager::DestroyTeam(int team)
+void CObjectManager::DestroyTeam(int team, DestructionType destructionType)
 {
     assert(team != 0);
 
@@ -209,7 +209,7 @@ void CObjectManager::DestroyTeam(int team)
         {
             if (object->Implements(ObjectInterfaceType::Destroyable))
             {
-                dynamic_cast<CDestroyableObject*>(object)->DestroyObject(DestructionType::Explosion);
+                dynamic_cast<CDestroyableObject*>(object)->DestroyObject(destructionType);
             }
             else
             {
diff --git a/src/object/object_manager.h b/src/object/object_manager.h
index 8a4cbd6..73ae001 100644
--- a/src/object/object_manager.h
+++ b/src/object/object_manager.h
@@ -32,6 +32,7 @@
 #include "object/object_create_params.h"
 #include "object/object_interface_type.h"
 #include "object/object_type.h"
+#include "object/interface/destroyable_object.h"
 
 #include <map>
 #include <vector>
@@ -180,7 +181,7 @@ public:
 
     //! Destroy all objects of team
     // TODO: This should be probably moved to separate class
-    void DestroyTeam(int team);
+    void DestroyTeam(int team, DestructionType destructionType = DestructionType::Explosion);
 
     //! Counts all objects implementing given interface
     int CountObjectsImplementing(ObjectInterfaceType interface);
diff --git a/src/object/old_object.cpp b/src/object/old_object.cpp
index 5c67ee4..fb510d8 100644
--- a/src/object/old_object.cpp
+++ b/src/object/old_object.cpp
@@ -34,6 +34,7 @@
 #include "graphics/engine/terrain.h"
 
 #include "level/robotmain.h"
+#include "level/scoreboard.h"
 
 #include "level/parser/parserexceptions.h"
 #include "level/parser/parserline.h"
@@ -335,7 +336,7 @@ void COldObject::Simplify()
 }
 
 
-bool COldObject::DamageObject(DamageType type, float force)
+bool COldObject::DamageObject(DamageType type, float force, CObject* killer)
 {
     assert(Implements(ObjectInterfaceType::Damageable));
     assert(!Implements(ObjectInterfaceType::Destroyable) || Implements(ObjectInterfaceType::Shielded) || Implements(ObjectInterfaceType::Fragile));
@@ -355,7 +356,7 @@ bool COldObject::DamageObject(DamageType type, float force)
     {
         if ( m_type == OBJECT_BOMB && type != DamageType::Explosive ) return false; // Mine can't be destroyed by shooting
 
-        DestroyObject(DestructionType::Explosion);
+        DestroyObject(DestructionType::Explosion, killer);
         return true;
     }
 
@@ -400,11 +401,11 @@ bool COldObject::DamageObject(DamageType type, float force)
     {
         if (type == DamageType::Fire)
         {
-            DestroyObject(DestructionType::Burn);
+            DestroyObject(DestructionType::Burn, killer);
         }
         else
         {
-            DestroyObject(DestructionType::Explosion);
+            DestroyObject(DestructionType::Explosion, killer);
         }
         return true;
     }
@@ -425,7 +426,7 @@ bool COldObject::DamageObject(DamageType type, float force)
     return false;
 }
 
-void COldObject::DestroyObject(DestructionType type)
+void COldObject::DestroyObject(DestructionType type, CObject* killer)
 {
     assert(Implements(ObjectInterfaceType::Destroyable));
 
@@ -522,6 +523,10 @@ void COldObject::DestroyObject(DestructionType type)
     {
         pyroType = Gfx::PT_DEADW;
     }
+    else if ( type == DestructionType::Win )
+    {
+        pyroType = Gfx::PT_WPCHECK;
+    }
     assert(pyroType != Gfx::PT_NULL);
     m_engine->GetPyroManager()->Create(pyroType, this);
 
@@ -539,6 +544,10 @@ void COldObject::DestroyObject(DestructionType type)
     }
     m_main->RemoveFromSelectionHistory(this);
 
+    CScoreboard* scoreboard = m_main->GetScoreboard();
+    if (scoreboard)
+        scoreboard->ProcessKill(this, killer);
+
     m_team = 0; // Back to neutral on destruction
 
     if ( m_botVar != nullptr )
diff --git a/src/object/old_object.h b/src/object/old_object.h
index b000ac6..bc2d0b0 100644
--- a/src/object/old_object.h
+++ b/src/object/old_object.h
@@ -110,8 +110,8 @@ public:
 
     void        Simplify() override;
 
-    bool        DamageObject(DamageType type, float force = std::numeric_limits<float>::infinity()) override;
-    void        DestroyObject(DestructionType type) override;
+    bool        DamageObject(DamageType type, float force = std::numeric_limits<float>::infinity(), CObject* killer = nullptr) override;
+    void        DestroyObject(DestructionType type, CObject* killer = nullptr) override;
 
     bool EventProcess(const Event& event) override;
     void        UpdateMapping();
diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp
index 8fcc08c..6125a69 100644
--- a/src/physics/physics.cpp
+++ b/src/physics/physics.cpp
@@ -2701,6 +2701,7 @@ bool CPhysics::ExploOther(ObjectType iType,
 
         if ( force > destructionForce )
         {
+            // TODO: implement "killer"?
             dynamic_cast<CDamageableObject*>(pObj)->DamageObject(damageType);
         }
     }
@@ -2726,6 +2727,7 @@ bool CPhysics::ExploOther(ObjectType iType,
             oType == OBJECT_HUSTON    )  // building?
         {
             assert(pObj->Implements(ObjectInterfaceType::Damageable));
+            // TODO: implement "killer"?
             dynamic_cast<CDamageableObject*>(pObj)->DamageObject(DamageType::Collision, force/400.0f);
         }
 
@@ -2756,6 +2758,7 @@ bool CPhysics::ExploOther(ObjectType iType,
             oType == OBJECT_MOBILEit  )  // vehicle?
         {
             assert(pObj->Implements(ObjectInterfaceType::Damageable));
+            // TODO: implement "killer"?
             dynamic_cast<CDamageableObject*>(pObj)->DamageObject(DamageType::Collision, force/200.0f);
         }
     }
@@ -2780,6 +2783,7 @@ int CPhysics::ExploHimself(ObjectType iType, ObjectType oType, float force)
 
     if ( force > destructionForce && destructionForce >= 0.0f )
     {
+        // TODO: implement "killer"?
         dynamic_cast<CDamageableObject*>(m_object)->DamageObject(DamageType::Explosive);
         return 2;
     }
@@ -2860,6 +2864,7 @@ int CPhysics::ExploHimself(ObjectType iType, ObjectType oType, float force)
                 force /= 200.0f;
             }
 
+            // TODO: implement "killer"?
             if ( dynamic_cast<CDamageableObject*>(m_object)->DamageObject(DamageType::Collision, force) )  return 2;
         }
     }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/colobot.git



More information about the Pkg-games-commits mailing list