Support for cloning battles for AI purposes.
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
Signed-off-by: Deukhoofd <Deukhoofd@gmail.com>
This commit is contained in:
parent
a3b7002cd4
commit
84a14cff2b
|
@ -1,5 +1,5 @@
|
|||
Checks: 'readability-*,clang-diagnostic-*,clang-analyzer-*,-clang-analyzer-alpha*,performance-*,cppcoreguidelines-*,
|
||||
bugprone-*,modernize-*,-modernize-use-trailing-return-type'
|
||||
bugprone-*,modernize-*,-modernize-use-trailing-return-type,-cppcoreguidelines-non-private-member-variables-in-classes'
|
||||
HeaderFilterRegex: ''
|
||||
AnalyzeTemporaryDtors: false
|
||||
CheckOptions:
|
||||
|
|
|
@ -9,7 +9,9 @@ namespace CreatureLib::Battling {
|
|||
|
||||
protected:
|
||||
void Clear() override {
|
||||
if (_attack != nullptr) {
|
||||
_attack.reset();
|
||||
}
|
||||
HistoryElement::Clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,18 +6,24 @@
|
|||
namespace CreatureLib::Battling {
|
||||
class HistoryElement {
|
||||
friend class HistoryHolder;
|
||||
HistoryElement* _previous;
|
||||
u8* _previousOffset = nullptr;
|
||||
|
||||
protected:
|
||||
virtual void Clear() {
|
||||
if (_previous != nullptr) {
|
||||
_previous->Clear();
|
||||
if (_previousOffset != nullptr) {
|
||||
GetPrevious()->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
virtual HistoryElementKind GetKind() const noexcept = 0;
|
||||
ArbUt::BorrowedPtr<const HistoryElement> GetPrevious() const noexcept { return _previous; }
|
||||
ArbUt::BorrowedPtr<const HistoryElement> GetPrevious() const noexcept {
|
||||
return reinterpret_cast<HistoryElement*>((u8*)this - _previousOffset);
|
||||
}
|
||||
|
||||
ArbUt::BorrowedPtr<HistoryElement> GetPrevious() noexcept {
|
||||
return reinterpret_cast<HistoryElement*>((u8*)this - _previousOffset);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -4,13 +4,14 @@
|
|||
#include "../../Library/Exceptions/CreatureException.hpp"
|
||||
#include "HistoryElements/HistoryElement.hpp"
|
||||
|
||||
template <class T> concept HistoryElementType = std::is_base_of<CreatureLib::Battling::HistoryElement, T>::value;
|
||||
template <class T>
|
||||
concept HistoryElementType = std::is_base_of<CreatureLib::Battling::HistoryElement, T>::value;
|
||||
|
||||
namespace CreatureLib::Battling {
|
||||
class HistoryHolder {
|
||||
size_t _offset;
|
||||
size_t _capacity;
|
||||
uint8_t* _memory = nullptr;
|
||||
u8* _memory = nullptr;
|
||||
HistoryElement* _top = nullptr;
|
||||
|
||||
static constexpr size_t initialSize = 2048;
|
||||
|
@ -22,7 +23,7 @@ namespace CreatureLib::Battling {
|
|||
if (ptr == nullptr) {
|
||||
THROW("Out of memory.");
|
||||
}
|
||||
_memory = static_cast<uint8_t*>(ptr);
|
||||
_memory = static_cast<u8*>(ptr);
|
||||
}
|
||||
HistoryHolder(const HistoryHolder&) = delete;
|
||||
HistoryHolder& operator=(const HistoryHolder&) = delete;
|
||||
|
@ -33,19 +34,25 @@ namespace CreatureLib::Battling {
|
|||
free(_memory);
|
||||
}
|
||||
|
||||
template <HistoryElementType T, class... parameters> void Register(parameters... args) {
|
||||
if (_offset + sizeof(T) >= _capacity) {
|
||||
void Resize() {
|
||||
_capacity += stepSize;
|
||||
auto newPtr = realloc(_memory, _capacity);
|
||||
if (newPtr == nullptr) {
|
||||
THROW("Out of memory.");
|
||||
}
|
||||
_memory = static_cast<uint8_t*>(newPtr);
|
||||
_memory = static_cast<u8*>(newPtr);
|
||||
}
|
||||
uint8_t* ptr = _memory + _offset;
|
||||
|
||||
template <HistoryElementType T, class... parameters> void Register(parameters... args) {
|
||||
if (_offset + sizeof(T) >= _capacity) {
|
||||
Resize();
|
||||
}
|
||||
u8* ptr = _memory + _offset;
|
||||
T* element = new (ptr) T(args...);
|
||||
_offset += sizeof(T);
|
||||
element->_previous = _top;
|
||||
if (_top != nullptr) {
|
||||
element->_previousOffset = (u8*)sizeof(T);
|
||||
}
|
||||
_top = element;
|
||||
}
|
||||
|
||||
|
@ -56,10 +63,23 @@ namespace CreatureLib::Battling {
|
|||
while (c != nullptr) {
|
||||
if (c->GetKind() == HistoryElementKind::AttackUse)
|
||||
return c;
|
||||
c = c->_previous;
|
||||
c = c->GetPrevious();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void CloneOnto(HistoryHolder& other) {
|
||||
if (other._top != nullptr) {
|
||||
other._top->Clear();
|
||||
}
|
||||
free(other._memory);
|
||||
other._offset = _offset;
|
||||
other._capacity = _capacity;
|
||||
other._memory = static_cast<uint8_t*>(malloc(_capacity));
|
||||
if (_top != nullptr) {
|
||||
other._top = reinterpret_cast<HistoryElement*>(other._memory + ((u8*)_top - _memory));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -168,3 +168,45 @@ BattleScript* Battle::AddVolatileScript(const ArbUt::StringView& key) {
|
|||
BattleScript* Battle::AddVolatileScript(BattleScript* script) { return _volatile.Add(script); }
|
||||
void Battle::RemoveVolatileScript(BattleScript* script) { _volatile.Remove(script->GetName()); }
|
||||
void Battle::DisplayText(const ArbUt::StringView& text) { TriggerEventListener<DisplayTextEvent>(text); }
|
||||
|
||||
Battle* Battle::Clone() {
|
||||
auto parties = ArbUt::List<BattleParty*>(_parties.Count());
|
||||
for (auto* party : _parties) {
|
||||
parties.Append(party->Clone());
|
||||
}
|
||||
|
||||
auto* battle = new Battle(_library, 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);
|
||||
|
||||
return battle;
|
||||
}
|
||||
|
|
|
@ -131,6 +131,8 @@ namespace CreatureLib::Battling {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Battle* Clone();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace CreatureLib::Battling {
|
|||
BattleParty(CreatureParty* party, const ArbUt::List<CreatureIndex>& responsibleIndices)
|
||||
: _party(party), _responsibleIndices(responsibleIndices) {}
|
||||
|
||||
virtual ~BattleParty() = default;
|
||||
|
||||
inline const ArbUt::BorrowedPtr<CreatureParty>& GetParty() const { return _party; }
|
||||
inline const ArbUt::List<CreatureIndex>& GetResponsibleIndices() const { return _responsibleIndices; }
|
||||
|
||||
|
@ -40,6 +42,8 @@ namespace CreatureLib::Battling {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual BattleParty* Clone() { return new BattleParty(_party->Clone(), _responsibleIndices); }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -123,3 +123,10 @@ bool BattleSide::SwapPositions(u8 a, u8 b) {
|
|||
_battle->TriggerEventListener<SwapEvent>(_index, a, b);
|
||||
return true;
|
||||
}
|
||||
BattleSide* BattleSide::CloneWithoutCreatures() {
|
||||
auto* side = new BattleSide(_index, _battle, _creaturesPerSide);
|
||||
side->_choicesSet = _choicesSet;
|
||||
_volatile.Clone(side->_volatile);
|
||||
side->_hasFled = _hasFled;
|
||||
return side;
|
||||
}
|
||||
|
|
|
@ -87,6 +87,8 @@ namespace CreatureLib::Battling {
|
|||
uint8_t GetRandomCreatureIndex();
|
||||
|
||||
bool SwapPositions(u8 a, u8 b);
|
||||
|
||||
BattleSide* CloneWithoutCreatures();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "Creature.hpp"
|
||||
#include <utility>
|
||||
#include "../EventHooks/EventDataClasses.hpp"
|
||||
#include "../Models/Battle.hpp"
|
||||
#include "../ScriptHandling/ScriptMacros.hpp"
|
||||
|
@ -6,11 +7,11 @@
|
|||
using namespace CreatureLib;
|
||||
|
||||
namespace CreatureLib::Battling {
|
||||
Creature::Creature(ArbUt::BorrowedPtr<const BattleLibrary> library,
|
||||
Creature::Creature(const ArbUt::BorrowedPtr<const BattleLibrary>& library,
|
||||
const ArbUt::BorrowedPtr<const Library::CreatureSpecies>& species,
|
||||
const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant, level_int_t level,
|
||||
uint32_t experience, uint32_t uid, Library::Gender gender, uint8_t coloring,
|
||||
const ArbUt::OptionalBorrowedPtr<const Library::Item> heldItem, const std::string& nickname,
|
||||
ArbUt::OptionalBorrowedPtr<const Library::Item> heldItem, std::string nickname,
|
||||
const Library::TalentIndex& talent, const std::vector<LearnedAttack*>& attacks,
|
||||
bool allowedExperienceGain)
|
||||
: _library(library), _species(species), _variant(variant), _level(level), _experience(experience),
|
||||
|
@ -110,10 +111,11 @@ namespace CreatureLib::Battling {
|
|||
bool Creature::ChangeStatBoost(Library::Statistic stat, int8_t diffAmount) {
|
||||
bool changed = false;
|
||||
auto oldValue = this->_statBoost.GetStat(stat);
|
||||
if (diffAmount > 0)
|
||||
if (diffAmount > 0) {
|
||||
changed = this->_statBoost.IncreaseStatBy(stat, diffAmount);
|
||||
else
|
||||
} else if (diffAmount < 0) {
|
||||
changed = this->_statBoost.DecreaseStatBy(stat, -diffAmount);
|
||||
}
|
||||
if (this->GetBattle().HasValue()) {
|
||||
auto newValue = this->_statBoost.GetStat(stat);
|
||||
this->GetBattle().GetValue()->TriggerEventListener<ChangeStatBoostEvent>(this, stat, oldValue, newValue);
|
||||
|
@ -123,7 +125,7 @@ namespace CreatureLib::Battling {
|
|||
}
|
||||
|
||||
void Creature::RecalculateFlatStats() {
|
||||
auto& statCalc = this->_library->GetStatCalculator();
|
||||
const auto& statCalc = this->_library->GetStatCalculator();
|
||||
this->_flatStats = statCalc->CalculateFlatStats(this);
|
||||
RecalculateBoostedStats();
|
||||
}
|
||||
|
@ -329,4 +331,41 @@ namespace CreatureLib::Battling {
|
|||
_attacks.Set(index, attack);
|
||||
}
|
||||
|
||||
Creature* Creature::Clone() {
|
||||
auto attacks = std::vector<LearnedAttack*>(_attacks.Count());
|
||||
auto i = 0;
|
||||
for (auto* attack : _attacks) {
|
||||
if (attack == nullptr) {
|
||||
attacks[i++] = nullptr;
|
||||
} else {
|
||||
attacks[i++] = attack->Clone();
|
||||
}
|
||||
}
|
||||
|
||||
auto* c = new Creature(_library, _species, _variant, _level, _experience, _uniqueIdentifier, _gender, _coloring,
|
||||
_heldItem, _nickname, _talentIndex, attacks, _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<BattleScript>(_activeTalent->Clone());
|
||||
}
|
||||
c->_hasOverridenTalent = _hasOverridenTalent;
|
||||
c->_overridenTalentName = _overridenTalentName;
|
||||
if (_status != nullptr) {
|
||||
c->_status = std::unique_ptr<BattleScript>(_status->Clone());
|
||||
}
|
||||
_volatile.Clone(c->_volatile);
|
||||
c->_types = std::vector<u8>(_types);
|
||||
c->RecalculateFlatStats();
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
|
@ -43,7 +43,7 @@ namespace CreatureLib::Battling {
|
|||
ArbUt::OptionalBorrowedPtr<BattleSide> _side = nullptr;
|
||||
bool _onBattleField = false;
|
||||
|
||||
std::string _nickname = "";
|
||||
std::string _nickname;
|
||||
CreatureLib::Library::TalentIndex _talentIndex;
|
||||
std::unique_ptr<BattleScript> _activeTalent = nullptr;
|
||||
|
||||
|
@ -57,17 +57,17 @@ namespace CreatureLib::Battling {
|
|||
std::unique_ptr<BattleScript> _status = nullptr;
|
||||
ScriptSet _volatile = {};
|
||||
|
||||
std::vector<uint8_t> _types;
|
||||
std::vector<u8> _types;
|
||||
|
||||
private:
|
||||
void OnFaint();
|
||||
|
||||
public:
|
||||
Creature(ArbUt::BorrowedPtr<const BattleLibrary> library,
|
||||
Creature(const ArbUt::BorrowedPtr<const BattleLibrary>& library,
|
||||
const ArbUt::BorrowedPtr<const Library::CreatureSpecies>& species,
|
||||
const ArbUt::BorrowedPtr<const Library::SpeciesVariant>& variant, level_int_t level,
|
||||
uint32_t experience, uint32_t uid, Library::Gender gender, uint8_t coloring,
|
||||
const ArbUt::OptionalBorrowedPtr<const Library::Item> heldItem, const std::string& nickname,
|
||||
ArbUt::OptionalBorrowedPtr<const Library::Item> heldItem, std::string nickname,
|
||||
const Library::TalentIndex& talent, const std::vector<LearnedAttack*>& attacks,
|
||||
bool allowedExperienceGain = true);
|
||||
|
||||
|
@ -186,6 +186,8 @@ namespace CreatureLib::Battling {
|
|||
void RecalculateBoostedStat(Library::Statistic);
|
||||
|
||||
// endregion
|
||||
|
||||
virtual Creature* Clone();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,18 @@ namespace CreatureLib::Battling {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual CreatureParty* Clone() {
|
||||
auto party = new CreatureParty(_party.Count());
|
||||
auto i = 0;
|
||||
for (auto c : _party) {
|
||||
if (c != nullptr) {
|
||||
party->SwapInto(i, c->Clone());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return party;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,12 @@ namespace CreatureLib::Battling {
|
|||
virtual void DecreaseUses(uint8_t amount) noexcept;
|
||||
virtual void RestoreUses(uint8_t amount) noexcept;
|
||||
virtual void RestoreAllUses() noexcept;
|
||||
|
||||
virtual LearnedAttack* Clone() {
|
||||
auto* attack = new LearnedAttack(_attack, _maxUses, _learnMethod);
|
||||
attack->_remainingUses = _remainingUses;
|
||||
return attack;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace CreatureLib::Battling {
|
|||
|
||||
virtual ~BattleScript() = default;
|
||||
|
||||
virtual BattleScript* Clone() = 0;
|
||||
|
||||
virtual void Stack(){};
|
||||
virtual void OnRemove(){};
|
||||
|
||||
|
|
|
@ -15,6 +15,12 @@ namespace CreatureLib::Battling {
|
|||
static constexpr size_t defaultCapacity = 8;
|
||||
ScriptSet() : _scripts(defaultCapacity), _lookup(defaultCapacity){};
|
||||
|
||||
void Clone(ScriptSet& s) {
|
||||
for (auto* script : _scripts) {
|
||||
s.Add(script->Clone());
|
||||
}
|
||||
}
|
||||
|
||||
BattleScript* Add(BattleScript* script) {
|
||||
auto v = _lookup.TryGet(script->GetName());
|
||||
if (v.has_value()) {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
#ifdef TESTS_BUILD
|
||||
|
||||
#include "../../extern/doctest.hpp"
|
||||
#include "../../src/Battling/History/HistoryElements/AttackUseHistory.hpp"
|
||||
#include "../../src/Battling/Models/Battle.hpp"
|
||||
#include "../../src/Battling/Models/BattleSide.hpp"
|
||||
#include "../../src/Battling/Models/CreateCreature.hpp"
|
||||
#include "../../src/Battling/TurnChoices/PassTurnChoice.hpp"
|
||||
#include "../TestLibrary/TestLibrary.hpp"
|
||||
|
||||
using namespace CreatureLib::Battling;
|
||||
|
||||
TEST_CASE("Clone battle, test basic functionality") {
|
||||
auto lib = TestLibrary::Get();
|
||||
|
||||
auto creature = CreateCreature(lib, "testSpecies1"_cnc, 1).Create();
|
||||
auto party = CreatureParty(6);
|
||||
party.SwapInto(0, creature);
|
||||
|
||||
auto bp = ArbUt::List<BattleParty*>();
|
||||
bp.Append(new BattleParty(&party, {CreatureIndex(0, 0)}));
|
||||
auto battle = Battle(lib, bp);
|
||||
battle.SwitchCreature(0, 0, creature);
|
||||
|
||||
auto clone = battle.Clone();
|
||||
|
||||
REQUIRE_NE(battle.GetCreature(0, 0), clone->GetCreature(0, 0));
|
||||
REQUIRE(battle.GetCreature(0, 0).GetValue()->GetSpecies() == clone->GetCreature(0, 0).GetValue()->GetSpecies());
|
||||
|
||||
auto clonedParty = clone->GetParties().At(0)->GetParty().GetRaw();
|
||||
delete clone;
|
||||
delete clonedParty;
|
||||
}
|
||||
|
||||
TEST_CASE("Clone battle, test history history holder") {
|
||||
auto lib = TestLibrary::Get();
|
||||
auto battle = Battle(lib, {});
|
||||
auto side = BattleSide(0, &battle, 1);
|
||||
auto c = CreateCreature(TestLibrary::Get(), "testSpecies1"_cnc, 5).Create();
|
||||
side.SetCreature(c, 0);
|
||||
|
||||
auto clone = battle.Clone();
|
||||
clone->RegisterHistoryElement<AttackUseHistory>(nullptr);
|
||||
clone->RegisterHistoryElement<AttackUseHistory>(nullptr);
|
||||
|
||||
delete c;
|
||||
delete clone;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -12,10 +12,11 @@ private:
|
|||
ArbUt::StringView _name;
|
||||
|
||||
public:
|
||||
explicit TestScript(const std::string& name) : _name(name.c_str(), name.length()){};
|
||||
explicit TestScript(const ArbUt::StringView& name) : _name(name){};
|
||||
const ArbUt::StringView& GetName() const noexcept override { return _name; }
|
||||
|
||||
void TestMethod(int& runCount) { runCount++; }
|
||||
BattleScript* Clone() override { return new TestScript(_name); }
|
||||
};
|
||||
|
||||
TEST_CASE("Script Aggregator properly iterates containing script.") {
|
||||
|
|
|
@ -12,8 +12,10 @@ private:
|
|||
ArbUt::StringView _name;
|
||||
|
||||
public:
|
||||
explicit TestScript(const std::string& name) : _name(name.c_str(), name.length()){};
|
||||
explicit TestScript(const ArbUt::StringView& name) : _name(name){};
|
||||
const ArbUt::StringView& GetName() const noexcept override { return _name; }
|
||||
|
||||
BattleScript* Clone() override { return new TestScript(_name); }
|
||||
};
|
||||
|
||||
TEST_CASE("Empty script set count == 0") {
|
||||
|
|
|
@ -11,10 +11,11 @@ private:
|
|||
ArbUt::StringView _name;
|
||||
|
||||
public:
|
||||
explicit TestScript(const std::string& name) : _name(name.c_str(), name.length()){};
|
||||
explicit TestScript(const ArbUt::StringView& name) : _name(name){};
|
||||
const ArbUt::StringView& GetName() const noexcept override { return _name; }
|
||||
|
||||
void TestMethod(int& runCount) { runCount++; }
|
||||
BattleScript* Clone() override { return new TestScript(_name); }
|
||||
};
|
||||
|
||||
class ScriptSourceWithScriptPtr : public ScriptSource {
|
||||
|
|
Loading…
Reference in New Issue