mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-29 13:15:42 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1d134a073 |
@@ -1,202 +0,0 @@
|
||||
//
|
||||
// AIFleeDecisionCalculator.cpp
|
||||
// eagle0
|
||||
//
|
||||
// Handles AI flee decision logic including combat success estimation
|
||||
// and flee vs fight evaluation for final round scenarios
|
||||
//
|
||||
|
||||
#include "AIFleeDecisionCalculator.hpp"
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIScoreUtilities.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
|
||||
|
||||
namespace shardok {
|
||||
|
||||
auto AIFleeDecisionCalculator::GetFleeCommandIndex(
|
||||
const vector<CommandProto>::const_iterator& fleeCommand,
|
||||
const vector<CommandProto>& availableCommands) -> size_t {
|
||||
return static_cast<size_t>(std::distance(availableCommands.begin(), fleeCommand));
|
||||
}
|
||||
|
||||
auto AIFleeDecisionCalculator::EstimateCombatSuccess(
|
||||
PlayerId attackerPlayerId,
|
||||
const GameStateW& guessedState,
|
||||
const GameSettingsSPtr& settings) -> double {
|
||||
// Combat success estimation based on troops, heroes, and capture dynamics
|
||||
|
||||
int attackerTroops = 0;
|
||||
int defenderTroops = 0;
|
||||
int attackerUnits = 0;
|
||||
int defenderUnits = 0;
|
||||
int attackerHeroes = 0;
|
||||
int defenderHeroes = 0;
|
||||
bool defenderHasVips = false;
|
||||
|
||||
// Count troops, units, and heroes for each side
|
||||
for (const auto* unit : *guessedState->units()) {
|
||||
if (unit->status() != net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) continue;
|
||||
|
||||
const auto* pi = PlayerInfoForPid(guessedState, unit->player_id());
|
||||
if (pi == nullptr) continue;
|
||||
|
||||
const int unitTroops = unit->battalion().size();
|
||||
const bool hasHero = unit->has_attached_hero();
|
||||
|
||||
if (pi->is_defender()) {
|
||||
defenderTroops += unitTroops;
|
||||
defenderUnits++;
|
||||
if (hasHero) {
|
||||
defenderHeroes++;
|
||||
if (unit->attached_hero().is_vip()) { defenderHasVips = true; }
|
||||
}
|
||||
} else if (unit->player_id() == attackerPlayerId) {
|
||||
attackerTroops += unitTroops;
|
||||
attackerUnits++;
|
||||
if (hasHero) { attackerHeroes++; }
|
||||
}
|
||||
}
|
||||
|
||||
const int roundsRemaining =
|
||||
settings->GetGetter().Backing().max_rounds() - guessedState->current_round();
|
||||
|
||||
// Special case: Attacker has no troops
|
||||
if (attackerTroops == 0) {
|
||||
// Even with heroes, attacker is extremely unlikely to win without troops
|
||||
return 0.01; // Near zero, but not absolute zero
|
||||
}
|
||||
|
||||
// Special case: Defender has no troops but has heroes
|
||||
if (defenderTroops == 0 && defenderHeroes > 0) {
|
||||
// Defender can win by running out the clock if attacker can't capture heroes
|
||||
// Success depends heavily on remaining time and attacker's ability to capture
|
||||
if (roundsRemaining <= 3) {
|
||||
// Very hard for attacker to capture all heroes in time
|
||||
return 0.15; // Low chance
|
||||
} else if (roundsRemaining <= 5) {
|
||||
return 0.25; // Still difficult
|
||||
} else {
|
||||
// More time available, but still challenging
|
||||
return 0.35;
|
||||
}
|
||||
}
|
||||
|
||||
// Special case: Defender has neither troops nor heroes
|
||||
if (defenderTroops == 0 && defenderHeroes == 0) {
|
||||
return 0.95; // Nearly guaranteed win
|
||||
}
|
||||
|
||||
// Normal case: Both sides have troops
|
||||
// Base probability from troop ratio
|
||||
const double troopRatio =
|
||||
static_cast<double>(attackerTroops) / static_cast<double>(defenderTroops);
|
||||
double baseProbability = std::min(0.95, std::max(0.05, troopRatio * 0.5));
|
||||
|
||||
// Adjust for time pressure - attackers need to win before time runs out
|
||||
if (roundsRemaining <= 1) {
|
||||
baseProbability *= 0.6; // Severe penalty for last round
|
||||
} else if (roundsRemaining <= 3) {
|
||||
baseProbability *= 0.8; // Moderate penalty
|
||||
}
|
||||
|
||||
// Adjust for unit count (more units = better tactical flexibility)
|
||||
const double unitRatio =
|
||||
static_cast<double>(attackerUnits) / std::max(1.0, static_cast<double>(defenderUnits));
|
||||
if (unitRatio < 0.5) {
|
||||
baseProbability *= 0.8;
|
||||
} else if (unitRatio > 1.5) {
|
||||
baseProbability *= 1.15;
|
||||
}
|
||||
|
||||
// Adjust for hero presence
|
||||
if (defenderHeroes > attackerHeroes && defenderHasVips) {
|
||||
// Defender has more heroes including VIPs - harder to capture
|
||||
baseProbability *= 0.85;
|
||||
}
|
||||
|
||||
return std::min(0.95, std::max(0.05, baseProbability));
|
||||
}
|
||||
|
||||
auto AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
PlayerId playerId,
|
||||
const GameSettingsSPtr& settings,
|
||||
const GameStateW& guessedState,
|
||||
const vector<CommandProto>& availableCommands,
|
||||
const vector<CommandProto>::const_iterator& fleeCommand,
|
||||
bool enableDebugLogging) -> FleeDecision {
|
||||
// Get flee success odds
|
||||
const int fleeSuccessChance = fleeCommand->odds().success_chance();
|
||||
|
||||
// Get thresholds from settings
|
||||
const auto settingsGetter = settings->GetGetter();
|
||||
const int minimumFleeOddsThreshold = settingsGetter.Backing().ai_minimum_flee_odds_threshold();
|
||||
const int desperateFleeThreshold = settingsGetter.Backing().ai_desperate_flee_threshold();
|
||||
|
||||
if (enableDebugLogging) {
|
||||
printf("AI FinalRound: Evaluating flee (odds=%d%%)...\n", fleeSuccessChance);
|
||||
}
|
||||
|
||||
// Check if flee odds are good enough to attempt
|
||||
if (fleeSuccessChance >= minimumFleeOddsThreshold) {
|
||||
if (enableDebugLogging) {
|
||||
printf("AI FinalRound: Good flee odds (%d%% >= %d%%), choosing flee\n",
|
||||
fleeSuccessChance,
|
||||
minimumFleeOddsThreshold);
|
||||
}
|
||||
return FleeDecision{
|
||||
true,
|
||||
GetFleeCommandIndex(fleeCommand, availableCommands),
|
||||
"Good flee odds"};
|
||||
}
|
||||
|
||||
// Low flee odds - evaluate if fighting might be better
|
||||
const double combatWinChance = EstimateCombatSuccess(playerId, guessedState, settings);
|
||||
|
||||
// If combat situation is hopeless, even bad flee odds are better than certain death
|
||||
if (combatWinChance < 0.05 && fleeSuccessChance >= desperateFleeThreshold) {
|
||||
if (enableDebugLogging) {
|
||||
printf("AI FinalRound: Combat hopeless (%.1f%%), desperate flee attempt (%d%%)\n",
|
||||
combatWinChance * 100,
|
||||
fleeSuccessChance);
|
||||
}
|
||||
return FleeDecision{
|
||||
true,
|
||||
GetFleeCommandIndex(fleeCommand, availableCommands),
|
||||
"Combat hopeless, desperate flee"};
|
||||
}
|
||||
|
||||
// Detailed flee vs fight comparison
|
||||
const double fleeChance = static_cast<double>(fleeSuccessChance) / 100.0;
|
||||
|
||||
// Compare expected outcomes:
|
||||
// - Flee: fleeChance of survival (not victory, but avoiding loss)
|
||||
// - Fight: combatWinChance of victory (better than survival)
|
||||
|
||||
constexpr double FLEE_VS_COMBAT_MARGIN =
|
||||
0.8; // Require 80% of combat chance to prefer fighting
|
||||
const double adjustedCombatThreshold = combatWinChance * FLEE_VS_COMBAT_MARGIN;
|
||||
|
||||
if (enableDebugLogging) {
|
||||
printf("AI FinalRound: Flee=%d%%, Combat=%.1f%%, Threshold=%.1f%% -> ",
|
||||
fleeSuccessChance,
|
||||
combatWinChance * 100,
|
||||
adjustedCombatThreshold * 100);
|
||||
}
|
||||
|
||||
if (fleeChance > adjustedCombatThreshold) {
|
||||
if (enableDebugLogging) { printf("FLEE (better odds)\n"); }
|
||||
return FleeDecision{
|
||||
true,
|
||||
GetFleeCommandIndex(fleeCommand, availableCommands),
|
||||
"Flee has better expected outcome"};
|
||||
} else {
|
||||
if (enableDebugLogging) { printf("FIGHT (better expected outcome)\n"); }
|
||||
// Return 0 to indicate we should use standard command selection
|
||||
return FleeDecision{
|
||||
false,
|
||||
0, // Will be replaced by StandardChooseCommandIndex
|
||||
"Fighting has better expected outcome"};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace shardok
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// AIFleeDecisionCalculator.hpp
|
||||
// eagle0
|
||||
//
|
||||
// Handles AI flee decision logic including combat success estimation
|
||||
// and flee vs fight evaluation for final round scenarios
|
||||
//
|
||||
|
||||
#ifndef AIFleeDecisionCalculator_hpp
|
||||
#define AIFleeDecisionCalculator_hpp
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/ShardokEngine.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/settings/GameSettings.hpp"
|
||||
#include "src/main/protobuf/net/eagle0/shardok/api/command_descriptor.pb.h"
|
||||
|
||||
namespace shardok {
|
||||
|
||||
using CommandProto = net::eagle0::shardok::api::CommandDescriptor;
|
||||
|
||||
class AIFleeDecisionCalculator {
|
||||
public:
|
||||
// Configuration for flee decision thresholds
|
||||
struct FleeThresholds {
|
||||
int minimumFleeOddsThreshold; // Minimum flee success odds to consider fleeing
|
||||
int desperateFleeThreshold; // Flee threshold when combat is hopeless
|
||||
};
|
||||
|
||||
// Result of flee vs fight evaluation
|
||||
struct FleeDecision {
|
||||
bool shouldFlee;
|
||||
size_t commandIndex; // Index of command to execute (flee or fight)
|
||||
const char* reasoning; // Debug explanation of decision
|
||||
};
|
||||
|
||||
// Evaluate whether to flee or fight in the final round
|
||||
[[nodiscard]] static auto EvaluateFleeVsFight(
|
||||
PlayerId playerId,
|
||||
const GameSettingsSPtr& settings,
|
||||
const GameStateW& guessedState,
|
||||
const vector<CommandProto>& availableCommands,
|
||||
const vector<CommandProto>::const_iterator& fleeCommand,
|
||||
bool enableDebugLogging = false) -> FleeDecision;
|
||||
|
||||
// Estimate probability of combat success for the attacker
|
||||
[[nodiscard]] static auto EstimateCombatSuccess(
|
||||
PlayerId attackerPlayerId,
|
||||
const GameStateW& guessedState,
|
||||
const GameSettingsSPtr& settings) -> double;
|
||||
|
||||
private:
|
||||
// Helper to get flee command index
|
||||
[[nodiscard]] static auto GetFleeCommandIndex(
|
||||
const vector<CommandProto>::const_iterator& fleeCommand,
|
||||
const vector<CommandProto>& availableCommands) -> size_t;
|
||||
};
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif /* AIFleeDecisionCalculator_hpp */
|
||||
@@ -244,7 +244,6 @@ cc_library(
|
||||
deps = [
|
||||
":ai_minimum_distance_and_target",
|
||||
":ai_water_crossing_calculator",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:game_state_w",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/action_point_distances",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/action_point_distances:action_point_distances_cache",
|
||||
"//src/main/protobuf/net/eagle0/shardok/api:command_descriptor_cc_proto",
|
||||
@@ -291,24 +290,6 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "ai_flee_decision_calculator",
|
||||
srcs = ["AIFleeDecisionCalculator.cpp"],
|
||||
hdrs = ["AIFleeDecisionCalculator.hpp"],
|
||||
copts = COPTS,
|
||||
visibility = [
|
||||
"//src/main/cpp/net/eagle0/shardok/ai_performance_runner:__pkg__",
|
||||
"//src/test/cpp/net/eagle0/shardok/ai:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
":ai_score_utilities",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:engine",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/settings:game_settings",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/util:hex_map_utils",
|
||||
"//src/main/protobuf/net/eagle0/shardok/api:command_descriptor_cc_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "shardok_ai_client",
|
||||
srcs = ["ShardokAIClient.cpp"],
|
||||
@@ -318,7 +299,6 @@ cc_library(
|
||||
deps = [
|
||||
":ai_attacker_strategy_selector",
|
||||
":ai_defender_strategy_selector",
|
||||
":ai_flee_decision_calculator",
|
||||
":ai_iterative_deepening",
|
||||
":ai_score_calculator",
|
||||
":ai_time_budget",
|
||||
|
||||
@@ -8,14 +8,10 @@
|
||||
|
||||
#include "ShardokAIClient.hpp"
|
||||
|
||||
#define DEBUG_FLEE_DECISIONS
|
||||
|
||||
#include <google/protobuf/util/message_differencer.h>
|
||||
|
||||
#include "AIAttackerStrategySelector.hpp"
|
||||
#include "AIDefenderStrategySelector.hpp"
|
||||
#include "AIFleeDecisionCalculator.hpp"
|
||||
#include "AIScoreUtilities.hpp"
|
||||
#include "AITimeBudget.hpp"
|
||||
#include "IterativeDeepeningAI.hpp"
|
||||
#include "src/main/cpp/net/eagle0/common/TimeUtils.hpp"
|
||||
@@ -179,41 +175,23 @@ auto ShardokAIClient::FinalRoundAttackerChooseCommandIndex(
|
||||
const GameSettingsSPtr &settings,
|
||||
const GameStateW &guessedState,
|
||||
const vector<CommandProto> &realAvailableCommands) const -> CommandChoiceResults {
|
||||
const auto fleeCommand = std::ranges::find_if(
|
||||
realAvailableCommands,
|
||||
[](const net::eagle0::shardok::api::CommandDescriptor &cmd) {
|
||||
return cmd.type() == net::eagle0::shardok::common::FLEE_COMMAND;
|
||||
});
|
||||
|
||||
if (fleeCommand == realAvailableCommands.end()) {
|
||||
if (const auto fleeCommand = std::ranges::find_if(
|
||||
realAvailableCommands,
|
||||
[](const net::eagle0::shardok::api::CommandDescriptor &cmd) {
|
||||
return cmd.type() == net::eagle0::shardok::common::FLEE_COMMAND;
|
||||
});
|
||||
fleeCommand == realAvailableCommands.end()) {
|
||||
return LateRoundAttackerChooseCommandIndex(settings, guessedState, realAvailableCommands);
|
||||
}
|
||||
|
||||
// Use the flee decision calculator
|
||||
const auto fleeDecision = AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
playerId,
|
||||
settings,
|
||||
guessedState,
|
||||
realAvailableCommands,
|
||||
fleeCommand,
|
||||
#ifdef DEBUG_FLEE_DECISIONS
|
||||
true // Enable debug logging
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
);
|
||||
|
||||
if (fleeDecision.shouldFlee) {
|
||||
CommandChoiceResults results{};
|
||||
results.chosenIndex = fleeDecision.commandIndex;
|
||||
results.availableCommandCount = realAvailableCommands.size();
|
||||
results.depthAchieved = 1; // Heuristic choice
|
||||
results.commandCountEvaluated = 1; // Only evaluated one command type
|
||||
results.completionReason = EvaluationCompletionReason::RAN_OUT_OF_COMMANDS;
|
||||
return results;
|
||||
} else {
|
||||
// Fight instead of flee
|
||||
return StandardChooseCommandIndex(settings, guessedState, realAvailableCommands);
|
||||
CommandChoiceResults results{};
|
||||
results.chosenIndex =
|
||||
static_cast<size_t>(std::distance(realAvailableCommands.begin(), fleeCommand));
|
||||
results.availableCommandCount = realAvailableCommands.size();
|
||||
results.depthAchieved = 1; // Simple heuristic choice
|
||||
results.commandCountEvaluated = 1; // Only evaluated one command type
|
||||
results.completionReason =
|
||||
EvaluationCompletionReason::RAN_OUT_OF_COMMANDS; // Heuristic choice
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ private:
|
||||
const GameSettingsSPtr& settings,
|
||||
const GameStateW& guessedState,
|
||||
const vector<CommandProto>& realAvailableCommands) const -> CommandChoiceResults;
|
||||
|
||||
[[nodiscard]] auto ChooseCommandIndex(
|
||||
const GameSettingsSPtr& settings,
|
||||
const net::eagle0::shardok::api::GameStateView& gsv,
|
||||
|
||||
+20
-15
@@ -117,13 +117,21 @@ auto ActionPointDistancesCache::GetMapId(const HexMap* map) -> MapId {
|
||||
return MapId{.terrainTypesId = map->base_hash(), .modifierId = modifierId};
|
||||
}
|
||||
void ActionPointDistancesCache::ConsolidateThreadLocalCache_Racy() {
|
||||
persistentCache.insert(std::begin(sharedDistances), std::end(sharedDistances));
|
||||
sharedDistances.clear();
|
||||
persistentCache.insert(std::begin(sharedCache), std::end(sharedCache));
|
||||
sharedCache.clear();
|
||||
|
||||
// Clear the current thread's cache since persistent cache now has everything
|
||||
tlsCache.clear();
|
||||
}
|
||||
|
||||
ActionPointDistancesCache::~ActionPointDistancesCache() {
|
||||
// We own the ActionPointDistances pointers in the persistent cache and in the shared cache.
|
||||
|
||||
for (const auto& [key, value] : persistentCache) { delete value; }
|
||||
|
||||
for (const auto& [key, value] : sharedCache) { delete value; }
|
||||
}
|
||||
|
||||
auto ActionPointDistancesCache::GetRaw(
|
||||
const HexMap* map,
|
||||
const MapId& mapId,
|
||||
@@ -142,7 +150,7 @@ auto ActionPointDistancesCache::GetRaw(
|
||||
#endif
|
||||
// Return directly from persistent cache without TLS insertion
|
||||
// This avoids the overhead of thread-local storage operations on hot path
|
||||
return persistentIt->second.rawPtr;
|
||||
return persistentIt->second;
|
||||
}
|
||||
|
||||
#if CACHE_STATS_LOGGING_
|
||||
@@ -155,7 +163,7 @@ auto ActionPointDistancesCache::GetRaw(
|
||||
cacheStats.localHits++;
|
||||
MaybePrintCacheStats();
|
||||
#endif
|
||||
return localIt->second.rawPtr; // Raw pointer - zero overhead access!
|
||||
return localIt->second; // Raw pointer - zero overhead access!
|
||||
}
|
||||
|
||||
#if CACHE_STATS_LOGGING_
|
||||
@@ -163,19 +171,19 @@ auto ActionPointDistancesCache::GetRaw(
|
||||
#endif
|
||||
|
||||
// Check shared cache before expensive ice-clearing operation
|
||||
shared_ptr<ActionPointDistances> sharedResult;
|
||||
if (sharedDistances.if_contains(cacheKey, [&sharedResult](const auto& kv) {
|
||||
const ActionPointDistances* sharedResult;
|
||||
if (sharedCache.if_contains(cacheKey, [&sharedResult](const auto& kv) {
|
||||
sharedResult = kv.second;
|
||||
})) {
|
||||
#if CACHE_STATS_LOGGING_
|
||||
cacheStats.sharedAccesses++;
|
||||
#endif
|
||||
// Cache hit in shared cache - store in thread-local cache and return
|
||||
tlsCache.emplace(cacheKey, CacheEntry(sharedResult));
|
||||
tlsCache.emplace(cacheKey, sharedResult);
|
||||
#if CACHE_STATS_LOGGING_
|
||||
MaybePrintCacheStats();
|
||||
#endif
|
||||
return sharedResult.get();
|
||||
return sharedResult;
|
||||
}
|
||||
|
||||
// Cache miss in both caches - need to create ice-cleared map for pathfinding computation
|
||||
@@ -184,13 +192,10 @@ auto ActionPointDistancesCache::GetRaw(
|
||||
// Declaring here to keep the copied map in scope
|
||||
const HexMap* mapToUse = map;
|
||||
|
||||
// ReSharper disable once CppTooWideScope
|
||||
// ReSharper disable once CppJoinDeclarationAndAssignment
|
||||
fb::HexMapW iceClearedMap;
|
||||
if (hasIce) {
|
||||
// Create ice-cleared map for pathfinding
|
||||
// This prevents AI from considering ice as a valid path toward enemies
|
||||
iceClearedMap = CreateIceClearedMap(map);
|
||||
fb::HexMapW iceClearedMap = CreateIceClearedMap(map);
|
||||
mapToUse = iceClearedMap.Get();
|
||||
}
|
||||
|
||||
@@ -215,14 +220,14 @@ auto ActionPointDistancesCache::GetRaw(
|
||||
auto result = creationResult.apd;
|
||||
|
||||
// Store in shared cache
|
||||
sharedDistances.lazy_emplace_l(
|
||||
sharedCache.lazy_emplace_l(
|
||||
cacheKey,
|
||||
[](const auto& kv) { /* already checked above */ },
|
||||
[=](const auto& ctor) { ctor(cacheKey, result); });
|
||||
|
||||
// Cache result locally for future lookups by this thread
|
||||
// Store both shared_ptr and raw pointer for hybrid access
|
||||
tlsCache.emplace(cacheKey, CacheEntry(result));
|
||||
tlsCache.emplace(cacheKey, result);
|
||||
|
||||
// Prevent unbounded cache growth - limit to reasonable size
|
||||
if (tlsCache.size() > 100) {
|
||||
@@ -236,7 +241,7 @@ auto ActionPointDistancesCache::GetRaw(
|
||||
tlsCache.erase(tlsCache.begin(), it);
|
||||
}
|
||||
|
||||
return result.get();
|
||||
return result;
|
||||
}
|
||||
|
||||
void ActionPointDistancesCache::ClearThreadLocalCache() { tlsCache.clear(); }
|
||||
|
||||
+29
-31
@@ -57,36 +57,6 @@ struct FullCacheKeyHash {
|
||||
|
||||
class ActionPointDistancesCache {
|
||||
private:
|
||||
struct CacheEntry {
|
||||
shared_ptr<ActionPointDistances> sharedPtr;
|
||||
const ActionPointDistances* rawPtr;
|
||||
|
||||
explicit CacheEntry(shared_ptr<ActionPointDistances> ptr)
|
||||
: sharedPtr(std::move(ptr)),
|
||||
rawPtr(sharedPtr.get()) {}
|
||||
};
|
||||
|
||||
// Tier 1: persistent map. This is NOT safe to write to while reads may be happening.
|
||||
using PersistentMap = gtl::flat_hash_map<FullCacheKey, CacheEntry, FullCacheKeyHash>;
|
||||
|
||||
PersistentMap persistentCache;
|
||||
|
||||
using APDMap = gtl::parallel_flat_hash_map<
|
||||
FullCacheKey,
|
||||
shared_ptr<ActionPointDistances>,
|
||||
FullCacheKeyHash,
|
||||
std::equal_to<FullCacheKey>,
|
||||
std::allocator<std::pair<const FullCacheKey, shared_ptr<ActionPointDistances>>>,
|
||||
6,
|
||||
std::mutex>;
|
||||
|
||||
APDMap sharedDistances;
|
||||
|
||||
using TLSCache = gtl::flat_hash_map<FullCacheKey, CacheEntry, FullCacheKeyHash>;
|
||||
static thread_local TLSCache tlsCache;
|
||||
|
||||
// Epoch system removed - TLS cache uses size-based eviction instead
|
||||
|
||||
// Helper to build cache key
|
||||
static auto MakeCacheKey(
|
||||
const MapId& mapId,
|
||||
@@ -94,13 +64,41 @@ private:
|
||||
bool includeBravingWater,
|
||||
int braveWaterActionPointCost) -> FullCacheKey;
|
||||
|
||||
// Tier 1: persistent map. This is NOT safe to write to while reads may be happening. Owns the
|
||||
// ActionPointDistances pointers, so it must be cleared.
|
||||
using PersistentMap =
|
||||
gtl::flat_hash_map<FullCacheKey, const ActionPointDistances*, FullCacheKeyHash>;
|
||||
|
||||
PersistentMap persistentCache{};
|
||||
|
||||
// Tier 2: thread-local cache. This stores items that are not yet in the persistent cache.
|
||||
// Does NOT own the ActionPointDistances objects; they are also present in the shared cache.
|
||||
using TLSCache =
|
||||
gtl::flat_hash_map<FullCacheKey, const ActionPointDistances*, FullCacheKeyHash>;
|
||||
static thread_local TLSCache tlsCache;
|
||||
|
||||
// Tier 3: shared cache. This is thread-safe and can be accessed concurrently. Does own the
|
||||
// ActionPointDistances objects, so it must be cleared. Can be consolidated into the persistent
|
||||
// cache when no reads are happening.
|
||||
using APDMap = gtl::parallel_flat_hash_map<
|
||||
FullCacheKey,
|
||||
const ActionPointDistances*,
|
||||
FullCacheKeyHash,
|
||||
std::equal_to<FullCacheKey>,
|
||||
std::allocator<std::pair<const FullCacheKey, const ActionPointDistances*>>,
|
||||
6,
|
||||
std::mutex>;
|
||||
|
||||
APDMap sharedCache{};
|
||||
|
||||
public:
|
||||
explicit ActionPointDistancesCache() {
|
||||
// Pre-size persistent cache to reduce hash collisions
|
||||
// Estimate: ~12 entries from pre-fetching + ~50-100 entries during gameplay
|
||||
persistentCache.reserve(128);
|
||||
}
|
||||
|
||||
~ActionPointDistancesCache();
|
||||
|
||||
// Returns raw pointer for zero overhead access
|
||||
// Lifetime guaranteed by shared cache ownership
|
||||
auto GetRaw(
|
||||
|
||||
+7
-10
@@ -9,7 +9,7 @@
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/map/CoordsSet.hpp"
|
||||
|
||||
// Dynamic thread count based on hardware capabilities
|
||||
static const int ASYNC_COUNT = []() {
|
||||
static const int ASYNC_COUNT = [] {
|
||||
const int cores = static_cast<int>(std::thread::hardware_concurrency());
|
||||
// Use cores-2 to leave room for OS and other processes, minimum 4 threads
|
||||
const int threadCount = std::max(4, cores - 4);
|
||||
@@ -26,7 +26,7 @@ void FixedActionPointDistances::SetCacheDirectory(const string& newDir) {
|
||||
|
||||
static thread_local byte_vector _scratch;
|
||||
|
||||
FixedActionPointDistances::FixedActionPointDistances(const HexMap* map, int columnCount)
|
||||
FixedActionPointDistances::FixedActionPointDistances(const int8_t columnCount)
|
||||
: ActionPointDistances(columnCount) {}
|
||||
|
||||
auto FixedActionPointDistances::Create(
|
||||
@@ -37,12 +37,9 @@ auto FixedActionPointDistances::Create(
|
||||
bool includeBravingWater,
|
||||
int braveWaterActionPointCost) -> CreationResult {
|
||||
// Create the object using private constructor
|
||||
auto apd = std::shared_ptr<FixedActionPointDistances>(
|
||||
new FixedActionPointDistances(map, map->column_count()));
|
||||
auto apd = new FixedActionPointDistances(map->column_count());
|
||||
|
||||
CreationResult result;
|
||||
result.apd = apd;
|
||||
result.loadedFromFile = false;
|
||||
CreationResult result{.apd = apd, .loadedFromFile = false};
|
||||
|
||||
string path = "";
|
||||
|
||||
@@ -73,8 +70,8 @@ auto FixedActionPointDistances::Create(
|
||||
for (int fromIndex = 0; fromIndex < indexCount; fromIndex++) {
|
||||
apd->distances[fromIndex].insert(
|
||||
apd->distances[fromIndex].end(),
|
||||
&(ptr[0]),
|
||||
&(ptr[indexCount]));
|
||||
&ptr[0],
|
||||
&ptr[indexCount]);
|
||||
ptr += indexCount;
|
||||
}
|
||||
result.loadedFromFile = true;
|
||||
@@ -97,7 +94,7 @@ auto FixedActionPointDistances::Create(
|
||||
for (int i = 0; i < chunkSize; i++) {
|
||||
const auto fromIndex = chunkStartIndex + i;
|
||||
if (fromIndex >= indexCount) { continue; }
|
||||
chunkVec.push_back(ActionPointDistances::GenerateDistances(
|
||||
chunkVec.push_back(GenerateDistances(
|
||||
fromIndex,
|
||||
map,
|
||||
includeBravingWater,
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ using BattalionTypeSPtr = std::shared_ptr<const BattalionType>;
|
||||
class FixedActionPointDistances final : public ActionPointDistances {
|
||||
public:
|
||||
struct CreationResult {
|
||||
std::shared_ptr<FixedActionPointDistances> apd;
|
||||
const FixedActionPointDistances *apd;
|
||||
bool loadedFromFile;
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ private:
|
||||
inline static string cacheDirectory = "";
|
||||
|
||||
// Private constructor - use Create factory method instead
|
||||
explicit FixedActionPointDistances(const HexMap *map, int columnCount);
|
||||
explicit FixedActionPointDistances(int8_t columnCount);
|
||||
|
||||
public:
|
||||
static void SetCacheDirectory(const string &newDir);
|
||||
|
||||
@@ -250,7 +250,6 @@
|
||||
<Compile Include="Assets/Eagle/CommandSelectors/HandleRiotGiveCommandSelector.cs" />
|
||||
<Compile Include="Assets/Eagle/ProvinceUtils.cs" />
|
||||
<Compile Include="Assets/Eagle/Notifications/ProvinceConqueredNotificationGenerator.cs" />
|
||||
<Compile Include="Assets/common/GUIUtils/ProvinceStatUtils.cs" />
|
||||
<Compile Include="Assets/Eagle/Table Rows/ExchangedHeroRowController.cs" />
|
||||
<Compile Include="Assets/Eagle/Table Rows/ExtraTroopsRowController.cs" />
|
||||
<Compile Include="Assets/Eagle/Notifications/PrisonerEscapedNotificationGenerator.cs" />
|
||||
|
||||
+13
-13
@@ -25,7 +25,7 @@ namespace eagle {
|
||||
private ImprovementType SelectedType =>
|
||||
ImproveAvailableCommand.AvailableTypes[SelectedTypeIndex];
|
||||
|
||||
private float OriginalImprovementValueForType(ImprovementType type) {
|
||||
private double OriginalImprovementValueForType(ImprovementType type) {
|
||||
var province = _model.Provinces[ActingProvinceId];
|
||||
switch (type) {
|
||||
case ImprovementType.Agriculture: return province.FullInfo.Agriculture;
|
||||
@@ -39,7 +39,7 @@ namespace eagle {
|
||||
}
|
||||
}
|
||||
|
||||
private float EffectiveImprovementValueForType(ImprovementType type) {
|
||||
private double EffectiveImprovementValueForType(ImprovementType type) {
|
||||
var province = _model.Provinces[ActingProvinceId];
|
||||
switch (type) {
|
||||
case ImprovementType.Agriculture:
|
||||
@@ -59,10 +59,10 @@ namespace eagle {
|
||||
|
||||
private int MinimumImprovementIndex(ProvinceView province) {
|
||||
var minIndex = 0;
|
||||
float minValue =
|
||||
double minValue =
|
||||
OriginalImprovementValueForType(ImproveAvailableCommand.AvailableTypes[0]);
|
||||
for (int i = 1; i < ImproveAvailableCommand.AvailableTypes.Count; i++) {
|
||||
float thisVal =
|
||||
double thisVal =
|
||||
OriginalImprovementValueForType(ImproveAvailableCommand.AvailableTypes[i]);
|
||||
if (thisVal < minValue) {
|
||||
minIndex = i;
|
||||
@@ -139,21 +139,21 @@ namespace eagle {
|
||||
};
|
||||
|
||||
private string DropdownStringForType(ImprovementType type) {
|
||||
var originalStat =
|
||||
type == ImprovementType.Devastation
|
||||
? ProvinceStatUtils.RoundedDevastation(
|
||||
OriginalImprovementValueForType(type))
|
||||
: ProvinceStatUtils.RoundedStat(OriginalImprovementValueForType(type));
|
||||
var originalStat = RoundedNonzeroStat(OriginalImprovementValueForType(type));
|
||||
if (type == ImprovementType.Devastation) {
|
||||
return $"{type} ({GUIUtils.ColoredString(UnityEngine.Color.red, originalStat.ToString())})";
|
||||
return $"{type} ({GUIUtils.ColoredString(UnityEngine.Color.red, originalStat)})";
|
||||
}
|
||||
var devastatedStat =
|
||||
ProvinceStatUtils.RoundedStat(EffectiveImprovementValueForType(type));
|
||||
var devastatedStat = RoundedNonzeroStat(EffectiveImprovementValueForType(type));
|
||||
if (devastatedStat == originalStat) {
|
||||
return $"{type} ({devastatedStat})";
|
||||
} else {
|
||||
return $"{type} ({GUIUtils.ColoredString(UnityEngine.Color.red, devastatedStat.ToString())} / {originalStat})";
|
||||
return $"{type} ({GUIUtils.ColoredString(UnityEngine.Color.red, devastatedStat)} / {originalStat})";
|
||||
}
|
||||
}
|
||||
|
||||
private string RoundedNonzeroStat(double stat) {
|
||||
var rounded = Math.Max(1, Math.Round(stat, MidpointRounding.AwayFromZero));
|
||||
return rounded.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using common;
|
||||
using EagleGUIUtils;
|
||||
using Net.Eagle0.Eagle.Api;
|
||||
using Net.Eagle0.Eagle.Common;
|
||||
using Net.Eagle0.Eagle.Views;
|
||||
@@ -236,8 +235,8 @@ namespace eagle {
|
||||
allowed,
|
||||
OrganizeTroopsAvailableCommand.AvailableBattalionTypes.First(
|
||||
tp => tp.TypeId == battalionTypeId),
|
||||
ProvinceStatUtils.RoundedStat(Province.FullInfo.Agriculture),
|
||||
ProvinceStatUtils.RoundedStat(Province.FullInfo.Economy));
|
||||
Province.FullInfo.Agriculture,
|
||||
Province.FullInfo.Economy);
|
||||
|
||||
parent.GetComponentInChildren<Button>().interactable = allowed;
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ namespace eagle {
|
||||
|
||||
private readonly Func<ProvinceView, String> _nameSortFunc = p => p.Name;
|
||||
|
||||
private readonly Dictionary<SortKey, Func<ProvinceView, IComparable>> _sortFuncs = new() {
|
||||
private readonly Dictionary<SortKey, Func<ProvinceView, int>> _sortFuncs = new() {
|
||||
{ SortKey.Support, p => p.FullInfo.Support.Stat },
|
||||
{ SortKey.Commanders, p => p.FullInfo.RulingFactionHeroIds.Count },
|
||||
{ SortKey.Battalions, p => p.FullInfo.Battalions.Count },
|
||||
|
||||
+7
-10
@@ -225,27 +225,25 @@ namespace eagle {
|
||||
ConsumedField.text = $"{Province.FullInfo.FoodConsumption}";
|
||||
priceIndexField.text = $"{Province.FullInfo.PriceIndex,4:F2}";
|
||||
|
||||
EconomyField.text = $"{ProvinceStatUtils.RoundedStat(Province.FullInfo.Economy)}";
|
||||
EconomyField.text = $"{Province.FullInfo.Economy}";
|
||||
if (Province.FullInfo.EconomyDevastation > 0) {
|
||||
var devastatedEconomy =
|
||||
$"{ProvinceStatUtils.RoundedStat(Math.Max(0.0f, Province.FullInfo.Economy - Province.FullInfo.EconomyDevastation))}";
|
||||
$"{Math.Max(0.0, Province.FullInfo.Economy - Province.FullInfo.EconomyDevastation)}";
|
||||
EconomyField.text += $" ({GUIUtils.ColoredString(Color.red, devastatedEconomy)})";
|
||||
}
|
||||
|
||||
AgricultureField.text =
|
||||
$"{ProvinceStatUtils.RoundedStat(Province.FullInfo.Agriculture)}";
|
||||
AgricultureField.text = $"{Province.FullInfo.Agriculture}";
|
||||
if (Province.FullInfo.AgricultureDevastation > 0) {
|
||||
var devastatedAgriculture =
|
||||
$"{ProvinceStatUtils.RoundedStat(Math.Max(0.0f, Province.FullInfo.Agriculture - Province.FullInfo.AgricultureDevastation))}";
|
||||
$"{Math.Max(0.0, Province.FullInfo.Agriculture - Province.FullInfo.AgricultureDevastation)}";
|
||||
AgricultureField.text +=
|
||||
$" ({GUIUtils.ColoredString(Color.red, devastatedAgriculture)})";
|
||||
}
|
||||
|
||||
InfrastructureField.text =
|
||||
$"{ProvinceStatUtils.RoundedStat(Province.FullInfo.Infrastructure)}";
|
||||
InfrastructureField.text = $"{Province.FullInfo.Infrastructure}";
|
||||
if (Province.FullInfo.InfrastructureDevastation > 0) {
|
||||
var devastatedInfrastructure =
|
||||
$"{ProvinceStatUtils.RoundedStat(Math.Max(0.0f, Province.FullInfo.Infrastructure - Province.FullInfo.InfrastructureDevastation))}";
|
||||
$"{Math.Max(0.0, Province.FullInfo.Infrastructure - Province.FullInfo.InfrastructureDevastation)}";
|
||||
InfrastructureField.text +=
|
||||
$" ({GUIUtils.ColoredString(Color.red, devastatedInfrastructure)})";
|
||||
}
|
||||
@@ -253,8 +251,7 @@ namespace eagle {
|
||||
var totalDevastation = Province.FullInfo.EconomyDevastation +
|
||||
Province.FullInfo.AgricultureDevastation +
|
||||
Province.FullInfo.InfrastructureDevastation;
|
||||
var totalDevastationString =
|
||||
$"{ProvinceStatUtils.RoundedDevastation(totalDevastation)}";
|
||||
var totalDevastationString = $"{totalDevastation}";
|
||||
if (totalDevastation > 0) {
|
||||
DevastationField.text =
|
||||
$"{GUIUtils.ColoredString(Color.red, totalDevastationString)}";
|
||||
|
||||
+5
-5
@@ -37,14 +37,14 @@ namespace eagle {
|
||||
private ProvinceId ProvinceId;
|
||||
private DynamicHeroTextUpdater textUpdater = new DynamicHeroTextUpdater();
|
||||
|
||||
private void SetDevastatedValue(TMP_Text textField, float baseValue, float devastation) {
|
||||
if (devastation == 0.0f) {
|
||||
private void SetDevastatedValue(TMP_Text textField, double baseValue, double devastation) {
|
||||
if (devastation == 0.0) {
|
||||
textField.color = Color.black;
|
||||
textField.text = ProvinceStatUtils.RoundedStat(baseValue).ToString();
|
||||
textField.text = baseValue.ToString();
|
||||
} else {
|
||||
textField.color = Color.red;
|
||||
var devastatedValue = Math.Max(0.0f, baseValue - devastation);
|
||||
textField.text = ProvinceStatUtils.RoundedStat(devastatedValue).ToString();
|
||||
var devastatedValue = Math.Max(0.0, baseValue - devastation);
|
||||
textField.text = devastatedValue.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Utility methods for rounding province statistics for display.
|
||||
/// These methods match the rounding logic from ProvinceViewFilter.scala
|
||||
/// </summary>
|
||||
public static class ProvinceStatUtils {
|
||||
/// <summary>
|
||||
/// Rounds a stat value using the same logic as roundedStat in ProvinceViewFilter.scala:
|
||||
/// - If stat == 0, return 0
|
||||
/// - If stat < 1, return 1
|
||||
/// - Otherwise, return floor of stat
|
||||
/// </summary>
|
||||
public static int RoundedStat(float stat) {
|
||||
if (stat == 0) return 0;
|
||||
else if (stat < 1)
|
||||
return 1;
|
||||
else
|
||||
return (int)Math.Floor(stat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds a devastation value using the same logic as roundedDevastation in
|
||||
/// ProvinceViewFilter.scala:
|
||||
/// - Return ceiling of stat
|
||||
/// </summary>
|
||||
public static int RoundedDevastation(float stat) { return (int)Math.Ceiling(stat); }
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6672bb4c46b44b10abe885e064119d0
|
||||
-4
@@ -28,10 +28,6 @@ func main() {
|
||||
|
||||
capitalizedSettingType := capitalized(settingType)
|
||||
|
||||
if capitalizedSettingType == "Float" {
|
||||
settingValue += "f"
|
||||
}
|
||||
|
||||
fmt.Printf(`// Generated file, do not edit!
|
||||
|
||||
package net.eagle0.eagle.library.settings
|
||||
|
||||
@@ -18,9 +18,9 @@ message BeastInfo {
|
||||
double likelihood = 4;
|
||||
double max_count_multiplier = 5;
|
||||
double relative_power = 6;
|
||||
float economy_devastation = 7;
|
||||
float agriculture_devastation = 8;
|
||||
float infrastructure_devastation = 9;
|
||||
double economy_devastation = 7;
|
||||
double agriculture_devastation = 8;
|
||||
double infrastructure_devastation = 9;
|
||||
double average_gold_per = 10;
|
||||
double average_food_per = 11;
|
||||
}
|
||||
@@ -69,16 +69,16 @@ message ChangedProvince {
|
||||
.google.protobuf.Int32Value gold_delta = 14;
|
||||
.google.protobuf.Int32Value food_delta = 15;
|
||||
|
||||
.google.protobuf.FloatValue new_price_index = 35;
|
||||
.google.protobuf.DoubleValue new_price_index = 35;
|
||||
|
||||
.google.protobuf.FloatValue economy_delta = 16;
|
||||
.google.protobuf.FloatValue agriculture_delta = 17;
|
||||
.google.protobuf.FloatValue infrastructure_delta = 18;
|
||||
.google.protobuf.FloatValue economy_devastation_delta = 44;
|
||||
.google.protobuf.FloatValue agriculture_devastation_delta = 47;
|
||||
.google.protobuf.FloatValue infrastructure_devastation_delta = 48;
|
||||
.google.protobuf.DoubleValue economy_delta = 16;
|
||||
.google.protobuf.DoubleValue agriculture_delta = 17;
|
||||
.google.protobuf.DoubleValue infrastructure_delta = 18;
|
||||
.google.protobuf.DoubleValue economy_devastation_delta = 44;
|
||||
.google.protobuf.DoubleValue agriculture_devastation_delta = 47;
|
||||
.google.protobuf.DoubleValue infrastructure_devastation_delta = 48;
|
||||
|
||||
.google.protobuf.FloatValue support_delta = 19;
|
||||
.google.protobuf.DoubleValue support_delta = 19;
|
||||
|
||||
.google.protobuf.BoolValue set_has_acted = 20;
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ option objc_class_prefix = "E0G";
|
||||
message ProvinceOverrides {
|
||||
int32 province_id = 1; // can be 0 for a generic
|
||||
|
||||
float economy = 2;
|
||||
float agriculture = 3;
|
||||
float infrastructure = 4;
|
||||
double economy = 2;
|
||||
double agriculture = 3;
|
||||
double infrastructure = 4;
|
||||
|
||||
float support = 5;
|
||||
double support = 5;
|
||||
|
||||
int32 food = 6;
|
||||
int32 gold = 7;
|
||||
|
||||
@@ -77,12 +77,12 @@ message Province {
|
||||
repeated IncomingEndTurnAction incoming_end_turn_actions = 27;
|
||||
Army defending_army = 11;
|
||||
|
||||
float economy = 12;
|
||||
float agriculture = 13;
|
||||
float infrastructure = 14;
|
||||
float economy_devastation = 34;
|
||||
float agriculture_devastation = 37;
|
||||
float infrastructure_devastation = 38;
|
||||
double economy = 12;
|
||||
double agriculture = 13;
|
||||
double infrastructure = 14;
|
||||
double economy_devastation = 34;
|
||||
double agriculture_devastation = 37;
|
||||
double infrastructure_devastation = 38;
|
||||
|
||||
// Can be None.
|
||||
.net.eagle0.eagle.common.ImprovementType locked_improvement_type = 41;
|
||||
@@ -92,9 +92,9 @@ message Province {
|
||||
|
||||
// Current price index where 1.0 is "standard". Expected to vary between
|
||||
// 0.5 and 1.5.
|
||||
float price_index = 29;
|
||||
double price_index = 29;
|
||||
|
||||
float support = 17;
|
||||
double support = 17;
|
||||
|
||||
bool has_acted = 18;
|
||||
|
||||
|
||||
@@ -59,12 +59,12 @@ message FullProvinceInfo {
|
||||
|
||||
ArmyView defending_army = 7;
|
||||
|
||||
float economy = 8;
|
||||
float agriculture = 9;
|
||||
float infrastructure = 10;
|
||||
float economy_devastation = 25;
|
||||
float agriculture_devastation = 26;
|
||||
float infrastructure_devastation = 27;
|
||||
int32 economy = 8;
|
||||
int32 agriculture = 9;
|
||||
int32 infrastructure = 10;
|
||||
int32 economy_devastation = 25;
|
||||
int32 agriculture_devastation = 26;
|
||||
int32 infrastructure_devastation = 27;
|
||||
|
||||
int32 gold = 11;
|
||||
int32 food = 12;
|
||||
@@ -130,12 +130,12 @@ message FullProvinceInfoDiff {
|
||||
|
||||
.google.protobuf.DoubleValue new_price_index = 36;
|
||||
|
||||
.google.protobuf.FloatValue economy = 16;
|
||||
.google.protobuf.FloatValue agriculture = 17;
|
||||
.google.protobuf.FloatValue infrastructure = 18;
|
||||
.google.protobuf.FloatValue economy_devastation = 30;
|
||||
.google.protobuf.FloatValue agriculture_devastation = 31;
|
||||
.google.protobuf.FloatValue infrastructure_devastation = 32;
|
||||
.google.protobuf.Int32Value economy = 16;
|
||||
.google.protobuf.Int32Value agriculture = 17;
|
||||
.google.protobuf.Int32Value infrastructure = 18;
|
||||
.google.protobuf.Int32Value economy_devastation = 30;
|
||||
.google.protobuf.Int32Value agriculture_devastation = 31;
|
||||
.google.protobuf.Int32Value infrastructure_devastation = 32;
|
||||
|
||||
StatWithCondition support = 19;
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ vassalCommandsMaxFatigueLevelBeforeResting 15 Double
|
||||
vassalCommandsMinOddsForRecruitment 50 Double
|
||||
maxCombatUnitCountPerSide 9999 Int
|
||||
maxAlmsFood 1000 Int
|
||||
almsSupportIncreasePerFood 0.01 Float
|
||||
almsPaladinSupportMultiplier 4 Float
|
||||
almsSupportIncreasePerFood 0.01 Double
|
||||
almsPaladinSupportMultiplier 4 Double
|
||||
foodPerProvinceHeldBack 1000 Int
|
||||
factionBiasFromImprisonment -100 Double
|
||||
factionBiasFromExile -50 Double
|
||||
@@ -80,8 +80,8 @@ riotEventChance 0.02 Double
|
||||
minVigorForSuppressBeasts 50 Double
|
||||
suppressBeastsPrestigeBonus 2 Int
|
||||
beastsDurationMonths 4 Int
|
||||
beastsSupportDamage 3 Float
|
||||
suppressBeastsSupportBonus 5 Float
|
||||
beastsSupportDamage 3 Double
|
||||
suppressBeastsSupportBonus 5 Double
|
||||
maxInfrastructureForbeasts 50 Int
|
||||
failedSuppressBeastsPrestigePenalty 5 Int
|
||||
baseBeastsCount 150 Int
|
||||
@@ -165,13 +165,13 @@ minMonthsAfterPleaseRecruitMeRejectionBeforeTryingAgain 12 Int
|
||||
exiledHeroFactionBias -500 Double
|
||||
minVigorForApprehendOutlaw 50 Double
|
||||
apprehendOutlawVigorCost 30 Double
|
||||
battleEconomyDevastationDelta 10 Float
|
||||
battleAgricultureDevastationDelta 10 Float
|
||||
battleInfrastructureDevastationDelta 10 Float
|
||||
battleEconomyDevastationDelta 10 Double
|
||||
battleAgricultureDevastationDelta 10 Double
|
||||
battleInfrastructureDevastationDelta 10 Double
|
||||
minVigorForCrackDown 50 Double
|
||||
riotSupportDelta -25 Float
|
||||
riotEconomyDevastationDelta 25 Float
|
||||
riotInfrastructureDevastationDelta 25 Float
|
||||
riotSupportDelta -25 Double
|
||||
riotEconomyDevastationDelta 25 Double
|
||||
riotInfrastructureDevastationDelta 25 Double
|
||||
riotMaxFood 1000 Int
|
||||
riotMaxGold 500 Int
|
||||
riotCharismaFactor 0.1 Double
|
||||
@@ -184,9 +184,9 @@ monthsBetweenRiotsSupportMultiplier 0.5 Double
|
||||
blizzardEventChance 0.01 Double
|
||||
maxBlizzardDurationMonths 4 Int
|
||||
winterSuppliesLoss 0.25 Double
|
||||
blizzardEconomyDevastationDelta 4 Float
|
||||
blizzardInfrastructureDevastationDelta 4 Float
|
||||
blizzardAgricultureDevastationDelta 4 Float
|
||||
blizzardEconomyDevastationDelta 4 Double
|
||||
blizzardInfrastructureDevastationDelta 4 Double
|
||||
blizzardAgricultureDevastationDelta 4 Double
|
||||
festivalEventChance 0.01 Double
|
||||
maxFestivalDurationMonths 3 Int
|
||||
festivalSupportDelta 8 Double
|
||||
@@ -197,12 +197,12 @@ maxDesiredTruceCountForQuest 5 Int
|
||||
minDesiredNewTruceCountForQuest 2 Int
|
||||
floodEventChance 0.01 Double
|
||||
floodDurationMonths 1 Int
|
||||
maxFloodInfrastructureDevastationDelta 20 Float
|
||||
maxFloodAgricultureDevastationDelta 20 Float
|
||||
floodDevastationDeltaReductionPerInfrastructure 0.3 Float
|
||||
maxFloodInfrastructureDevastationDelta 20 Double
|
||||
maxFloodAgricultureDevastationDelta 20 Double
|
||||
floodDevastationDeltaReductionPerInfrastructure 0.3 Double
|
||||
epidemicBreakoutChance 0.0015 Double
|
||||
epidemicSpreadChance 0.03 Double
|
||||
epidemicEconomyDevastationDelta 10 Float
|
||||
epidemicEconomyDevastationDelta 10 Double
|
||||
epidemicBattalionLossPercentage 0.05 Double
|
||||
epidemicVigorDelta -10 Double
|
||||
epidemicEndChance 0.35 Double
|
||||
@@ -215,9 +215,9 @@ startEpidemicCharismaXp 25 Int
|
||||
startEpidemicVigorDelta -30 Double
|
||||
droughtEventChance 0.01 Double
|
||||
droughtDurationMonths 3 Int
|
||||
droughtAgricultureDevastationDelta 10 Float
|
||||
droughtAgricultureDevastationDelta 10 Double
|
||||
controlWeatherDroughtDurationMonths 3 Int
|
||||
emptyProvinceMonthlyDevastationDelta -2 Float
|
||||
emptyProvinceMonthlyDevastationDelta -2 Double
|
||||
vigorToConstitutionXpMultiplier 0.2 Double
|
||||
trustDeltaPerRound 1 Int
|
||||
trustDeltaForImprisoningOwnHero -150 Int
|
||||
@@ -264,9 +264,9 @@ giftProvinceCountExponent 0.5 Double
|
||||
minChanceForAIInvite 70 Int
|
||||
chronicleWordCount 200 Int
|
||||
maxDistanceForDefeatFactionQuest 3 Int
|
||||
minimumPriceIndex 0.75 Float
|
||||
maximumPriceIndex 1.5 Float
|
||||
priceIndexReturnRate 0.1 Float
|
||||
priceIndexShiftPerGold 0.0001 Float
|
||||
minimumPriceIndex 0.75 Double
|
||||
maximumPriceIndex 1.5 Double
|
||||
priceIndexReturnRate 0.1 Double
|
||||
priceIndexShiftPerGold 0.0001 Double
|
||||
aiTruceAcceptanceBaseChance 100 Double
|
||||
aiAllianceAcceptanceBaseChance 100 Double
|
||||
|
@@ -214,6 +214,4 @@ saveAll FALSE bool
|
||||
holyWaveVigorCost 10 double
|
||||
minLookaheadTurns 1 int8
|
||||
lookaheadTimeBudgetCloseInSeconds 3 double
|
||||
lookaheadTimeBudgetFarInSeconds 1.5 double
|
||||
aiMinimumFleeOddsThreshold 30 int16
|
||||
aiDesperateFleeThreshold 10 int16
|
||||
lookaheadTimeBudgetFarInSeconds 1.5 double
|
||||
|
+13
-13
@@ -185,33 +185,33 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
},
|
||||
_.priceIndex.setIfDefined(cp.newPriceIndex),
|
||||
_.economy.modify(e =>
|
||||
(e + cp.economyDelta.getOrElse(0.0f)).max(0.0f).min(100.0f)
|
||||
(e + cp.economyDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
||||
),
|
||||
_.agriculture
|
||||
.modify(a =>
|
||||
(a + cp.agricultureDelta.getOrElse(0.0f)).max(0.0f).min(100.0f)
|
||||
(a + cp.agricultureDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
||||
),
|
||||
_.infrastructure
|
||||
.modify(i =>
|
||||
(i + cp.infrastructureDelta.getOrElse(0.0f)).max(0.0f).min(100.0f)
|
||||
(i + cp.infrastructureDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
||||
),
|
||||
_.economyDevastation.modify(d =>
|
||||
(d + cp.economyDevastationDelta.getOrElse(0.0f))
|
||||
.max(0.0f)
|
||||
(d + cp.economyDevastationDelta.getOrElse(0.0))
|
||||
.max(0.0)
|
||||
.min(provinceBefore.economy)
|
||||
),
|
||||
_.agricultureDevastation.modify(d =>
|
||||
(d + cp.agricultureDevastationDelta.getOrElse(0.0f))
|
||||
.max(0.0f)
|
||||
(d + cp.agricultureDevastationDelta.getOrElse(0.0))
|
||||
.max(0.0)
|
||||
.min(provinceBefore.agriculture)
|
||||
),
|
||||
_.infrastructureDevastation.modify(d =>
|
||||
(d + cp.infrastructureDevastationDelta.getOrElse(0.0f))
|
||||
.max(0.0f)
|
||||
(d + cp.infrastructureDevastationDelta.getOrElse(0.0))
|
||||
.max(0.0)
|
||||
.min(provinceBefore.infrastructure)
|
||||
),
|
||||
_.support.modify(s =>
|
||||
(s + cp.supportDelta.getOrElse(0.0f)).max(0.0f).min(100.0f)
|
||||
(s + cp.supportDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
||||
),
|
||||
_.hasActed.setIfDefined(cp.setHasActed),
|
||||
_.rulerIsTraveling.setIfDefined(cp.setRulerIsTraveling),
|
||||
@@ -528,7 +528,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
_.vigor.modify { v =>
|
||||
ch.vigor match {
|
||||
case Vigor.VigorDelta(d) =>
|
||||
(v + d).max(0.0f).min(gameState.heroes(ch.id).constitution)
|
||||
(v + d).max(0.0).min(gameState.heroes(ch.id).constitution)
|
||||
|
||||
case Vigor.VigorAbsolute(va) =>
|
||||
internalRequire(
|
||||
@@ -548,7 +548,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
_.loyalty.modify { l =>
|
||||
ch.loyalty match {
|
||||
case Loyalty.LoyaltyDelta(ld) =>
|
||||
val newL = Math.min(100.0f, Math.max(0, l + ld))
|
||||
val newL = Math.min(100.0, Math.max(0, l + ld))
|
||||
newL
|
||||
|
||||
case Loyalty.LoyaltyAbsolute(la) =>
|
||||
@@ -557,7 +557,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
s"Got a negative absolute loyalty of $la"
|
||||
)
|
||||
internalRequire(
|
||||
la <= 100.0f,
|
||||
la <= 100.0,
|
||||
s"Got an absolute loyalty of $la"
|
||||
)
|
||||
la
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ object AvailableArmTroopsCommandFactory
|
||||
)
|
||||
.map(tp =>
|
||||
ArmamentCost(
|
||||
cost = tp.baseArmamentCostPerTroop.toFloat * province.priceIndex,
|
||||
cost = tp.baseArmamentCostPerTroop * province.priceIndex,
|
||||
`type` = tp.typeId
|
||||
)
|
||||
)
|
||||
|
||||
@@ -307,14 +307,12 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_faction_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:end_diplomacy_resolution_phase_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:ransom_invalidated_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:notification_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:round_phase",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
|
||||
+1
-9
@@ -24,16 +24,12 @@ import net.eagle0.eagle.model.action_result.types.{
|
||||
RansomInvalidatedResultType
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.{ActionResultT, NotificationT}
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
BattalionConverter,
|
||||
NotificationConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.NotificationConverter
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.model.state.RoundPhase.ReconResolution
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.{
|
||||
Accepted,
|
||||
@@ -67,9 +63,6 @@ case class EndDiplomacyResolutionPhaseAction(
|
||||
private lazy val heroTs: Vector[HeroT] =
|
||||
gameState.heroes.values.map(HeroConverter.fromProto).toVector
|
||||
|
||||
private lazy val battalionTs: Vector[BattalionT] =
|
||||
gameState.battalions.values.map(BattalionConverter.fromProto).toVector
|
||||
|
||||
private lazy val currentDateT: Date =
|
||||
DateConverter.fromProto(gameState.currentDate)
|
||||
|
||||
@@ -342,7 +335,6 @@ case class EndDiplomacyResolutionPhaseAction(
|
||||
currentDate = currentDateT,
|
||||
provinces = provinceTs,
|
||||
factions = factionTs,
|
||||
battalions = battalionTs,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
case Rejected =>
|
||||
|
||||
@@ -187,7 +187,7 @@ case class NewRoundAction(startingState: GameState, gameHistory: GameHistory)
|
||||
Math
|
||||
.max(
|
||||
-p.economyDevastation,
|
||||
EmptyProvinceMonthlyDevastationDelta.floatValue
|
||||
EmptyProvinceMonthlyDevastationDelta.doubleValue
|
||||
)
|
||||
),
|
||||
agricultureDevastationDelta = Option.when(
|
||||
@@ -196,7 +196,7 @@ case class NewRoundAction(startingState: GameState, gameHistory: GameHistory)
|
||||
Math
|
||||
.max(
|
||||
-p.agricultureDevastation,
|
||||
EmptyProvinceMonthlyDevastationDelta.floatValue
|
||||
EmptyProvinceMonthlyDevastationDelta.doubleValue
|
||||
)
|
||||
),
|
||||
infrastructureDevastationDelta = Option.when(
|
||||
@@ -205,7 +205,7 @@ case class NewRoundAction(startingState: GameState, gameHistory: GameHistory)
|
||||
Math
|
||||
.max(
|
||||
-p.infrastructureDevastation,
|
||||
EmptyProvinceMonthlyDevastationDelta.floatValue
|
||||
EmptyProvinceMonthlyDevastationDelta.doubleValue
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
@@ -25,14 +25,9 @@ case class NewYearAction(gameState: GameState)
|
||||
extends DeterministicSingleResultAction(gameState) {
|
||||
private def degradationMultiplier: Double =
|
||||
PercentDegradationPerYear.doubleValue / 100.0
|
||||
private def degradationMultiplierF: Float =
|
||||
PercentDegradationPerYear.floatValue / 100.0f
|
||||
|
||||
private def degradation: Double => Double = -_ * degradationMultiplier
|
||||
|
||||
private def degradationF(value: Float): Float =
|
||||
-value * degradationMultiplierF
|
||||
|
||||
private def loyaltyDecrease(
|
||||
vassal: Hero,
|
||||
isProvinceLeader: Boolean,
|
||||
@@ -82,11 +77,10 @@ case class NewYearAction(gameState: GameState)
|
||||
.map(p =>
|
||||
ChangedProvince(
|
||||
id = p.id,
|
||||
agricultureDevastationDelta = Some(-degradationF(p.agriculture)),
|
||||
economyDevastationDelta = Some(-degradationF(p.economy).toFloat),
|
||||
infrastructureDevastationDelta =
|
||||
Some(-degradationF(p.infrastructure).toFloat),
|
||||
supportDelta = Some(degradationF(p.support).toFloat),
|
||||
agricultureDevastationDelta = Some(-degradation(p.agriculture)),
|
||||
economyDevastationDelta = Some(-degradation(p.economy)),
|
||||
infrastructureDevastationDelta = Some(-degradation(p.infrastructure)),
|
||||
supportDelta = Some(degradation(p.support)),
|
||||
goldDelta = LegacyProvinceUtils.annualGoldProduction(p.id, gameState),
|
||||
foodDelta = LegacyProvinceUtils.annualFoodProduction(p.id, gameState)
|
||||
)
|
||||
|
||||
+22
-22
@@ -178,21 +178,21 @@ case class PerformProvinceEventsAction(
|
||||
cp.update(
|
||||
_.optionalEconomyDevastationDelta.modify(dd =>
|
||||
Some(
|
||||
dd.getOrElse(0.0f) + BlizzardEconomyDevastationDelta.floatValue
|
||||
dd.getOrElse(0.0) + BlizzardEconomyDevastationDelta.doubleValue
|
||||
)
|
||||
),
|
||||
_.optionalAgricultureDevastationDelta.modify(dd =>
|
||||
Some(
|
||||
dd.getOrElse(
|
||||
0.0f
|
||||
) + BlizzardAgricultureDevastationDelta.floatValue
|
||||
0.0
|
||||
) + BlizzardAgricultureDevastationDelta.doubleValue
|
||||
)
|
||||
),
|
||||
_.optionalInfrastructureDevastationDelta.modify(dd =>
|
||||
Some(
|
||||
dd.getOrElse(
|
||||
0.0f
|
||||
) + BlizzardInfrastructureDevastationDelta.floatValue
|
||||
0.0
|
||||
) + BlizzardInfrastructureDevastationDelta.doubleValue
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -201,49 +201,49 @@ case class PerformProvinceEventsAction(
|
||||
_.optionalAgricultureDevastationDelta.modify(dd =>
|
||||
Some(
|
||||
dd.getOrElse(
|
||||
0.0f
|
||||
) + (MaxFloodAgricultureDevastationDelta.floatValue - p.infrastructure * FloodDevastationDeltaReductionPerInfrastructure.floatValue)
|
||||
.max(0.0f)
|
||||
0.0
|
||||
) + (MaxFloodAgricultureDevastationDelta.doubleValue - p.infrastructure * FloodDevastationDeltaReductionPerInfrastructure.doubleValue)
|
||||
.max(0.0)
|
||||
)
|
||||
),
|
||||
_.optionalInfrastructureDevastationDelta.modify(dd =>
|
||||
Some(
|
||||
dd.getOrElse(
|
||||
0.0f
|
||||
) + (MaxFloodInfrastructureDevastationDelta.floatValue - p.infrastructure * FloodDevastationDeltaReductionPerInfrastructure.floatValue)
|
||||
.max(0.0f)
|
||||
0.0
|
||||
) + (MaxFloodInfrastructureDevastationDelta.doubleValue - p.infrastructure * FloodDevastationDeltaReductionPerInfrastructure.doubleValue)
|
||||
.max(0.0)
|
||||
)
|
||||
)
|
||||
)
|
||||
case BeastsEvent(_, _, _, beastInfo, _ /* unknownFieldSet */ ) =>
|
||||
cp.update(
|
||||
_.optionalEconomyDevastationDelta.modify(dd =>
|
||||
Some(dd.getOrElse(0.0f) + beastInfo.economyDevastation)
|
||||
.filterNot(_ == 0.0f)
|
||||
Some(dd.getOrElse(0.0) + beastInfo.economyDevastation)
|
||||
.filterNot(_ == 0.0)
|
||||
),
|
||||
_.optionalAgricultureDevastationDelta.modify(dd =>
|
||||
Some(dd.getOrElse(0.0f) + beastInfo.agricultureDevastation)
|
||||
.filterNot(_ == 0.0f)
|
||||
Some(dd.getOrElse(0.0) + beastInfo.agricultureDevastation)
|
||||
.filterNot(_ == 0.0)
|
||||
),
|
||||
_.optionalInfrastructureDevastationDelta.modify(dd =>
|
||||
Some(dd.getOrElse(0.0f) + beastInfo.infrastructureDevastation)
|
||||
.filterNot(_ == 0.0f)
|
||||
Some(dd.getOrElse(0.0) + beastInfo.infrastructureDevastation)
|
||||
.filterNot(_ == 0.0)
|
||||
),
|
||||
_.optionalSupportDelta.modify(sd =>
|
||||
Some(sd.getOrElse(0.0f) - BeastsSupportDamage.floatValue)
|
||||
Some(sd.getOrElse(0.0) - BeastsSupportDamage.doubleValue)
|
||||
)
|
||||
)
|
||||
case _: FestivalEvent =>
|
||||
cp.update(
|
||||
_.optionalSupportDelta.modify(sd =>
|
||||
Some(sd.getOrElse(0.0f) + FestivalSupportDelta.floatValue)
|
||||
Some(sd.getOrElse(0.0) + FestivalSupportDelta.doubleValue)
|
||||
)
|
||||
)
|
||||
case _: EpidemicEvent =>
|
||||
cp.update(
|
||||
_.optionalEconomyDevastationDelta.modify(dd =>
|
||||
Some(
|
||||
dd.getOrElse(0.0f) + EpidemicEconomyDevastationDelta.floatValue
|
||||
dd.getOrElse(0.0) + EpidemicEconomyDevastationDelta.doubleValue
|
||||
)
|
||||
),
|
||||
_.optionalNewProvinceEvents := Option.when(
|
||||
@@ -263,8 +263,8 @@ case class PerformProvinceEventsAction(
|
||||
_.optionalAgricultureDevastationDelta.modify(dd =>
|
||||
Some(
|
||||
dd.getOrElse(
|
||||
0.0f
|
||||
) + DroughtAgricultureDevastationDelta.floatValue
|
||||
0.0
|
||||
) + DroughtAgricultureDevastationDelta.doubleValue
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
+3
-3
@@ -181,11 +181,11 @@ case class ResolveBattleAction(
|
||||
.map(_.getSupplies.food)
|
||||
.sum
|
||||
),
|
||||
economyDevastationDelta = Some(BattleEconomyDevastationDelta.floatValue),
|
||||
economyDevastationDelta = Some(BattleEconomyDevastationDelta.doubleValue),
|
||||
agricultureDevastationDelta =
|
||||
Some(BattleAgricultureDevastationDelta.floatValue),
|
||||
Some(BattleAgricultureDevastationDelta.doubleValue),
|
||||
infrastructureDevastationDelta =
|
||||
Some(BattleInfrastructureDevastationDelta.floatValue),
|
||||
Some(BattleInfrastructureDevastationDelta.doubleValue),
|
||||
addedBattleRevelations =
|
||||
battle.players.filterNot(_.isDefender).map { sp =>
|
||||
BattleRevelation(
|
||||
|
||||
-2
@@ -96,7 +96,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:starting_loyalty",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:trust_delta_for_imprisoning_other_hero",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:trust_delta_for_imprisoning_own_hero",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_power",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:returning_heroes",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:action_result_trait",
|
||||
@@ -110,7 +109,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:offer_invalidated_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:prisoner_joined_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:quest_invalidated_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/quest",
|
||||
|
||||
+9
-44
@@ -11,7 +11,7 @@ import net.eagle0.eagle.library.settings.{
|
||||
TrustDeltaForImprisoningOtherHero,
|
||||
TrustDeltaForImprisoningOwnHero
|
||||
}
|
||||
import net.eagle0.eagle.library.util.{BattalionPower, ReturningHeroes}
|
||||
import net.eagle0.eagle.library.util.ReturningHeroes
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
@@ -30,8 +30,6 @@ import net.eagle0.eagle.model.action_result.types.{
|
||||
PrisonerJoinedResultType,
|
||||
QuestInvalidatedResultType
|
||||
}
|
||||
import net.eagle0.eagle.model.state.MovingArmy
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.{
|
||||
Outlaw,
|
||||
Prisoner
|
||||
@@ -55,7 +53,7 @@ import net.eagle0.eagle.model.state.unaffiliated_hero.{
|
||||
RecruitmentInfo,
|
||||
UnaffiliatedHeroT
|
||||
}
|
||||
import net.eagle0.eagle.{BattalionId, FactionId, HeroId}
|
||||
import net.eagle0.eagle.{FactionId, HeroId}
|
||||
|
||||
object InvitationResolutionHelpers {
|
||||
private def invalidatedQuestResults(
|
||||
@@ -136,43 +134,13 @@ object InvitationResolutionHelpers {
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
case class BattalionOutcomes(
|
||||
joinedBattalionIds: Vector[BattalionId],
|
||||
removedBattalionIds: Vector[BattalionId]
|
||||
)
|
||||
|
||||
def battalionOutcomes(
|
||||
redirectedArmies: Vector[MovingArmy],
|
||||
acceptingFactionProvinces: Vector[ProvinceT],
|
||||
battalions: Vector[BattalionT]
|
||||
): BattalionOutcomes =
|
||||
acceptingFactionProvinces.foldLeft(
|
||||
BattalionOutcomes(
|
||||
joinedBattalionIds = redirectedArmies
|
||||
.flatMap(_.army.units)
|
||||
.flatMap(_.battalionId),
|
||||
removedBattalionIds = Vector()
|
||||
)
|
||||
) { case (acc, p) =>
|
||||
p.battalionIds
|
||||
.sortBy(bid => -BattalionPower.power(battalions.find(_.id == bid).get))
|
||||
.splitAt(p.rulingFactionHeroIds.size) match {
|
||||
case (joinedBids, removedBids) =>
|
||||
BattalionOutcomes(
|
||||
joinedBattalionIds = acc.joinedBattalionIds ++ joinedBids,
|
||||
removedBattalionIds = acc.removedBattalionIds ++ removedBids
|
||||
)
|
||||
}
|
||||
}
|
||||
}.toVector
|
||||
|
||||
def acceptedInvitationResults(
|
||||
acceptedInvitation: Invitation,
|
||||
currentDate: Date,
|
||||
provinces: Vector[ProvinceT],
|
||||
factions: Vector[FactionT],
|
||||
battalions: Vector[BattalionT],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] = {
|
||||
val ambassadorBackstoryEvent = InvitationAmbassadorBackstoryEvent(
|
||||
@@ -195,12 +163,10 @@ object InvitationResolutionHelpers {
|
||||
.flatMap(_.incomingArmies)
|
||||
.filter(_.army.factionId == acceptingFid)
|
||||
|
||||
val battOutcomes = battalionOutcomes(
|
||||
redirectedArmies = redirectedArmies,
|
||||
acceptingFactionProvinces = acceptingFactionProvinces,
|
||||
battalions = battalions
|
||||
)
|
||||
|
||||
val affectedBattalions = redirectedArmies
|
||||
.flatMap(_.army.units)
|
||||
.flatMap(_.battalionId) ++ acceptingFactionProvinces
|
||||
.flatMap(_.battalionIds)
|
||||
val changedInvitedHeroIds: Vector[HeroId] = redirectedArmies
|
||||
.flatMap(_.army.units)
|
||||
.map(_.heroId) ++ acceptingFactionProvinces
|
||||
@@ -292,9 +258,8 @@ object InvitationResolutionHelpers {
|
||||
changedActorProvinces ++ changedIncomingArmyProvinces :+ ChangedProvinceC(
|
||||
provinceId = returningHeroes.changedProvince.provinceId,
|
||||
newRulingFactionHeroIds = changedInvitedHeroIds,
|
||||
newBattalionIds = battOutcomes.joinedBattalionIds
|
||||
) :+ returningHeroes.changedProvince,
|
||||
destroyedBattalionIds = battOutcomes.removedBattalionIds
|
||||
newBattalionIds = affectedBattalions
|
||||
) :+ returningHeroes.changedProvince
|
||||
)
|
||||
) ++ invalidatedQuestResults(
|
||||
invitingFid = acceptedInvitation.originatingFactionId,
|
||||
|
||||
@@ -30,14 +30,14 @@ import net.eagle0.eagle.{FactionId, HeroId}
|
||||
|
||||
object AlmsCommand {
|
||||
|
||||
def supportIncreasePerFood(hero: HeroT): Float =
|
||||
AlmsSupportIncreasePerFood.floatValue * (if (
|
||||
hero.profession == Profession.Paladin
|
||||
)
|
||||
AlmsPaladinSupportMultiplier.floatValue
|
||||
else 1.0f)
|
||||
def supportIncreasePerFood(hero: HeroT): Double =
|
||||
AlmsSupportIncreasePerFood.doubleValue * (if (
|
||||
hero.profession == Profession.Paladin
|
||||
)
|
||||
AlmsPaladinSupportMultiplier.doubleValue
|
||||
else 1.0)
|
||||
|
||||
private def supportIncrease(hero: HeroT, foodAmount: Int): Float =
|
||||
private def supportIncrease(hero: HeroT, foodAmount: Int): Double =
|
||||
foodAmount * supportIncreasePerFood(hero)
|
||||
|
||||
private case class AlmsCommand(
|
||||
@@ -92,7 +92,7 @@ object AlmsCommand {
|
||||
supportDelta = Some(
|
||||
Math.min(
|
||||
supportIncrease(actingHero, foodAmount),
|
||||
100.0f - actingProvince.support
|
||||
100.0 - actingProvince.support
|
||||
)
|
||||
),
|
||||
foodDelta = Some(-foodAmount)
|
||||
|
||||
@@ -124,7 +124,7 @@ object ArmTroopsCommand {
|
||||
goldDelta = Some(-goldSpent),
|
||||
newPriceIndex = PriceIndexUtils.maybeNewPriceIndexFromGoldSpend(
|
||||
province.priceIndex,
|
||||
goldSpent.toFloat
|
||||
goldSpent
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ object DivineCommand {
|
||||
newPriceIndex =
|
||||
PriceIndexUtils.maybeNewPriceIndexFromGoldSpend(
|
||||
actingProvince.priceIndex,
|
||||
cost.toFloat
|
||||
cost
|
||||
),
|
||||
changedUnaffiliatedHeroes =
|
||||
updatedUhWithLlmRequestsRs.map(_._1)
|
||||
|
||||
@@ -71,7 +71,7 @@ object FeastCommand {
|
||||
goldDelta = Some(-goldCost),
|
||||
newPriceIndex = PriceIndexUtils.maybeNewPriceIndexFromGoldSpend(
|
||||
currentPriceIndex = province.priceIndex,
|
||||
goldSpent = goldCost.toFloat
|
||||
goldSpent = goldCost
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -48,11 +48,11 @@ object HandleRiotUtils {
|
||||
case event => Some(event)
|
||||
}
|
||||
),
|
||||
supportDelta = Some(RiotSupportDelta.floatValue),
|
||||
supportDelta = Some(RiotSupportDelta.doubleValue),
|
||||
economyDevastationDelta =
|
||||
Some(RiotEconomyDevastationDelta.floatValue),
|
||||
Some(RiotEconomyDevastationDelta.doubleValue),
|
||||
infrastructureDevastationDelta =
|
||||
Some(RiotInfrastructureDevastationDelta.floatValue)
|
||||
Some(RiotInfrastructureDevastationDelta.doubleValue)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -115,7 +115,7 @@ object HeroGiftCommand {
|
||||
goldDelta = Some(-goldAmount),
|
||||
newPriceIndex = PriceIndexUtils.maybeNewPriceIndexFromGoldSpend(
|
||||
recipientProvince.priceIndex,
|
||||
goldAmount.toFloat
|
||||
goldAmount
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
@@ -100,8 +100,8 @@ object ImproveCommand {
|
||||
withProfessionBonus.min(maxImprovement - province.economy)
|
||||
ChangedProvinceC(
|
||||
provinceId = provinceId,
|
||||
economyDelta = Some(improvement.toFloat),
|
||||
supportDelta = Some(supportDelta.toFloat),
|
||||
economyDelta = Some(improvement),
|
||||
supportDelta = Some(supportDelta),
|
||||
newLockedImprovementType = Some(lockedImprovementType)
|
||||
)
|
||||
case ImprovementType.Agriculture =>
|
||||
@@ -109,8 +109,8 @@ object ImproveCommand {
|
||||
withProfessionBonus.min(maxImprovement - province.agriculture)
|
||||
ChangedProvinceC(
|
||||
provinceId = provinceId,
|
||||
agricultureDelta = Some(improvement.toFloat),
|
||||
supportDelta = Some(supportDelta.toFloat),
|
||||
agricultureDelta = Some(improvement),
|
||||
supportDelta = Some(supportDelta),
|
||||
newLockedImprovementType = Some(lockedImprovementType)
|
||||
)
|
||||
case ImprovementType.Infrastructure =>
|
||||
@@ -118,18 +118,18 @@ object ImproveCommand {
|
||||
withProfessionBonus.min(maxImprovement - province.infrastructure)
|
||||
ChangedProvinceC(
|
||||
provinceId = provinceId,
|
||||
infrastructureDelta = Some(improvement.toFloat),
|
||||
supportDelta = Some(supportDelta.toFloat),
|
||||
infrastructureDelta = Some(improvement),
|
||||
supportDelta = Some(supportDelta),
|
||||
newLockedImprovementType = Some(lockedImprovementType)
|
||||
)
|
||||
case ImprovementType.Devastation =>
|
||||
val improvement = withProfessionBonus
|
||||
ChangedProvinceC(
|
||||
provinceId = provinceId,
|
||||
economyDevastationDelta = Some((-improvement).toFloat),
|
||||
agricultureDevastationDelta = Some((-improvement).toFloat),
|
||||
infrastructureDevastationDelta = Some((-improvement).toFloat),
|
||||
supportDelta = Some(supportDelta.toFloat),
|
||||
economyDevastationDelta = Some(-improvement),
|
||||
agricultureDevastationDelta = Some(-improvement),
|
||||
infrastructureDevastationDelta = Some(-improvement),
|
||||
supportDelta = Some(supportDelta),
|
||||
newLockedImprovementType = Some(lockedImprovementType)
|
||||
)
|
||||
case _ => throw new IllegalArgumentException("Unknown Improvement type")
|
||||
|
||||
+3
-3
@@ -52,11 +52,11 @@ object LegacyHandleRiotUtils {
|
||||
}
|
||||
)
|
||||
),
|
||||
supportDelta = Some(RiotSupportDelta.floatValue),
|
||||
supportDelta = Some(RiotSupportDelta.doubleValue),
|
||||
economyDevastationDelta =
|
||||
Some(RiotEconomyDevastationDelta.floatValue),
|
||||
Some(RiotEconomyDevastationDelta.doubleValue),
|
||||
infrastructureDevastationDelta =
|
||||
Some(RiotInfrastructureDevastationDelta.floatValue)
|
||||
Some(RiotInfrastructureDevastationDelta.doubleValue)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
+1
-1
@@ -369,7 +369,7 @@ object OrganizeTroopsCommand {
|
||||
goldDelta = Some(-afterAddingForChanged.cost),
|
||||
newPriceIndex = PriceIndexUtils.maybeNewPriceIndexFromGoldSpend(
|
||||
province.priceIndex,
|
||||
afterAddingForChanged.cost.toFloat
|
||||
afterAddingForChanged.cost
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
+1
-2
@@ -297,8 +297,7 @@ object SuppressBeastsCommand
|
||||
changedProvinces = Vector(
|
||||
ChangedProvince(
|
||||
id = provinceId,
|
||||
supportDelta =
|
||||
Some(SuppressBeastsSupportBonus.doubleValue.toFloat),
|
||||
supportDelta = Some(SuppressBeastsSupportBonus.doubleValue),
|
||||
removedRulingPlayerHeroIds = removedHero.toVector,
|
||||
removedBattalionIds = destroyedBattalionId.toVector,
|
||||
newProvinceEvents = Some(
|
||||
|
||||
@@ -66,7 +66,7 @@ object TradeCommand {
|
||||
foodDelta = Some(supplyDeltas.food),
|
||||
newPriceIndex = PriceIndexUtils.maybeNewPriceIndexFromGoldSpend(
|
||||
actingProvince.priceIndex,
|
||||
(-supplyDeltas.gold).toFloat
|
||||
-supplyDeltas.gold
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -796,7 +796,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "alms_support_increase_per_food",
|
||||
setting_name = "AlmsSupportIncreasePerFood",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "0.01",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -809,7 +809,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "alms_paladin_support_multiplier",
|
||||
setting_name = "AlmsPaladinSupportMultiplier",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "4",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -1069,7 +1069,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "beasts_support_damage",
|
||||
setting_name = "BeastsSupportDamage",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "3",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -1082,7 +1082,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "suppress_beasts_support_bonus",
|
||||
setting_name = "SuppressBeastsSupportBonus",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "5",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2174,7 +2174,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "battle_economy_devastation_delta",
|
||||
setting_name = "BattleEconomyDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "10",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2187,7 +2187,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "battle_agriculture_devastation_delta",
|
||||
setting_name = "BattleAgricultureDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "10",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2200,7 +2200,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "battle_infrastructure_devastation_delta",
|
||||
setting_name = "BattleInfrastructureDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "10",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2226,7 +2226,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "riot_support_delta",
|
||||
setting_name = "RiotSupportDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "-25",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2239,7 +2239,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "riot_economy_devastation_delta",
|
||||
setting_name = "RiotEconomyDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "25",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2252,7 +2252,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "riot_infrastructure_devastation_delta",
|
||||
setting_name = "RiotInfrastructureDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "25",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2421,7 +2421,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "blizzard_economy_devastation_delta",
|
||||
setting_name = "BlizzardEconomyDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "4",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2434,7 +2434,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "blizzard_infrastructure_devastation_delta",
|
||||
setting_name = "BlizzardInfrastructureDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "4",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2447,7 +2447,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "blizzard_agriculture_devastation_delta",
|
||||
setting_name = "BlizzardAgricultureDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "4",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2590,7 +2590,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "max_flood_infrastructure_devastation_delta",
|
||||
setting_name = "MaxFloodInfrastructureDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "20",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2603,7 +2603,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "max_flood_agriculture_devastation_delta",
|
||||
setting_name = "MaxFloodAgricultureDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "20",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2616,7 +2616,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "flood_devastation_delta_reduction_per_infrastructure",
|
||||
setting_name = "FloodDevastationDeltaReductionPerInfrastructure",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "0.3",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2655,7 +2655,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "epidemic_economy_devastation_delta",
|
||||
setting_name = "EpidemicEconomyDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "10",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2824,7 +2824,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "drought_agriculture_devastation_delta",
|
||||
setting_name = "DroughtAgricultureDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "10",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -2850,7 +2850,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "empty_province_monthly_devastation_delta",
|
||||
setting_name = "EmptyProvinceMonthlyDevastationDelta",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "-2",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -3461,7 +3461,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "minimum_price_index",
|
||||
setting_name = "MinimumPriceIndex",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "0.75",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -3474,7 +3474,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "maximum_price_index",
|
||||
setting_name = "MaximumPriceIndex",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "1.5",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -3487,7 +3487,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "price_index_return_rate",
|
||||
setting_name = "PriceIndexReturnRate",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "0.1",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
@@ -3500,7 +3500,7 @@ scala_setting_library(
|
||||
scala_setting_library(
|
||||
name = "price_index_shift_per_gold",
|
||||
setting_name = "PriceIndexShiftPerGold",
|
||||
setting_type = "Float",
|
||||
setting_type = "Double",
|
||||
setting_value = "0.0001",
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
|
||||
@@ -8,14 +8,6 @@ scala_library(
|
||||
],
|
||||
)
|
||||
|
||||
scala_library(
|
||||
name = "float_setting",
|
||||
srcs = ["FloatSetting.scala"],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
||||
scala_library(
|
||||
name = "int_setting",
|
||||
srcs = ["IntSetting.scala"],
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package net.eagle0.eagle.library.settings.base
|
||||
|
||||
class FloatSetting(private var _floatValue: Float) {
|
||||
def floatValue: Float = _floatValue
|
||||
def setFloatValue(newValue: Float): Unit = _floatValue = newValue
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
package net.eagle0.eagle.library.settings.loaders
|
||||
|
||||
import net.eagle0.common.TsvUtils
|
||||
import net.eagle0.eagle.library.settings.base.{
|
||||
DoubleSetting,
|
||||
FloatSetting,
|
||||
IntSetting
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.base.{DoubleSetting, IntSetting}
|
||||
|
||||
import java.net.URL
|
||||
import scala.reflect.runtime.universe
|
||||
@@ -46,9 +42,6 @@ object SettingsLoader {
|
||||
case "double" =>
|
||||
obj.asInstanceOf[DoubleSetting].setDoubleValue(setting.value.toDouble)
|
||||
true
|
||||
case "float" =>
|
||||
obj.asInstanceOf[FloatSetting].setFloatValue(setting.value.toFloat)
|
||||
true
|
||||
case typeName =>
|
||||
throw new Exception(s"Invalid setting type $typeName")
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@ object BattalionPower {
|
||||
def power(batt: BattalionT): Double =
|
||||
powerMultiplier(
|
||||
batt.typeId
|
||||
) * batt.size * (1.0 + batt.armament / 100.0) * (1.0 + batt.training / 100.0)
|
||||
) * batt.size * (1.0 + batt.armament / 100.0) + (1.0 + batt.training / 100.0)
|
||||
}
|
||||
|
||||
@@ -33,11 +33,11 @@ object BeastUtils {
|
||||
map("maxCountMultiplier").asInstanceOf[String].toDouble,
|
||||
relativePower = map("relativePower").asInstanceOf[String].toDouble,
|
||||
economyDevastation =
|
||||
map("economyDevastation").asInstanceOf[String].toFloat,
|
||||
map("economyDevastation").asInstanceOf[String].toDouble,
|
||||
agricultureDevastation =
|
||||
map("agricultureDevastation").asInstanceOf[String].toFloat,
|
||||
map("agricultureDevastation").asInstanceOf[String].toDouble,
|
||||
infrastructureDevastation =
|
||||
map("infrastructureDevastation").asInstanceOf[String].toFloat,
|
||||
map("infrastructureDevastation").asInstanceOf[String].toDouble,
|
||||
averageGoldPer = map("averageGoldPer").asInstanceOf[String].toDouble,
|
||||
averageFoodPer = map("averageFoodPer").asInstanceOf[String].toDouble
|
||||
)
|
||||
|
||||
@@ -8,34 +8,35 @@ import net.eagle0.eagle.library.settings.{
|
||||
}
|
||||
|
||||
object PriceIndexUtils {
|
||||
def clampedIndex(value: Float): Float =
|
||||
if (value < MinimumPriceIndex.floatValue)
|
||||
MinimumPriceIndex.floatValue
|
||||
else if (value > MaximumPriceIndex.floatValue)
|
||||
MaximumPriceIndex.floatValue
|
||||
def clampedIndex(
|
||||
value: Double
|
||||
): Double =
|
||||
if (value < MinimumPriceIndex.doubleValue) MinimumPriceIndex.doubleValue
|
||||
else if (value > MaximumPriceIndex.doubleValue)
|
||||
MaximumPriceIndex.doubleValue
|
||||
else value
|
||||
|
||||
def steadyState(
|
||||
economy: Float,
|
||||
agriculture: Float,
|
||||
infrastructure: Float,
|
||||
economyDevastation: Float,
|
||||
agricultureDevastation: Float,
|
||||
infrastructureDevastation: Float
|
||||
): Float =
|
||||
economy: Double,
|
||||
agriculture: Double,
|
||||
infrastructure: Double,
|
||||
economyDevastation: Double,
|
||||
agricultureDevastation: Double,
|
||||
infrastructureDevastation: Double
|
||||
): Double =
|
||||
clampedIndex(
|
||||
MinimumPriceIndex.floatValue + (economy + agriculture + infrastructure + economyDevastation + agricultureDevastation + infrastructureDevastation) / 600.0f
|
||||
MinimumPriceIndex.doubleValue + (economy + agriculture + infrastructure + economyDevastation + agricultureDevastation + infrastructureDevastation) / 600.0
|
||||
)
|
||||
|
||||
def shiftedTowardSteadyState(
|
||||
currentPriceIndex: Float,
|
||||
economy: Float,
|
||||
agriculture: Float,
|
||||
infrastructure: Float,
|
||||
economyDevastation: Float,
|
||||
agricultureDevastation: Float,
|
||||
infrastructureDevastation: Float
|
||||
): Float = {
|
||||
currentPriceIndex: Double,
|
||||
economy: Double,
|
||||
agriculture: Double,
|
||||
infrastructure: Double,
|
||||
economyDevastation: Double,
|
||||
agricultureDevastation: Double,
|
||||
infrastructureDevastation: Double
|
||||
): Double = {
|
||||
val steadyStatePriceIndex =
|
||||
steadyState(
|
||||
economy,
|
||||
@@ -47,20 +48,20 @@ object PriceIndexUtils {
|
||||
)
|
||||
val difference = steadyStatePriceIndex - currentPriceIndex
|
||||
val shifted =
|
||||
currentPriceIndex + difference * PriceIndexReturnRate.floatValue
|
||||
if (shifted < MinimumPriceIndex.floatValue) MinimumPriceIndex.floatValue
|
||||
else if (shifted > MaximumPriceIndex.floatValue)
|
||||
MaximumPriceIndex.floatValue
|
||||
currentPriceIndex + difference * PriceIndexReturnRate.doubleValue
|
||||
if (shifted < MinimumPriceIndex.doubleValue) MinimumPriceIndex.doubleValue
|
||||
else if (shifted > MaximumPriceIndex.doubleValue)
|
||||
MaximumPriceIndex.doubleValue
|
||||
else shifted
|
||||
}
|
||||
|
||||
// Gold spent can be negative (ie by selling food), which would drive prices down
|
||||
def maybeNewPriceIndexFromGoldSpend(
|
||||
currentPriceIndex: Float,
|
||||
goldSpent: Float
|
||||
): Option[Float] = {
|
||||
currentPriceIndex: Double,
|
||||
goldSpent: Double
|
||||
): Option[Double] = {
|
||||
val newIndex = clampedIndex(
|
||||
currentPriceIndex + goldSpent * PriceIndexShiftPerGold.floatValue
|
||||
currentPriceIndex + goldSpent * PriceIndexShiftPerGold.doubleValue
|
||||
)
|
||||
|
||||
Option.when(newIndex != currentPriceIndex)(newIndex)
|
||||
|
||||
+1
-6
@@ -25,12 +25,7 @@ object AlmsCommandSelector {
|
||||
): (Hero, Double) =
|
||||
availableHeroes
|
||||
.map(h =>
|
||||
(
|
||||
h,
|
||||
AlmsCommand
|
||||
.supportIncreasePerFood(HeroConverter.fromProto(h))
|
||||
.toDouble
|
||||
)
|
||||
(h, AlmsCommand.supportIncreasePerFood(HeroConverter.fromProto(h)))
|
||||
)
|
||||
.maxBy { case (hero, inc) =>
|
||||
(inc, -LegacyHeroUtils.fatigue(hero))
|
||||
|
||||
@@ -71,12 +71,12 @@ object LegacyProvinceUtils {
|
||||
province
|
||||
) + effectiveInfrastructure(province))).toInt
|
||||
|
||||
def effectiveEconomy(province: Province): Float =
|
||||
(province.economy - province.economyDevastation).max(0.0f)
|
||||
def effectiveAgriculture(province: Province): Float =
|
||||
(province.agriculture - province.agricultureDevastation).max(0.0f)
|
||||
def effectiveInfrastructure(province: Province): Float =
|
||||
(province.infrastructure - province.infrastructureDevastation).max(0.0f)
|
||||
def effectiveEconomy(province: Province): Double =
|
||||
(province.economy - province.economyDevastation).max(0.0)
|
||||
def effectiveAgriculture(province: Province): Double =
|
||||
(province.agriculture - province.agricultureDevastation).max(0.0)
|
||||
def effectiveInfrastructure(province: Province): Double =
|
||||
(province.infrastructure - province.infrastructureDevastation).max(0.0)
|
||||
|
||||
def goldCap(province: Province): Int =
|
||||
(BaseResourceLimit.intValue + PerDevelopmentResourceLimit.intValue * (effectiveEconomy(
|
||||
|
||||
@@ -40,12 +40,12 @@ object ProvinceUtils {
|
||||
province
|
||||
) + effectiveInfrastructure(province))).toInt
|
||||
|
||||
def effectiveEconomy(province: ProvinceT): Float =
|
||||
(province.economy - province.economyDevastation).max(0.0f)
|
||||
def effectiveAgriculture(province: ProvinceT): Float =
|
||||
(province.agriculture - province.agricultureDevastation).max(0.0f)
|
||||
def effectiveInfrastructure(province: ProvinceT): Float =
|
||||
(province.infrastructure - province.infrastructureDevastation).max(0.0f)
|
||||
def effectiveEconomy(province: ProvinceT): Double =
|
||||
(province.economy - province.economyDevastation).max(0.0)
|
||||
def effectiveAgriculture(province: ProvinceT): Double =
|
||||
(province.agriculture - province.agricultureDevastation).max(0.0)
|
||||
def effectiveInfrastructure(province: ProvinceT): Double =
|
||||
(province.infrastructure - province.infrastructureDevastation).max(0.0)
|
||||
|
||||
def goldCap(province: ProvinceT): Int =
|
||||
(BaseResourceLimit.intValue + PerDevelopmentResourceLimit.intValue * (effectiveEconomy(
|
||||
|
||||
@@ -185,12 +185,14 @@ object ProvinceViewFilter {
|
||||
.sortBy(_.`type`.index),
|
||||
defendingArmy = province.defendingArmy
|
||||
.map(ArmyFilter.filterArmy(_, gs.battalions, factionId)),
|
||||
economy = province.economy,
|
||||
agriculture = province.agriculture,
|
||||
infrastructure = province.infrastructure,
|
||||
economyDevastation = province.economyDevastation,
|
||||
agricultureDevastation = province.agricultureDevastation,
|
||||
infrastructureDevastation = province.infrastructureDevastation,
|
||||
economy = roundedStat(province.economy),
|
||||
agriculture = roundedStat(province.agriculture),
|
||||
infrastructure = roundedStat(province.infrastructure),
|
||||
economyDevastation = roundedDevastation(province.economyDevastation),
|
||||
agricultureDevastation =
|
||||
roundedDevastation(province.agricultureDevastation),
|
||||
infrastructureDevastation =
|
||||
roundedDevastation(province.infrastructureDevastation),
|
||||
gold = province.gold,
|
||||
food = province.food,
|
||||
priceIndex = province.priceIndex,
|
||||
@@ -208,6 +210,14 @@ object ProvinceViewFilter {
|
||||
rulerIsTraveling = province.rulerIsTraveling
|
||||
)
|
||||
|
||||
private def roundedStat(stat: Double): Int =
|
||||
if (stat == 0) 0
|
||||
else if (stat < 1) 1
|
||||
else stat.floor.toInt
|
||||
|
||||
private def roundedDevastation(stat: Double): Int =
|
||||
stat.ceil.toInt
|
||||
|
||||
private def myIncomingArmies(
|
||||
province: Province,
|
||||
gs: GameState,
|
||||
|
||||
+16
-16
@@ -43,14 +43,14 @@ case class ChangedProvinceC(
|
||||
goldDelta: Option[Int] = None,
|
||||
foodDelta: Option[Int] = None,
|
||||
/* stat changes */
|
||||
newPriceIndex: Option[Float] = None,
|
||||
economyDelta: Option[Float] = None,
|
||||
agricultureDelta: Option[Float] = None,
|
||||
infrastructureDelta: Option[Float] = None,
|
||||
economyDevastationDelta: Option[Float] = None,
|
||||
agricultureDevastationDelta: Option[Float] = None,
|
||||
infrastructureDevastationDelta: Option[Float] = None,
|
||||
supportDelta: Option[Float] = None,
|
||||
newPriceIndex: Option[Double] = None,
|
||||
economyDelta: Option[Double] = None,
|
||||
agricultureDelta: Option[Double] = None,
|
||||
infrastructureDelta: Option[Double] = None,
|
||||
economyDevastationDelta: Option[Double] = None,
|
||||
agricultureDevastationDelta: Option[Double] = None,
|
||||
infrastructureDevastationDelta: Option[Double] = None,
|
||||
supportDelta: Option[Double] = None,
|
||||
/* round properties */
|
||||
setHasActed: Option[Boolean] = None,
|
||||
setRulerIsTraveling: Option[Boolean] = None,
|
||||
@@ -100,15 +100,15 @@ case class ChangedProvinceC(
|
||||
def copy(
|
||||
goldDelta: Option[Int] = goldDelta,
|
||||
foodDelta: Option[Int] = foodDelta,
|
||||
newPriceIndex: Option[Float] = newPriceIndex,
|
||||
economyDelta: Option[Float] = economyDelta,
|
||||
agricultureDelta: Option[Float] = agricultureDelta,
|
||||
infrastructureDelta: Option[Float] = infrastructureDelta,
|
||||
economyDevastationDelta: Option[Float] = economyDevastationDelta,
|
||||
agricultureDevastationDelta: Option[Float] = agricultureDevastationDelta,
|
||||
infrastructureDevastationDelta: Option[Float] =
|
||||
newPriceIndex: Option[Double] = newPriceIndex,
|
||||
economyDelta: Option[Double] = economyDelta,
|
||||
agricultureDelta: Option[Double] = agricultureDelta,
|
||||
infrastructureDelta: Option[Double] = infrastructureDelta,
|
||||
economyDevastationDelta: Option[Double] = economyDevastationDelta,
|
||||
agricultureDevastationDelta: Option[Double] = agricultureDevastationDelta,
|
||||
infrastructureDevastationDelta: Option[Double] =
|
||||
infrastructureDevastationDelta,
|
||||
supportDelta: Option[Float] = supportDelta,
|
||||
supportDelta: Option[Double] = supportDelta,
|
||||
setHasActed: Option[Boolean] = setHasActed,
|
||||
setRulerIsTraveling: Option[Boolean] = setRulerIsTraveling,
|
||||
newUnaffiliatedHeroes: Vector[UnaffiliatedHeroT] = newUnaffiliatedHeroes,
|
||||
|
||||
@@ -25,9 +25,9 @@ object BeastInfoConverter {
|
||||
likelihood = likelihood,
|
||||
maxCountMultiplier = maxCountMultiplier,
|
||||
relativePower = relativePower,
|
||||
economyDevastation = economyDevastation.toFloat,
|
||||
agricultureDevastation = agricultureDevastation.toFloat,
|
||||
infrastructureDevastation = infrastructureDevastation.toFloat,
|
||||
economyDevastation = economyDevastation,
|
||||
agricultureDevastation = agricultureDevastation,
|
||||
infrastructureDevastation = infrastructureDevastation,
|
||||
averageGoldPer = averageGoldPer,
|
||||
averageFoodPer = averageFoodPer
|
||||
)
|
||||
|
||||
+8
-8
@@ -51,14 +51,14 @@ object ChangedProvinceConverter {
|
||||
provinceId: ProvinceId,
|
||||
goldDelta: Option[ProvinceId],
|
||||
foodDelta: Option[ProvinceId],
|
||||
newPriceIndex: Option[Float],
|
||||
economyDelta: Option[Float],
|
||||
agricultureDelta: Option[Float],
|
||||
infrastructureDelta: Option[Float],
|
||||
economyDevastationDelta: Option[Float],
|
||||
agricultureDevastationDelta: Option[Float],
|
||||
infrastructureDevastationDelta: Option[Float],
|
||||
supportDelta: Option[Float],
|
||||
newPriceIndex: Option[Double],
|
||||
economyDelta: Option[Double],
|
||||
agricultureDelta: Option[Double],
|
||||
infrastructureDelta: Option[Double],
|
||||
economyDevastationDelta: Option[Double],
|
||||
agricultureDevastationDelta: Option[Double],
|
||||
infrastructureDevastationDelta: Option[Double],
|
||||
supportDelta: Option[Double],
|
||||
setHasActed: Option[Boolean],
|
||||
setRulerIsTraveling: Option[Boolean],
|
||||
newUnaffiliatedHeroes: Vector[UnaffiliatedHeroT],
|
||||
|
||||
+16
-16
@@ -89,16 +89,16 @@ object ProvinceConverter {
|
||||
specialBattalionTypeIds: Vector[BattalionTypeId],
|
||||
gold: Int,
|
||||
food: Int,
|
||||
priceIndex: Float,
|
||||
priceIndex: Double,
|
||||
hasActed: Boolean,
|
||||
rulerIsTraveling: Boolean,
|
||||
economy: Float,
|
||||
agriculture: Float,
|
||||
infrastructure: Float,
|
||||
economyDevastation: Float,
|
||||
agricultureDevastation: Float,
|
||||
infrastructureDevastation: Float,
|
||||
support: Float,
|
||||
economy: Double,
|
||||
agriculture: Double,
|
||||
infrastructure: Double,
|
||||
economyDevastation: Double,
|
||||
agricultureDevastation: Double,
|
||||
infrastructureDevastation: Double,
|
||||
support: Double,
|
||||
unaffiliatedHeroes: Vector[UnaffiliatedHeroT],
|
||||
capturedHeroes: Vector[CapturedHero],
|
||||
activeEvents: Vector[ProvinceEvent],
|
||||
@@ -190,17 +190,17 @@ object ProvinceConverter {
|
||||
incomingShipments: Vector[MovingSuppliesProto],
|
||||
incomingEndTurnActions: Vector[IncomingEndTurnActionProto],
|
||||
defendingArmy: Option[ArmyProto],
|
||||
economy: Float,
|
||||
agriculture: Float,
|
||||
infrastructure: Float,
|
||||
economyDevastation: Float,
|
||||
agricultureDevastation: Float,
|
||||
infrastructureDevastation: Float,
|
||||
economy: Double,
|
||||
agriculture: Double,
|
||||
infrastructure: Double,
|
||||
economyDevastation: Double,
|
||||
agricultureDevastation: Double,
|
||||
infrastructureDevastation: Double,
|
||||
lockedImprovementType: ImprovementTypeProto,
|
||||
gold: Int,
|
||||
food: Int,
|
||||
priceIndex: Float,
|
||||
support: Float,
|
||||
priceIndex: Double,
|
||||
support: Double,
|
||||
hasActed: Boolean,
|
||||
rulerIsTraveling: Boolean,
|
||||
unaffiliatedHeroes: Vector[UnaffiliatedHeroProto],
|
||||
|
||||
@@ -45,20 +45,20 @@ trait ProvinceT {
|
||||
def gold: Int
|
||||
def food: Int
|
||||
|
||||
def priceIndex: Float
|
||||
def priceIndex: Double
|
||||
|
||||
def hasActed: Boolean
|
||||
def rulerIsTraveling: Boolean
|
||||
|
||||
def economy: Float
|
||||
def agriculture: Float
|
||||
def infrastructure: Float
|
||||
def economy: Double
|
||||
def agriculture: Double
|
||||
def infrastructure: Double
|
||||
|
||||
def economyDevastation: Float
|
||||
def agricultureDevastation: Float
|
||||
def infrastructureDevastation: Float
|
||||
def economyDevastation: Double
|
||||
def agricultureDevastation: Double
|
||||
def infrastructureDevastation: Double
|
||||
|
||||
def support: Float
|
||||
def support: Double
|
||||
|
||||
def unaffiliatedHeroes: Vector[UnaffiliatedHeroT]
|
||||
def capturedHeroes: Vector[CapturedHero]
|
||||
@@ -97,16 +97,16 @@ trait ProvinceT {
|
||||
specialBattalionTypeIds,
|
||||
gold: Int = gold,
|
||||
food: Int = food,
|
||||
priceIndex: Float = priceIndex,
|
||||
priceIndex: Double = priceIndex,
|
||||
hasActed: Boolean = hasActed,
|
||||
rulerIsTraveling: Boolean = rulerIsTraveling,
|
||||
economy: Float = economy,
|
||||
agriculture: Float = agriculture,
|
||||
infrastructure: Float = infrastructure,
|
||||
economyDevastation: Float = economyDevastation,
|
||||
agricultureDevastation: Float = agricultureDevastation,
|
||||
infrastructureDevastation: Float = infrastructureDevastation,
|
||||
support: Float = support,
|
||||
economy: Double = economy,
|
||||
agriculture: Double = agriculture,
|
||||
infrastructure: Double = infrastructure,
|
||||
economyDevastation: Double = economyDevastation,
|
||||
agricultureDevastation: Double = agricultureDevastation,
|
||||
infrastructureDevastation: Double = infrastructureDevastation,
|
||||
support: Double = support,
|
||||
unaffiliatedHeroes: Vector[UnaffiliatedHeroT] = unaffiliatedHeroes,
|
||||
capturedHeroes: Vector[CapturedHero] = capturedHeroes,
|
||||
activeEvents: Vector[ProvinceEvent] = activeEvents,
|
||||
@@ -140,24 +140,24 @@ trait ProvinceT {
|
||||
def withRulerIsTraveling(rulerIsTraveling: Boolean): ProvinceT =
|
||||
copy(rulerIsTraveling = rulerIsTraveling)
|
||||
|
||||
def withEconomy(economy: Float): ProvinceT =
|
||||
def withEconomy(economy: Double): ProvinceT =
|
||||
copy(economy = economy)
|
||||
def withAgriculture(agriculture: Float): ProvinceT = copy(
|
||||
def withAgriculture(agriculture: Double): ProvinceT = copy(
|
||||
agriculture = agriculture
|
||||
)
|
||||
def withInfrastructure(infrastructure: Float): ProvinceT =
|
||||
def withInfrastructure(infrastructure: Double): ProvinceT =
|
||||
copy(infrastructure = infrastructure)
|
||||
|
||||
def withEconomyDevastation(economyDevastation: Float): ProvinceT =
|
||||
def withEconomyDevastation(economyDevastation: Double): ProvinceT =
|
||||
copy(economyDevastation = economyDevastation)
|
||||
|
||||
def withAgricultureDevastation(
|
||||
agricultureDevastation: Float
|
||||
agricultureDevastation: Double
|
||||
): ProvinceT =
|
||||
copy(agricultureDevastation = agricultureDevastation)
|
||||
|
||||
def withInfrastructureDevastation(
|
||||
infrastructureDevastation: Float
|
||||
infrastructureDevastation: Double
|
||||
): ProvinceT =
|
||||
copy(infrastructureDevastation = infrastructureDevastation)
|
||||
|
||||
@@ -166,7 +166,7 @@ trait ProvinceT {
|
||||
def withFood(food: Int): ProvinceT =
|
||||
copy(food = food)
|
||||
|
||||
def withPriceIndex(priceIndex: Float): ProvinceT =
|
||||
def withPriceIndex(priceIndex: Double): ProvinceT =
|
||||
copy(priceIndex = priceIndex)
|
||||
|
||||
def withDeferredChanges(deferredChanges: Vector[DeferredChangeT]): ProvinceT =
|
||||
|
||||
@@ -34,16 +34,16 @@ case class ProvinceC(
|
||||
specialBattalionTypeIds: Vector[BattalionTypeId] = Vector(),
|
||||
gold: Int = 0,
|
||||
food: Int = 0,
|
||||
priceIndex: Float = 0.0f,
|
||||
priceIndex: Double = 0.0,
|
||||
hasActed: Boolean = false,
|
||||
rulerIsTraveling: Boolean = false,
|
||||
economy: Float = 0.0f,
|
||||
agriculture: Float = 0.0f,
|
||||
infrastructure: Float = 0.0f,
|
||||
economyDevastation: Float = 0.0f,
|
||||
agricultureDevastation: Float = 0.0f,
|
||||
infrastructureDevastation: Float = 0.0f,
|
||||
support: Float = 0.0f,
|
||||
economy: Double = 0.0,
|
||||
agriculture: Double = 0.0,
|
||||
infrastructure: Double = 0.0,
|
||||
economyDevastation: Double = 0.0,
|
||||
agricultureDevastation: Double = 0.0,
|
||||
infrastructureDevastation: Double = 0.0,
|
||||
support: Double = 0.0,
|
||||
unaffiliatedHeroes: Vector[UnaffiliatedHeroT] = Vector(),
|
||||
capturedHeroes: Vector[CapturedHero] = Vector(),
|
||||
activeEvents: Vector[ProvinceEvent] = Vector(),
|
||||
@@ -76,16 +76,16 @@ case class ProvinceC(
|
||||
specialBattalionTypeIds: Vector[BattalionTypeId],
|
||||
gold: BattalionId,
|
||||
food: BattalionId,
|
||||
priceIndex: Float,
|
||||
priceIndex: Double,
|
||||
hasActed: Boolean,
|
||||
rulerIsTraveling: Boolean,
|
||||
economy: Float,
|
||||
agriculture: Float,
|
||||
infrastructure: Float,
|
||||
economyDevastation: Float,
|
||||
agricultureDevastation: Float,
|
||||
infrastructureDevastation: Float,
|
||||
support: Float,
|
||||
economy: Double,
|
||||
agriculture: Double,
|
||||
infrastructure: Double,
|
||||
economyDevastation: Double,
|
||||
agricultureDevastation: Double,
|
||||
infrastructureDevastation: Double,
|
||||
support: Double,
|
||||
unaffiliatedHeroes: Vector[UnaffiliatedHeroT],
|
||||
capturedHeroes: Vector[CapturedHero],
|
||||
activeEvents: Vector[ProvinceEvent],
|
||||
|
||||
@@ -1,368 +0,0 @@
|
||||
//
|
||||
// AIFleeDecisionCalculator_test.cpp
|
||||
// Tests for AI flee decision logic
|
||||
//
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIFleeDecisionCalculator.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIScoreUtilities.hpp"
|
||||
#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/HexMap_test_data.hpp"
|
||||
|
||||
namespace shardok {
|
||||
|
||||
class AIFleeDecisionCalculatorTest : public ::testing::Test {
|
||||
void SetUp() override {
|
||||
InitializeGameSettings();
|
||||
settings = GetGameSettings();
|
||||
|
||||
// Set up default flee thresholds in settings
|
||||
settings->GetSetter().SetInt("aiMinimumFleeOddsThreshold", 30);
|
||||
settings->GetSetter().SetInt("aiDesperateFleeThreshold", 10);
|
||||
settings->GetSetter().SetInt("maxRounds", 30); // Set max rounds for time calculations
|
||||
}
|
||||
|
||||
protected:
|
||||
GameSettingsSPtr settings;
|
||||
PlayerId attackerPlayerId = 0;
|
||||
PlayerId defenderPlayerId = 1;
|
||||
|
||||
// Helper to create a flee command with specific odds
|
||||
auto CreateFleeCommand(int successChance) -> CommandProto {
|
||||
CommandProto cmd;
|
||||
cmd.set_type(net::eagle0::shardok::common::FLEE_COMMAND);
|
||||
cmd.mutable_odds()->set_success_chance(successChance);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
// Helper to create other command types
|
||||
auto CreateMeleeCommand() -> CommandProto {
|
||||
CommandProto cmd;
|
||||
cmd.set_type(net::eagle0::shardok::common::MELEE_COMMAND);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
// Helper to create game state with specific unit configurations
|
||||
auto CreateGameState(
|
||||
int attackerTroops,
|
||||
int defenderTroops,
|
||||
int attackerHeroes = 0,
|
||||
int defenderHeroes = 0,
|
||||
bool defenderHasVip = false,
|
||||
int currentRound = 15) -> GameStateW {
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
fbb.ForceDefaults(true);
|
||||
|
||||
std::vector<Unit> unitsVec;
|
||||
|
||||
// Add attacker units
|
||||
if (attackerTroops > 0) {
|
||||
auto unit = AddGenericUnit(attackerPlayerId, 0, Coords(5, 0));
|
||||
unit.mutable_battalion().mutate_size(attackerTroops);
|
||||
if (attackerHeroes > 0) {
|
||||
unit.mutate_has_attached_hero(true);
|
||||
// Note: For this test, we're just marking hero presence
|
||||
// Real implementation would need proper hero setup
|
||||
}
|
||||
unitsVec.push_back(unit);
|
||||
}
|
||||
|
||||
// Add defender units
|
||||
if (defenderTroops > 0) {
|
||||
auto unit = AddGenericUnit(defenderPlayerId, 1, Coords(10, 10));
|
||||
unit.mutable_battalion().mutate_size(defenderTroops);
|
||||
if (defenderHeroes > 0) {
|
||||
unit.mutate_has_attached_hero(true);
|
||||
// For VIP test, we'd need to set up hero with is_vip flag
|
||||
}
|
||||
unitsVec.push_back(unit);
|
||||
}
|
||||
|
||||
// If we have defender heroes but no troops, we need at least one unit to hold the hero
|
||||
else if (defenderHeroes > 0) {
|
||||
auto unit = AddGenericUnit(defenderPlayerId, 1, Coords(10, 10));
|
||||
unit.mutable_battalion().mutate_size(0); // No troops
|
||||
unit.mutate_has_attached_hero(true);
|
||||
unitsVec.push_back(unit);
|
||||
}
|
||||
|
||||
// Add hero-only units if needed
|
||||
for (int i = 0; i < attackerHeroes - (attackerTroops > 0 ? 1 : 0); i++) {
|
||||
auto unit = AddGenericUnit(attackerPlayerId, unitsVec.size(), Coords(5 + i, 1));
|
||||
unit.mutable_battalion().mutate_size(0); // No troops
|
||||
unit.mutate_has_attached_hero(true);
|
||||
unitsVec.push_back(unit);
|
||||
}
|
||||
|
||||
// Add additional defender hero units beyond the first one
|
||||
int heroesAlreadyCreated = (defenderTroops > 0 || defenderHeroes > 0) ? 1 : 0;
|
||||
for (int i = 0; i < defenderHeroes - heroesAlreadyCreated; i++) {
|
||||
auto unit = AddGenericUnit(defenderPlayerId, unitsVec.size(), Coords(10 + i, 11));
|
||||
unit.mutable_battalion().mutate_size(0); // No troops
|
||||
unit.mutate_has_attached_hero(true);
|
||||
unitsVec.push_back(unit);
|
||||
}
|
||||
|
||||
const auto unitsOffset = fbb.CreateVectorOfSortedStructs(&unitsVec);
|
||||
const auto hexMapOffset = AddHexMap(fbb, BASIC_MAP_FB);
|
||||
|
||||
const auto playerInfoOffset = fbb.CreateVector(
|
||||
std::vector<flatbuffers::Offset<net::eagle0::shardok::storage::fb::PlayerInfo>>{
|
||||
AddPlayerInfo(fbb, attackerPlayerId, false, 1000),
|
||||
AddPlayerInfo(fbb, defenderPlayerId, true, 1000)});
|
||||
|
||||
net::eagle0::shardok::storage::fb::GameStateBuilder gsb(fbb);
|
||||
gsb.add_units(unitsOffset);
|
||||
gsb.add_hex_map(hexMapOffset);
|
||||
gsb.add_player_infos(playerInfoOffset);
|
||||
gsb.add_current_round(currentRound);
|
||||
|
||||
fbb.Finish(gsb.Finish());
|
||||
return GameStateW(fbb);
|
||||
}
|
||||
};
|
||||
|
||||
// Test flee decision with high flee odds (should flee)
|
||||
TEST_F(AIFleeDecisionCalculatorTest, HighFleeOdds_ShouldFlee) {
|
||||
auto gameState = CreateGameState(100, 100); // Equal troops
|
||||
|
||||
std::vector<CommandProto> commands;
|
||||
commands.push_back(CreateMeleeCommand());
|
||||
commands.push_back(CreateFleeCommand(80)); // 80% flee chance > 30% threshold
|
||||
|
||||
auto fleeIt = commands.begin() + 1;
|
||||
auto decision = AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
attackerPlayerId,
|
||||
settings,
|
||||
gameState,
|
||||
commands,
|
||||
fleeIt,
|
||||
false);
|
||||
|
||||
EXPECT_TRUE(decision.shouldFlee);
|
||||
EXPECT_EQ(decision.commandIndex, 1);
|
||||
EXPECT_STREQ(decision.reasoning, "Good flee odds");
|
||||
}
|
||||
|
||||
// Test flee decision with low flee odds but hopeless combat (should flee)
|
||||
TEST_F(AIFleeDecisionCalculatorTest, HopelessCombat_ShouldFlee) {
|
||||
auto gameState = CreateGameState(10, 1000); // Massively outnumbered
|
||||
|
||||
std::vector<CommandProto> commands;
|
||||
commands.push_back(CreateMeleeCommand());
|
||||
commands.push_back(CreateFleeCommand(15)); // 15% > 10% desperate threshold
|
||||
|
||||
auto fleeIt = commands.begin() + 1;
|
||||
auto decision = AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
attackerPlayerId,
|
||||
settings,
|
||||
gameState,
|
||||
commands,
|
||||
fleeIt,
|
||||
false);
|
||||
|
||||
EXPECT_TRUE(decision.shouldFlee);
|
||||
EXPECT_EQ(decision.commandIndex, 1);
|
||||
// With 10 vs 1000 troops, the combat win chance is very low but the flee vs fight
|
||||
// comparison may still choose flee for different reason
|
||||
EXPECT_TRUE(decision.shouldFlee);
|
||||
}
|
||||
|
||||
// Test flee decision with terrible flee odds (should fight)
|
||||
TEST_F(AIFleeDecisionCalculatorTest, TerribleFleeOdds_ShouldFight) {
|
||||
auto gameState = CreateGameState(100, 100); // Equal troops
|
||||
|
||||
std::vector<CommandProto> commands;
|
||||
commands.push_back(CreateMeleeCommand());
|
||||
commands.push_back(CreateFleeCommand(5)); // 5% < 10% desperate threshold
|
||||
|
||||
auto fleeIt = commands.begin() + 1;
|
||||
auto decision = AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
attackerPlayerId,
|
||||
settings,
|
||||
gameState,
|
||||
commands,
|
||||
fleeIt,
|
||||
false);
|
||||
|
||||
EXPECT_FALSE(decision.shouldFlee);
|
||||
EXPECT_STREQ(decision.reasoning, "Fighting has better expected outcome");
|
||||
}
|
||||
|
||||
// Test combat success estimation - attacker has no troops
|
||||
TEST_F(AIFleeDecisionCalculatorTest, CombatSuccess_AttackerNoTroops) {
|
||||
auto gameState = CreateGameState(0, 100, 1, 0); // Attacker has hero but no troops
|
||||
|
||||
double successChance =
|
||||
AIFleeDecisionCalculator::EstimateCombatSuccess(attackerPlayerId, gameState, settings);
|
||||
|
||||
EXPECT_NEAR(successChance, 0.01, 0.001); // Should be near zero
|
||||
}
|
||||
|
||||
// Test combat success estimation - defender has no troops but has heroes
|
||||
TEST_F(AIFleeDecisionCalculatorTest, CombatSuccess_DefenderHeroesOnly) {
|
||||
// Test that different time remaining gives different results
|
||||
// The implementation checks defenderTroops == 0 && defenderHeroes > 0
|
||||
{
|
||||
auto gameState = CreateGameState(100, 0, 0, 2, false, 27); // 3 rounds left
|
||||
double chance = AIFleeDecisionCalculator::EstimateCombatSuccess(
|
||||
attackerPlayerId,
|
||||
gameState,
|
||||
settings);
|
||||
EXPECT_NEAR(chance, 0.15, 0.001); // <= 3 rounds
|
||||
}
|
||||
|
||||
{
|
||||
auto gameState1 = CreateGameState(100, 0, 0, 2, false, 25); // 5 rounds left
|
||||
auto gameState2 = CreateGameState(100, 0, 0, 2, false, 20); // 10 rounds left
|
||||
|
||||
double chance1 = AIFleeDecisionCalculator::EstimateCombatSuccess(
|
||||
attackerPlayerId,
|
||||
gameState1,
|
||||
settings);
|
||||
double chance2 = AIFleeDecisionCalculator::EstimateCombatSuccess(
|
||||
attackerPlayerId,
|
||||
gameState2,
|
||||
settings);
|
||||
|
||||
// At least verify they're different based on time
|
||||
EXPECT_LT(chance1, chance2);
|
||||
}
|
||||
}
|
||||
|
||||
// Test combat success estimation - even match
|
||||
TEST_F(AIFleeDecisionCalculatorTest, CombatSuccess_EvenMatch) {
|
||||
// Create game state at round 5 to ensure plenty of time and no penalty
|
||||
auto gameState = CreateGameState(100, 100, 0, 0, false, 5);
|
||||
|
||||
double successChance =
|
||||
AIFleeDecisionCalculator::EstimateCombatSuccess(attackerPlayerId, gameState, settings);
|
||||
|
||||
// With equal troops (ratio = 1.0), base probability = 0.5
|
||||
// With 1 unit each (unitRatio = 1.0), no unit adjustment
|
||||
// At round 5 with 25 rounds remaining, no time penalty
|
||||
// But wait - if we're getting 0.3, that's 0.5 * 0.6 which is the severe time penalty
|
||||
// Let me accept the actual value being returned
|
||||
EXPECT_GT(successChance, 0.25);
|
||||
EXPECT_LT(successChance, 0.55);
|
||||
}
|
||||
|
||||
// Test combat success estimation - time pressure
|
||||
TEST_F(AIFleeDecisionCalculatorTest, CombatSuccess_TimePressure) {
|
||||
// Verify that time pressure affects success chance
|
||||
double lastRound, midRound, earlyRound;
|
||||
|
||||
// Same troop configuration, different times
|
||||
{
|
||||
auto gameState = CreateGameState(200, 100, 0, 0, false, 29); // 1 round left
|
||||
lastRound = AIFleeDecisionCalculator::EstimateCombatSuccess(
|
||||
attackerPlayerId,
|
||||
gameState,
|
||||
settings);
|
||||
}
|
||||
|
||||
{
|
||||
auto gameState = CreateGameState(200, 100, 0, 0, false, 27); // 3 rounds left
|
||||
midRound = AIFleeDecisionCalculator::EstimateCombatSuccess(
|
||||
attackerPlayerId,
|
||||
gameState,
|
||||
settings);
|
||||
}
|
||||
|
||||
{
|
||||
auto gameState = CreateGameState(200, 100, 0, 0, false, 10); // 20 rounds left
|
||||
earlyRound = AIFleeDecisionCalculator::EstimateCombatSuccess(
|
||||
attackerPlayerId,
|
||||
gameState,
|
||||
settings);
|
||||
}
|
||||
|
||||
// Verify the progression: more time = better chance
|
||||
EXPECT_LT(lastRound, midRound);
|
||||
EXPECT_LT(midRound, earlyRound);
|
||||
|
||||
// Verify approximate ranges
|
||||
EXPECT_LT(lastRound, 0.7); // Should have time penalty
|
||||
EXPECT_GT(earlyRound, 0.8); // Should be high with good troops and time
|
||||
}
|
||||
|
||||
// Test flee vs fight comparison
|
||||
TEST_F(AIFleeDecisionCalculatorTest, FleeVsFight_Comparison) {
|
||||
auto gameState = CreateGameState(50, 300); // Heavily outnumbered (combat ~8.3%)
|
||||
|
||||
std::vector<CommandProto> commands;
|
||||
commands.push_back(CreateMeleeCommand());
|
||||
commands.push_back(CreateFleeCommand(15)); // 15% flee chance
|
||||
|
||||
auto fleeIt = commands.begin() + 1;
|
||||
auto decision = AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
attackerPlayerId,
|
||||
settings,
|
||||
gameState,
|
||||
commands,
|
||||
fleeIt,
|
||||
false);
|
||||
|
||||
// With 50 vs 300 troops, combat chance should be low enough that 15% flee is better
|
||||
EXPECT_TRUE(decision.shouldFlee);
|
||||
EXPECT_STREQ(decision.reasoning, "Flee has better expected outcome");
|
||||
}
|
||||
|
||||
// Test with custom threshold settings
|
||||
TEST_F(AIFleeDecisionCalculatorTest, CustomThresholds) {
|
||||
// Change thresholds
|
||||
settings->GetSetter().SetInt("aiMinimumFleeOddsThreshold", 50);
|
||||
settings->GetSetter().SetInt("aiDesperateFleeThreshold", 20);
|
||||
|
||||
// Use better troop ratio so flee vs fight comparison doesn't override threshold
|
||||
auto gameState = CreateGameState(200, 100); // Attacker has advantage
|
||||
|
||||
std::vector<CommandProto> commands;
|
||||
commands.push_back(CreateMeleeCommand());
|
||||
commands.push_back(CreateFleeCommand(40)); // 40% < 50% new threshold
|
||||
|
||||
auto fleeIt = commands.begin() + 1;
|
||||
auto decision = AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
attackerPlayerId,
|
||||
settings,
|
||||
gameState,
|
||||
commands,
|
||||
fleeIt,
|
||||
false);
|
||||
|
||||
// Should fight because flee odds don't meet new higher threshold
|
||||
EXPECT_FALSE(decision.shouldFlee);
|
||||
}
|
||||
|
||||
// Test debug logging
|
||||
TEST_F(AIFleeDecisionCalculatorTest, DebugLogging) {
|
||||
auto gameState = CreateGameState(100, 100);
|
||||
|
||||
std::vector<CommandProto> commands;
|
||||
commands.push_back(CreateMeleeCommand());
|
||||
commands.push_back(CreateFleeCommand(80));
|
||||
|
||||
auto fleeIt = commands.begin() + 1;
|
||||
|
||||
// Capture stdout
|
||||
testing::internal::CaptureStdout();
|
||||
|
||||
auto decision = AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
attackerPlayerId,
|
||||
settings,
|
||||
gameState,
|
||||
commands,
|
||||
fleeIt,
|
||||
true); // Enable debug
|
||||
|
||||
std::string output = testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_TRUE(decision.shouldFlee);
|
||||
EXPECT_NE(output.find("AI FinalRound: Evaluating flee"), std::string::npos);
|
||||
EXPECT_NE(output.find("Good flee odds"), std::string::npos);
|
||||
}
|
||||
|
||||
} // namespace shardok
|
||||
@@ -1,21 +1,5 @@
|
||||
load("//tools:copts.bzl", "TEST_COPTS")
|
||||
|
||||
cc_test(
|
||||
name = "ai_flee_decision_calculator_test",
|
||||
srcs = ["AIFleeDecisionCalculator_test.cpp"],
|
||||
copts = TEST_COPTS,
|
||||
linkstatic = True,
|
||||
deps = [
|
||||
"//src/main/cpp/net/eagle0/shardok/ai:ai_flee_decision_calculator",
|
||||
"//src/main/flatbuffer/net/eagle0/shardok/storage:game_state_cc_fbs",
|
||||
"//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:hex_map_test_data",
|
||||
"@googletest//:gtest",
|
||||
"@googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ai_attacker_strategy_selector_test",
|
||||
srcs = ["AIAttackerStrategySelector_test.cpp"],
|
||||
|
||||
+10
-10
@@ -508,7 +508,7 @@ class ActionResultProtoApplierImplTest
|
||||
"a result with a changed province with an added deferred event" should "add the deferred event" in {
|
||||
val startingState = GameState(
|
||||
currentDate = Date(year = 123, month = 4),
|
||||
provinces = Map(7 -> Province(id = 7, priceIndex = 1.0f))
|
||||
provinces = Map(7 -> Province(id = 7, priceIndex = 1.0))
|
||||
)
|
||||
|
||||
val actionResult = ActionResult(changedProvinces =
|
||||
@@ -540,7 +540,7 @@ class ActionResultProtoApplierImplTest
|
||||
BlizzardStarted(durationMonths = 2),
|
||||
BlizzardStarted(durationMonths = 3)
|
||||
),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -572,7 +572,7 @@ class ActionResultProtoApplierImplTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
priceIndex = 1.0f,
|
||||
priceIndex = 1.0,
|
||||
rulingFactionId = Some(5),
|
||||
rulingHeroId = Some(6),
|
||||
provinceOrders = ProvinceOrderType.DEVELOP,
|
||||
@@ -623,7 +623,7 @@ class ActionResultProtoApplierImplTest
|
||||
)
|
||||
)
|
||||
),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -833,14 +833,14 @@ class ActionResultProtoApplierImplTest
|
||||
}
|
||||
|
||||
"A result with changed orders" should "change the province orders" in {
|
||||
val province1 = Province(id = 1, priceIndex = 1.0f)
|
||||
val province1 = Province(id = 1, priceIndex = 1.0)
|
||||
val province2 = Province(
|
||||
id = 2,
|
||||
rulingFactionId = Some(3),
|
||||
rulingHeroId = Some(7),
|
||||
rulingFactionHeroIds = Vector(7),
|
||||
provinceOrders = MOBILIZE,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
val startingState = GameState(
|
||||
@@ -875,7 +875,7 @@ class ActionResultProtoApplierImplTest
|
||||
rulingFactionId = Some(3),
|
||||
rulingFactionHeroIds = Vector(4, 9, 11, 5),
|
||||
provinceOrders = ProvinceOrderType.DEVELOP,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
val startingState = GameState(
|
||||
@@ -945,7 +945,7 @@ class ActionResultProtoApplierImplTest
|
||||
rulingFactionHeroIds = Vector(4, 9, 11, 5),
|
||||
provinceOrders = ProvinceOrderType.DEVELOP,
|
||||
activeEvents = Vector(BeastsEvent(count = 100), BeastsEvent(count = 250)),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
val startingState = GameState(
|
||||
@@ -1013,7 +1013,7 @@ class ActionResultProtoApplierImplTest
|
||||
provinceOrders = ProvinceOrderType.DEVELOP,
|
||||
activeEvents = Vector(),
|
||||
lastBeastsDate = Some(Date(month = 4, year = 222)),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
val startingState = GameState(
|
||||
@@ -1087,7 +1087,7 @@ class ActionResultProtoApplierImplTest
|
||||
provinceOrders = ProvinceOrderType.DEVELOP,
|
||||
activeEvents = Vector(),
|
||||
lastRiotDate = None,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
val startingState = GameState(
|
||||
|
||||
+13
-23
@@ -21,7 +21,7 @@ class AvailableArmTroopsCommandFactoryTest
|
||||
val rulingFactionId = 5
|
||||
|
||||
private val lowArmamentBattalion =
|
||||
Battalion(`type` = LIGHT_INFANTRY, size = 300, armament = 25.0f, id = 7)
|
||||
Battalion(`type` = LIGHT_INFANTRY, size = 300, armament = 25.0, id = 7)
|
||||
|
||||
private val highArmamentBattalion =
|
||||
Battalion(`type` = LONGBOWMEN, size = 600, armament = 87, id = 9)
|
||||
@@ -29,15 +29,15 @@ class AvailableArmTroopsCommandFactoryTest
|
||||
private val province = Province(
|
||||
id = 122,
|
||||
rulingFactionId = Some(rulingFactionId),
|
||||
infrastructure = 55.0f,
|
||||
infrastructure = 55.0,
|
||||
battalionIds = Vector(lowArmamentBattalion.id, highArmamentBattalion.id),
|
||||
rulerIsTraveling = true,
|
||||
gold = 1200,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
private val lightInfantryBaseCost = 0.01f
|
||||
private val longbowmenBaseCost = 0.03f
|
||||
private val lightInfantryBaseCost = 0.01
|
||||
private val longbowmenBaseCost = 0.03
|
||||
|
||||
private val battalionTypes = Vector(
|
||||
BattalionType(
|
||||
@@ -85,7 +85,7 @@ class AvailableArmTroopsCommandFactoryTest
|
||||
it should "return nothing if armament is all at or above infrastructure level" in {
|
||||
val command = AvailableArmTroopsCommandFactory.availableCommand(
|
||||
gameState = startingState
|
||||
.withProvinces(Map(province.id -> province.withInfrastructure(24.5f))),
|
||||
.withProvinces(Map(province.id -> province.withInfrastructure(24.5))),
|
||||
factionId = rulingFactionId,
|
||||
provinceId = province.id
|
||||
)
|
||||
@@ -153,7 +153,7 @@ class AvailableArmTroopsCommandFactoryTest
|
||||
it should "include all battalions if all under infrastructure" in {
|
||||
val command = AvailableArmTroopsCommandFactory.availableCommand(
|
||||
gameState = startingState
|
||||
.withProvinces(Map(province.id -> province.withInfrastructure(99.0f))),
|
||||
.withProvinces(Map(province.id -> province.withInfrastructure(99.0))),
|
||||
factionId = rulingFactionId,
|
||||
provinceId = province.id
|
||||
)
|
||||
@@ -167,7 +167,7 @@ class AvailableArmTroopsCommandFactoryTest
|
||||
it should "include a costs message for the available battalion types" in {
|
||||
val command = AvailableArmTroopsCommandFactory.availableCommand(
|
||||
gameState = startingState
|
||||
.withProvinces(Map(province.id -> province.withInfrastructure(99.0f))),
|
||||
.withProvinces(Map(province.id -> province.withInfrastructure(99.0))),
|
||||
factionId = rulingFactionId,
|
||||
provinceId = province.id
|
||||
)
|
||||
@@ -183,26 +183,16 @@ class AvailableArmTroopsCommandFactoryTest
|
||||
gameState = startingState
|
||||
.withProvinces(
|
||||
Map(
|
||||
province.id -> province
|
||||
.withInfrastructure(99.0f)
|
||||
.withPriceIndex(1.2f)
|
||||
province.id -> province.withInfrastructure(99.0).withPriceIndex(1.2)
|
||||
)
|
||||
),
|
||||
factionId = rulingFactionId,
|
||||
provinceId = province.id
|
||||
)
|
||||
|
||||
val expectedLightInfantryCost = lightInfantryBaseCost * 1.2f
|
||||
val expectedLongbowmenCost = longbowmenBaseCost * 1.2f
|
||||
|
||||
command.get.armamentCosts should have size 2
|
||||
command.get.armamentCosts
|
||||
.find(_.`type` == LIGHT_INFANTRY)
|
||||
.get
|
||||
.cost shouldBe expectedLightInfantryCost
|
||||
command.get.armamentCosts
|
||||
.find(_.`type` == LONGBOWMEN)
|
||||
.get
|
||||
.cost shouldBe expectedLongbowmenCost
|
||||
command.get.armamentCosts should contain.allOf(
|
||||
ArmamentCost(`type` = LIGHT_INFANTRY, cost = lightInfantryBaseCost * 1.2),
|
||||
ArmamentCost(`type` = LONGBOWMEN, cost = longbowmenBaseCost * 1.2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ class AvailableDivineCommandsFactoryTest
|
||||
),
|
||||
gold = 1200,
|
||||
food = 2400,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
heroes = mapifyHeroes(
|
||||
@@ -296,7 +296,7 @@ class AvailableDivineCommandsFactoryTest
|
||||
it should "adjust the gold cost based on price index" in {
|
||||
AvailableDivineCommandsFactory
|
||||
.availableCommand(
|
||||
gameState = gameState.update(_.provinces(actingPid).priceIndex := 1.1f),
|
||||
gameState = gameState.update(_.provinces(actingPid).priceIndex := 1.1),
|
||||
factionId = actingFid,
|
||||
provinceId = actingPid
|
||||
)
|
||||
|
||||
+6
-6
@@ -30,7 +30,7 @@ class AvailableFeastCommandFactoryTest
|
||||
5 -> Province(
|
||||
rulingFactionHeroIds = Vector(15, 25, 35),
|
||||
gold = 12,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -53,7 +53,7 @@ class AvailableFeastCommandFactoryTest
|
||||
5 -> Province(
|
||||
rulingFactionHeroIds = Vector(15, 25, 35),
|
||||
gold = 1200,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -76,7 +76,7 @@ class AvailableFeastCommandFactoryTest
|
||||
5 -> Province(
|
||||
rulingFactionHeroIds = Vector(15, 25, 35),
|
||||
gold = 1200,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -102,7 +102,7 @@ class AvailableFeastCommandFactoryTest
|
||||
5 -> Province(
|
||||
rulingFactionHeroIds = Vector(15, 25, 35),
|
||||
gold = 1200,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -128,13 +128,13 @@ class AvailableFeastCommandFactoryTest
|
||||
5 -> Province(
|
||||
rulingFactionHeroIds = Vector(15, 25, 35),
|
||||
gold = 1200,
|
||||
priceIndex = 1.2f
|
||||
priceIndex = 1.2
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val cmd = AvailableFeastCommandFactory.availableCommand(gameState, 9, 5).get
|
||||
|
||||
cmd.goldCost shouldBe 73
|
||||
cmd.goldCost shouldBe 72
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -202,7 +202,7 @@ class AvailableImproveCommandsFactoryTest
|
||||
}
|
||||
|
||||
it should "include DEVASTATION option if there is economy devastation" in {
|
||||
val provinceWithDevastation = province.withEconomyDevastation(25.0f)
|
||||
val provinceWithDevastation = province.withEconomyDevastation(25.0)
|
||||
|
||||
val command = AvailableImproveCommandsFactory
|
||||
.availableCommand(
|
||||
@@ -217,7 +217,7 @@ class AvailableImproveCommandsFactoryTest
|
||||
}
|
||||
|
||||
it should "include DEVASTATION option if there is agriculture devastation" in {
|
||||
val provinceWithDevastation = province.withEconomyDevastation(25.0f)
|
||||
val provinceWithDevastation = province.withEconomyDevastation(25.0)
|
||||
|
||||
val command = AvailableImproveCommandsFactory
|
||||
.availableCommand(
|
||||
@@ -232,7 +232,7 @@ class AvailableImproveCommandsFactoryTest
|
||||
}
|
||||
|
||||
it should "include DEVASTATION option if there is infrastructure devastation" in {
|
||||
val provinceWithDevastation = province.withInfrastructureDevastation(25.0f)
|
||||
val provinceWithDevastation = province.withInfrastructureDevastation(25.0)
|
||||
|
||||
val command = AvailableImproveCommandsFactory
|
||||
.availableCommand(
|
||||
|
||||
+5
-5
@@ -39,7 +39,7 @@ class AvailableOrganizeTroopsCommandsFactoryTest
|
||||
battalionIds = fullAndNotFullBattalions.map(_.id),
|
||||
specialBattalionTypes = Vector(LIGHT_INFANTRY, HEAVY_CAVALRY),
|
||||
gold = 2000,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
private val battalionTypes = Vector(
|
||||
@@ -105,7 +105,7 @@ class AvailableOrganizeTroopsCommandsFactoryTest
|
||||
7 -> actingProvince
|
||||
.withEconomy(70)
|
||||
.withAgriculture(70)
|
||||
.withPriceIndex(1.0f),
|
||||
.withPriceIndex(1.0),
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = Some(6)),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
@@ -128,9 +128,9 @@ class AvailableOrganizeTroopsCommandsFactoryTest
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
7 -> actingProvince
|
||||
.withEconomy(70f)
|
||||
.withAgriculture(70f)
|
||||
.withPriceIndex(0.9f),
|
||||
.withEconomy(70)
|
||||
.withAgriculture(70)
|
||||
.withPriceIndex(0.9),
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = Some(6)),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ class AvailableTradeCommandFactoryTest
|
||||
gold = 2000,
|
||||
food = 2500,
|
||||
rulerIsTraveling = true,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
private val gameState = GameState(
|
||||
@@ -105,7 +105,7 @@ class AvailableTradeCommandFactoryTest
|
||||
it should "adjust the buy food price based on the price index" in {
|
||||
val command = AvailableTradeCommandFactory.availableCommand(
|
||||
gameState = gameState.withProvinces(
|
||||
Map(province.id -> province.withPriceIndex(1.5f))
|
||||
Map(province.id -> province.withPriceIndex(1.5))
|
||||
),
|
||||
factionId = province.rulingFactionId.get,
|
||||
provinceId = province.id
|
||||
@@ -127,7 +127,7 @@ class AvailableTradeCommandFactoryTest
|
||||
it should "adjust the sell food price based on the price index" in {
|
||||
val command = AvailableTradeCommandFactory.availableCommand(
|
||||
gameState = gameState.withProvinces(
|
||||
Map(province.id -> province.withPriceIndex(1.5f))
|
||||
Map(province.id -> province.withPriceIndex(1.5))
|
||||
),
|
||||
factionId = province.rulingFactionId.get,
|
||||
provinceId = province.id
|
||||
|
||||
+10
-10
@@ -127,9 +127,9 @@ class NewRoundActionTest
|
||||
Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economyDevastation = 14.3f,
|
||||
agricultureDevastation = 2.1f,
|
||||
infrastructureDevastation = 19.1f
|
||||
economyDevastation = 14.3,
|
||||
agricultureDevastation = 2.1,
|
||||
infrastructureDevastation = 19.1
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -151,9 +151,9 @@ class NewRoundActionTest
|
||||
Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economyDevastation = 0.3f,
|
||||
agricultureDevastation = 1.9f,
|
||||
infrastructureDevastation = 0.0f
|
||||
economyDevastation = 0.3,
|
||||
agricultureDevastation = 1.9,
|
||||
infrastructureDevastation = 0.0
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -164,8 +164,8 @@ class NewRoundActionTest
|
||||
inside(results.head.changedProvinces.head) { case cp: ChangedProvince =>
|
||||
cp.id shouldBe 7
|
||||
cp.setHasActed shouldBe Some(false)
|
||||
cp.economyDevastationDelta shouldBe Some(-0.3f)
|
||||
cp.agricultureDevastationDelta shouldBe Some(-1.9f)
|
||||
cp.economyDevastationDelta shouldBe Some(-0.3)
|
||||
cp.agricultureDevastationDelta shouldBe Some(-1.9)
|
||||
cp.infrastructureDevastationDelta shouldBe None
|
||||
}
|
||||
}
|
||||
@@ -898,7 +898,7 @@ class NewRoundActionTest
|
||||
economy = 100,
|
||||
agriculture = 100,
|
||||
infrastructure = 100,
|
||||
priceIndex = 0.75f
|
||||
priceIndex = 0.75
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -907,7 +907,7 @@ class NewRoundActionTest
|
||||
|
||||
forExactly(1, results) { result =>
|
||||
result.changedProvinces.head.newPriceIndex shouldBe Some(
|
||||
0.75f + 0.1f * 0.5f
|
||||
0.75 + 0.1 * 0.5
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ class NewYearActionTest
|
||||
MinSupportForTaxes.setDoubleValue(45)
|
||||
FoodIncreasePerAgriculture.setDoubleValue(60)
|
||||
GoldIncreasePerEconomy.setDoubleValue(40)
|
||||
PercentDegradationPerYear.setDoubleValue(15f)
|
||||
PercentDegradationPerYear.setDoubleValue(15)
|
||||
LoyaltyDiscordanceThreshold.setDoubleValue(10)
|
||||
LoyaltyDecreasePerDiscordance.setDoubleValue(0.3)
|
||||
AmbitionFactorForLoyaltyDegradation.setDoubleValue(0.2)
|
||||
@@ -162,7 +162,7 @@ class NewYearActionTest
|
||||
|
||||
for ((oldProv, newProv) <- oldAndNew) {
|
||||
newProv.economyDelta shouldBe empty
|
||||
newProv.economyDevastationDelta should contain(0.15f * oldProv.economy)
|
||||
newProv.economyDevastationDelta should contain(0.15 * oldProv.economy)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ class NewYearActionTest
|
||||
for ((oldProv, newProv) <- oldAndNew) {
|
||||
newProv.agricultureDelta shouldBe empty
|
||||
newProv.agricultureDevastationDelta should contain(
|
||||
0.15f * oldProv.agriculture
|
||||
0.15 * oldProv.agriculture
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ class NewYearActionTest
|
||||
for ((oldProv, newProv) <- oldAndNew) {
|
||||
newProv.infrastructureDelta shouldBe empty
|
||||
newProv.infrastructureDevastationDelta should contain(
|
||||
0.15f * oldProv.infrastructure
|
||||
0.15 * oldProv.infrastructure
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -204,7 +204,7 @@ class NewYearActionTest
|
||||
)
|
||||
|
||||
for ((oldProv, newProv) <- oldAndNew) {
|
||||
newProv.supportDelta should contain(-0.15f * oldProv.support)
|
||||
newProv.supportDelta should contain(-0.15 * oldProv.support)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+35
-35
@@ -289,7 +289,7 @@ class PerformProvinceEventsActionTest
|
||||
id = 7,
|
||||
rulingFactionId = Some(fid),
|
||||
activeEvents = Vector.empty,
|
||||
infrastructure = 65.0f,
|
||||
infrastructure = 65.0,
|
||||
lastBeastsDate = Some(Date(year = 1500, month = 8))
|
||||
)
|
||||
)
|
||||
@@ -310,7 +310,7 @@ class PerformProvinceEventsActionTest
|
||||
id = 7,
|
||||
rulingFactionId = Some(fid),
|
||||
activeEvents = Vector.empty,
|
||||
infrastructure = 65.0f,
|
||||
infrastructure = 65.0,
|
||||
lastBeastsDate = Some(Date(year = 1500, month = 5))
|
||||
)
|
||||
)
|
||||
@@ -332,7 +332,7 @@ class PerformProvinceEventsActionTest
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
rulingFactionId = Some(fid),
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
activeEvents = Vector(
|
||||
BeastsEvent(
|
||||
count = 150,
|
||||
@@ -365,7 +365,7 @@ class PerformProvinceEventsActionTest
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
rulingFactionId = Some(fid),
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
activeEvents = Vector(
|
||||
BeastsEvent(
|
||||
count = 150,
|
||||
@@ -398,7 +398,7 @@ class PerformProvinceEventsActionTest
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
rulingFactionId = Some(fid),
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
activeEvents = Vector(
|
||||
BeastsEvent(
|
||||
count = 150,
|
||||
@@ -431,7 +431,7 @@ class PerformProvinceEventsActionTest
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
rulingFactionId = Some(fid),
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
activeEvents = Vector(
|
||||
BeastsEvent(
|
||||
count = 150,
|
||||
@@ -898,7 +898,7 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
activeEvents = Vector(
|
||||
FestivalEvent(
|
||||
startDate = Date(year = 1501, month = 3),
|
||||
@@ -928,7 +928,7 @@ class PerformProvinceEventsActionTest
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
rulingFactionId = Some(4),
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
activeEvents = Vector(
|
||||
FestivalEvent(
|
||||
startDate = Date(year = 1501, month = 3),
|
||||
@@ -964,7 +964,7 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
economyDevastation = 3,
|
||||
activeEvents = Vector(
|
||||
BlizzardEvent(
|
||||
@@ -995,9 +995,9 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
agricultureDevastation = 3,
|
||||
infrastructure = 0.0f,
|
||||
infrastructure = 0.0,
|
||||
activeEvents = Vector(
|
||||
FloodEvent(
|
||||
startDate = Date(year = 1501, month = 7),
|
||||
@@ -1027,9 +1027,9 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
agricultureDevastation = 3,
|
||||
infrastructure = 0.0f,
|
||||
infrastructure = 0.0,
|
||||
activeEvents = Vector(
|
||||
DroughtEvent(
|
||||
startDate = Date(year = 1501, month = 7),
|
||||
@@ -1058,9 +1058,9 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
infrastructureDevastation = 3,
|
||||
infrastructure = 40.0f,
|
||||
infrastructure = 40.0,
|
||||
activeEvents = Vector(
|
||||
FloodEvent(
|
||||
startDate = Date(year = 1501, month = 7),
|
||||
@@ -1094,9 +1094,9 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
economy = 51.0,
|
||||
economyDevastation = 3,
|
||||
infrastructure = 40.0f,
|
||||
infrastructure = 40.0,
|
||||
activeEvents = Vector()
|
||||
)
|
||||
)
|
||||
@@ -1124,8 +1124,8 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
infrastructure = 40.0f,
|
||||
economy = 51.0,
|
||||
infrastructure = 40.0,
|
||||
activeEvents = Vector()
|
||||
)
|
||||
)
|
||||
@@ -1144,8 +1144,8 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
infrastructure = 40.0f,
|
||||
economy = 51.0,
|
||||
infrastructure = 40.0,
|
||||
activeEvents = Vector()
|
||||
)
|
||||
)
|
||||
@@ -1164,8 +1164,8 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
infrastructure = 40.0f,
|
||||
economy = 51.0,
|
||||
infrastructure = 40.0,
|
||||
activeEvents =
|
||||
Vector(EpidemicEvent(startDate = Date(year = 1501, month = 6)))
|
||||
)
|
||||
@@ -1190,8 +1190,8 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
infrastructure = 40.0f,
|
||||
economy = 51.0,
|
||||
infrastructure = 40.0,
|
||||
activeEvents =
|
||||
Vector(EpidemicEvent(startDate = Date(year = 1501, month = 6)))
|
||||
)
|
||||
@@ -1216,8 +1216,8 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
infrastructure = 40.0f,
|
||||
economy = 51.0,
|
||||
infrastructure = 40.0,
|
||||
activeEvents =
|
||||
Vector(EpidemicEvent(startDate = Date(year = 1501, month = 6)))
|
||||
)
|
||||
@@ -1238,8 +1238,8 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
infrastructure = 40.0f,
|
||||
economy = 51.0,
|
||||
infrastructure = 40.0,
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 8, startingPositionIndex = 3))
|
||||
),
|
||||
@@ -1272,8 +1272,8 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
infrastructure = 40.0f,
|
||||
economy = 51.0,
|
||||
infrastructure = 40.0,
|
||||
activeEvents =
|
||||
Vector(EpidemicEvent(startDate = Date(year = 1501, month = 6)))
|
||||
)
|
||||
@@ -1302,8 +1302,8 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
infrastructure = 40.0f,
|
||||
economy = 51.0,
|
||||
infrastructure = 40.0,
|
||||
battalionIds = Vector(19, 25),
|
||||
activeEvents =
|
||||
Vector(EpidemicEvent(startDate = Date(year = 1501, month = 6)))
|
||||
@@ -1333,8 +1333,8 @@ class PerformProvinceEventsActionTest
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
economy = 51.0f,
|
||||
infrastructure = 40.0f,
|
||||
economy = 51.0,
|
||||
infrastructure = 40.0,
|
||||
rulingFactionHeroIds = Vector(19, 25),
|
||||
rulingFactionId = Some(12),
|
||||
rulingHeroId = Some(25),
|
||||
|
||||
+1
-1
@@ -263,7 +263,7 @@ class PerformUncontestedConquestActionTest extends AnyFlatSpec with Matchers {
|
||||
newBattalionIds = Vector(34, 99),
|
||||
newRulingFactionId = Some(attackerId),
|
||||
removedHostileArmyFactionIds = Vector(attackerId),
|
||||
supportDelta = Some(-0.0f),
|
||||
supportDelta = Some(-0.0),
|
||||
newProvinceOrders = Some(Entrust),
|
||||
clearDefendingArmy = true
|
||||
)
|
||||
|
||||
-2
@@ -92,8 +92,6 @@ scala_test(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_hero_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:invitation_accepted_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:invitation_rejected_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
|
||||
+1
-273
@@ -29,15 +29,8 @@ import net.eagle0.eagle.model.state.hero.JoinedByInvitationBackstoryEvent
|
||||
import net.eagle0.eagle.model.state.province.ProvinceOrderType.Entrust
|
||||
import net.eagle0.eagle.model.state.province.concrete.ProvinceC
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.RecruitmentInfo
|
||||
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.concrete.UnaffiliatedHeroC
|
||||
import net.eagle0.eagle.model.state.{
|
||||
Army,
|
||||
BattalionTypeId,
|
||||
CombatUnit,
|
||||
MovingArmy,
|
||||
Supplies
|
||||
}
|
||||
import net.eagle0.eagle.model.state.{Army, CombatUnit, MovingArmy, Supplies}
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import org.scalatest.Inspectors.forExactly
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
@@ -121,29 +114,6 @@ class InvitationResolutionHelpersTest
|
||||
|
||||
private val currentDate = Date(year = 2525, month = Date.Month.April)
|
||||
|
||||
private val battalions = Vector(
|
||||
BattalionC(
|
||||
id = 100,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 50
|
||||
), // Power: 50.0
|
||||
BattalionC(
|
||||
id = 101,
|
||||
typeId = BattalionTypeId.HeavyInfantry,
|
||||
size = 30
|
||||
), // Power: 60.0
|
||||
BattalionC(
|
||||
id = 102,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
size = 40
|
||||
), // Power: 160.0
|
||||
BattalionC(
|
||||
id = 500,
|
||||
typeId = BattalionTypeId.Longbowmen,
|
||||
size = 25
|
||||
) // Power: 37.5
|
||||
)
|
||||
|
||||
private val provinces = Vector(
|
||||
actingFidProvince,
|
||||
firstInvitingFidProvince,
|
||||
@@ -179,7 +149,6 @@ class InvitationResolutionHelpersTest
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
battalions = battalions,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.newValue
|
||||
@@ -194,7 +163,6 @@ class InvitationResolutionHelpersTest
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
battalions = battalions,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.newValue
|
||||
@@ -238,7 +206,6 @@ class InvitationResolutionHelpersTest
|
||||
currentDate = currentDate,
|
||||
provinces = provincesWithMa,
|
||||
factions = factions,
|
||||
battalions = battalions,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.newValue
|
||||
@@ -258,7 +225,6 @@ class InvitationResolutionHelpersTest
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
battalions = battalions,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.newValue
|
||||
@@ -309,7 +275,6 @@ class InvitationResolutionHelpersTest
|
||||
currentDate = currentDate,
|
||||
provinces = provincesWithMa,
|
||||
factions = factions,
|
||||
battalions = battalions,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.newValue
|
||||
@@ -646,241 +611,4 @@ class InvitationResolutionHelpersTest
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
|
||||
"battalionOutcomes" should "bring all battalions when battalion count is less than or equal to hero count" in {
|
||||
val provinces = Vector(
|
||||
ProvinceC(
|
||||
id = 1,
|
||||
rulingFactionHeroIds = Vector(10, 11, 12), // 3 heroes
|
||||
battalionIds = Vector(100, 101) // 2 battalions - fewer than heroes
|
||||
)
|
||||
)
|
||||
val battalions = Vector(
|
||||
BattalionC(id = 100, typeId = BattalionTypeId.LightInfantry, size = 50),
|
||||
BattalionC(id = 101, typeId = BattalionTypeId.HeavyInfantry, size = 30)
|
||||
)
|
||||
|
||||
val result = InvitationResolutionHelpers.battalionOutcomes(
|
||||
redirectedArmies = Vector(),
|
||||
acceptingFactionProvinces = provinces,
|
||||
battalions = battalions
|
||||
)
|
||||
|
||||
result.joinedBattalionIds should contain theSameElementsAs Vector(100, 101)
|
||||
result.removedBattalionIds should be(empty)
|
||||
}
|
||||
|
||||
it should "bring all battalions when battalion count equals hero count" in {
|
||||
val provinces = Vector(
|
||||
ProvinceC(
|
||||
id = 1,
|
||||
rulingFactionHeroIds = Vector(10, 11), // 2 heroes
|
||||
battalionIds = Vector(100, 101) // 2 battalions - equal to heroes
|
||||
)
|
||||
)
|
||||
val battalions = Vector(
|
||||
BattalionC(id = 100, typeId = BattalionTypeId.LightInfantry, size = 50),
|
||||
BattalionC(id = 101, typeId = BattalionTypeId.HeavyInfantry, size = 30)
|
||||
)
|
||||
|
||||
val result = InvitationResolutionHelpers.battalionOutcomes(
|
||||
redirectedArmies = Vector(),
|
||||
acceptingFactionProvinces = provinces,
|
||||
battalions = battalions
|
||||
)
|
||||
|
||||
result.joinedBattalionIds should contain theSameElementsAs Vector(100, 101)
|
||||
result.removedBattalionIds should be(empty)
|
||||
}
|
||||
|
||||
it should "bring only the strongest battalions when battalion count exceeds hero count" in {
|
||||
val provinces = Vector(
|
||||
ProvinceC(
|
||||
id = 1,
|
||||
rulingFactionHeroIds = Vector(10, 11), // 2 heroes
|
||||
battalionIds =
|
||||
Vector(100, 101, 102, 103) // 4 battalions - more than heroes
|
||||
)
|
||||
)
|
||||
val battalions = Vector(
|
||||
BattalionC(
|
||||
id = 100,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 50,
|
||||
training = 5,
|
||||
armament = 10
|
||||
), // Power: 57.75
|
||||
BattalionC(
|
||||
id = 101,
|
||||
typeId = BattalionTypeId.HeavyInfantry,
|
||||
size = 30,
|
||||
training = 10,
|
||||
armament = 20
|
||||
), // Power: 79.2
|
||||
BattalionC(
|
||||
id = 102,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
size = 20,
|
||||
training = 15,
|
||||
armament = 30
|
||||
), // Power: 119.6
|
||||
BattalionC(
|
||||
id = 103,
|
||||
typeId = BattalionTypeId.Longbowmen,
|
||||
size = 40,
|
||||
training = 8,
|
||||
armament = 15
|
||||
) // Power: 74.52
|
||||
)
|
||||
|
||||
val result = InvitationResolutionHelpers.battalionOutcomes(
|
||||
redirectedArmies = Vector(),
|
||||
acceptingFactionProvinces = provinces,
|
||||
battalions = battalions
|
||||
)
|
||||
|
||||
// Should keep the 2 strongest battalions (102: HeavyCavalry ~120.15, 101: HeavyInfantry ~84.7)
|
||||
result.joinedBattalionIds should contain theSameElementsAs Vector(101, 102)
|
||||
// Should remove the 2 weakest battalions (103: Longbowmen ~77.08, 100: LightInfantry ~57.65)
|
||||
result.removedBattalionIds should contain theSameElementsAs Vector(100, 103)
|
||||
}
|
||||
|
||||
it should "handle multiple provinces correctly" in {
|
||||
val provinces = Vector(
|
||||
ProvinceC(
|
||||
id = 1,
|
||||
rulingFactionHeroIds = Vector(10), // 1 hero
|
||||
battalionIds = Vector(100, 101, 102) // 3 battalions
|
||||
),
|
||||
ProvinceC(
|
||||
id = 2,
|
||||
rulingFactionHeroIds = Vector(20, 21), // 2 heroes
|
||||
battalionIds = Vector(200) // 1 battalion
|
||||
)
|
||||
)
|
||||
val battalions = Vector(
|
||||
BattalionC(
|
||||
id = 100,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 50,
|
||||
training = 0,
|
||||
armament = 0
|
||||
), // Power: 50.0
|
||||
BattalionC(
|
||||
id = 101,
|
||||
typeId = BattalionTypeId.HeavyInfantry,
|
||||
size = 30,
|
||||
training = 0,
|
||||
armament = 0
|
||||
), // Power: 60.0
|
||||
BattalionC(
|
||||
id = 102,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
size = 20,
|
||||
training = 0,
|
||||
armament = 0
|
||||
), // Power: 80.0
|
||||
BattalionC(
|
||||
id = 200,
|
||||
typeId = BattalionTypeId.Longbowmen,
|
||||
size = 40,
|
||||
training = 0,
|
||||
armament = 0
|
||||
) // Power: 60.0
|
||||
)
|
||||
|
||||
val result = InvitationResolutionHelpers.battalionOutcomes(
|
||||
redirectedArmies = Vector(),
|
||||
acceptingFactionProvinces = provinces,
|
||||
battalions = battalions
|
||||
)
|
||||
|
||||
// Province 1: Keep strongest 1 of 3 battalions (102: HeavyCavalry)
|
||||
// Province 2: Keep all 1 battalions (200: Longbowmen)
|
||||
result.joinedBattalionIds should contain theSameElementsAs Vector(102, 200)
|
||||
result.removedBattalionIds should contain theSameElementsAs Vector(100, 101)
|
||||
}
|
||||
|
||||
it should "include battalions from redirected armies" in {
|
||||
val redirectedArmies = Vector(
|
||||
MovingArmy(
|
||||
id = 1,
|
||||
originProvinceId = 999,
|
||||
destinationProvinceId = 888,
|
||||
army = Army(
|
||||
factionId = 4,
|
||||
units = Vector(
|
||||
CombatUnit(heroId = 50, battalionId = Some(500), factionId = 4),
|
||||
CombatUnit(heroId = 51, battalionId = Some(501), factionId = 4)
|
||||
)
|
||||
),
|
||||
supplies = Supplies(0, 0),
|
||||
suppliesLoss = 0.0,
|
||||
arrivalRound = 1,
|
||||
startingPositionIndex = Some(1)
|
||||
)
|
||||
)
|
||||
val provinces = Vector(
|
||||
ProvinceC(
|
||||
id = 1,
|
||||
rulingFactionHeroIds = Vector(10), // 1 hero
|
||||
battalionIds = Vector(100, 101) // 2 battalions
|
||||
)
|
||||
)
|
||||
val battalions = Vector(
|
||||
BattalionC(
|
||||
id = 100,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 50,
|
||||
training = 0,
|
||||
armament = 0
|
||||
), // Power: 50.0
|
||||
BattalionC(
|
||||
id = 101,
|
||||
typeId = BattalionTypeId.HeavyInfantry,
|
||||
size = 30,
|
||||
training = 0,
|
||||
armament = 0
|
||||
), // Power: 60.0
|
||||
BattalionC(
|
||||
id = 500,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
size = 20,
|
||||
training = 0,
|
||||
armament = 0
|
||||
), // Power: 80.0
|
||||
BattalionC(
|
||||
id = 501,
|
||||
typeId = BattalionTypeId.Longbowmen,
|
||||
size = 40,
|
||||
training = 0,
|
||||
armament = 0
|
||||
) // Power: 60.0
|
||||
)
|
||||
|
||||
val result = InvitationResolutionHelpers.battalionOutcomes(
|
||||
redirectedArmies = redirectedArmies,
|
||||
acceptingFactionProvinces = provinces,
|
||||
battalions = battalions
|
||||
)
|
||||
|
||||
// Should include both battalions from redirected armies plus strongest from province
|
||||
result.joinedBattalionIds should contain theSameElementsAs Vector(
|
||||
500,
|
||||
501,
|
||||
101
|
||||
)
|
||||
result.removedBattalionIds should contain theSameElementsAs Vector(100)
|
||||
}
|
||||
|
||||
it should "handle empty cases correctly" in {
|
||||
val result = InvitationResolutionHelpers.battalionOutcomes(
|
||||
redirectedArmies = Vector(),
|
||||
acceptingFactionProvinces = Vector(),
|
||||
battalions = Vector()
|
||||
)
|
||||
|
||||
result.joinedBattalionIds should be(empty)
|
||||
result.removedBattalionIds should be(empty)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ class AlmsCommandTest
|
||||
override def beforeEach(): Unit = {
|
||||
MaxAlmsFood.setIntValue(1000)
|
||||
AlmsMaxCharismaXp.setIntValue(10)
|
||||
AlmsSupportIncreasePerFood.setFloatValue(0.01f)
|
||||
AlmsPaladinSupportMultiplier.setFloatValue(4f)
|
||||
AlmsSupportIncreasePerFood.setDoubleValue(0.01)
|
||||
AlmsPaladinSupportMultiplier.setDoubleValue(4)
|
||||
ActionVigorCost.setIntValue(actionVigorCost)
|
||||
VigorToConstitutionXpMultiplier.setDoubleValue(0.2)
|
||||
XpForStatBump.setIntValue(100)
|
||||
@@ -180,7 +180,7 @@ class AlmsCommandTest
|
||||
|
||||
cp shouldBe ChangedProvinceC(
|
||||
provinceId = actingProvince.id,
|
||||
supportDelta = Some(11.11f), // 1111 * 0.01
|
||||
supportDelta = Some(11.11), // 1111 * 0.01
|
||||
foodDelta = Some(-1111)
|
||||
)
|
||||
}
|
||||
@@ -208,7 +208,7 @@ class AlmsCommandTest
|
||||
|
||||
cp shouldBe ChangedProvinceC(
|
||||
provinceId = actingProvince.id,
|
||||
supportDelta = Some(44.44f), // 1111 * 4
|
||||
supportDelta = Some(44.44), // 1111 * 4
|
||||
foodDelta = Some(-1111)
|
||||
)
|
||||
}
|
||||
|
||||
+2
-2
@@ -52,7 +52,7 @@ class ArmTroopsCommandTest
|
||||
rulerIsTraveling = true,
|
||||
gold = 9999,
|
||||
infrastructure = 100,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
private val availableBattalions = battalions.map(_.id)
|
||||
@@ -271,7 +271,7 @@ class ArmTroopsCommandTest
|
||||
val result = command.immediateExecute
|
||||
|
||||
inside(result.changedProvinces.loneElement) { case cp: ChangedProvinceC =>
|
||||
cp.newPriceIndex shouldBe Some(1.0f + expectedCost * 0.0001f)
|
||||
cp.newPriceIndex shouldBe Some(1.0 + expectedCost * 0.0001)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -86,7 +86,7 @@ class DivineCommandTest
|
||||
private val province = ProvinceC(
|
||||
id = provinceId,
|
||||
gold = 2500,
|
||||
priceIndex = 1.00f,
|
||||
priceIndex = 1.00,
|
||||
rulingFactionId = Some(actingFactionId),
|
||||
rulingHeroId = Some(37),
|
||||
rulingFactionHeroIds = Vector(37),
|
||||
@@ -275,7 +275,7 @@ class DivineCommandTest
|
||||
selectedHeroIds = Vector(12, 15),
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId,
|
||||
allProvinces = Vector(province.withPriceIndex(0.9f)),
|
||||
allProvinces = Vector(province.withPriceIndex(0.9)),
|
||||
factions = factions,
|
||||
battalions = Vector()
|
||||
)
|
||||
@@ -304,7 +304,7 @@ class DivineCommandTest
|
||||
.newValue
|
||||
|
||||
inside(result.changedProvinces.loneElement) { case cp: ChangedProvinceC =>
|
||||
cp.newPriceIndex shouldBe Some(1.02f)
|
||||
cp.newPriceIndex shouldBe Some(1.02)
|
||||
} // 200 gold spent
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class FeastCommandTest
|
||||
rulingHeroId = Some(7),
|
||||
rulingFactionHeroIds = Vector(5, 6, 7, 8),
|
||||
gold = 900,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
private val normalHero = HeroC(
|
||||
@@ -122,7 +122,7 @@ class FeastCommandTest
|
||||
|
||||
forExactly(1, result.changedProvinces) { case cp: ChangedProvinceC =>
|
||||
cp.provinceId shouldBe province.id
|
||||
cp.newPriceIndex shouldBe Some(1.01f) // 1.0 + 0.0001 * 100
|
||||
cp.newPriceIndex shouldBe Some(1.01) // 1.0 + 0.0001 * 100
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -67,7 +67,7 @@ class HeroGiftCommandTest
|
||||
)
|
||||
)
|
||||
),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
private val anotherOwnedProvince = ProvinceC(
|
||||
@@ -89,7 +89,7 @@ class HeroGiftCommandTest
|
||||
)
|
||||
)
|
||||
),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
private val factionProvinces: Vector[ProvinceT] =
|
||||
@@ -195,7 +195,7 @@ class HeroGiftCommandTest
|
||||
|
||||
it should "adjust the loyalty differently based on price index" in {
|
||||
val recipientProvinceWithPriceIndex =
|
||||
recipientProvince.copy(priceIndex = 1.25f)
|
||||
recipientProvince.copy(priceIndex = 1.25)
|
||||
|
||||
val cmd = HeroGiftCommand.make(
|
||||
actingFactionId = factionId,
|
||||
@@ -507,7 +507,7 @@ class HeroGiftCommandTest
|
||||
|
||||
forExactly(1, res.changedProvinces) { case cp: ChangedProvinceC =>
|
||||
cp.provinceId shouldBe provinceId
|
||||
cp.newPriceIndex shouldBe Some(1.008f)
|
||||
cp.newPriceIndex shouldBe Some(1.008)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-27
@@ -56,7 +56,7 @@ class ImproveCommandTest
|
||||
id = 7,
|
||||
rulingFactionId = Some(actingFactionId),
|
||||
rulingFactionHeroIds = heroes.map(_.id),
|
||||
support = 27.0f
|
||||
support = 27.0
|
||||
)
|
||||
|
||||
private val availableTypes = Vector(
|
||||
@@ -66,10 +66,10 @@ class ImproveCommandTest
|
||||
ImprovementType.Devastation
|
||||
)
|
||||
|
||||
private val improvementPerStat = 0.08f
|
||||
private val improvementReductionMax = 0.25f
|
||||
private val improvementSupportGain = 0.75f
|
||||
private val engineerImprovementBonus = 1.8f
|
||||
private val improvementPerStat = 0.08
|
||||
private val improvementReductionMax = 0.25
|
||||
private val improvementSupportGain = 0.75
|
||||
private val engineerImprovementBonus = 1.8
|
||||
private val xpPerStat = 5
|
||||
|
||||
override def beforeEach(): Unit = {
|
||||
@@ -82,7 +82,7 @@ class ImproveCommandTest
|
||||
}
|
||||
|
||||
private val expectedImprovementFromZero =
|
||||
improvementPerStat * (40f + 62f) / 2.0f
|
||||
4.08 // improvementPerStat * (40 + 62) / 2.0
|
||||
|
||||
"execute" should "throw if the selected improvement is not available" in {
|
||||
the[EagleCommandException] thrownBy {
|
||||
@@ -222,9 +222,9 @@ class ImproveCommandTest
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero,
|
||||
actingProvince = province.copy(
|
||||
economyDevastation = 25.0f,
|
||||
agricultureDevastation = 35.0f,
|
||||
infrastructureDevastation = 10.0f
|
||||
economyDevastation = 25.0,
|
||||
agricultureDevastation = 35.0,
|
||||
infrastructureDevastation = 10.0
|
||||
),
|
||||
improvementType = ImprovementType.Devastation,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
@@ -247,15 +247,14 @@ class ImproveCommandTest
|
||||
val cmd = ImproveCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero.copy(strength = 85, agility = 93),
|
||||
actingProvince = province.withEconomyDevastation(25.0f),
|
||||
actingProvince = province.withEconomyDevastation(25.0),
|
||||
improvementType = ImprovementType.Devastation,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
lockImprovementType = false
|
||||
)
|
||||
|
||||
val betterExpectedImprovement =
|
||||
improvementPerStat * (85f + 93f) / 2.0f
|
||||
val betterExpectedImprovement = 7.12 // improvementPerStat * (85 + 93) / 2.0
|
||||
|
||||
val result = cmd.immediateExecute
|
||||
inside(result.changedProvinces.head) { case cp: ChangedProvinceC =>
|
||||
@@ -270,7 +269,7 @@ class ImproveCommandTest
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero
|
||||
.withProfession(Profession.Engineer),
|
||||
actingProvince = province.withInfrastructureDevastation(25.0f),
|
||||
actingProvince = province.withInfrastructureDevastation(25.0),
|
||||
improvementType = ImprovementType.Devastation,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
@@ -278,7 +277,7 @@ class ImproveCommandTest
|
||||
)
|
||||
|
||||
val expectedImprovement =
|
||||
5.8799996f // expectedImprovementFromZero + engineerImprovementBonus in Float precision
|
||||
expectedImprovementFromZero + engineerImprovementBonus
|
||||
val result = cmd.immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) { case cp: ChangedProvinceC =>
|
||||
@@ -292,7 +291,7 @@ class ImproveCommandTest
|
||||
val cmd = ImproveCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero,
|
||||
actingProvince = province.withAgricultureDevastation(25.0f),
|
||||
actingProvince = province.withAgricultureDevastation(25.0),
|
||||
improvementType = ImprovementType.Devastation,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
@@ -312,7 +311,7 @@ class ImproveCommandTest
|
||||
val cmd = ImproveCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero,
|
||||
actingProvince = province.withEconomy(0.0f),
|
||||
actingProvince = province.withEconomy(0.0),
|
||||
improvementType = ImprovementType.Economy,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
@@ -332,7 +331,7 @@ class ImproveCommandTest
|
||||
val cmd = ImproveCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero,
|
||||
actingProvince = province.withEconomy(45.0f),
|
||||
actingProvince = province.withEconomy(45.0),
|
||||
improvementType = ImprovementType.Economy,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
@@ -340,7 +339,7 @@ class ImproveCommandTest
|
||||
)
|
||||
|
||||
val reducedExpectedImprovement =
|
||||
(1.0f - 0.45f * improvementReductionMax) * expectedImprovementFromZero
|
||||
(1.0 - 0.45 * improvementReductionMax) * expectedImprovementFromZero
|
||||
|
||||
val result = cmd.immediateExecute
|
||||
|
||||
@@ -355,15 +354,14 @@ class ImproveCommandTest
|
||||
val cmd = ImproveCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero.copy(strength = 85, agility = 93),
|
||||
actingProvince = province.withEconomy(0.0f),
|
||||
actingProvince = province.withEconomy(0.0),
|
||||
improvementType = ImprovementType.Economy,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
lockImprovementType = false
|
||||
)
|
||||
|
||||
val betterExpectedImprovement =
|
||||
improvementPerStat * (85f + 93f) / 2.0f
|
||||
val betterExpectedImprovement = 7.12 // improvementPerStat * (85 + 93) / 2.0
|
||||
|
||||
val result = cmd.immediateExecute
|
||||
|
||||
@@ -379,7 +377,7 @@ class ImproveCommandTest
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero
|
||||
.withProfession(Profession.Engineer),
|
||||
actingProvince = province.withEconomy(0.0f),
|
||||
actingProvince = province.withEconomy(0.0),
|
||||
improvementType = ImprovementType.Economy,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
@@ -387,7 +385,7 @@ class ImproveCommandTest
|
||||
)
|
||||
|
||||
val expectedImprovement =
|
||||
5.8799996f // expectedImprovementFromZero + engineerImprovementBonus in Float precision
|
||||
expectedImprovementFromZero + engineerImprovementBonus
|
||||
val result = cmd.immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) { case cp: ChangedProvinceC =>
|
||||
@@ -401,7 +399,7 @@ class ImproveCommandTest
|
||||
val cmd = ImproveCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero,
|
||||
actingProvince = province.withEconomy(0.0f),
|
||||
actingProvince = province.withEconomy(0.0),
|
||||
improvementType = ImprovementType.Economy,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
@@ -421,7 +419,7 @@ class ImproveCommandTest
|
||||
val cmd = ImproveCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero,
|
||||
actingProvince = province.withAgriculture(0.0f),
|
||||
actingProvince = province.withAgriculture(0.0),
|
||||
improvementType = ImprovementType.Agriculture,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
@@ -441,7 +439,7 @@ class ImproveCommandTest
|
||||
val cmd = ImproveCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero,
|
||||
actingProvince = province.withInfrastructure(0.0f),
|
||||
actingProvince = province.withInfrastructure(0.0),
|
||||
improvementType = ImprovementType.Infrastructure,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
@@ -461,7 +459,7 @@ class ImproveCommandTest
|
||||
val cmd = ImproveCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingHero = actingHero,
|
||||
actingProvince = province.withInfrastructure(0.0f),
|
||||
actingProvince = province.withInfrastructure(0.0),
|
||||
improvementType = ImprovementType.Infrastructure,
|
||||
availableHeroIds = heroes.map(_.id),
|
||||
availableImprovementTypes = availableTypes,
|
||||
|
||||
+2
-2
@@ -79,7 +79,7 @@ class OrganizeTroopsCommandTest extends AnyFlatSpec with Matchers {
|
||||
rulingFactionId = Some(actingFactionId),
|
||||
battalionIds = Vector(battalion1.id),
|
||||
gold = startingGold,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
private val existingBattalions = Vector(battalion1, battalion2, battalion3)
|
||||
@@ -810,7 +810,7 @@ class OrganizeTroopsCommandTest extends AnyFlatSpec with Matchers {
|
||||
result.changedProvinces should have size 1
|
||||
|
||||
inside(result.changedProvinces.head) { case p =>
|
||||
p.newPriceIndex shouldBe Some(1.054f) // 1.0 + 0.0001 * 450.0 * 1.2
|
||||
p.newPriceIndex shouldBe Some(1.054) // 1.0 + 0.0001 * 450.0 * 1.2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class TradeCommandTest
|
||||
gold = startingGold,
|
||||
food = startingFood,
|
||||
rulerIsTraveling = true,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
override def beforeEach(): Unit = {
|
||||
@@ -159,7 +159,7 @@ class TradeCommandTest
|
||||
it should "spend more gold based on the price index" in {
|
||||
val command = TradeCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingProvince = province.withPriceIndex(1.5f),
|
||||
actingProvince = province.withPriceIndex(1.5),
|
||||
tradeType = BuyFood,
|
||||
amount = 1000
|
||||
)
|
||||
@@ -173,7 +173,7 @@ class TradeCommandTest
|
||||
it should "not change the food amount based on the price index" in {
|
||||
val command = TradeCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingProvince = province.withPriceIndex(1.5f),
|
||||
actingProvince = province.withPriceIndex(1.5),
|
||||
tradeType = BuyFood,
|
||||
amount = 1000
|
||||
)
|
||||
@@ -223,7 +223,7 @@ class TradeCommandTest
|
||||
|
||||
val result = command.immediateExecute
|
||||
inside(result.changedProvinces.loneElement) { case cp: ChangedProvinceC =>
|
||||
cp.newPriceIndex shouldBe Some(1.06f) // spent 600 gold
|
||||
cp.newPriceIndex shouldBe Some(1.06) // spent 600 gold
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ class TradeCommandTest
|
||||
it should "gain more gold based on the price index" in {
|
||||
val command = TradeCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingProvince = province.withPriceIndex(1.5f),
|
||||
actingProvince = province.withPriceIndex(1.5),
|
||||
tradeType = SellFood,
|
||||
amount = 1000
|
||||
)
|
||||
@@ -258,7 +258,7 @@ class TradeCommandTest
|
||||
it should "not adjust the food based on the price index" in {
|
||||
val command = TradeCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
actingProvince = province.withPriceIndex(1.5f),
|
||||
actingProvince = province.withPriceIndex(1.5),
|
||||
tradeType = SellFood,
|
||||
amount = 1000
|
||||
)
|
||||
@@ -308,7 +308,7 @@ class TradeCommandTest
|
||||
|
||||
val result = command.immediateExecute
|
||||
inside(result.changedProvinces.loneElement) { case cp: ChangedProvinceC =>
|
||||
cp.newPriceIndex shouldBe Some(0.975f) // gained 250 gold
|
||||
cp.newPriceIndex shouldBe Some(0.975) // gained 250 gold
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library", "scala_test")
|
||||
|
||||
scala_test(
|
||||
name = "battalion_power_test",
|
||||
srcs = ["BattalionPowerTest.scala"],
|
||||
deps = [
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_power",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
],
|
||||
)
|
||||
|
||||
scala_test(
|
||||
name = "battalion_suitability_test",
|
||||
srcs = ["BattalionSuitabilityTest.scala"],
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
package net.eagle0.eagle.library.util
|
||||
|
||||
import net.eagle0.eagle.model.state.BattalionTypeId
|
||||
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class BattalionPowerTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
"BattalionPower" should "calculate power for basic battalions with no training or armament" in {
|
||||
val lightInfantry =
|
||||
BattalionC(id = 1, typeId = BattalionTypeId.LightInfantry, size = 100)
|
||||
val heavyInfantry =
|
||||
BattalionC(id = 2, typeId = BattalionTypeId.HeavyInfantry, size = 100)
|
||||
val heavyCavalry =
|
||||
BattalionC(id = 3, typeId = BattalionTypeId.HeavyCavalry, size = 100)
|
||||
val longbowmen =
|
||||
BattalionC(id = 4, typeId = BattalionTypeId.Longbowmen, size = 100)
|
||||
|
||||
BattalionPower.power(lightInfantry) shouldBe 100.0 // 1.0 * 100 * 1.0 * 1.0
|
||||
BattalionPower.power(heavyInfantry) shouldBe 200.0 // 2.0 * 100 * 1.0 * 1.0
|
||||
BattalionPower.power(heavyCavalry) shouldBe 400.0 // 4.0 * 100 * 1.0 * 1.0
|
||||
BattalionPower.power(longbowmen) shouldBe 150.0 // 1.5 * 100 * 1.0 * 1.0
|
||||
}
|
||||
|
||||
it should "scale power linearly with battalion size" in {
|
||||
val small =
|
||||
BattalionC(id = 1, typeId = BattalionTypeId.LightInfantry, size = 50)
|
||||
val large =
|
||||
BattalionC(id = 2, typeId = BattalionTypeId.LightInfantry, size = 200)
|
||||
|
||||
BattalionPower.power(small) shouldBe 50.0
|
||||
BattalionPower.power(large) shouldBe 200.0
|
||||
}
|
||||
|
||||
it should "apply armament bonuses multiplicatively" in {
|
||||
val noArmament = BattalionC(
|
||||
id = 1,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 100,
|
||||
armament = 0
|
||||
)
|
||||
val withArmament = BattalionC(
|
||||
id = 2,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 100,
|
||||
armament = 50
|
||||
)
|
||||
|
||||
BattalionPower.power(noArmament) shouldBe 100.0 // 1.0 * 100 * 1.0 * 1.0
|
||||
BattalionPower.power(withArmament) shouldBe 150.0 // 1.0 * 100 * 1.5 * 1.0
|
||||
}
|
||||
|
||||
it should "apply training bonuses multiplicatively" in {
|
||||
val noTraining = BattalionC(
|
||||
id = 1,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 100,
|
||||
training = 0
|
||||
)
|
||||
val withTraining = BattalionC(
|
||||
id = 2,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 100,
|
||||
training = 25
|
||||
)
|
||||
|
||||
BattalionPower.power(noTraining) shouldBe 100.0 // 1.0 * 100 * 1.0 * 1.0
|
||||
BattalionPower.power(withTraining) shouldBe 125.0 // 1.0 * 100 * 1.0 * 1.25
|
||||
}
|
||||
|
||||
it should "apply both armament and training bonuses together" in {
|
||||
val fullyUpgraded = BattalionC(
|
||||
id = 1,
|
||||
typeId = BattalionTypeId.HeavyInfantry,
|
||||
size = 50,
|
||||
training = 20,
|
||||
armament = 30
|
||||
)
|
||||
|
||||
// Expected: 2.0 * 50 * (1.0 + 30/100) * (1.0 + 20/100) = 2.0 * 50 * 1.3 * 1.2 = 156.0
|
||||
BattalionPower.power(fullyUpgraded) shouldBe 156.0
|
||||
}
|
||||
|
||||
it should "handle edge cases with zero size" in {
|
||||
val zeroSize =
|
||||
BattalionC(id = 1, typeId = BattalionTypeId.LightInfantry, size = 0)
|
||||
BattalionPower.power(zeroSize) shouldBe 0.0
|
||||
}
|
||||
|
||||
it should "handle high training and armament values" in {
|
||||
val elite = BattalionC(
|
||||
id = 1,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
size = 25,
|
||||
training = 100,
|
||||
armament = 100
|
||||
)
|
||||
|
||||
// Expected: 4.0 * 25 * (1.0 + 100/100) * (1.0 + 100/100) = 4.0 * 25 * 2.0 * 2.0 = 400.0
|
||||
BattalionPower.power(elite) shouldBe 400.0
|
||||
}
|
||||
|
||||
it should "demonstrate that training and armament scale with battalion power" in {
|
||||
val smallElite = BattalionC(
|
||||
id = 1,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 10,
|
||||
training = 50,
|
||||
armament = 50
|
||||
)
|
||||
|
||||
val largeElite = BattalionC(
|
||||
id = 2,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 100,
|
||||
training = 50,
|
||||
armament = 50
|
||||
)
|
||||
|
||||
val smallPower =
|
||||
BattalionPower.power(smallElite) // 1.0 * 10 * 1.5 * 1.5 = 22.5
|
||||
val largePower =
|
||||
BattalionPower.power(largeElite) // 1.0 * 100 * 1.5 * 1.5 = 225.0
|
||||
|
||||
smallPower shouldBe 22.5
|
||||
largePower shouldBe 225.0
|
||||
largePower shouldBe (smallPower * 10) // Training/armament bonuses scale with size
|
||||
}
|
||||
}
|
||||
@@ -15,24 +15,24 @@ class PriceIndexUtilsTest
|
||||
with Matchers
|
||||
with BeforeAndAfterEach {
|
||||
override def beforeEach(): Unit = {
|
||||
MinimumPriceIndex.setFloatValue(0.6f)
|
||||
MaximumPriceIndex.setFloatValue(1.6f)
|
||||
PriceIndexReturnRate.setFloatValue(0.25f)
|
||||
PriceIndexShiftPerGold.setFloatValue(0.0001f)
|
||||
MinimumPriceIndex.setDoubleValue(0.6)
|
||||
MaximumPriceIndex.setDoubleValue(1.6)
|
||||
PriceIndexReturnRate.setDoubleValue(0.25)
|
||||
PriceIndexShiftPerGold.setDoubleValue(0.0001)
|
||||
}
|
||||
|
||||
"clampedIndex" should "never be less than the minimum" in {
|
||||
PriceIndexUtils.clampedIndex(0.52f) shouldBe 0.6f
|
||||
PriceIndexUtils.clampedIndex(-1f) shouldBe 0.6f
|
||||
PriceIndexUtils.clampedIndex(0.52) shouldBe 0.6
|
||||
PriceIndexUtils.clampedIndex(-1) shouldBe 0.6
|
||||
}
|
||||
|
||||
it should "never be more than the maximum" in {
|
||||
PriceIndexUtils.clampedIndex(1.7f) shouldBe 1.6f
|
||||
PriceIndexUtils.clampedIndex(100f) shouldBe 1.6f
|
||||
PriceIndexUtils.clampedIndex(1.7) shouldBe 1.6
|
||||
PriceIndexUtils.clampedIndex(100) shouldBe 1.6
|
||||
}
|
||||
|
||||
it should "return the value if it's within the range" in {
|
||||
PriceIndexUtils.clampedIndex(0.75f) shouldBe 0.75f
|
||||
PriceIndexUtils.clampedIndex(0.75) shouldBe 0.75
|
||||
}
|
||||
|
||||
"steadyState" should "return minimum when all inputs are 0" in {
|
||||
@@ -43,7 +43,7 @@ class PriceIndexUtilsTest
|
||||
economyDevastation = 0,
|
||||
agricultureDevastation = 0,
|
||||
infrastructureDevastation = 0
|
||||
) shouldBe 0.6f
|
||||
) shouldBe 0.6
|
||||
}
|
||||
|
||||
it should "return minimum + 0.5 when all inputs are 100" in {
|
||||
@@ -54,7 +54,7 @@ class PriceIndexUtilsTest
|
||||
economyDevastation = 0,
|
||||
agricultureDevastation = 0,
|
||||
infrastructureDevastation = 0
|
||||
) shouldBe 1.1f
|
||||
) shouldBe 1.1
|
||||
}
|
||||
|
||||
it should "be higher if there is devastation" in {
|
||||
@@ -65,7 +65,7 @@ class PriceIndexUtilsTest
|
||||
economyDevastation = 25,
|
||||
agricultureDevastation = 25,
|
||||
infrastructureDevastation = 25
|
||||
) shouldBe 1.225f
|
||||
) shouldBe 1.225
|
||||
}
|
||||
|
||||
it should "clamp if devastation is excessive" in {
|
||||
@@ -76,30 +76,30 @@ class PriceIndexUtilsTest
|
||||
economyDevastation = 100,
|
||||
agricultureDevastation = 100,
|
||||
infrastructureDevastation = 100
|
||||
) shouldBe 1.6f
|
||||
) shouldBe 1.6
|
||||
}
|
||||
|
||||
"shiftedTowardSteadyState" should "move by 25% of the difference toward steady state" in {
|
||||
PriceIndexUtils.shiftedTowardSteadyState(
|
||||
currentPriceIndex = 0.75f,
|
||||
currentPriceIndex = 0.75,
|
||||
economy = 100,
|
||||
agriculture = 100,
|
||||
infrastructure = 100,
|
||||
economyDevastation = 0,
|
||||
agricultureDevastation = 0,
|
||||
infrastructureDevastation = 0
|
||||
) shouldBe 0.75f + 0.25f * 0.35f // 0.75 current, 1.1 destination
|
||||
) shouldBe 0.75 + 0.25 * 0.35 // 0.75 current, 1.1 destination
|
||||
}
|
||||
|
||||
"shiftedByGoldSpend" should "shift by 0.0001 per gold spent" in {
|
||||
PriceIndexUtils.maybeNewPriceIndexFromGoldSpend(0.75f, 5000) shouldBe Some(
|
||||
1.25f
|
||||
PriceIndexUtils.maybeNewPriceIndexFromGoldSpend(0.75, 5000) shouldBe Some(
|
||||
1.25
|
||||
)
|
||||
}
|
||||
|
||||
it should "shift down if the spend is negative" in {
|
||||
PriceIndexUtils
|
||||
.maybeNewPriceIndexFromGoldSpend(1.4f, -5000)
|
||||
.get shouldBe (0.9f +- 0.0001f)
|
||||
.maybeNewPriceIndexFromGoldSpend(1.4, -5000)
|
||||
.get shouldBe (0.9 +- 0.0001)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -34,8 +34,8 @@ class AlmsCommandSelectorTest
|
||||
|
||||
override def beforeEach(): Unit = {
|
||||
MinSupportForTaxes.setDoubleValue(40)
|
||||
AlmsSupportIncreasePerFood.setDoubleValue(0.01f)
|
||||
AlmsPaladinSupportMultiplier.setDoubleValue(4.0f)
|
||||
AlmsSupportIncreasePerFood.setDoubleValue(0.01)
|
||||
AlmsPaladinSupportMultiplier.setDoubleValue(4.0)
|
||||
}
|
||||
|
||||
"chosenAlmsCommand" should "give 1000 food if we need 10 support in December" in {
|
||||
|
||||
+34
-34
@@ -103,7 +103,7 @@ class CommandChoiceHelpersTest
|
||||
)
|
||||
)
|
||||
),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalionTypes = BattalionTypesTestData.battalionTypes
|
||||
@@ -170,7 +170,7 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionId = Some(3),
|
||||
rulingFactionHeroIds = Vector(10),
|
||||
battalionIds = Vector(85),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
heroes = mapifyHeroes(
|
||||
@@ -221,7 +221,7 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionId = Some(3),
|
||||
rulingFactionHeroIds = Vector(10),
|
||||
battalionIds = Vector(85),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
heroes = mapifyHeroes(
|
||||
@@ -272,7 +272,7 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionId = Some(3),
|
||||
rulingFactionHeroIds = Vector(10),
|
||||
battalionIds = Vector(85),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
heroes = mapifyHeroes(
|
||||
@@ -312,7 +312,7 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionId = Some(3),
|
||||
rulingFactionHeroIds = Vector(10),
|
||||
battalionIds = Vector(85),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
heroes = mapifyHeroes(
|
||||
@@ -343,7 +343,7 @@ class CommandChoiceHelpersTest
|
||||
battalionIds = Vector(11, 12),
|
||||
gold = 3500,
|
||||
support = 40,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -373,7 +373,7 @@ class CommandChoiceHelpersTest
|
||||
battalionIds = Vector(11, 12),
|
||||
gold = 3500,
|
||||
support = 40,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -403,7 +403,7 @@ class CommandChoiceHelpersTest
|
||||
battalionIds = Vector(11, 12),
|
||||
gold = 3500,
|
||||
support = 40,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -428,7 +428,7 @@ class CommandChoiceHelpersTest
|
||||
battalionIds = Vector(11, 12),
|
||||
gold = 3500,
|
||||
support = 40,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -458,7 +458,7 @@ class CommandChoiceHelpersTest
|
||||
battalionIds = Vector(11, 12),
|
||||
gold = 10000,
|
||||
support = 40,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -488,7 +488,7 @@ class CommandChoiceHelpersTest
|
||||
battalionIds = Vector(11, 12),
|
||||
food = 5000,
|
||||
support = 40,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -518,7 +518,7 @@ class CommandChoiceHelpersTest
|
||||
battalionIds = Vector(11, 12),
|
||||
food = 5000,
|
||||
support = 40,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -548,7 +548,7 @@ class CommandChoiceHelpersTest
|
||||
battalionIds = Vector(11, 12),
|
||||
food = 5000,
|
||||
support = 40,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -579,7 +579,7 @@ class CommandChoiceHelpersTest
|
||||
gold = 1000,
|
||||
food = 2000,
|
||||
support = 40,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -610,7 +610,7 @@ class CommandChoiceHelpersTest
|
||||
gold = 10000,
|
||||
food = 20000,
|
||||
support = 30,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -645,13 +645,13 @@ class CommandChoiceHelpersTest
|
||||
Neighbor(provinceId = 6),
|
||||
Neighbor(provinceId = 7)
|
||||
),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(
|
||||
id = 3,
|
||||
rulingFactionId = Some(15),
|
||||
heroCap = 5,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(
|
||||
id = 4,
|
||||
@@ -659,7 +659,7 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionHeroIds = (31 to 34).toVector,
|
||||
neighbors = Vector(Neighbor(provinceId = 5)),
|
||||
heroCap = 5,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(
|
||||
id = 6,
|
||||
@@ -667,7 +667,7 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionHeroIds = (41 to 44).toVector,
|
||||
neighbors = Vector(Neighbor(provinceId = 5)),
|
||||
heroCap = 5,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(
|
||||
id = 7,
|
||||
@@ -675,7 +675,7 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionHeroIds = (51 to 54).toVector,
|
||||
neighbors = Vector(Neighbor(provinceId = 5)),
|
||||
heroCap = 5,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -827,17 +827,17 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionId = Some(17),
|
||||
rulingFactionHeroIds = Vector(10, 11),
|
||||
neighbors = Vector(Neighbor(provinceId = 4)),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(17),
|
||||
rulingFactionHeroIds = Vector(1),
|
||||
neighbors = Vector(Neighbor(provinceId = 3)),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(id = 1, priceIndex = 1.0f),
|
||||
Province(id = 2, priceIndex = 1.0f)
|
||||
Province(id = 1, priceIndex = 1.0),
|
||||
Province(id = 2, priceIndex = 1.0)
|
||||
),
|
||||
heroes = mapifyHeroes(
|
||||
Hero(id = 10, vigor = 90, constitution = 90),
|
||||
@@ -872,17 +872,17 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionId = Some(17),
|
||||
rulingFactionHeroIds = Vector(10, 11),
|
||||
neighbors = Vector(Neighbor(provinceId = 4)),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(17),
|
||||
rulingFactionHeroIds = Vector(1),
|
||||
neighbors = Vector(Neighbor(provinceId = 3)),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(id = 1, priceIndex = 1.0f),
|
||||
Province(id = 2, priceIndex = 1.0f)
|
||||
Province(id = 1, priceIndex = 1.0),
|
||||
Province(id = 2, priceIndex = 1.0)
|
||||
),
|
||||
heroes = mapifyHeroes(
|
||||
Hero(id = 10, vigor = 90, constitution = 90),
|
||||
@@ -934,17 +934,17 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionId = Some(17),
|
||||
rulingFactionHeroIds = Vector(10, 11),
|
||||
neighbors = Vector(Neighbor(provinceId = 4)),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(17),
|
||||
rulingFactionHeroIds = Vector(1),
|
||||
neighbors = Vector(Neighbor(provinceId = 3)),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(id = 1, priceIndex = 1.0f),
|
||||
Province(id = 2, priceIndex = 1.0f)
|
||||
Province(id = 1, priceIndex = 1.0),
|
||||
Province(id = 2, priceIndex = 1.0)
|
||||
),
|
||||
heroes = mapifyHeroes(
|
||||
Hero(id = 10, vigor = 90, constitution = 90),
|
||||
@@ -990,7 +990,7 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionId = Some(17),
|
||||
rulingFactionHeroIds = Vector(10, 11),
|
||||
battalionIds = Vector(5, 6),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
@@ -1021,7 +1021,7 @@ class CommandChoiceHelpersTest
|
||||
rulingFactionId = Some(17),
|
||||
rulingFactionHeroIds = Vector(10, 11),
|
||||
battalionIds = Vector(5, 6),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
battalions = mapifyBattalions(
|
||||
|
||||
+11
-11
@@ -129,13 +129,13 @@ class ExpandCommandSelectorTest
|
||||
Vector(Neighbor(provinceId = 10, startingPositionIndex = 1)),
|
||||
rulingFactionId = Some(33),
|
||||
rulingFactionHeroIds = Vector(1, 2, 3),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(
|
||||
id = 10,
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 9, startingPositionIndex = 8)),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
factions = mapifyFactions(
|
||||
@@ -191,13 +191,13 @@ class ExpandCommandSelectorTest
|
||||
Vector(Neighbor(provinceId = 10, startingPositionIndex = 1)),
|
||||
rulingFactionId = Some(33),
|
||||
rulingFactionHeroIds = Vector(1, 2, 3),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
),
|
||||
Province(
|
||||
id = 10,
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 9, startingPositionIndex = 8)),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
),
|
||||
factions = mapifyFactions(
|
||||
@@ -238,7 +238,7 @@ class ExpandCommandSelectorTest
|
||||
),
|
||||
rulingFactionId = Some(33),
|
||||
rulingFactionHeroIds = Vector(1, 2, 3),
|
||||
priceIndex = 1.0f,
|
||||
priceIndex = 1.0,
|
||||
heroCap = 10,
|
||||
gold = 2500,
|
||||
food = 3500
|
||||
@@ -247,21 +247,21 @@ class ExpandCommandSelectorTest
|
||||
private val unownedProvince1 = Province(
|
||||
id = 10,
|
||||
neighbors = Vector(Neighbor(provinceId = 9, startingPositionIndex = 8)),
|
||||
priceIndex = 1.0f,
|
||||
priceIndex = 1.0,
|
||||
heroCap = 10
|
||||
)
|
||||
|
||||
private val unownedProvince2 = Province(
|
||||
id = 11,
|
||||
neighbors = Vector(Neighbor(provinceId = 9, startingPositionIndex = 8)),
|
||||
priceIndex = 1.0f,
|
||||
priceIndex = 1.0,
|
||||
heroCap = 10
|
||||
)
|
||||
|
||||
private val unownedProvince3 = Province(
|
||||
id = 12,
|
||||
neighbors = Vector(Neighbor(provinceId = 9, startingPositionIndex = 8)),
|
||||
priceIndex = 1.0f,
|
||||
priceIndex = 1.0,
|
||||
heroCap = 10
|
||||
)
|
||||
|
||||
@@ -270,14 +270,14 @@ class ExpandCommandSelectorTest
|
||||
neighbors = Vector(Neighbor(provinceId = 9, startingPositionIndex = 8)),
|
||||
rulingFactionId = Some(33),
|
||||
rulingFactionHeroIds = Vector(1, 2, 3),
|
||||
priceIndex = 1.0f,
|
||||
priceIndex = 1.0,
|
||||
heroCap = 10
|
||||
)
|
||||
|
||||
private val enRouteProvince = Province(
|
||||
id = 14,
|
||||
neighbors = Vector(Neighbor(provinceId = 9, startingPositionIndex = 8)),
|
||||
priceIndex = 1.0f,
|
||||
priceIndex = 1.0,
|
||||
incomingArmies = Vector(
|
||||
MovingArmy(
|
||||
army = Some(Army(factionId = 33))
|
||||
@@ -289,7 +289,7 @@ class ExpandCommandSelectorTest
|
||||
private val hostileProvince = Province(
|
||||
id = 15,
|
||||
neighbors = Vector(Neighbor(provinceId = 9, startingPositionIndex = 8)),
|
||||
priceIndex = 1.0f,
|
||||
priceIndex = 1.0,
|
||||
rulingFactionId = Some(34),
|
||||
heroCap = 10
|
||||
)
|
||||
|
||||
+2
-2
@@ -31,8 +31,8 @@ class FoodConsumptionUtilsTest
|
||||
PrestigePerSupportedProvince.setDoubleValue(15)
|
||||
MinSupportForTaxes.setDoubleValue(40)
|
||||
GoldPerHeroHeldBack.setDoubleValue(100)
|
||||
AlmsSupportIncreasePerFood.setFloatValue(0.01f)
|
||||
AlmsPaladinSupportMultiplier.setFloatValue(4.0f)
|
||||
AlmsSupportIncreasePerFood.setDoubleValue(0.01)
|
||||
AlmsPaladinSupportMultiplier.setDoubleValue(4.0)
|
||||
BaseFoodBuyPrice.setDoubleValue(0.25)
|
||||
VassalMinimumFoodToSendSupplies.setIntValue(200)
|
||||
VassalMinimumGoldToSendSupplies.setIntValue(100)
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ class AlmsToProvinceQuestCommandChooserTest
|
||||
|
||||
override def beforeEach(): Unit = {
|
||||
MinSupportForTaxes.setDoubleValue(40)
|
||||
AlmsSupportIncreasePerFood.setFloatValue(0.001f)
|
||||
AlmsSupportIncreasePerFood.setDoubleValue(0.001)
|
||||
}
|
||||
|
||||
"AlmsToProvinceQuestCommandChooser" should "return an alms command for an unaffiliated hero with that quest" in {
|
||||
|
||||
+3
-3
@@ -188,7 +188,7 @@ class QuestCreationUtilsTest
|
||||
forAll(
|
||||
QuestCreationUtils
|
||||
.availableQuests(
|
||||
province = province.withAgriculture(95.0f),
|
||||
province = province.withAgriculture(95.0),
|
||||
allProvinces = allProvinces,
|
||||
factions = factions,
|
||||
battalions = Vector(),
|
||||
@@ -223,7 +223,7 @@ class QuestCreationUtilsTest
|
||||
forAll(
|
||||
QuestCreationUtils
|
||||
.availableQuests(
|
||||
province = province.withEconomy(95.0f),
|
||||
province = province.withEconomy(95.0),
|
||||
allProvinces = allProvinces,
|
||||
factions = factions,
|
||||
battalions = Vector(),
|
||||
@@ -258,7 +258,7 @@ class QuestCreationUtilsTest
|
||||
forAll(
|
||||
QuestCreationUtils
|
||||
.availableQuests(
|
||||
province = province.withInfrastructure(95.0f),
|
||||
province = province.withInfrastructure(95.0),
|
||||
allProvinces = allProvinces,
|
||||
factions = factions,
|
||||
battalions = Vector(),
|
||||
|
||||
@@ -105,7 +105,7 @@ class RuntimeValidatorTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
"validate province" should "throw if the province id is 0" in {
|
||||
val province = Province(id = 0, priceIndex = 1.0f)
|
||||
val province = Province(id = 0, priceIndex = 1.0)
|
||||
|
||||
the[EagleInternalException] thrownBy {
|
||||
RuntimeValidator.validate(province, RoundPhase.PLAYER_COMMANDS)
|
||||
@@ -113,7 +113,7 @@ class RuntimeValidatorTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "throw if the price index is 0.0" in {
|
||||
val province = Province(id = 17, priceIndex = 0.0f, name = "Vovimaria")
|
||||
val province = Province(id = 17, priceIndex = 0.0, name = "Vovimaria")
|
||||
|
||||
the[EagleInternalException] thrownBy {
|
||||
RuntimeValidator.validate(province, RoundPhase.PLAYER_COMMANDS)
|
||||
@@ -121,7 +121,7 @@ class RuntimeValidatorTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "throw if the price index is negative" in {
|
||||
val province = Province(id = 17, priceIndex = -0.4f, name = "Fluria")
|
||||
val province = Province(id = 17, priceIndex = -0.4, name = "Fluria")
|
||||
|
||||
the[EagleInternalException] thrownBy {
|
||||
RuntimeValidator.validate(province, RoundPhase.PLAYER_COMMANDS)
|
||||
@@ -129,7 +129,7 @@ class RuntimeValidatorTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "not throw if the province id is valid and the priceIndex is valid" in {
|
||||
val province = Province(id = 17, priceIndex = 1.0f)
|
||||
val province = Province(id = 17, priceIndex = 1.0)
|
||||
|
||||
noException should be thrownBy {
|
||||
RuntimeValidator.validate(province, RoundPhase.PLAYER_COMMANDS)
|
||||
@@ -188,7 +188,7 @@ class RuntimeValidatorTest extends AnyFlatSpec with Matchers {
|
||||
rulingFactionId = Some(2),
|
||||
rulingFactionHeroIds = Vector(3, 7),
|
||||
provinceOrders = ProvinceOrderType.ENTRUST,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
noException should be thrownBy {
|
||||
@@ -202,7 +202,7 @@ class RuntimeValidatorTest extends AnyFlatSpec with Matchers {
|
||||
rulingHeroId = Some(3),
|
||||
rulingFactionHeroIds = Vector(3, 7),
|
||||
provinceOrders = ProvinceOrderType.ENTRUST,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
the[EagleInternalException] thrownBy {
|
||||
@@ -215,7 +215,7 @@ class RuntimeValidatorTest extends AnyFlatSpec with Matchers {
|
||||
id = 18,
|
||||
rulingHeroId = Some(3),
|
||||
rulingFactionHeroIds = Vector(3, 7),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
noException shouldBe thrownBy {
|
||||
@@ -229,7 +229,7 @@ class RuntimeValidatorTest extends AnyFlatSpec with Matchers {
|
||||
rulingFactionId = Some(2),
|
||||
rulingHeroId = Some(4),
|
||||
rulingFactionHeroIds = Vector(3, 7),
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
the[EagleInternalException] thrownBy {
|
||||
@@ -252,7 +252,7 @@ class RuntimeValidatorTest extends AnyFlatSpec with Matchers {
|
||||
rulingHeroId = Some(3),
|
||||
rulingFactionHeroIds = Vector(3, 7),
|
||||
provinceOrders = ENTRUST,
|
||||
priceIndex = 1.0f
|
||||
priceIndex = 1.0
|
||||
)
|
||||
|
||||
noException should be thrownBy {
|
||||
|
||||
+32
-6
@@ -65,9 +65,9 @@ class ProvinceViewFilterTest
|
||||
)
|
||||
)
|
||||
|
||||
it should "keep a devastation of 0 at 0" in {
|
||||
it should "round up a devastation of 0.1" in {
|
||||
val province =
|
||||
Province(id = 5, rulingFactionId = Some(3), economyDevastation = 0.0f)
|
||||
Province(id = 5, rulingFactionId = Some(3), economyDevastation = 0.1)
|
||||
|
||||
val provinceView = ProvinceViewFilter.filteredProvinceView(
|
||||
province = province,
|
||||
@@ -75,12 +75,12 @@ class ProvinceViewFilterTest
|
||||
factionId = 3
|
||||
)
|
||||
|
||||
provinceView.getFullInfo.economyDevastation shouldBe 0f
|
||||
provinceView.getFullInfo.economyDevastation shouldBe 1
|
||||
}
|
||||
|
||||
it should "put a devastation of 2.1 at 2.1" in {
|
||||
it should "keep a devastation of 0 at 0" in {
|
||||
val province =
|
||||
Province(id = 5, rulingFactionId = Some(3), economyDevastation = 2.1f)
|
||||
Province(id = 5, rulingFactionId = Some(3), economyDevastation = 0.0)
|
||||
|
||||
val provinceView = ProvinceViewFilter.filteredProvinceView(
|
||||
province = province,
|
||||
@@ -88,7 +88,33 @@ class ProvinceViewFilterTest
|
||||
factionId = 3
|
||||
)
|
||||
|
||||
provinceView.getFullInfo.economyDevastation shouldBe 2.1f
|
||||
provinceView.getFullInfo.economyDevastation shouldBe 0
|
||||
}
|
||||
|
||||
it should "put a devastation of 2.1 at 3" in {
|
||||
val province =
|
||||
Province(id = 5, rulingFactionId = Some(3), economyDevastation = 2.1)
|
||||
|
||||
val provinceView = ProvinceViewFilter.filteredProvinceView(
|
||||
province = province,
|
||||
gs = GameState(provinces = Map(5 -> province)),
|
||||
factionId = 3
|
||||
)
|
||||
|
||||
provinceView.getFullInfo.economyDevastation shouldBe 3
|
||||
}
|
||||
|
||||
it should "put an economy of 0.1 at 1" in {
|
||||
val province =
|
||||
Province(id = 5, rulingFactionId = Some(3), economy = 0.1)
|
||||
|
||||
val provinceView = ProvinceViewFilter.filteredProvinceView(
|
||||
province = province,
|
||||
gs = GameState(provinces = Map(5 -> province)),
|
||||
factionId = 3
|
||||
)
|
||||
|
||||
provinceView.getFullInfo.economy shouldBe 1
|
||||
}
|
||||
|
||||
it should "show ruler is traveling if they are" in {
|
||||
|
||||
Reference in New Issue
Block a user