From a88719e2b342692bed62f4e658eb01c2334c0d39 Mon Sep 17 00:00:00 2001
From: Deukhoofd <Deukhoofd@gmail.com>
Date: Sun, 11 Apr 2021 16:27:21 +0200
Subject: [PATCH] Supports cloning battles for AI purposes.

---
 src/Battling/Battle/Battle.cpp                | 45 +++++++++++++++++++
 src/Battling/Battle/Battle.hpp                |  2 +
 src/Battling/Pokemon/LearnedMove.hpp          |  6 +++
 src/Battling/Pokemon/Pokemon.cpp              | 45 ++++++++++++++++++-
 src/Battling/Pokemon/Pokemon.hpp              |  2 +
 src/Battling/Pokemon/PokemonParty.hpp         | 13 ++++++
 .../AngelScript/AngelScriptScript.cpp         | 20 ++++++---
 .../AngelScript/AngelScriptScript.hpp         |  2 +
 8 files changed, 129 insertions(+), 6 deletions(-)

diff --git a/src/Battling/Battle/Battle.cpp b/src/Battling/Battle/Battle.cpp
index d7da087..c5c1855 100644
--- a/src/Battling/Battle/Battle.cpp
+++ b/src/Battling/Battle/Battle.cpp
@@ -14,3 +14,48 @@ void PkmnLib::Battling::Battle::ClearWeather() {
     _weatherScript = nullptr;
     _eventHook.Trigger<WeatherChangeEvent>(""_cnc);
 }
+PkmnLib::Battling::Battle* PkmnLib::Battling::Battle::Clone() const {
+    auto parties = ArbUt::List<CreatureLib::Battling::BattleParty*>(_parties.Count());
+    for (auto* party : _parties) {
+        parties.Append(party->Clone());
+    }
+
+    auto* battle =
+        new Battle(_library.ForceAs<const BattleLibrary>(), parties, _canFlee, _numberOfSides, _creaturesPerSide);
+
+    for (int i = 0; i < _numberOfSides; ++i) {
+        battle->_sides.Set(i, _sides[i]->CloneWithoutCreatures());
+        // FIXME: This is horrible code to translate the creature from the old battle into the same creature in the
+        // new battle. This needs to be cleaned up, as it's ugly and slow.
+        for (int creatureIndex = 0; creatureIndex < _creaturesPerSide; ++creatureIndex) {
+            auto creature = _sides[i]->GetCreature(creatureIndex);
+            if (!creature.HasValue()) {
+                continue;
+            }
+            for (size_t j = 0; j < _parties.Count(); ++j) {
+                auto party = _parties[j];
+                if (!party->IsResponsibleForIndex(i, creatureIndex)) {
+                    continue;
+                }
+                auto partyIndex = party->GetParty()->GetParty().IndexOf(creature.GetValue());
+                if (partyIndex != -1U) {
+                    auto c = battle->_parties.At(j)->GetParty()->GetParty()[partyIndex];
+                    battle->_sides.At(i)->SetCreature(c, creatureIndex);
+                    j = _parties.Count();
+                    break;
+                }
+            }
+        }
+    }
+    battle->_random = _random;
+    battle->_hasEnded = _hasEnded;
+    battle->_battleResult = _battleResult;
+    _historyHolder.CloneOnto(battle->_historyHolder);
+    battle->_currentTurn = _currentTurn;
+    _volatile.Clone(battle->_volatile);
+    if (_weatherScript != nullptr) {
+        battle->_weatherScript = std::unique_ptr<CreatureLib::Battling::BattleScript>(_weatherScript->Clone());
+    }
+
+    return battle;
+}
diff --git a/src/Battling/Battle/Battle.hpp b/src/Battling/Battle/Battle.hpp
index beab0f0..fc09711 100644
--- a/src/Battling/Battle/Battle.hpp
+++ b/src/Battling/Battle/Battle.hpp
@@ -36,6 +36,8 @@ namespace PkmnLib::Battling {
             scripts.Append(CreatureLib::Battling::ScriptWrapper(
                 CreatureLib::Battling::ScriptWrapper::FromScript(&_weatherScript)));
         }
+
+        Battle* Clone() const override;
     };
 }
 
diff --git a/src/Battling/Pokemon/LearnedMove.hpp b/src/Battling/Pokemon/LearnedMove.hpp
index 53eb4a8..47aefb7 100644
--- a/src/Battling/Pokemon/LearnedMove.hpp
+++ b/src/Battling/Pokemon/LearnedMove.hpp
@@ -13,6 +13,12 @@ namespace PkmnLib::Battling {
         const ArbUt::BorrowedPtr<const Library::MoveData> GetMoveData() const {
             return GetAttack().ForceAs<const Library::MoveData>();
         }
+
+        LearnedAttack* Clone() const override {
+            auto move = new LearnedMove(GetAttack().ForceAs<const Library::MoveData>(), GetLearnMethod());
+            move->DecreaseUses(GetMaxUses() - GetRemainingUses());
+            return move;
+        }
     };
 }
 
diff --git a/src/Battling/Pokemon/Pokemon.cpp b/src/Battling/Pokemon/Pokemon.cpp
index 36ee355..a94feb0 100644
--- a/src/Battling/Pokemon/Pokemon.cpp
+++ b/src/Battling/Pokemon/Pokemon.cpp
@@ -46,4 +46,47 @@ void PkmnLib::Battling::Pokemon::ClearStatus() {
     if (_battle.HasValue()) {
         _battle.GetValue()->TriggerEventListener<StatusChangeEvent>(this, ""_cnc);
     }
-}
\ No newline at end of file
+}
+
+CreatureLib::Battling::Creature* PkmnLib::Battling::Pokemon::Clone() const {
+    auto moves = std::vector<CreatureLib::Battling::LearnedAttack*>(_attacks.Count());
+    auto i = 0;
+    for (auto* attack : _attacks) {
+        if (attack == nullptr) {
+            moves[i++] = nullptr;
+        } else {
+            moves[i++] = dynamic_cast<LearnedMove*>(attack->Clone());
+        }
+    }
+
+    auto* c = new Pokemon(_library.ForceAs<const BattleLibrary>(), _species.ForceAs<const Library::PokemonSpecies>(),
+                          _variant.ForceAs<const Library::PokemonForme>(), _level, _experience, _uniqueIdentifier,
+                          _gender, _coloring, _heldItem.ForceAs<const Library::Item>(), _nickname, _talentIndex, moves,
+                          _individualValues, _effortValues, _nature, _allowedExperienceGain);
+    c->_displaySpecies = _displaySpecies;
+    c->_displayVariant = _displayVariant;
+    c->_currentHealth = _currentHealth;
+    c->_statBoost = _statBoost;
+    c->_flatStats = _flatStats;
+    c->_boostedStats = _boostedStats;
+    c->_battle = _battle;
+    c->_side = _side;
+    c->_onBattleField = _onBattleField;
+    if (_activeTalent != nullptr) {
+        c->_activeTalent = std::unique_ptr<PkmnScript::BattleScript>(_activeTalent->Clone());
+    }
+    c->_hasOverridenTalent = _hasOverridenTalent;
+    c->_overridenTalentName = _overridenTalentName;
+    if (_status != nullptr) {
+        c->_status = std::unique_ptr<PkmnScript::BattleScript>(_status->Clone());
+    }
+    _volatile.Clone(c->_volatile);
+    c->_types = std::vector<u8>(_types);
+    if (_statusScript != nullptr) {
+        c->_statusScript = std::unique_ptr<PkmnScript::BattleScript>(_statusScript->Clone());
+    }
+    c->_friendship = _friendship;
+    c->RecalculateFlatStats();
+
+    return c;
+}
diff --git a/src/Battling/Pokemon/Pokemon.hpp b/src/Battling/Pokemon/Pokemon.hpp
index ba81436..ac1805a 100644
--- a/src/Battling/Pokemon/Pokemon.hpp
+++ b/src/Battling/Pokemon/Pokemon.hpp
@@ -87,6 +87,8 @@ namespace PkmnLib::Battling {
             }
             _friendship = newValue;
         }
+
+        Creature* Clone() const override;
     };
 }
 
diff --git a/src/Battling/Pokemon/PokemonParty.hpp b/src/Battling/Pokemon/PokemonParty.hpp
index 73f0835..c795fba 100644
--- a/src/Battling/Pokemon/PokemonParty.hpp
+++ b/src/Battling/Pokemon/PokemonParty.hpp
@@ -10,10 +10,23 @@ namespace PkmnLib::Battling {
             : CreatureLib::Battling::CreatureParty(std::move(party)) {}
         PokemonParty(std::initializer_list<CreatureLib::Battling::Creature*> party)
             : CreatureLib::Battling::CreatureParty(party) {}
+        PokemonParty(size_t size) : CreatureLib::Battling::CreatureParty(size) {}
 
         ArbUt::OptionalBorrowedPtr<Pokemon> GetAtIndex(int index) const {
             return CreatureLib::Battling::CreatureParty::GetAtIndex(index).As<Pokemon>();
         }
+
+        CreatureParty* Clone() const override {
+            auto party = new PokemonParty(GetParty().Count());
+            auto i = 0;
+            for (auto c : GetParty()) {
+                if (c != nullptr) {
+                    party->SwapInto(i, c->Clone());
+                    i++;
+                }
+            }
+            return party;
+        }
     };
 }
 
diff --git a/src/ScriptResolving/AngelScript/AngelScriptScript.cpp b/src/ScriptResolving/AngelScript/AngelScriptScript.cpp
index 6c8217a..931db23 100644
--- a/src/ScriptResolving/AngelScript/AngelScriptScript.cpp
+++ b/src/ScriptResolving/AngelScript/AngelScriptScript.cpp
@@ -294,9 +294,19 @@ void AngelScriptScript::PreventOpponentSwitch(const CreatureLib::Battling::Switc
 void AngelScriptScript::OnEndTurn(CreatureLib::Battling::Creature* creature) {
     CALL_HOOK(OnEndTurn, { ctx->SetArgObject(0, (void*)creature); })
 }
-void AngelScriptScript::ModifyNumberOfHits(CreatureLib::Battling::AttackTurnChoice* choice, u8* numberOfHits) {
-    CALL_HOOK(ModifyNumberOfHits, {
-        ctx->SetArgObject(0, (void*)choice);
-        ctx->SetArgAddress(1, numberOfHits);
-    })
+void AngelScriptScript::ModifyNumberOfHits(CreatureLib::Battling::AttackTurnChoice* choice,
+                                           u8* numberOfHits){CALL_HOOK(ModifyNumberOfHits,
+                                                                       {
+                                                                           ctx->SetArgObject(0, (void*)choice);
+                                                                           ctx->SetArgAddress(1, numberOfHits);
+                                                                       })}
+
+CreatureLib::Battling::BattleScript* AngelScriptScript::Clone() {
+    auto* ctx = _ctxPool->RequestContext();
+    auto* obj = _type->Instantiate(ctx);
+    if (_obj != nullptr){
+        obj->CopyFrom(_obj);
+    }
+    _ctxPool->ReturnContextToPool(ctx);
+    return new AngelScriptScript(_resolver, _type, obj, _ctxPool);
 }
diff --git a/src/ScriptResolving/AngelScript/AngelScriptScript.hpp b/src/ScriptResolving/AngelScript/AngelScriptScript.hpp
index 2c7950d..9cc45cf 100644
--- a/src/ScriptResolving/AngelScript/AngelScriptScript.hpp
+++ b/src/ScriptResolving/AngelScript/AngelScriptScript.hpp
@@ -25,6 +25,8 @@ public:
 
     ~AngelScriptScript() override { _obj->Release(); }
 
+    BattleScript* Clone() override;
+
     inline asIScriptObject* GetRawAngelscriptObject() const noexcept { return _obj; }
 
     [[nodiscard]] const ArbUt::StringView& GetName() const noexcept override { return _type->GetName(); }