mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-29 00:15:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbd9e95875 |
@@ -49,6 +49,7 @@ static auto IsDeterministic(const CommandType type) -> bool {
|
||||
case net::eagle0::shardok::common::FORTIFY_COMMAND:
|
||||
case net::eagle0::shardok::common::BECOME_OUTLAW_COMMAND:
|
||||
case net::eagle0::shardok::common::HOLY_WAVE_COMMAND:
|
||||
case net::eagle0::shardok::common::EVACUATE_PRISONERS_COMMAND:
|
||||
case net::eagle0::shardok::common::REPAIR_COMMAND: return true;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
@@ -226,6 +226,7 @@ double AIHeuristicWeighting::GetCommandWeight(
|
||||
// === ZERO WEIGHT - NEVER SELECT (0.0) ===
|
||||
// Explicitly bad actions
|
||||
case CommandType::FLEE_COMMAND: return 0.0; // Never flee in simulation
|
||||
case CommandType::EVACUATE_PRISONERS_COMMAND: return 0.0;
|
||||
case CommandType::RETREAT_COMMAND: return 0.0;
|
||||
case CommandType::BECOME_OUTLAW_COMMAND: return 0.0; // Never become outlaw
|
||||
case CommandType::DISMISS_UNIT_COMMAND:
|
||||
|
||||
@@ -91,6 +91,7 @@ auto AbstractAIScoreCalculator::CalculateUnitsScoreComponents(
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_FLED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_NEVER_ENTERED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_OUTLAWED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_EVACUATED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RESERVE_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RETREATED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RESERVED_SLOT:
|
||||
|
||||
@@ -725,6 +725,10 @@ void AddUnits(vector<net::eagle0::shardok::storage::ResolvedUnit> &to, const Uni
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_OUTLAWED_UNIT:
|
||||
ru.set_status(net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_OUTLAWED_UNIT);
|
||||
break;
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_EVACUATED_UNIT:
|
||||
ru.set_status(
|
||||
net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_EVACUATED_UNIT);
|
||||
break;
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RESERVE_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_NEVER_ENTERED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RESERVED_SLOT:
|
||||
|
||||
@@ -28,6 +28,7 @@ auto IsResolved(const net::eagle0::shardok::storage::fb::UnitStatus status) -> b
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_FLED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RETREATED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_OUTLAWED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_EVACUATED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_DESTROYED_SUMMONED_UNIT: return true;
|
||||
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT:
|
||||
|
||||
@@ -17,6 +17,7 @@ cc_library(
|
||||
":challenge_duel_command_factory",
|
||||
":charge_command_factory",
|
||||
":control_command_factory",
|
||||
":evacuate_prisoners_command_factory",
|
||||
":extinguish_fire_command_factory",
|
||||
":fear_command_factory",
|
||||
":flee_command_factory",
|
||||
@@ -178,6 +179,24 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "evacuate_prisoners_command_factory",
|
||||
srcs = ["EvacuatePrisonersCommandFactory.cpp"],
|
||||
hdrs = ["EvacuatePrisonersCommandFactory.hpp"],
|
||||
copts = COPTS,
|
||||
visibility = [
|
||||
"//src/main/cpp/net/eagle0/shardok/library:__subpackages__",
|
||||
"//src/test/cpp/net/eagle0/shardok/library:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":command_factory",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:shardok_command",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/commands:evacuate_prisoners_command",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/map:coordinates",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/settings:game_settings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "extinguish_fire_command_factory",
|
||||
srcs = ["ExtinguishFireCommandFactory.cpp"],
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/command_factories/ChallengeDuelCommandFactory.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/command_factories/ChargeCommandFactory.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/command_factories/ControlCommandFactory.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/command_factories/EvacuatePrisonersCommandFactory.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/command_factories/ExtinguishFireCommandFactory.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/command_factories/FearCommandFactory.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/command_factories/FleeCommandFactory.hpp"
|
||||
@@ -47,6 +48,7 @@ auto MakeFactories(const SettingsGetter& settings) -> vector<shared_ptr<const Co
|
||||
make_shared<BuildBridgeCommandFactory>(settings),
|
||||
make_shared<ChallengeDuelCommandFactory>(settings),
|
||||
make_shared<ChargeCommandFactory>(settings),
|
||||
make_shared<EvacuatePrisonersCommandFactory>(settings),
|
||||
make_shared<ExtinguishFireCommandFactory>(settings),
|
||||
make_shared<FearCommandFactory>(settings),
|
||||
make_shared<FleeCommandFactory>(settings),
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// EvacuatePrisonersCommandFactory.cpp
|
||||
// eagle0
|
||||
//
|
||||
|
||||
#include "EvacuatePrisonersCommandFactory.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/commands/EvacuatePrisonersCommand.hpp"
|
||||
|
||||
namespace shardok {
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] auto IsWarden(const Unit* unit) -> bool {
|
||||
return unit->has_attached_hero() &&
|
||||
unit->attached_hero().profession_info().profession() ==
|
||||
net::eagle0::shardok::storage::fb::Profession_WARDEN;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto IsHostileToActor(
|
||||
const Unit* unit,
|
||||
const PlayerId actorPlayerId,
|
||||
const vector<PlayerId>& allyPids) -> bool {
|
||||
return unit->player_id() != actorPlayerId &&
|
||||
!std::ranges::contains(allyPids, unit->player_id());
|
||||
}
|
||||
|
||||
[[nodiscard]] auto CapturedHostileHeroUnitIds(
|
||||
const Units* units,
|
||||
const PlayerId actorPlayerId,
|
||||
const vector<PlayerId>& allyPids) -> std::vector<UnitId> {
|
||||
std::vector<UnitId> captiveUnitIds;
|
||||
for (const auto* unit : *units) {
|
||||
if (unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_CAPTURED_UNIT &&
|
||||
unit->has_attached_hero() && IsHostileToActor(unit, actorPlayerId, allyPids)) {
|
||||
captiveUnitIds.push_back(unit->unit_id());
|
||||
}
|
||||
}
|
||||
return captiveUnitIds;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
EvacuatePrisonersCommandFactory::EvacuatePrisonersCommandFactory(const SettingsGetter& getter)
|
||||
: settings(getter) {}
|
||||
|
||||
void EvacuatePrisonersCommandFactory::AddAvailableCommands(
|
||||
CommandList& commands,
|
||||
const CommandFactory::CommandParams& params) const {
|
||||
if (!params.unit->can_flee()) return;
|
||||
if (!IsWarden(params.unit)) return;
|
||||
if (!settings.ActionCostFor(settings.Backing().flee_action_point_cost())
|
||||
.IsPossible(params.remainingActionPoints))
|
||||
return;
|
||||
|
||||
const auto captiveUnitIds =
|
||||
CapturedHostileHeroUnitIds(params.units, params.unit->player_id(), params.allyPids);
|
||||
if (captiveUnitIds.empty()) return;
|
||||
|
||||
commands.push_back(std::make_shared<EvacuatePrisonersCommand>(
|
||||
settings.ActionCostFor(settings.Backing().flee_action_point_cost()),
|
||||
params.unit->player_id(),
|
||||
params.unit->unit_id(),
|
||||
captiveUnitIds));
|
||||
}
|
||||
|
||||
} // namespace shardok
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// EvacuatePrisonersCommandFactory.hpp
|
||||
// eagle0
|
||||
//
|
||||
|
||||
#ifndef EvacuatePrisonersCommandFactory_hpp
|
||||
#define EvacuatePrisonersCommandFactory_hpp
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/command_factories/CommandFactory.hpp"
|
||||
|
||||
namespace shardok {
|
||||
|
||||
class EvacuatePrisonersCommandFactory : public CommandFactory {
|
||||
const SettingsGetter& settings;
|
||||
|
||||
public:
|
||||
explicit EvacuatePrisonersCommandFactory(const SettingsGetter& getter);
|
||||
|
||||
void AddAvailableCommands(CommandList& commands, const CommandFactory::CommandParams& params)
|
||||
const override;
|
||||
};
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif /* EvacuatePrisonersCommandFactory_hpp */
|
||||
@@ -189,6 +189,23 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "evacuate_prisoners_command",
|
||||
srcs = ["EvacuatePrisonersCommand.cpp"],
|
||||
hdrs = ["EvacuatePrisonersCommand.hpp"],
|
||||
copts = COPTS,
|
||||
visibility = [
|
||||
"//src/main/cpp/net/eagle0/shardok/library:__subpackages__",
|
||||
"//src/test/cpp/net/eagle0/shardok/ai:__subpackages__",
|
||||
"//src/test/cpp/net/eagle0/shardok/library:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/cpp/net/eagle0/shardok/library:action_cost",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:shardok_command",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/util:action_result_flatbuffer_helpers",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "fear_command",
|
||||
srcs = ["FearCommand.cpp"],
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// EvacuatePrisonersCommand.cpp
|
||||
// eagle0
|
||||
//
|
||||
|
||||
#include "EvacuatePrisonersCommand.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/util/ActionResultFlatbufferHelpers.hpp"
|
||||
|
||||
namespace shardok {
|
||||
|
||||
auto EvacuatePrisonersCommand::InternalExecute(
|
||||
const GameStateW& currentState,
|
||||
const std::shared_ptr<RandomGenerator>& /*generator*/) const -> vector<ActionResult> {
|
||||
ActionResult result{};
|
||||
result.mutable_player()->set_value(GetPlayerId());
|
||||
result.mutable_actor()->set_value(actorId);
|
||||
result.set_type(net::eagle0::shardok::common::ActionType::EVACUATED_PRISONERS);
|
||||
|
||||
const auto* actorUnit = currentState->units()->Get(actorId);
|
||||
auto* resolvedActor = result.add_resolved_units();
|
||||
AddUnitToResolved(*resolvedActor, *actorUnit);
|
||||
resolvedActor->set_status(
|
||||
net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_EVACUATED_UNIT);
|
||||
|
||||
for (const auto captiveUnitId : captiveUnitIds) {
|
||||
const auto* captiveUnit = currentState->units()->Get(captiveUnitId);
|
||||
auto* resolvedCaptive = result.add_resolved_units();
|
||||
AddUnitToResolved(*resolvedCaptive, *captiveUnit);
|
||||
resolvedCaptive->set_status(
|
||||
net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_EVACUATED_UNIT);
|
||||
}
|
||||
|
||||
return {result};
|
||||
}
|
||||
|
||||
EvacuatePrisonersCommand::EvacuatePrisonersCommand(
|
||||
const ActionCost& cost,
|
||||
const PlayerId playerId,
|
||||
const UnitId actorId,
|
||||
std::vector<UnitId> captiveUnitIds)
|
||||
: ShardokCommand(playerId),
|
||||
cost(cost),
|
||||
actorId(actorId),
|
||||
captiveUnitIds(std::move(captiveUnitIds)) {}
|
||||
|
||||
auto EvacuatePrisonersCommand::GetCommandProto() const -> CommandProto {
|
||||
CommandProto proto;
|
||||
|
||||
proto.set_player(GetPlayerId());
|
||||
proto.set_type(net::eagle0::shardok::common::EVACUATE_PRISONERS_COMMAND);
|
||||
proto.mutable_actor()->set_value(actorId);
|
||||
|
||||
return proto;
|
||||
}
|
||||
} // namespace shardok
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// EvacuatePrisonersCommand.hpp
|
||||
// eagle0
|
||||
//
|
||||
|
||||
#ifndef EvacuatePrisonersCommand_hpp
|
||||
#define EvacuatePrisonersCommand_hpp
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/ActionCost.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/ShardokCommand.hpp"
|
||||
|
||||
namespace shardok {
|
||||
class EvacuatePrisonersCommand : public ShardokCommand {
|
||||
protected:
|
||||
auto InternalExecute(
|
||||
const GameStateW& currentState,
|
||||
const std::shared_ptr<RandomGenerator>& generator) const
|
||||
-> vector<ActionResult> override;
|
||||
|
||||
const ActionCost cost;
|
||||
const UnitId actorId;
|
||||
const std::vector<UnitId> captiveUnitIds;
|
||||
|
||||
public:
|
||||
EvacuatePrisonersCommand(
|
||||
const ActionCost& cost,
|
||||
PlayerId playerId,
|
||||
UnitId actorId,
|
||||
std::vector<UnitId> captiveUnitIds);
|
||||
|
||||
~EvacuatePrisonersCommand() override = default;
|
||||
|
||||
[[nodiscard]] auto GetCommandType() const -> CommandType override {
|
||||
return net::eagle0::shardok::common::EVACUATE_PRISONERS_COMMAND;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto GetCommandProto() const -> CommandProto override;
|
||||
[[nodiscard]] auto GetOddsPercentile() const -> int32_t override { return 100; }
|
||||
[[nodiscard]] auto CanBeFollowUp() const -> bool override { return false; }
|
||||
[[nodiscard]] auto CanDoWithLowMorale() const -> bool override { return true; }
|
||||
|
||||
[[nodiscard]] int GetActorUnitId() const override { return actorId; }
|
||||
};
|
||||
} // namespace shardok
|
||||
|
||||
#endif /* EvacuatePrisonersCommand_hpp */
|
||||
@@ -73,6 +73,9 @@ void DumpGameStateToFile(const GameStateW& gameState, const std::string& gameId)
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RETREATED_UNIT:
|
||||
out << "RETREATED_UNIT\n";
|
||||
break;
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_EVACUATED_UNIT:
|
||||
out << "EVACUATED_UNIT\n";
|
||||
break;
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_PENDING_REINFORCEMENT:
|
||||
out << "PENDING_REINFORCEMENT\n";
|
||||
break;
|
||||
|
||||
@@ -40,13 +40,16 @@ void Validate(const GameStateW &inState, const GameStateW &outState) {
|
||||
inStatus != net::eagle0::shardok::storage::fb::UnitStatus_DESTROYED_SUMMONED_UNIT &&
|
||||
inStatus != net::eagle0::shardok::storage::fb::UnitStatus_CAPTURED_UNIT &&
|
||||
inStatus != net::eagle0::shardok::storage::fb::UnitStatus_RETREATED_UNIT &&
|
||||
inStatus != net::eagle0::shardok::storage::fb::UnitStatus_OUTLAWED_UNIT)
|
||||
inStatus != net::eagle0::shardok::storage::fb::UnitStatus_OUTLAWED_UNIT &&
|
||||
inStatus != net::eagle0::shardok::storage::fb::UnitStatus_EVACUATED_UNIT)
|
||||
continue;
|
||||
|
||||
const auto outStatus =
|
||||
outState->units()->Get(static_cast<unsigned int>(inUnit->unit_id()))->status();
|
||||
internalAssert(
|
||||
outStatus == inStatus,
|
||||
outStatus == inStatus ||
|
||||
(inStatus == net::eagle0::shardok::storage::fb::UnitStatus_CAPTURED_UNIT &&
|
||||
outStatus == net::eagle0::shardok::storage::fb::UnitStatus_EVACUATED_UNIT),
|
||||
"Can't come back from fled/destroyed/captured status");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ auto GameStateFilteredForPlayerWithAllies(
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_FLED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RETREATED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_OUTLAWED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_EVACUATED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RESERVED_SLOT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_PENDING_REINFORCEMENT: break;
|
||||
|
||||
@@ -110,6 +111,7 @@ auto GameStateFilteredForPlayerWithAllies(
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_FLED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RETREATED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_OUTLAWED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_EVACUATED_UNIT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_RESERVED_SLOT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_PENDING_REINFORCEMENT:
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_UNKNOWN_UNIT: break;
|
||||
@@ -122,4 +124,4 @@ auto GameStateFilteredForPlayerWithAllies(
|
||||
|
||||
return gsv;
|
||||
}
|
||||
} // namespace shardok
|
||||
} // namespace shardok
|
||||
|
||||
@@ -81,7 +81,8 @@ auto IncludeUnitProtoInReturn(
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_FLED_UNIT:
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_RETREATED_UNIT:
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_NEVER_ENTERED_UNIT:
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_OUTLAWED_UNIT: return true;
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_OUTLAWED_UNIT:
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_EVACUATED_UNIT: return true;
|
||||
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_DESTROYED_SUMMONED_UNIT:
|
||||
return false;
|
||||
@@ -161,6 +162,9 @@ auto FromInternalStatus(
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_OUTLAWED_UNIT:
|
||||
return net::eagle0::common::UnitStatus::OUTLAWED_UNIT;
|
||||
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_EVACUATED_UNIT:
|
||||
return net::eagle0::common::UnitStatus::EVACUATED_UNIT;
|
||||
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_DESTROYED_SUMMONED_UNIT:
|
||||
case net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_UNKNOWN_UNIT:
|
||||
case net::eagle0::shardok::storage::
|
||||
|
||||
@@ -20,6 +20,7 @@ enum UnitStatus : int8 {
|
||||
RESERVE_UNIT = 8,
|
||||
RESERVED_SLOT = 9,
|
||||
PENDING_REINFORCEMENT = 10,
|
||||
EVACUATED_UNIT = 11,
|
||||
}
|
||||
|
||||
struct Unit {
|
||||
@@ -53,4 +54,3 @@ struct Unit {
|
||||
location:net.eagle0.shardok.storage.fb.Coords (native_inline);
|
||||
battalion:net.eagle0.shardok.storage.fb.Battalion (native_inline);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ enum UnitStatus {
|
||||
RETREATED_UNIT = 4;
|
||||
NEVER_ENTERED_UNIT = 5;
|
||||
OUTLAWED_UNIT = 6;
|
||||
EVACUATED_UNIT = 7;
|
||||
}
|
||||
|
||||
message ResolvedUnit {
|
||||
|
||||
@@ -80,6 +80,7 @@ message FoughtInBattleBattalionBackstoryEvent {
|
||||
UNIT_STATUS_RETREATED = 4;
|
||||
UNIT_STATUS_NEVER_ENTERED = 5;
|
||||
UNIT_STATUS_OUTLAWED = 6;
|
||||
UNIT_STATUS_EVACUATED = 7;
|
||||
}
|
||||
UnitStatus status = 6;
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ message FoughtInBattleBackstoryEvent {
|
||||
UNIT_STATUS_RETREATED = 4;
|
||||
UNIT_STATUS_NEVER_ENTERED = 5;
|
||||
UNIT_STATUS_OUTLAWED = 6;
|
||||
UNIT_STATUS_EVACUATED = 7;
|
||||
}
|
||||
UnitStatus status = 6;
|
||||
}
|
||||
|
||||
@@ -94,4 +94,5 @@ enum ActionType {
|
||||
TUTORIAL_REINFORCEMENTS_ARRIVED = 79;
|
||||
BLOW_BRIDGE = 80;
|
||||
BLOW_BRIDGE_FAILED = 81;
|
||||
EVACUATED_PRISONERS = 82;
|
||||
}
|
||||
|
||||
@@ -47,4 +47,5 @@ enum CommandType {
|
||||
BECOME_OUTLAW_COMMAND = 36;
|
||||
// MANAGE_PRISONER = 37; // Removed: Eagle-only command, not used in Shardok
|
||||
BLOW_BRIDGE_COMMAND = 38;
|
||||
EVACUATE_PRISONERS_COMMAND = 39;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ message ResolvedUnit {
|
||||
DESTROYED_SUMMONED_UNIT = 5;
|
||||
NEVER_ENTERED_UNIT = 6;
|
||||
OUTLAWED_UNIT = 7;
|
||||
// Keep this aligned with storage/unit.fbs; values 8-10 are active/reserve flatbuffer-only statuses.
|
||||
EVACUATED_UNIT = 11;
|
||||
}
|
||||
|
||||
bytes unit_bytes = 1;
|
||||
|
||||
+153
-30
@@ -16,7 +16,7 @@ import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedPro
|
||||
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedHeroC, StatAbsolute}
|
||||
import net.eagle0.eagle.model.action_result.types.ActionResultType.BattleEnded
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.state.{AftermathClaimant, AftermathClaimantUnit, Army}
|
||||
import net.eagle0.eagle.model.state.{AftermathClaimant, AftermathClaimantUnit, Army, CapturedHero}
|
||||
import net.eagle0.eagle.model.state.{
|
||||
BattleRevelation,
|
||||
BattleRevelationType,
|
||||
@@ -27,7 +27,7 @@ import net.eagle0.eagle.model.state.{
|
||||
}
|
||||
import net.eagle0.eagle.model.state.battalion.{BattalionT, EventForBattalionBackstory, EventForBattalionBackstoryT}
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.model.state.hero.{EventForHeroBackstory, EventForHeroBackstoryT, HeroT}
|
||||
import net.eagle0.eagle.model.state.hero.{EventForHeroBackstory, EventForHeroBackstoryT, HeroT, Profession}
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.model.state.quest.{DefeatFactionQuest, Quest, WinBattleOutnumberedQuest, WinBattlesQuest}
|
||||
import net.eagle0.eagle.model.state.shardok_battle.{BattleType, ShardokBattle, ShardokPlayer}
|
||||
@@ -97,7 +97,7 @@ case class ResolveBattleAction(
|
||||
if defenderProvince.rulingFactionId.contains(rsp.eagleFid)
|
||||
rse <- rsp.units
|
||||
if battle.reinforcementHeroIds.contains(rse.hero.id)
|
||||
if rse.status != UnitStatus.Fled && rse.status != UnitStatus.Outlawed
|
||||
if rse.status != UnitStatus.Fled && rse.status != UnitStatus.Outlawed && rse.status != UnitStatus.Evacuated
|
||||
} yield rse).toVector
|
||||
|
||||
ChangedProvinceC(
|
||||
@@ -113,7 +113,9 @@ case class ResolveBattleAction(
|
||||
if unitReturned(
|
||||
hid = rse.hero.id,
|
||||
in = in,
|
||||
out = rsp
|
||||
out = rsp,
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
) || rse.status == UnitStatus.Outlawed
|
||||
} yield rse.hero.id).toVector,
|
||||
newBattalionIds = survivingReinforcementUnits.flatMap(_.optionalBattalionId),
|
||||
@@ -125,7 +127,9 @@ case class ResolveBattleAction(
|
||||
if unitReturned(
|
||||
hid = rse.hero.id,
|
||||
in = in,
|
||||
out = rsp
|
||||
out = rsp,
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
) || rse.status == UnitStatus.Outlawed
|
||||
} yield rse.optionalBattalionId).flatten.toVector,
|
||||
goldDelta = Some(
|
||||
@@ -362,6 +366,9 @@ case class ResolveBattleAction(
|
||||
batt = battle,
|
||||
br = battleResolution,
|
||||
destroyedBattalionIds = destroyedBattalionIds(battleResolution)
|
||||
) ++ changedProvincesFromEvacuatedCaptives(
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
) :+ defenderCP(
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
@@ -422,10 +429,13 @@ case class ResolveBattleAction(
|
||||
in = battle.players
|
||||
.find(_.eagleFid == losingPlayer.eagleFid)
|
||||
.get,
|
||||
out = losingPlayer
|
||||
out = losingPlayer,
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
)
|
||||
)
|
||||
.filterNot(_.status == UnitStatus.Outlawed)
|
||||
.filterNot(u => evacuatedCaptiveDestination(u, losingPlayer, battle, battleResolution).isDefined)
|
||||
}
|
||||
|
||||
ProvinceHeldAction(
|
||||
@@ -459,10 +469,20 @@ case class ResolveBattleAction(
|
||||
unitReturned(
|
||||
hid = u.hero.id,
|
||||
in = wsp,
|
||||
out = winningResolvedPlayers.find(_.eagleFid == wsp.eagleFid).get
|
||||
out = winningResolvedPlayers.find(_.eagleFid == wsp.eagleFid).get,
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
)
|
||||
)
|
||||
.filterNot(_.status == UnitStatus.Outlawed)
|
||||
.filterNot(u =>
|
||||
evacuatedCaptiveDestination(
|
||||
u,
|
||||
winningResolvedPlayers.find(_.eagleFid == wsp.eagleFid).get,
|
||||
battle,
|
||||
battleResolution
|
||||
).isDefined
|
||||
)
|
||||
.map(u =>
|
||||
ResolvedEagleUnit(
|
||||
hero = startingState.heroes(u.hero.id),
|
||||
@@ -481,10 +501,13 @@ case class ResolveBattleAction(
|
||||
unitReturned(
|
||||
hid = u.hero.id,
|
||||
in = sp,
|
||||
out = rsp
|
||||
out = rsp,
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
)
|
||||
)
|
||||
.filterNot(_.status == UnitStatus.Outlawed)
|
||||
.filterNot(u => evacuatedCaptiveDestination(u, rsp, battle, battleResolution).isDefined)
|
||||
} yield ResolvedEagleUnit(
|
||||
hero = startingState.heroes(u.hero.id),
|
||||
battalion = u.battalion.map(b => startingState.battalions(b.id)),
|
||||
@@ -511,10 +534,13 @@ case class ResolveBattleAction(
|
||||
unitReturned(
|
||||
hid = u.hero.id,
|
||||
in = sp,
|
||||
out = rsp
|
||||
out = rsp,
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
)
|
||||
)
|
||||
.filterNot(_.status == UnitStatus.Outlawed)
|
||||
.filterNot(u => evacuatedCaptiveDestination(u, rsp, battle, battleResolution).isDefined)
|
||||
} yield AftermathClaimantUnit(
|
||||
heroId = u.hero.id,
|
||||
battalionId = u.optionalBattalionId.filterNot(destroyed.contains)
|
||||
@@ -523,8 +549,17 @@ case class ResolveBattleAction(
|
||||
val claimants = attackingWinners.map { wsp =>
|
||||
val rsp = winningResolvedPlayers.find(_.eagleFid == wsp.eagleFid).get
|
||||
val notFledUnits = rsp.units
|
||||
.filterNot(u => unitReturned(hid = u.hero.id, in = wsp, out = rsp))
|
||||
.filterNot(u =>
|
||||
unitReturned(
|
||||
hid = u.hero.id,
|
||||
in = wsp,
|
||||
out = rsp,
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
)
|
||||
)
|
||||
.filterNot(_.status == UnitStatus.Outlawed)
|
||||
.filterNot(u => evacuatedCaptiveDestination(u, rsp, battle, battleResolution).isDefined)
|
||||
.toVector
|
||||
|
||||
val armySize = notFledUnits
|
||||
@@ -683,6 +718,8 @@ case class ResolveBattleAction(
|
||||
ma = ma,
|
||||
sp = sp,
|
||||
rsp = rsp,
|
||||
battle = batt,
|
||||
battleResolution = br,
|
||||
destroyedBattalionIds = destroyedBattalionIds
|
||||
)
|
||||
} yield ma.army.fleeProvinceId
|
||||
@@ -705,11 +742,21 @@ case class ResolveBattleAction(
|
||||
ma: MovingArmy,
|
||||
sp: ShardokPlayer,
|
||||
rsp: ResolvedShardokPlayer,
|
||||
battle: ShardokBattle,
|
||||
battleResolution: BattleResolution,
|
||||
destroyedBattalionIds: Vector[BattalionId]
|
||||
): Option[Army] =
|
||||
armyFromResolvedUnits(
|
||||
units = ma.army.units
|
||||
.filter(u => unitReturned(hid = u.heroId, in = sp, out = rsp))
|
||||
.filter(u =>
|
||||
unitReturned(
|
||||
hid = u.heroId,
|
||||
in = sp,
|
||||
out = rsp,
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
)
|
||||
)
|
||||
.flatMap(cu => rsp.units.find(_.hero.id == cu.heroId))
|
||||
.toVector,
|
||||
removedBattalionIds = destroyedBattalionIds,
|
||||
@@ -774,29 +821,105 @@ case class ResolveBattleAction(
|
||||
incomingArmy = shatteredArmy
|
||||
)
|
||||
|
||||
private def changedProvincesFromEvacuatedCaptives(
|
||||
battle: ShardokBattle,
|
||||
battleResolution: BattleResolution
|
||||
): Vector[ChangedProvinceC] = {
|
||||
val capturedHeroesByProvince = (for {
|
||||
rsp <- battleResolution.resolvedPlayers
|
||||
unit <- rsp.units
|
||||
(_, destinationProvince) <- evacuatedCaptiveDestination(
|
||||
unit = unit,
|
||||
resolvedPlayer = rsp,
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
)
|
||||
} yield destinationProvince -> CapturedHero(
|
||||
heroId = unit.hero.id,
|
||||
recruitmentAttempted = false,
|
||||
previousFactionId = startingGameState.heroes(unit.hero.id).factionId.get
|
||||
)).groupBy(_._1).view.mapValues(_.map(_._2).toVector).toMap
|
||||
|
||||
capturedHeroesByProvince.toVector
|
||||
.sortBy(_._1)
|
||||
.map {
|
||||
case (provinceId, capturedHeroes) =>
|
||||
ChangedProvinceC(
|
||||
provinceId = provinceId,
|
||||
newCapturedHeroes = capturedHeroes.sortBy(_.heroId)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def evacuatedCaptiveDestination(
|
||||
unit: ResolvedEagleUnit,
|
||||
resolvedPlayer: ResolvedShardokPlayer,
|
||||
battle: ShardokBattle,
|
||||
battleResolution: BattleResolution
|
||||
): Option[(FactionId, ProvinceId)] =
|
||||
if unit.status != UnitStatus.Evacuated then None
|
||||
else
|
||||
for {
|
||||
captivePlayer <- battle.players.find(_.eagleFid == resolvedPlayer.eagleFid)
|
||||
destination <- evacuatedWardenDestinations(battle, battleResolution).find {
|
||||
case (wardenPlayer, _, _) =>
|
||||
wardenPlayer.isDefender != captivePlayer.isDefender
|
||||
}
|
||||
} yield (destination._2, destination._3)
|
||||
|
||||
private def evacuatedWardenDestinations(
|
||||
battle: ShardokBattle,
|
||||
battleResolution: BattleResolution
|
||||
): Vector[(ShardokPlayer, FactionId, ProvinceId)] =
|
||||
(for {
|
||||
rsp <- battleResolution.resolvedPlayers
|
||||
sp <- battle.players.find(_.eagleFid == rsp.eagleFid).toVector
|
||||
unit <- rsp.units
|
||||
if unit.status == UnitStatus.Evacuated
|
||||
hero = startingGameState.heroes(unit.hero.id)
|
||||
if hero.profession == Profession.Warden
|
||||
destination <- fleeProvince(hero, battle).toVector
|
||||
} yield (sp, rsp.eagleFid, destination)).toVector.sortBy {
|
||||
case (_, factionId, provinceId) =>
|
||||
(factionId, provinceId)
|
||||
}
|
||||
|
||||
private def unitReturned(
|
||||
hid: HeroId,
|
||||
in: ShardokPlayer,
|
||||
out: ResolvedShardokPlayer
|
||||
out: ResolvedShardokPlayer,
|
||||
battle: ShardokBattle,
|
||||
battleResolution: BattleResolution
|
||||
): Boolean =
|
||||
out.units.find(_.hero.id == hid).get.status match {
|
||||
case UnitStatus.Fled => true
|
||||
case UnitStatus.NeverEntered =>
|
||||
!in.isDefender &&
|
||||
in.armyGroup.toVector
|
||||
.flatMap(_.armies)
|
||||
.find(_.army.units.exists(_.heroId == hid))
|
||||
.get
|
||||
.army
|
||||
.fleeProvinceId
|
||||
.isDefined &&
|
||||
!out.endGameCondition.isVictory
|
||||
case UnitStatus.Captured =>
|
||||
false
|
||||
case UnitStatus.Normal =>
|
||||
false
|
||||
case UnitStatus.Retreated => false
|
||||
case UnitStatus.Outlawed => false
|
||||
out.units.find(_.hero.id == hid).get match {
|
||||
case unit if unit.status == UnitStatus.Evacuated =>
|
||||
evacuatedCaptiveDestination(
|
||||
unit = unit,
|
||||
resolvedPlayer = out,
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
).isEmpty && fleeProvince(startingGameState.heroes(hid), battle).isDefined
|
||||
case unit =>
|
||||
unit.status match {
|
||||
case UnitStatus.Fled => true
|
||||
case UnitStatus.NeverEntered =>
|
||||
!in.isDefender &&
|
||||
in.armyGroup.toVector
|
||||
.flatMap(_.armies)
|
||||
.find(_.army.units.exists(_.heroId == hid))
|
||||
.get
|
||||
.army
|
||||
.fleeProvinceId
|
||||
.isDefined &&
|
||||
!out.endGameCondition.isVictory
|
||||
case UnitStatus.Captured =>
|
||||
false
|
||||
case UnitStatus.Normal =>
|
||||
false
|
||||
case UnitStatus.Retreated => false
|
||||
case UnitStatus.Outlawed => false
|
||||
case UnitStatus.Evacuated => false
|
||||
}
|
||||
}
|
||||
|
||||
private def outstandingBattle(
|
||||
|
||||
+1
@@ -132,6 +132,7 @@ object BattalionDescriptions {
|
||||
case UnitStatus.Retreated => " The unit retreated in good order."
|
||||
case UnitStatus.NeverEntered => " The unit never entered the battle."
|
||||
case UnitStatus.Outlawed => " The unit's commander was outlawed."
|
||||
case UnitStatus.Evacuated => " The unit evacuated prisoners from the battlefield."
|
||||
}
|
||||
val casualtyText =
|
||||
if casualties > 0 then s", suffering $casualties casualties (from $startingSize to $endingSize soldiers)"
|
||||
|
||||
+1
@@ -132,6 +132,7 @@ object HeroDescriptions {
|
||||
case UnitStatus.Retreated => " $heroName retreated."
|
||||
case UnitStatus.NeverEntered => " $heroName never entered the battle."
|
||||
case UnitStatus.Outlawed => " $heroName became an outlaw."
|
||||
case UnitStatus.Evacuated => " $heroName evacuated prisoners from the battlefield."
|
||||
}
|
||||
|
||||
TextGenerationSuccess(
|
||||
|
||||
+3
@@ -14,6 +14,7 @@ import net.eagle0.eagle.internal.event_for_battalion_backstory.{
|
||||
}
|
||||
import net.eagle0.eagle.internal.event_for_battalion_backstory.FoughtInBattleBattalionBackstoryEvent.UnitStatus.{
|
||||
UNIT_STATUS_CAPTURED,
|
||||
UNIT_STATUS_EVACUATED,
|
||||
UNIT_STATUS_FLED,
|
||||
UNIT_STATUS_NEVER_ENTERED,
|
||||
UNIT_STATUS_NORMAL,
|
||||
@@ -332,6 +333,7 @@ object EventForBattalionBackstoryConverter {
|
||||
case UnitStatus.Retreated => UNIT_STATUS_RETREATED
|
||||
case UnitStatus.NeverEntered => UNIT_STATUS_NEVER_ENTERED
|
||||
case UnitStatus.Outlawed => UNIT_STATUS_OUTLAWED
|
||||
case UnitStatus.Evacuated => UNIT_STATUS_EVACUATED
|
||||
}
|
||||
|
||||
private def unitStatusFromProto(status: UnitStatusProto): UnitStatus =
|
||||
@@ -342,6 +344,7 @@ object EventForBattalionBackstoryConverter {
|
||||
case UNIT_STATUS_RETREATED => UnitStatus.Retreated
|
||||
case UNIT_STATUS_NEVER_ENTERED => UnitStatus.NeverEntered
|
||||
case UNIT_STATUS_OUTLAWED => UnitStatus.Outlawed
|
||||
case UNIT_STATUS_EVACUATED => UnitStatus.Evacuated
|
||||
case UNIT_STATUS_UNKNOWN | UnitStatusProto.Unrecognized(_) =>
|
||||
throw new ProtoConversionException(s"Unknown unit status: $status")
|
||||
}
|
||||
|
||||
+3
@@ -39,6 +39,7 @@ import net.eagle0.eagle.internal.event_for_hero_backstory.{
|
||||
}
|
||||
import net.eagle0.eagle.internal.event_for_hero_backstory.FoughtInBattleBackstoryEvent.UnitStatus.{
|
||||
UNIT_STATUS_CAPTURED,
|
||||
UNIT_STATUS_EVACUATED,
|
||||
UNIT_STATUS_FLED,
|
||||
UNIT_STATUS_NEVER_ENTERED,
|
||||
UNIT_STATUS_NORMAL,
|
||||
@@ -406,6 +407,7 @@ object EventForHeroBackstoryConverter {
|
||||
case UnitStatus.Retreated => UNIT_STATUS_RETREATED
|
||||
case UnitStatus.NeverEntered => UNIT_STATUS_NEVER_ENTERED
|
||||
case UnitStatus.Outlawed => UNIT_STATUS_OUTLAWED
|
||||
case UnitStatus.Evacuated => UNIT_STATUS_EVACUATED
|
||||
}
|
||||
|
||||
def fromProto(
|
||||
@@ -798,6 +800,7 @@ object EventForHeroBackstoryConverter {
|
||||
case UNIT_STATUS_RETREATED => UnitStatus.Retreated
|
||||
case UNIT_STATUS_NEVER_ENTERED => UnitStatus.NeverEntered
|
||||
case UNIT_STATUS_OUTLAWED => UnitStatus.Outlawed
|
||||
case UNIT_STATUS_EVACUATED => UnitStatus.Evacuated
|
||||
case UNIT_STATUS_UNKNOWN =>
|
||||
throw new ProtoConversionException(
|
||||
s"Unknown UnitStatusProto"
|
||||
|
||||
+2
@@ -11,6 +11,7 @@ object UnitStatusConverter {
|
||||
case UnitStatusProto.RETREATED_UNIT => UnitStatus.Retreated
|
||||
case UnitStatusProto.NEVER_ENTERED_UNIT => UnitStatus.NeverEntered
|
||||
case UnitStatusProto.OUTLAWED_UNIT => UnitStatus.Outlawed
|
||||
case UnitStatusProto.EVACUATED_UNIT => UnitStatus.Evacuated
|
||||
case UnitStatusProto.UNKNOWN_UNIT =>
|
||||
throw new IllegalArgumentException("UnitStatus cannot be UNKNOWN_UNIT")
|
||||
case UnitStatusProto.Unrecognized(value) =>
|
||||
@@ -25,5 +26,6 @@ object UnitStatusConverter {
|
||||
case UnitStatus.Retreated => UnitStatusProto.RETREATED_UNIT
|
||||
case UnitStatus.NeverEntered => UnitStatusProto.NEVER_ENTERED_UNIT
|
||||
case UnitStatus.Outlawed => UnitStatusProto.OUTLAWED_UNIT
|
||||
case UnitStatus.Evacuated => UnitStatusProto.EVACUATED_UNIT
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@ enum UnitStatus {
|
||||
case Retreated
|
||||
case NeverEntered
|
||||
case Outlawed
|
||||
case Evacuated
|
||||
}
|
||||
|
||||
@@ -585,6 +585,8 @@ class ShardokInterfaceGrpcClient(
|
||||
UnitStatus.NeverEntered
|
||||
case net.eagle0.common.common_unit.UnitStatus.OUTLAWED_UNIT =>
|
||||
UnitStatus.Outlawed
|
||||
case net.eagle0.common.common_unit.UnitStatus.EVACUATED_UNIT =>
|
||||
UnitStatus.Evacuated
|
||||
case other =>
|
||||
throw new IllegalArgumentException(s"Unrecognized UnitStatus: $other")
|
||||
}
|
||||
|
||||
@@ -101,6 +101,22 @@ cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "evacuate_prisoners_command_factory_test",
|
||||
srcs = ["EvacuatePrisonersCommandFactory_test.cpp"],
|
||||
copts = TEST_COPTS,
|
||||
linkstatic = True,
|
||||
deps = [
|
||||
"//src/main/cpp/net/eagle0/shardok/library/command_factories:evacuate_prisoners_command_factory",
|
||||
"//src/test/cpp/net/eagle0/shardok/library:game_settings_test_utils",
|
||||
"//src/test/cpp/net/eagle0/shardok/library:game_start_test_data",
|
||||
"//src/test/cpp/net/eagle0/shardok/library:gtest_extensions",
|
||||
"//src/test/cpp/net/eagle0/shardok/library:hex_map_test_data",
|
||||
"@googletest//:gtest",
|
||||
"@googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "fear_command_factory_test",
|
||||
srcs = ["FearCommandFactory_test.cpp"],
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Created by Codex on 6/17/26.
|
||||
//
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/command_factories/EvacuatePrisonersCommandFactory.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "src/test/cpp/net/eagle0/shardok/library/GameSettings_test_utils.hpp"
|
||||
#include "src/test/cpp/net/eagle0/shardok/library/GameStart_test_data.hpp"
|
||||
#include "src/test/cpp/net/eagle0/shardok/library/GtestExtensions.hpp"
|
||||
#include "src/test/cpp/net/eagle0/shardok/library/HexMap_test_data.hpp"
|
||||
|
||||
using net::eagle0::shardok::storage::fb::GameStateBuilder;
|
||||
|
||||
using namespace shardok;
|
||||
|
||||
class EvacuatePrisonersCommandFactoryTest : public ::testing::Test {
|
||||
void SetUp() override {
|
||||
InitializeGameSettings();
|
||||
GetGameSettingsSetter().SetInt(kSettingsKeys.fleeActionPointCost, 5);
|
||||
|
||||
fbb.ForceDefaults(true);
|
||||
auto actorOff = AddGenericUnit(0, 0, position);
|
||||
auto enemyCaptiveOff = AddGenericUnit(1, 1, Coords(4, 4));
|
||||
auto alliedCaptiveOff = AddGenericUnit(2, 2, Coords(5, 5));
|
||||
auto unitsVec = std::vector<Unit>{actorOff, enemyCaptiveOff, alliedCaptiveOff};
|
||||
auto unitsOff = fbb.CreateVectorOfSortedStructs(&unitsVec);
|
||||
|
||||
GameStateBuilder gsb(fbb);
|
||||
gsb.add_units(unitsOff);
|
||||
fbb.Finish(gsb.Finish());
|
||||
|
||||
gameState = GameStateW(fbb);
|
||||
actor = const_cast<Unit*>(gameState->mutable_units()->GetMutableObject(0));
|
||||
enemyCaptive = const_cast<Unit*>(gameState->mutable_units()->GetMutableObject(1));
|
||||
alliedCaptive = const_cast<Unit*>(gameState->mutable_units()->GetMutableObject(2));
|
||||
|
||||
actor->mutate_can_flee(true);
|
||||
actor->mutate_remaining_action_points(remainingActionPoints);
|
||||
actor->mutable_attached_hero().mutable_profession_info().mutate_profession(
|
||||
net::eagle0::shardok::storage::fb::Profession_WARDEN);
|
||||
|
||||
enemyCaptive->mutate_status(net::eagle0::shardok::storage::fb::UnitStatus_CAPTURED_UNIT);
|
||||
alliedCaptive->mutate_status(net::eagle0::shardok::storage::fb::UnitStatus_CAPTURED_UNIT);
|
||||
|
||||
factory = std::make_shared<EvacuatePrisonersCommandFactory>(GetGameSettingsGetter());
|
||||
}
|
||||
|
||||
protected:
|
||||
GameStateW gameState;
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
Unit* actor = nullptr;
|
||||
Unit* enemyCaptive = nullptr;
|
||||
Unit* alliedCaptive = nullptr;
|
||||
CommandList commands{};
|
||||
PossibleChargees possibleChargees{};
|
||||
vector<PlayerId> allyPids{2};
|
||||
const Coords position = Coords(3, 3);
|
||||
const int remainingActionPoints = 9;
|
||||
std::shared_ptr<EvacuatePrisonersCommandFactory> factory = nullptr;
|
||||
|
||||
[[nodiscard]] auto Params() const -> CommandFactory::CommandParams {
|
||||
return CommandFactory::CommandParams{
|
||||
actor,
|
||||
static_cast<ActionPoints>(remainingActionPoints),
|
||||
position,
|
||||
nullptr,
|
||||
false,
|
||||
possibleChargees,
|
||||
BASIC_MAP_FB,
|
||||
gameState->units(),
|
||||
allyPids,
|
||||
true,
|
||||
false,
|
||||
false};
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(EvacuatePrisonersCommandFactoryTest, wardenWithCapturedHostileHeroCanEvacuatePrisoners) {
|
||||
factory->AddAvailableCommands(commands, Params());
|
||||
|
||||
ASSERT_EQ(1, commands.size());
|
||||
EXPECT_EQ(
|
||||
net::eagle0::shardok::common::EVACUATE_PRISONERS_COMMAND,
|
||||
commands[0]->GetCommandType());
|
||||
|
||||
const auto results = commands[0]->Execute(gameState, nullptr);
|
||||
const auto result = ResultWithType(results, net::eagle0::shardok::common::EVACUATED_PRISONERS);
|
||||
|
||||
EXPECT_EQ(2, result.resolved_units_size());
|
||||
EXPECT_EQ(actor->unit_id(), ((Unit*)result.resolved_units(0).unit_bytes().data())->unit_id());
|
||||
EXPECT_EQ(
|
||||
enemyCaptive->unit_id(),
|
||||
((Unit*)result.resolved_units(1).unit_bytes().data())->unit_id());
|
||||
}
|
||||
|
||||
TEST_F(EvacuatePrisonersCommandFactoryTest, nonWardenCannotEvacuatePrisoners) {
|
||||
actor->mutable_attached_hero().mutable_profession_info().mutate_profession(
|
||||
net::eagle0::shardok::storage::fb::Profession_PALADIN);
|
||||
|
||||
factory->AddAvailableCommands(commands, Params());
|
||||
|
||||
EXPECT_TRUE(commands.empty());
|
||||
}
|
||||
@@ -122,6 +122,21 @@ cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "evacuate_prisoners_command_test",
|
||||
srcs = ["EvacuatePrisonersCommand_test.cpp"],
|
||||
copts = TEST_COPTS,
|
||||
linkstatic = True,
|
||||
deps = [
|
||||
"//src/main/cpp/net/eagle0/shardok/library/commands:evacuate_prisoners_command",
|
||||
"//src/test/cpp/net/eagle0/shardok/library:game_settings_test_utils",
|
||||
"//src/test/cpp/net/eagle0/shardok/library:game_start_test_data",
|
||||
"//src/test/cpp/net/eagle0/shardok/library:gtest_extensions",
|
||||
"@googletest//:gtest",
|
||||
"@googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "fear_command_test",
|
||||
srcs = ["FearCommand_test.cpp"],
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// Created by Codex on 6/17/26.
|
||||
//
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/commands/EvacuatePrisonersCommand.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "src/test/cpp/net/eagle0/shardok/library/GameSettings_test_utils.hpp"
|
||||
#include "src/test/cpp/net/eagle0/shardok/library/GameStart_test_data.hpp"
|
||||
#include "src/test/cpp/net/eagle0/shardok/library/GtestExtensions.hpp"
|
||||
|
||||
using net::eagle0::shardok::storage::fb::GameStateBuilder;
|
||||
|
||||
using namespace shardok;
|
||||
|
||||
class EvacuatePrisonersCommandTest : public ::testing::Test {
|
||||
void SetUp() override {
|
||||
InitializeGameSettings();
|
||||
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
fbb.ForceDefaults(true);
|
||||
auto actorOff = AddGenericUnit(0, 0, Coords(3, 3));
|
||||
auto captiveOff = AddGenericUnit(1, 1, Coords(4, 4));
|
||||
auto unitsVec = std::vector<Unit>{actorOff, captiveOff};
|
||||
auto unitsOff = fbb.CreateVectorOfSortedStructs(&unitsVec);
|
||||
|
||||
GameStateBuilder gsb(fbb);
|
||||
gsb.add_units(unitsOff);
|
||||
|
||||
fbb.Finish(gsb.Finish());
|
||||
|
||||
gameState = GameStateW(fbb);
|
||||
}
|
||||
|
||||
public:
|
||||
const ActionCost cost = ActionCost::UsesAllActionCost(5);
|
||||
GameStateW gameState;
|
||||
};
|
||||
|
||||
TEST_F(EvacuatePrisonersCommandTest, execute_marksActorAndCaptivesEvacuated) {
|
||||
const auto results = EvacuatePrisonersCommand(cost, 0, 0, {1}).Execute(gameState, nullptr);
|
||||
|
||||
const ActionResult result =
|
||||
ResultWithType(results, net::eagle0::shardok::common::EVACUATED_PRISONERS);
|
||||
|
||||
EXPECT_EQ(2, result.resolved_units_size());
|
||||
EXPECT_EQ(0, result.actor().value());
|
||||
|
||||
EXPECT_EQ(0, ((Unit*)result.resolved_units(0).unit_bytes().data())->unit_id());
|
||||
EXPECT_EQ(
|
||||
net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_EVACUATED_UNIT,
|
||||
result.resolved_units(0).status());
|
||||
|
||||
EXPECT_EQ(1, ((Unit*)result.resolved_units(1).unit_bytes().data())->unit_id());
|
||||
EXPECT_EQ(
|
||||
net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_EVACUATED_UNIT,
|
||||
result.resolved_units(1).status());
|
||||
}
|
||||
Reference in New Issue
Block a user