mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 21:15:45 +00:00
+30
-1
@@ -1,12 +1,41 @@
|
||||
version = "3.9.9"
|
||||
runner.dialect = scala3
|
||||
rewrite.scala3.convertToNewSyntax = true
|
||||
# Keep braces, don't use significant indentation
|
||||
# rewrite.scala3.removeOptionalBraces = yes
|
||||
rewrite.scala3.insertEndMarkerMinLines = 15
|
||||
rewrite.scala3.removeEndMarkerMaxLines = 14
|
||||
|
||||
# Strip margin settings
|
||||
assumeStandardLibraryStripMargin = false
|
||||
align.stripMargin = true
|
||||
|
||||
# Code Style & Formatting
|
||||
align.preset = more
|
||||
align.multiline = true
|
||||
align.arrowEnumeratorGenerator = true
|
||||
spaces.inImportCurlyBraces = false
|
||||
spaces.beforeContextBoundColon = Never
|
||||
maxColumn = 120
|
||||
docstrings.style = Asterisk
|
||||
docstrings.wrap = yes
|
||||
|
||||
# Method chaining
|
||||
newlines.beforeCurlyLambdaParams = multilineWithCaseOnly
|
||||
optIn.breakChainOnFirstMethodDot = true
|
||||
includeCurlyBraceInSelectChains = false
|
||||
|
||||
# Advanced Scala 3 Features
|
||||
rewrite.scala3.countEndMarkerLines = all
|
||||
rewrite.redundantBraces.stringInterpolation = true
|
||||
rewrite.redundantBraces.parensForOneLineApply = true
|
||||
|
||||
# Project-Specific Considerations
|
||||
optIn.annotationNewlines = true
|
||||
runner.optimizer.forceConfigStyleMinArgCount = 3
|
||||
|
||||
# Import sorting configuration
|
||||
rewrite.rules = [SortImports]
|
||||
rewrite.rules = [SortImports, RedundantBraces, RedundantParens]
|
||||
rewrite.imports.sort = scalastyle
|
||||
rewrite.imports.groups = [
|
||||
["java\\..*"],
|
||||
|
||||
@@ -64,8 +64,7 @@ abstract class FunctionalRandom {
|
||||
val diff = lessThan - atLeast
|
||||
val max = (Int.MaxValue / diff) * diff
|
||||
val np = nextPositiveInt
|
||||
if np.newValue > max then
|
||||
np.nextRandom.nextInt(atLeast = atLeast, lessThan = lessThan)
|
||||
if np.newValue > max then np.nextRandom.nextInt(atLeast = atLeast, lessThan = lessThan)
|
||||
else RandomState(np.newValue % diff + atLeast, np.nextRandom)
|
||||
}
|
||||
}
|
||||
@@ -127,10 +126,9 @@ abstract class FunctionalRandom {
|
||||
shuffledHead: Vector[T],
|
||||
unshuffledTail: Vector[T],
|
||||
fr: FunctionalRandom
|
||||
): RandomState[Vector[T]] = {
|
||||
): RandomState[Vector[T]] =
|
||||
if unshuffledTail.isEmpty then RandomState(shuffledHead, fr)
|
||||
else if unshuffledTail.length == 1 then
|
||||
RandomState(shuffledHead ++ unshuffledTail, fr)
|
||||
else if unshuffledTail.length == 1 then RandomState(shuffledHead ++ unshuffledTail, fr)
|
||||
else {
|
||||
val ni = fr.nextInt(0, unshuffledTail.length)
|
||||
val index = ni.newValue
|
||||
@@ -150,13 +148,12 @@ abstract class FunctionalRandom {
|
||||
go(nextHead, newTail, ni.nextRandom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go(Vector(), seq, this)
|
||||
}
|
||||
|
||||
def nextRandomSubset[T](size: Int, seq: Vector[T]): RandomState[Vector[T]] =
|
||||
nextShuffled(seq).map { _.take(size) }
|
||||
nextShuffled(seq).map(_.take(size))
|
||||
|
||||
private def nextFlatMapImpl[T, U, S[X] <: Seq[X], FACT <: SeqFactory[S]](
|
||||
seq: S[T]
|
||||
@@ -164,7 +161,8 @@ abstract class FunctionalRandom {
|
||||
f: (T, FunctionalRandom) => RandomState[S[U]]
|
||||
)(using xFactory: FACT): RandomState[S[U]] = {
|
||||
@nowarn def foldGen(rsU: RandomState[S[U]], t: T): RandomState[S[U]] =
|
||||
rsU.continue { case (us: S[U], fr: FunctionalRandom) =>
|
||||
rsU.continue {
|
||||
case (us: S[U], fr: FunctionalRandom) =>
|
||||
f(t, fr).map(x => (us ++ x).asInstanceOf[S[U]])
|
||||
}
|
||||
FunctionalRandom.nextFoldLeft(
|
||||
@@ -189,7 +187,8 @@ abstract class FunctionalRandom {
|
||||
rsU: RandomState[S[U]],
|
||||
t: T
|
||||
): RandomState[S[U]] =
|
||||
rsU.continue { case (us: S[U], fr: FunctionalRandom) =>
|
||||
rsU.continue {
|
||||
case (us: S[U], fr: FunctionalRandom) =>
|
||||
f(t, fr).map(x => (us :+ x).asInstanceOf[S[U]])
|
||||
}
|
||||
|
||||
@@ -213,8 +212,7 @@ abstract class FunctionalRandom {
|
||||
): RandomState[Vector[U]] = remVec match {
|
||||
case head +: tail =>
|
||||
val newAccRS =
|
||||
if pf.isDefinedAt(head) then
|
||||
pf(head)(accRS.nextRandom).map(accRS.newValue :+ _)
|
||||
if pf.isDefinedAt(head) then pf(head)(accRS.nextRandom).map(accRS.newValue :+ _)
|
||||
else accRS
|
||||
go(tail, newAccRS)
|
||||
case _ => accRS
|
||||
@@ -249,8 +247,7 @@ abstract class FunctionalRandom {
|
||||
case items if items.isEmpty => accRS
|
||||
case head +: tail =>
|
||||
val newAccRS =
|
||||
if pf.isDefinedAt(head) then
|
||||
pf(head)(accRS.nextRandom).map(accRS.newValue ++ _)
|
||||
if pf.isDefinedAt(head) then pf(head)(accRS.nextRandom).map(accRS.newValue ++ _)
|
||||
else accRS
|
||||
go(tail, newAccRS)
|
||||
case _ => ??? // above cases should cover
|
||||
|
||||
@@ -18,11 +18,9 @@ final case class GrpcRetrier[Stub <: AbstractStub[Stub]](grpcClient: Stub) {
|
||||
private def go[Response](
|
||||
clientOp: Stub => Future[Response],
|
||||
delayMillis: Long
|
||||
): Future[Response] = {
|
||||
clientOp(grpcClient.withDeadlineAfter(timeoutSeconds, TimeUnit.SECONDS))
|
||||
.recoverWith {
|
||||
case sre: StatusRuntimeException
|
||||
if sre.getStatus.getCode == Status.Code.UNAVAILABLE =>
|
||||
): Future[Response] =
|
||||
clientOp(grpcClient.withDeadlineAfter(timeoutSeconds, TimeUnit.SECONDS)).recoverWith {
|
||||
case sre: StatusRuntimeException if sre.getStatus.getCode == Status.Code.UNAVAILABLE =>
|
||||
println(
|
||||
s"Unavailable, retrying in ${TimeUnit.MILLISECONDS.toSeconds(delayMillis)}s..."
|
||||
)
|
||||
@@ -31,8 +29,7 @@ final case class GrpcRetrier[Stub <: AbstractStub[Stub]](grpcClient: Stub) {
|
||||
clientOp = clientOp,
|
||||
delayMillis = Math.min(delayMillis * 2, maxDelayMillis)
|
||||
)
|
||||
case sre: StatusRuntimeException
|
||||
if sre.getStatus.getCode == Status.Code.CANCELLED =>
|
||||
case sre: StatusRuntimeException if sre.getStatus.getCode == Status.Code.CANCELLED =>
|
||||
println("Cancelled, reconnecting...")
|
||||
go(
|
||||
clientOp = clientOp,
|
||||
@@ -40,7 +37,6 @@ final case class GrpcRetrier[Stub <: AbstractStub[Stub]](grpcClient: Stub) {
|
||||
)
|
||||
case fr: Throwable => throw fr
|
||||
}
|
||||
}
|
||||
|
||||
def withRetries[Response](
|
||||
clientOp: Stub => Future[Response]
|
||||
|
||||
@@ -19,15 +19,15 @@ case class Metrics[T](logFrequency: Int) {
|
||||
}
|
||||
|
||||
def logCounters: Unit = this.synchronized {
|
||||
counters.toVector.sortBy(-_._2).foreach { case (t, count) =>
|
||||
counters.toVector.sortBy(-_._2).foreach {
|
||||
case (t, count) =>
|
||||
println(s"$count $t")
|
||||
}
|
||||
}
|
||||
|
||||
def maybeLogCounters: Unit = {
|
||||
def maybeLogCounters: Unit =
|
||||
if totalCount - lastLog > logFrequency then {
|
||||
logCounters
|
||||
lastLog = totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@ object MoreSeq {
|
||||
|
||||
def irregularTranspose[A](coll: Iterable[Seq[A]]): Vector[Vector[A]] = {
|
||||
val maxLength = coll.map(_.length).max
|
||||
coll.foldLeft(Vector.fill(maxLength)(Vector[A]())) { case (acc, nextLine) =>
|
||||
acc.zipWithIndex.map { case (vec, idx) =>
|
||||
coll.foldLeft(Vector.fill(maxLength)(Vector[A]())) {
|
||||
case (acc, nextLine) =>
|
||||
acc.zipWithIndex.map {
|
||||
case (vec, idx) =>
|
||||
if idx < nextLine.length then vec :+ nextLine(idx)
|
||||
else vec
|
||||
}
|
||||
|
||||
@@ -44,15 +44,14 @@ object SimpleTimedLogger {
|
||||
override def logLine(str: String): Unit = print(formatLine(str))
|
||||
}
|
||||
|
||||
private class SimpleTimedLogger(osConstructor: () => OutputStream)
|
||||
extends SimpleTimedLoggerT {
|
||||
private class SimpleTimedLogger(osConstructor: () => OutputStream) extends SimpleTimedLoggerT {
|
||||
def log(str: String): Unit = write(formatNoLine(str))
|
||||
|
||||
def logLine(str: String): Unit = write(formatLine(str))
|
||||
|
||||
private def write(str: String): Unit =
|
||||
Using(new OutputStreamWriter(osConstructor(), StandardCharsets.UTF_8)) {
|
||||
fw => fw.write(str)
|
||||
Using(new OutputStreamWriter(osConstructor(), StandardCharsets.UTF_8)) { fw =>
|
||||
fw.write(str)
|
||||
} match {
|
||||
case Success(_) => ()
|
||||
case Failure(exception) =>
|
||||
|
||||
@@ -34,11 +34,11 @@ object TsvUtils {
|
||||
}
|
||||
|
||||
def loadLines(url: URL): Try[Vector[Vector[String]]] =
|
||||
Using(Source.fromURL(url, "UTF-8")) { loadLines }
|
||||
Using(Source.fromURL(url, "UTF-8"))(loadLines)
|
||||
|
||||
// Loads a tsv and returns a Vector of arrays of strings.
|
||||
def loadLines(path: String): Try[Vector[Vector[String]]] =
|
||||
Using(Source.fromFile(path, "UTF-8")) { loadLines }
|
||||
Using(Source.fromFile(path, "UTF-8"))(loadLines)
|
||||
|
||||
def loadMaps(url: URL): Try[Vector[ListMap[String, String]]] =
|
||||
loadLines(url).map(linesToMaps)
|
||||
@@ -90,10 +90,9 @@ object TsvUtils {
|
||||
val file = new File(url.getPath)
|
||||
val pw = new PrintWriter(file)
|
||||
|
||||
try {
|
||||
try
|
||||
lines.foreach(line => pw.write(line.mkString("\t") + "\n"))
|
||||
} finally {
|
||||
finally
|
||||
pw.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ object ApiKeys {
|
||||
private val openAIKeyName = "openai_api_key"
|
||||
private val anthropicKeyName = "anthropic_api_key"
|
||||
|
||||
private def readDictionary(): Map[String, String] = {
|
||||
private def readDictionary(): Map[String, String] =
|
||||
Using(scala.io.Source.fromFile(apiKeyFilePath)) { source =>
|
||||
val lines = source.getLines().filterNot(_.startsWith("#")).toList
|
||||
lines.map { line =>
|
||||
@@ -27,11 +27,10 @@ object ApiKeys {
|
||||
"Failed to read api_keys.txt. Make sure you have copied api_keys_template.txt to api_keys.txt and set the keys."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val dictionary = readDictionary()
|
||||
|
||||
private def getKey(key: String): String = {
|
||||
private def getKey(key: String): String =
|
||||
dictionary.get(key) match {
|
||||
case None =>
|
||||
throw new ApiKeyException(s"$key not found")
|
||||
@@ -41,7 +40,6 @@ object ApiKeys {
|
||||
)
|
||||
case Some(value) => value
|
||||
}
|
||||
}
|
||||
|
||||
lazy val openAI: String = getKey(openAIKeyName)
|
||||
|
||||
|
||||
@@ -7,10 +7,7 @@ import java.time.{Duration, ZonedDateTime}
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.function.Consumer
|
||||
|
||||
import net.eagle0.common.llm_integration.ClaudeServiceImpl.{
|
||||
dictionaryFor,
|
||||
messageVector
|
||||
}
|
||||
import net.eagle0.common.llm_integration.ClaudeServiceImpl.{dictionaryFor, messageVector}
|
||||
import org.json4s.{DefaultFormats, JString}
|
||||
import org.json4s.jvalue2extractable
|
||||
import org.json4s.jvalue2monadic
|
||||
@@ -142,8 +139,7 @@ class ClaudeServiceImpl(
|
||||
tokensRemaining <- headers.get("anthropic-ratelimit-tokens-remaining")
|
||||
requestResetTime <- headers.get("anthropic-ratelimit-requests-reset")
|
||||
tokenResetTime <- headers.get("anthropic-ratelimit-tokens-reset")
|
||||
} yield {
|
||||
RateLimits(
|
||||
} yield RateLimits(
|
||||
requestLimit = requestLimit.head.toInt,
|
||||
tokenLimit = tokenLimit.head.toInt,
|
||||
requestsRemaining = requestsRemaining.head.toInt,
|
||||
@@ -157,5 +153,4 @@ class ClaudeServiceImpl(
|
||||
rfc3339formatter
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-17
@@ -1,11 +1,6 @@
|
||||
package net.eagle0.common.llm_integration
|
||||
|
||||
import java.net.http.{
|
||||
HttpClient,
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
HttpTimeoutException
|
||||
}
|
||||
import java.net.http.{HttpClient, HttpRequest, HttpResponse, HttpTimeoutException}
|
||||
import java.net.http.HttpClient.{Redirect, Version}
|
||||
import java.net.http.HttpResponse.ResponseInfo
|
||||
import java.net.HttpURLConnection
|
||||
@@ -76,9 +71,8 @@ final class ExternalTextGenerationCaller(
|
||||
inputText: String,
|
||||
partialCompletion: Option[String],
|
||||
streamingConsumer: Consumer[StreamingTextResults],
|
||||
backoffSeconds: Double =
|
||||
ExternalTextGenerationCaller.initialBackoffSeconds
|
||||
): Future[HttpResponse[Unit]] = {
|
||||
backoffSeconds: Double = ExternalTextGenerationCaller.initialBackoffSeconds
|
||||
): Future[HttpResponse[Unit]] =
|
||||
streamCompletion(
|
||||
inputText = inputText,
|
||||
request = serviceImpl.makeRequest(
|
||||
@@ -88,7 +82,6 @@ final class ExternalTextGenerationCaller(
|
||||
backoffSeconds = backoffSeconds,
|
||||
streamingConsumer = streamingConsumer
|
||||
)
|
||||
}
|
||||
|
||||
private def streamCompletion(
|
||||
inputText: String,
|
||||
@@ -105,17 +98,15 @@ final class ExternalTextGenerationCaller(
|
||||
val t = new Timer()
|
||||
t.schedule(
|
||||
new TimerTask {
|
||||
override def run(): Unit = {
|
||||
override def run(): Unit =
|
||||
promise.completeWith(
|
||||
streamCompletion(
|
||||
inputText = inputText,
|
||||
request = request,
|
||||
backoffSeconds =
|
||||
ExternalTextGenerationCaller.nextBackoff(backoffSeconds),
|
||||
backoffSeconds = ExternalTextGenerationCaller.nextBackoff(backoffSeconds),
|
||||
streamingConsumer = streamingConsumer
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
(backoffSeconds * 1000).toLong
|
||||
)
|
||||
@@ -126,17 +117,17 @@ final class ExternalTextGenerationCaller(
|
||||
httpClient
|
||||
.sendAsync(
|
||||
request,
|
||||
(respInfo: ResponseInfo) => {
|
||||
(respInfo: ResponseInfo) =>
|
||||
if respInfo.statusCode() == HttpURLConnection.HTTP_OK then
|
||||
new SseSubscriber(serviceImpl.stringConsumer(streamingConsumer))
|
||||
else
|
||||
throw new RuntimeException(
|
||||
s"Unexpected response code: ${respInfo.statusCode()}"
|
||||
)
|
||||
}
|
||||
)
|
||||
.asScala
|
||||
.andThen { case x =>
|
||||
.andThen {
|
||||
case x =>
|
||||
inProgressCount -= 1
|
||||
x
|
||||
}
|
||||
|
||||
+1
-3
@@ -70,9 +70,7 @@ object ExternalTextGenerationCallerApp {
|
||||
val completion = caller.streamCompletion(
|
||||
inputText = inputText,
|
||||
partialCompletion = None,
|
||||
streamingConsumer = results => {
|
||||
print(results.value)
|
||||
}
|
||||
streamingConsumer = results => print(results.value)
|
||||
)
|
||||
Await.result(completion, Duration(500, SECONDS))
|
||||
println("")
|
||||
|
||||
+4
-9
@@ -6,10 +6,7 @@ import java.net.http.HttpRequest.BodyPublishers
|
||||
import java.time.{Duration, ZonedDateTime}
|
||||
import java.util.function.Consumer
|
||||
|
||||
import net.eagle0.common.llm_integration.OpenAIChatCompletionsServiceImpl.{
|
||||
dictionaryFor,
|
||||
messageVector
|
||||
}
|
||||
import net.eagle0.common.llm_integration.OpenAIChatCompletionsServiceImpl.{dictionaryFor, messageVector}
|
||||
import org.json4s.{DefaultFormats, JArray, JObject, JString}
|
||||
import org.json4s.jvalue2extractable
|
||||
import org.json4s.jvalue2monadic
|
||||
@@ -101,9 +98,9 @@ class OpenAIChatCompletionsServiceImpl(
|
||||
): Consumer[String] = (t: String) => {
|
||||
val json = new Json(DefaultFormats)
|
||||
val parsedJson =
|
||||
try {
|
||||
try
|
||||
json.parse(t)
|
||||
} catch {
|
||||
catch {
|
||||
case pe: org.json4s.ParserUtil.ParseException =>
|
||||
println(s"Failed to parse JSON: $t")
|
||||
throw pe
|
||||
@@ -160,8 +157,7 @@ class OpenAIChatCompletionsServiceImpl(
|
||||
tokensRemaining <- headers.get("x-ratelimit-remaining-tokens")
|
||||
requestResetTime <- headers.get("x-ratelimit-reset-requests")
|
||||
tokenResetTime <- headers.get("x-ratelimit-reset-tokens")
|
||||
} yield {
|
||||
RateLimits(
|
||||
} yield RateLimits(
|
||||
requestLimit = requestLimit.head.toInt,
|
||||
tokenLimit = tokenLimit.head.toInt,
|
||||
requestsRemaining = requestsRemaining.head.toInt,
|
||||
@@ -181,5 +177,4 @@ class OpenAIChatCompletionsServiceImpl(
|
||||
.getOrElse(Duration.ZERO)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ class StringConstructionParser(
|
||||
import ParseSequence.*
|
||||
|
||||
private def charString(seq: Seq[CharacterOrToken]): String =
|
||||
seq.collect { case C(char) =>
|
||||
seq.collect {
|
||||
case C(char) =>
|
||||
char
|
||||
}.mkString
|
||||
|
||||
@@ -25,8 +26,7 @@ class StringConstructionParser(
|
||||
def parseRemaining(str: String, acc: String): ParseStatus =
|
||||
str.head match {
|
||||
case '\\' =>
|
||||
if str.length < 2 then
|
||||
throw new IllegalArgumentException("Found \\ at end of literal")
|
||||
if str.length < 2 then throw new IllegalArgumentException("Found \\ at end of literal")
|
||||
else parseRemaining(str.drop(2), acc + str.charAt(1))
|
||||
case '\"' =>
|
||||
ParseStatus(str.drop(1), acc)
|
||||
@@ -41,12 +41,13 @@ class StringConstructionParser(
|
||||
|
||||
private def parseOptional(
|
||||
ts: ParseSequence
|
||||
): Try[(ParseSequence, StringConstructionToken)] = {
|
||||
): Try[(ParseSequence, StringConstructionToken)] =
|
||||
insideBalanced(ts, open = '{', closed = '}').flatMap {
|
||||
case (remainAfter, inWithOdds) =>
|
||||
val DecimalParseResults(inAfterOdds, odds) = readDecimal(inWithOdds)
|
||||
|
||||
parsePossiblyParsed(inAfterOdds).flatMap { case (rem, parsed) =>
|
||||
parsePossiblyParsed(inAfterOdds).flatMap {
|
||||
case (rem, parsed) =>
|
||||
if rem.nonEmpty then
|
||||
Failure(
|
||||
new IllegalArgumentException(
|
||||
@@ -56,11 +57,10 @@ class StringConstructionParser(
|
||||
else Success((remainAfter, OptionalToken(parsed, odds)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def parseOneof(
|
||||
ts: ParseSequence
|
||||
): Try[(ParseSequence, StringConstructionToken)] = {
|
||||
): Try[(ParseSequence, StringConstructionToken)] =
|
||||
insideBalanced(ts, open = '[', closed = ']').map {
|
||||
case (remainAfter, inside) =>
|
||||
var entries = Vector.empty[OneofListEntry]
|
||||
@@ -70,7 +70,8 @@ class StringConstructionParser(
|
||||
if t == ',' then pos = pos.drop(1)
|
||||
else {
|
||||
val DecimalParseResults(remIn, weight) = readDecimal(pos)
|
||||
parsePossiblyParsed(remIn).map { case (rem, parsed) =>
|
||||
parsePossiblyParsed(remIn).map {
|
||||
case (rem, parsed) =>
|
||||
entries = entries :+ OneofListEntry(parsed, weight)
|
||||
pos = rem
|
||||
}
|
||||
@@ -78,7 +79,6 @@ class StringConstructionParser(
|
||||
}
|
||||
(remainAfter, OneofListToken(entries))
|
||||
}
|
||||
}
|
||||
|
||||
def parseListSelector(
|
||||
ts: ParseSequence
|
||||
@@ -112,12 +112,11 @@ class StringConstructionParser(
|
||||
|
||||
def parseCapitalized(
|
||||
ts: ParseSequence
|
||||
): Try[(ParseSequence, StringConstructionToken)] = {
|
||||
): Try[(ParseSequence, StringConstructionToken)] =
|
||||
for {
|
||||
(remainAfter, inside) <- insideBalanced(ts, open = '-', closed = '+')
|
||||
(_, parsed) <- parsePossiblyParsed(inside)
|
||||
} yield (remainAfter, TitleCaseToken(parsed))
|
||||
}
|
||||
|
||||
def parseOrdinal(
|
||||
ts: ParseSequence
|
||||
@@ -146,7 +145,7 @@ class StringConstructionParser(
|
||||
def go(
|
||||
tokens: ParseSequence,
|
||||
acc: Seq[StringConstructionToken]
|
||||
): Try[(ParseSequence, Seq[StringConstructionToken])] = {
|
||||
): Try[(ParseSequence, Seq[StringConstructionToken])] =
|
||||
if tokens.isEmpty then Success((Vector.empty, acc))
|
||||
else {
|
||||
tokens.head match {
|
||||
@@ -168,9 +167,9 @@ class StringConstructionParser(
|
||||
case T(t) => go(tokens.drop(1), acc :+ t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go(tokens, Vector.empty).map { case (rem, ts) =>
|
||||
go(tokens, Vector.empty).map {
|
||||
case (rem, ts) =>
|
||||
(rem, if ts.size == 1 then ts.head else SequenceToken(ts))
|
||||
}
|
||||
}
|
||||
@@ -180,7 +179,7 @@ class StringConstructionParser(
|
||||
def parseNext(
|
||||
remainingString: String,
|
||||
acc: ParseSequence
|
||||
): ParseSequence = {
|
||||
): ParseSequence =
|
||||
if remainingString.isEmpty then acc
|
||||
else
|
||||
remainingString.head match {
|
||||
@@ -198,12 +197,11 @@ class StringConstructionParser(
|
||||
acc :+ C(remainingString.head)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val tokens = parseNext(formatString, Vector.empty)
|
||||
parsePossiblyParsed(tokens).flatMap { case (rem, tok) =>
|
||||
if rem.nonEmpty then
|
||||
Failure(new IllegalArgumentException("Failed to parse"))
|
||||
parsePossiblyParsed(tokens).flatMap {
|
||||
case (rem, tok) =>
|
||||
if rem.nonEmpty then Failure(new IllegalArgumentException("Failed to parse"))
|
||||
else Success(tok)
|
||||
}
|
||||
}
|
||||
@@ -226,8 +224,7 @@ class StringConstructionParser(
|
||||
pos = pos.drop(1)
|
||||
}
|
||||
|
||||
if level > 0 then
|
||||
Failure(new IllegalArgumentException("Did not find closing character"))
|
||||
if level > 0 then Failure(new IllegalArgumentException("Did not find closing character"))
|
||||
|
||||
Success((pos, inside))
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ object StringConstructionTester {
|
||||
val random = new JankyRandom(new Random())
|
||||
|
||||
print(s"---\n$typeName\n---\n")
|
||||
((1 to 20).toVector).foreach { _ =>
|
||||
(1 to 20).toVector.foreach { _ =>
|
||||
println(token.nextString(random, Map("PLACE" -> "Pieska")).newValue)
|
||||
}
|
||||
print("\n\n\n")
|
||||
|
||||
@@ -19,8 +19,7 @@ case class LiteralToken(literal: String) extends StringConstructionToken {
|
||||
RandomState(literal, random)
|
||||
}
|
||||
|
||||
class MissingLiteralKeyException(key: String)
|
||||
extends Exception(s"Missing literal key $key") {}
|
||||
class MissingLiteralKeyException(key: String) extends Exception(s"Missing literal key $key") {}
|
||||
|
||||
case class SubstitutionToken(key: String) extends StringConstructionToken {
|
||||
override def nextString(
|
||||
@@ -34,27 +33,25 @@ case class SubstitutionToken(key: String) extends StringConstructionToken {
|
||||
)
|
||||
}
|
||||
|
||||
case class SequenceToken(tokens: Seq[StringConstructionToken])
|
||||
extends StringConstructionToken {
|
||||
case class SequenceToken(tokens: Seq[StringConstructionToken]) extends StringConstructionToken {
|
||||
override def nextString(
|
||||
random: FunctionalRandom,
|
||||
literalSubstitutions: Map[String, String]
|
||||
): RandomState[String] =
|
||||
tokens.foldLeft(RandomState[String]("", random)) { case (state, tok) =>
|
||||
tokens.foldLeft(RandomState[String]("", random)) {
|
||||
case (state, tok) =>
|
||||
val nextStr = tok.nextString(state.nextRandom, literalSubstitutions)
|
||||
RandomState(state.newValue + nextStr.newValue, nextStr.nextRandom)
|
||||
}
|
||||
}
|
||||
|
||||
case class OptionalToken(token: StringConstructionToken, odds: Double)
|
||||
extends StringConstructionToken {
|
||||
case class OptionalToken(token: StringConstructionToken, odds: Double) extends StringConstructionToken {
|
||||
override def nextString(
|
||||
random: FunctionalRandom,
|
||||
literalSubstitutions: Map[String, String]
|
||||
): RandomState[String] = {
|
||||
val state = random.nextDouble
|
||||
if state.newValue < odds then
|
||||
token.nextString(state.nextRandom, literalSubstitutions)
|
||||
if state.newValue < odds then token.nextString(state.nextRandom, literalSubstitutions)
|
||||
else RandomState("", state.nextRandom)
|
||||
}
|
||||
}
|
||||
@@ -67,14 +64,14 @@ case class OneofListEntry(token: StringConstructionToken, weight: Double) {
|
||||
token.nextString(random, literalSubstitutions)
|
||||
}
|
||||
|
||||
case class OneofListToken(entries: Seq[OneofListEntry])
|
||||
extends StringConstructionToken {
|
||||
case class OneofListToken(entries: Seq[OneofListEntry]) extends StringConstructionToken {
|
||||
override def nextString(
|
||||
random: FunctionalRandom,
|
||||
literalSubstitutions: Map[String, String]
|
||||
): RandomState[String] = {
|
||||
val totalWeight = entries.map(_.weight).sum
|
||||
random.nextDouble.continue { case (double, fr) =>
|
||||
random.nextDouble.continue {
|
||||
case (double, fr) =>
|
||||
@tailrec
|
||||
def go(
|
||||
remainingEntries: Seq[OneofListEntry],
|
||||
@@ -92,8 +89,7 @@ case class OneofListToken(entries: Seq[OneofListEntry])
|
||||
}
|
||||
}
|
||||
|
||||
case class ListSelectionToken(choices: Seq[String])
|
||||
extends StringConstructionToken {
|
||||
case class ListSelectionToken(choices: Seq[String]) extends StringConstructionToken {
|
||||
override def nextString(
|
||||
random: FunctionalRandom,
|
||||
literalSubstitutions: Map[String, String]
|
||||
@@ -104,7 +100,7 @@ case class OrdinalSelectionToken(max: Int) extends StringConstructionToken {
|
||||
override def nextString(
|
||||
random: FunctionalRandom,
|
||||
literalSubstitutions: Map[String, String]
|
||||
): RandomState[String] = {
|
||||
): RandomState[String] =
|
||||
random.nextInt(1, max).map { value =>
|
||||
if value == 11 then "11th"
|
||||
else {
|
||||
@@ -116,11 +112,9 @@ case class OrdinalSelectionToken(max: Int) extends StringConstructionToken {
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class TitleCaseToken(base: StringConstructionToken)
|
||||
extends StringConstructionToken {
|
||||
case class TitleCaseToken(base: StringConstructionToken) extends StringConstructionToken {
|
||||
val uncapitalizedWords: Set[String] =
|
||||
Set("and", "but", "for", "or", "nor", "the", "a", "an", "to", "as", "of")
|
||||
|
||||
@@ -141,9 +135,8 @@ case class TitleCaseToken(base: StringConstructionToken)
|
||||
override def nextString(
|
||||
random: FunctionalRandom,
|
||||
literalSubstitutions: Map[String, String]
|
||||
): RandomState[String] = {
|
||||
): RandomState[String] =
|
||||
base.nextString(random, literalSubstitutions).map { cap =>
|
||||
firstAndLastCapitalized(byWordCapitalized(cap)).mkString(" ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-10
@@ -23,8 +23,8 @@ object StringConstructionTokenGenerator {
|
||||
}
|
||||
|
||||
val (stringsMap, unfilteredStringsMap) = {
|
||||
val allSpecializations = namesMap
|
||||
.map { case (key, words) =>
|
||||
val allSpecializations = namesMap.map {
|
||||
case (key, words) =>
|
||||
key.split("@", 2) match {
|
||||
case Array(realKey) => (realKey, "", words)
|
||||
case Array(realKey, spec) => (realKey, spec, words)
|
||||
@@ -33,15 +33,12 @@ object StringConstructionTokenGenerator {
|
||||
"Too many @s in column name"
|
||||
)
|
||||
}
|
||||
}
|
||||
.toVector
|
||||
.groupBy { _._1 }
|
||||
}.toVector.groupBy(_._1)
|
||||
|
||||
val stringsMap = allSpecializations
|
||||
.map { case (realWord, specializations) =>
|
||||
val includedSpecializations = specializations.filter(spec =>
|
||||
spec._2.isEmpty || atSpecializations.contains(spec._2)
|
||||
)
|
||||
val stringsMap = allSpecializations.map {
|
||||
case (realWord, specializations) =>
|
||||
val includedSpecializations =
|
||||
specializations.filter(spec => spec._2.isEmpty || atSpecializations.contains(spec._2))
|
||||
(realWord, includedSpecializations.flatMap(_._3))
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,7 @@ object SseEventReader {
|
||||
else
|
||||
readLines(
|
||||
IncompleteEvent(
|
||||
eventType =
|
||||
carryoverEvent.map(_.eventType).getOrElse(DefaultEventValue),
|
||||
eventType = carryoverEvent.map(_.eventType).getOrElse(DefaultEventValue),
|
||||
metadata = carryoverEvent.map(_.metadata).getOrElse(Map.empty),
|
||||
data = carryoverEvent.map(_.data).getOrElse(""),
|
||||
carryover = carryoverEvent.map(_.carryover).getOrElse("") + text
|
||||
@@ -81,7 +80,7 @@ object SseEventReader {
|
||||
}
|
||||
|
||||
@tailrec
|
||||
private def readLines(acc: IncompleteEvent): ReadResult = {
|
||||
private def readLines(acc: IncompleteEvent): ReadResult =
|
||||
acc.carryover.split("\n", 2) match {
|
||||
case Array("", moreText) => // blank line indicates end of event
|
||||
CompleteEvent(
|
||||
@@ -118,5 +117,4 @@ object SseEventReader {
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,9 @@ import scala.jdk.CollectionConverters.CollectionHasAsScala
|
||||
|
||||
import org.json4s.ParserUtil
|
||||
|
||||
class SseException(message: String, cause: Throwable = null)
|
||||
extends Exception(message, cause)
|
||||
class SseException(message: String, cause: Throwable = null) extends Exception(message, cause)
|
||||
|
||||
class SseSubscriber(messageDataConsumer: Consumer[String])
|
||||
extends BodySubscriber[Unit] {
|
||||
class SseSubscriber(messageDataConsumer: Consumer[String]) extends BodySubscriber[Unit] {
|
||||
@volatile private var subscription: Flow.Subscription = _
|
||||
private val future: CompletableFuture[Unit] = new CompletableFuture[Unit]
|
||||
|
||||
@@ -51,7 +49,7 @@ class SseSubscriber(messageDataConsumer: Consumer[String])
|
||||
def consumeEvents(
|
||||
incompleteEvent: Option[SseEventReader.IncompleteEvent],
|
||||
remainingText: String
|
||||
): Option[SseEventReader.IncompleteEvent] = {
|
||||
): Option[SseEventReader.IncompleteEvent] =
|
||||
SseEventReader.readOneEvent(incompleteEvent, remainingText) match {
|
||||
case SseEventReader.EndOfStream =>
|
||||
None
|
||||
@@ -92,7 +90,6 @@ class SseSubscriber(messageDataConsumer: Consumer[String])
|
||||
case incomplete: SseEventReader.IncompleteEvent =>
|
||||
Some(incomplete)
|
||||
}
|
||||
}
|
||||
|
||||
this.incompleteText = consumeEvents(this.incompleteText, newText)
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ object Main {
|
||||
def nextOption(
|
||||
map: Map[Symbol, String],
|
||||
list: Vector[String]
|
||||
): Map[Symbol, String] = {
|
||||
): Map[Symbol, String] =
|
||||
list match {
|
||||
case "--eagle-grpc-port" +: value +: tail =>
|
||||
nextOption(map ++ Map(eagleGrpcPortKey -> value), tail)
|
||||
@@ -45,7 +45,6 @@ object Main {
|
||||
|
||||
case _ => map
|
||||
}
|
||||
}
|
||||
nextOption(Map(), args.toVector)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,14 +18,13 @@ object MessageComparators {
|
||||
acc: Vector[FieldDescriptor] = Vector()
|
||||
): Vector[FieldDescriptor] = fieldNames match {
|
||||
case name +: tail =>
|
||||
val field = {
|
||||
val field =
|
||||
comp.scalaDescriptor.fields
|
||||
.find(_.name == name)
|
||||
.getOrElse {
|
||||
println(s"No field $name in ${comp.scalaDescriptor.name}")
|
||||
sys.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if tail.isEmpty then acc :+ field
|
||||
else
|
||||
@@ -59,7 +58,6 @@ object MessageComparators {
|
||||
value: String
|
||||
): (GeneratedMessage => Boolean) = comparator match {
|
||||
case "=" =>
|
||||
(
|
||||
gm =>
|
||||
nestedPValue(gm, fieldDescriptors) match {
|
||||
case PInt(i) => i == value.toInt
|
||||
@@ -70,7 +68,6 @@ object MessageComparators {
|
||||
case x =>
|
||||
throw new NotImplementedError(s"$x pvalue not implemented")
|
||||
}
|
||||
)
|
||||
case x => throw new NotImplementedError(s"$x comparator not implemented")
|
||||
}
|
||||
}
|
||||
@@ -121,11 +118,11 @@ object SavedGameUtils {
|
||||
def filterOne(
|
||||
results: Vector[ActionWithResultingState],
|
||||
filter: String
|
||||
): Vector[ActionWithResultingState] = {
|
||||
results.filter { case awrs =>
|
||||
): Vector[ActionWithResultingState] =
|
||||
results.filter {
|
||||
case awrs =>
|
||||
GameStateFilters.filter(filter)(awrs)
|
||||
}
|
||||
}
|
||||
|
||||
def filteredResults(
|
||||
results: Vector[ActionWithResultingState],
|
||||
@@ -141,7 +138,7 @@ object SavedGameUtils {
|
||||
def fieldValues(
|
||||
results: Vector[ActionWithResultingState],
|
||||
fields: Vector[String]
|
||||
): Vector[FieldWithValue] = {
|
||||
): Vector[FieldWithValue] =
|
||||
for {
|
||||
result <- results
|
||||
qualifiedFieldName <- fields
|
||||
@@ -172,7 +169,6 @@ object SavedGameUtils {
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
val argList = args.toList
|
||||
@@ -207,8 +203,7 @@ object SavedGameUtils {
|
||||
)
|
||||
|
||||
def withoutFilterIndices(indices: Vector[Int]): FilteredResultsState = {
|
||||
val newFilters = filters.zipWithIndex
|
||||
.filterNot { case (_, idx) => indices.contains(idx) }
|
||||
val newFilters = filters.zipWithIndex.filterNot { case (_, idx) => indices.contains(idx) }
|
||||
.map(_._1)
|
||||
this.copy(
|
||||
filters = newFilters,
|
||||
@@ -218,8 +213,7 @@ object SavedGameUtils {
|
||||
|
||||
def withoutPrintIndices(indices: Vector[Int]): FilteredResultsState =
|
||||
this.copy(
|
||||
prints = prints.zipWithIndex
|
||||
.filterNot { case (_, idx) => indices.contains(idx) }
|
||||
prints = prints.zipWithIndex.filterNot { case (_, idx) => indices.contains(idx) }
|
||||
.map(_._1)
|
||||
)
|
||||
}
|
||||
@@ -228,11 +222,13 @@ object SavedGameUtils {
|
||||
state: FilteredResultsState
|
||||
): FilteredResultsState = {
|
||||
println("Your filters:")
|
||||
state.filters.zipWithIndex.foreach { case (filt, idx) =>
|
||||
state.filters.zipWithIndex.foreach {
|
||||
case (filt, idx) =>
|
||||
println(s" ($idx) $filt")
|
||||
}
|
||||
println("Your prints:")
|
||||
state.prints.zipWithIndex.foreach { case (pf, idx) =>
|
||||
state.prints.zipWithIndex.foreach {
|
||||
case (pf, idx) =>
|
||||
println(s" ($idx) $pf")
|
||||
}
|
||||
println(s"${state.partialResults.size} filtered results")
|
||||
|
||||
@@ -18,8 +18,7 @@ object TestAIGame {
|
||||
|
||||
(1 to gameCount).foreach { _ =>
|
||||
gamesManager.synchronizedCreateGame(
|
||||
expandedGameParameters =
|
||||
GameParametersUtils.defaultExpandedGameParameters,
|
||||
expandedGameParameters = GameParametersUtils.defaultExpandedGameParameters,
|
||||
gameId = Random.nextLong(),
|
||||
humanPlayers = Vector.empty,
|
||||
aiPlayerCount = 5
|
||||
|
||||
@@ -14,11 +14,10 @@ object AIClientUtils {
|
||||
gs
|
||||
)).max(0)
|
||||
|
||||
def desiredHeroCount(pid: ProvinceId, fid: FactionId, gs: GameState): Int = {
|
||||
def desiredHeroCount(pid: ProvinceId, fid: FactionId, gs: GameState): Int =
|
||||
if LegacyFactionUtils.hostileNeighbors(pid, fid, gs).nonEmpty then 3
|
||||
else if gs.provinces(pid).support < 65 then 2
|
||||
else 1
|
||||
}
|
||||
|
||||
def desiredCountForMarchTowardFocus(
|
||||
originProvince: Province,
|
||||
@@ -55,8 +54,7 @@ object AIClientUtils {
|
||||
originProvince = originProvince,
|
||||
destinationProvince = destinationProvince,
|
||||
availableHeroIds = availableHeroIds,
|
||||
factionLeaders =
|
||||
gs.factions(originProvince.rulingFactionId.get).leaders.toVector,
|
||||
factionLeaders = gs.factions(originProvince.rulingFactionId.get).leaders.toVector,
|
||||
favorLeaders = favorLeaders
|
||||
),
|
||||
gs,
|
||||
@@ -78,8 +76,7 @@ object AIClientUtils {
|
||||
availableHeroes
|
||||
.sortBy(hero =>
|
||||
(
|
||||
if favorLeaders then
|
||||
!LegacyFactionUtils.isFactionLeader(hero.id, gs)
|
||||
if favorLeaders then !LegacyFactionUtils.isFactionLeader(hero.id, gs)
|
||||
else LegacyFactionUtils.isFactionLeader(hero.id, gs),
|
||||
-LegacyHeroUtils.power(hero)
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ object EarlyGameAIClient {
|
||||
gs: GameState,
|
||||
acs: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[CommandSelection] = {
|
||||
): RandomState[CommandSelection] =
|
||||
CommandChooser
|
||||
.choose(
|
||||
actingFactionId = actingFactionId,
|
||||
@@ -40,5 +40,4 @@ object EarlyGameAIClient {
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.map(_.get)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,7 @@ object FactionLeaderProvinceRanker {
|
||||
factionId: FactionId,
|
||||
gameState: GameState
|
||||
): Ordering[Province] = Ordering
|
||||
.by((p: Province) =>
|
||||
LegacyFactionUtils.hostileNeighbors(p, factionId, gameState).size
|
||||
)
|
||||
.by((p: Province) => LegacyFactionUtils.hostileNeighbors(p, factionId, gameState).size)
|
||||
.reverse
|
||||
|
||||
// Orders by most-neutral-neighbors-first
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package net.eagle0.eagle.ai
|
||||
|
||||
import net.eagle0.common.{FunctionalRandom, RandomState}
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
AvailableCommand,
|
||||
MarchAvailableCommand
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{AvailableCommand, MarchAvailableCommand}
|
||||
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
@@ -23,7 +20,8 @@ object FixLeaderAloneCommandSelector {
|
||||
randomSelectionForType[MarchAvailableCommand](
|
||||
availableCommands,
|
||||
functionalRandom
|
||||
) { case (ac, frOuter) =>
|
||||
) {
|
||||
case (ac, frOuter) =>
|
||||
frOuter.nextFlatCollectFirst(
|
||||
ac.oneProvinceCommands
|
||||
.filter(opmc =>
|
||||
@@ -62,8 +60,7 @@ object FixLeaderAloneCommandSelector {
|
||||
combatUnit
|
||||
)
|
||||
),
|
||||
reason =
|
||||
"Moving a hero so that a faction leader won't be alone"
|
||||
reason = "Moving a hero so that a faction leader won't be alone"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
package net.eagle0.eagle.ai
|
||||
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
AvailableCommand,
|
||||
DiplomacyAvailableCommand
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{AvailableCommand, DiplomacyAvailableCommand}
|
||||
import net.eagle0.eagle.api.command.util.diplomacy_option.InvitationOption
|
||||
import net.eagle0.eagle.api.selected_command.DiplomacySelectedCommand
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.settings.MinChanceForAIInvite
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.{
|
||||
ProvinceGoldSurplusCalculator,
|
||||
TrustForDiplomacy
|
||||
}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.{ProvinceGoldSurplusCalculator, TrustForDiplomacy}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.flatSelectionForType
|
||||
import net.eagle0.eagle.library.util.CommandSelection
|
||||
import net.eagle0.eagle.FactionId
|
||||
@@ -27,35 +21,28 @@ object InvitationCommandSelector {
|
||||
gameState: GameState,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[DiplomacyAvailableCommand](acs = availableCommands) {
|
||||
diplomacyAvailableCommand =>
|
||||
diplomacyAvailableCommand.options
|
||||
.collect { case io: InvitationOption => io }
|
||||
.filter { invitationOption =>
|
||||
flatSelectionForType[DiplomacyAvailableCommand](acs = availableCommands) { diplomacyAvailableCommand =>
|
||||
diplomacyAvailableCommand.options.collect { case io: InvitationOption => io }.filter { invitationOption =>
|
||||
TrustForDiplomacy.meetsConditionsForInvitation(
|
||||
actingFactionId = actingFactionId,
|
||||
targetFactionId = invitationOption.targetFactionId,
|
||||
gameState = gameState
|
||||
)
|
||||
}
|
||||
.filter { invitationOption =>
|
||||
}.filter { invitationOption =>
|
||||
ProvinceGoldSurplusCalculator.provinceGoldSurplus(
|
||||
diplomacyAvailableCommand.actingProvinceId,
|
||||
gameState
|
||||
) >= invitationOption.goldCost
|
||||
}
|
||||
.map { invitationOption =>
|
||||
}.map { invitationOption =>
|
||||
InvitationOptionWithChance(
|
||||
invitationOption = invitationOption,
|
||||
acceptanceChance =
|
||||
ResolveDiplomacyCommandSelector.invitationAcceptanceChance(
|
||||
acceptanceChance = ResolveDiplomacyCommandSelector.invitationAcceptanceChance(
|
||||
actingFactionId,
|
||||
invitationOption.targetFactionId,
|
||||
gameState
|
||||
)
|
||||
)
|
||||
}
|
||||
.maxByOption { _.acceptanceChance }
|
||||
}.maxByOption(_.acceptanceChance)
|
||||
.filter(_.acceptanceChance >= MinChanceForAIInvite.intValue)
|
||||
.map { iowc =>
|
||||
CommandSelection(
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
package net.eagle0.eagle.ai
|
||||
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
MarchAvailableCommand,
|
||||
MarchCommandFromOneProvince
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{MarchAvailableCommand, MarchCommandFromOneProvince}
|
||||
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.{
|
||||
CommandSelection,
|
||||
LegacyBattalionUtils,
|
||||
ProvinceDistances
|
||||
}
|
||||
import net.eagle0.eagle.library.util.{CommandSelection, LegacyBattalionUtils, ProvinceDistances}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.ProvinceGoldSurplusCalculator.provinceGoldSurplus
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
|
||||
@@ -28,7 +21,8 @@ object MarchTowardProvinceCommandChooser {
|
||||
val bestCommand =
|
||||
opmc.availableDestinationProvinces
|
||||
.map(dest => (opmc, gs.provinces(dest.provinceId)))
|
||||
.flatMap { case (opmc, p) =>
|
||||
.flatMap {
|
||||
case (opmc, p) =>
|
||||
ProvinceDistances
|
||||
.distanceThroughFriendliesOption(
|
||||
destinationProvinceId,
|
||||
@@ -42,7 +36,8 @@ object MarchTowardProvinceCommandChooser {
|
||||
|
||||
val actingProvinceId = ac.actingProvinceId
|
||||
|
||||
bestCommand.flatMap { case (opmc, dest, _) =>
|
||||
bestCommand.flatMap {
|
||||
case (opmc, dest, _) =>
|
||||
internalRequire(
|
||||
opmc.originProvinceId != destinationProvinceId,
|
||||
s"Marching away from $destinationProvinceId in an effort to get to that province"
|
||||
@@ -61,8 +56,7 @@ object MarchTowardProvinceCommandChooser {
|
||||
|
||||
Option.when(takenHeroIds.nonEmpty) {
|
||||
val takenBattalions =
|
||||
if originProvince.battalionIds.size <= takenHeroIds.size then
|
||||
originProvince.battalionIds.map(gs.battalions)
|
||||
if originProvince.battalionIds.size <= takenHeroIds.size then originProvince.battalionIds.map(gs.battalions)
|
||||
else
|
||||
originProvince.battalionIds
|
||||
.map(gs.battalions)
|
||||
@@ -75,7 +69,8 @@ object MarchTowardProvinceCommandChooser {
|
||||
0,
|
||||
None
|
||||
)
|
||||
.map { case (hid, bid) =>
|
||||
.map {
|
||||
case (hid, bid) =>
|
||||
CombatUnit(factionId = fid, heroId = hid, battalionId = bid)
|
||||
}
|
||||
|
||||
@@ -95,10 +90,9 @@ object MarchTowardProvinceCommandChooser {
|
||||
gold = Math.min(goldToTake, opmc.goldAvailable),
|
||||
food = Math.min(foodToTake, opmc.foodAvailable)
|
||||
),
|
||||
reason = if favorLeaders then
|
||||
s"marching leaders toward ${gs.provinces(destinationProvinceId).name}"
|
||||
else
|
||||
s"marching heroes toward ${gs.provinces(destinationProvinceId).name}"
|
||||
reason =
|
||||
if favorLeaders then s"marching leaders toward ${gs.provinces(destinationProvinceId).name}"
|
||||
else s"marching heroes toward ${gs.provinces(destinationProvinceId).name}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,19 +5,13 @@ import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.ai.AIClientUtils.extraHeroCount
|
||||
import net.eagle0.eagle.api.available_command.*
|
||||
import net.eagle0.eagle.api.command.OneProvinceAvailableCommands
|
||||
import net.eagle0.eagle.api.selected_command.{
|
||||
MarchSelectedCommand,
|
||||
ReconSelectedCommand
|
||||
}
|
||||
import net.eagle0.eagle.api.selected_command.{MarchSelectedCommand, ReconSelectedCommand}
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.common.improvement_type.ImprovementType
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
MinSupportForTaxes,
|
||||
MonthsReconConsideredRecent
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.{MinSupportForTaxes, MonthsReconConsideredRecent}
|
||||
import net.eagle0.eagle.library.util.*
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.{
|
||||
AlmsCommandSelector,
|
||||
@@ -189,8 +183,7 @@ object MidGameAIClient {
|
||||
.filter(_.monthsOfFood < 4)
|
||||
.minByOption(_.monthsOfFood)
|
||||
|
||||
bestDestination
|
||||
.map { destinationFoodStatus =>
|
||||
bestDestination.map { destinationFoodStatus =>
|
||||
val destinationProvince = destinationFoodStatus.province
|
||||
val desiredFood = LegacyProvinceUtils.monthlyFoodConsumption(
|
||||
destinationProvince.id,
|
||||
@@ -335,8 +328,7 @@ object MidGameAIClient {
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
provincesWithFactionLeader(actingFactionId, gameState)
|
||||
.map { province =>
|
||||
provincesWithFactionLeader(actingFactionId, gameState).map { province =>
|
||||
MoreOption
|
||||
.flatWhen(province.rulingFactionHeroIds.size < 4)(
|
||||
maybeMoveHeroesTowardFactionLeaderCommand(
|
||||
@@ -410,8 +402,7 @@ object MidGameAIClient {
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] = {
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChooserImplicits.DeterministicCommandChooser
|
||||
provincesWithFactionLeader(actingFactionId, gameState)
|
||||
.map { province =>
|
||||
provincesWithFactionLeader(actingFactionId, gameState).map { province =>
|
||||
MoreOption
|
||||
.flatWhen(province.rulingFactionHeroIds.size < 4)(
|
||||
maybeMoveHeroesTowardFactionLeaderCommand(
|
||||
@@ -673,8 +664,7 @@ object MidGameAIClient {
|
||||
}
|
||||
}
|
||||
),
|
||||
(fid, gs, acs, fr) =>
|
||||
chosenNoNeutralNeighborsCommand(fid, gs, acs, fr)
|
||||
(fid, gs, acs, fr) => chosenNoNeutralNeighborsCommand(fid, gs, acs, fr)
|
||||
),
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
@@ -899,11 +889,9 @@ object MidGameAIClient {
|
||||
extraHeroCount(opmc.originProvinceId, actingFactionId, gs) > 0
|
||||
}
|
||||
|
||||
val furthestOrigin = candidateCommands
|
||||
.flatMap { opmc =>
|
||||
val furthestOrigin = candidateCommands.flatMap { opmc =>
|
||||
// distance to the faction leader province we're closest to
|
||||
factionLeaderProvincesNeedingHeroes
|
||||
.flatMap { flp =>
|
||||
factionLeaderProvincesNeedingHeroes.flatMap { flp =>
|
||||
ProvinceDistances
|
||||
.distanceThroughFriendliesOption(
|
||||
p1 = flp.id,
|
||||
@@ -917,7 +905,8 @@ object MidGameAIClient {
|
||||
}
|
||||
.minByOption(_._1)
|
||||
.filterNot(_._1 == 0) // If distance is zero, we'd be moving away from the province we're trying to get to
|
||||
.map { case (distance: Int, destinationProvinceId: ProvinceId) =>
|
||||
.map {
|
||||
case (distance: Int, destinationProvinceId: ProvinceId) =>
|
||||
(
|
||||
// distance to the faction leader province we're closest to
|
||||
distance,
|
||||
@@ -928,9 +917,10 @@ object MidGameAIClient {
|
||||
}
|
||||
}
|
||||
.maxByOption(tup => (tup._1, tup._3))
|
||||
.map { tup => (tup._2, tup._4) }
|
||||
.map(tup => (tup._2, tup._4))
|
||||
|
||||
furthestOrigin.flatMap { case (destinationProvinceId, opmc) =>
|
||||
furthestOrigin.flatMap {
|
||||
case (destinationProvinceId, opmc) =>
|
||||
MarchTowardProvinceCommandChooser
|
||||
.marchTowardProvinceCommand(
|
||||
ac = marchCommand,
|
||||
@@ -957,8 +947,7 @@ object MidGameAIClient {
|
||||
actingProvinceId = ac.actingProvinceId,
|
||||
available = ac,
|
||||
selected = ReconSelectedCommand(
|
||||
actingHeroId =
|
||||
ac.availableHeroIds.maxBy(hid => gameState.heroes(hid).vigor),
|
||||
actingHeroId = ac.availableHeroIds.maxBy(hid => gameState.heroes(hid).vigor),
|
||||
targetProvinceId = targetProvinceId
|
||||
),
|
||||
reason = "selectedReconCommand"
|
||||
@@ -1074,8 +1063,7 @@ object MidGameAIClient {
|
||||
IncomingArmyUtils.isUnderAttack(province, gameState)
|
||||
) // don't send a leader into a province under attack
|
||||
|
||||
validDestinations.minByOption(_.rulingFactionHeroIds.size).map {
|
||||
destinationProvince =>
|
||||
validDestinations.minByOption(_.rulingFactionHeroIds.size).map { destinationProvince =>
|
||||
CommandSelection(
|
||||
actingFactionId = actingFactionId,
|
||||
actingProvinceId = marchAvailableCommand.actingProvinceId,
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package net.eagle0.eagle.ai
|
||||
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
AvailableCommand,
|
||||
MarchAvailableCommand,
|
||||
MarchCommandFromOneProvince
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{AvailableCommand, MarchAvailableCommand, MarchCommandFromOneProvince}
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.CommandSelection
|
||||
@@ -30,8 +26,7 @@ object MoveLeaderToBetterProvinceCommandChooser {
|
||||
actingFactionId = actingFactionId,
|
||||
gs = gs,
|
||||
acs = acs,
|
||||
betterDestinationChooser =
|
||||
FactionLeaderProvinceRanker.bestSettlementPidForFactionLeader
|
||||
betterDestinationChooser = FactionLeaderProvinceRanker.bestSettlementPidForFactionLeader
|
||||
)
|
||||
|
||||
private def candidatesForMarchCommand(
|
||||
@@ -57,8 +52,7 @@ object MoveLeaderToBetterProvinceCommandChooser {
|
||||
actingFactionId,
|
||||
mcfop.originProvinceId,
|
||||
gs
|
||||
)
|
||||
.flatMap { desiredPid =>
|
||||
).flatMap { desiredPid =>
|
||||
ProvinceDistances
|
||||
.closestProvinceThroughFriendliesOption(
|
||||
desiredPid,
|
||||
@@ -89,8 +83,7 @@ object MoveLeaderToBetterProvinceCommandChooser {
|
||||
val candidatesWithDistances = candidateCommandsWithDistances(
|
||||
actingFactionId = actingFactionId,
|
||||
gs = gs,
|
||||
candidateCommands =
|
||||
candidatesForMarchCommand(marchCommand, gs, faction),
|
||||
candidateCommands = candidatesForMarchCommand(marchCommand, gs, faction),
|
||||
betterDestinationChooser = betterDestinationChooser
|
||||
)
|
||||
|
||||
|
||||
@@ -77,7 +77,8 @@ object ResolveDiplomacyCommandSelector {
|
||||
randomSelectionForType[ResolveInvitationAvailableCommand](
|
||||
availableCommands,
|
||||
functionalRandom
|
||||
) { case (resolveInvitationAc, fr) =>
|
||||
) {
|
||||
case (resolveInvitationAc, fr) =>
|
||||
selectionForResolveInvitationCommand(
|
||||
actingFactionId = actingFactionId,
|
||||
ac = resolveInvitationAc,
|
||||
@@ -104,9 +105,7 @@ object ResolveDiplomacyCommandSelector {
|
||||
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
|
||||
val invitedFid = actingFactionId
|
||||
val bestInvitation = ac.invitations
|
||||
.maxBy(inv =>
|
||||
invitationAcceptanceChance(inv.originatingFactionId, invitedFid, gs)
|
||||
)
|
||||
.maxBy(inv => invitationAcceptanceChance(inv.originatingFactionId, invitedFid, gs))
|
||||
|
||||
internalRequire(
|
||||
bestInvitation.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
|
||||
@@ -150,7 +149,8 @@ object ResolveDiplomacyCommandSelector {
|
||||
randomSelectionForType[ResolveTruceOfferAvailableCommand](
|
||||
availableCommands,
|
||||
functionalRandom
|
||||
) { case (resolveTruceAc, fr) =>
|
||||
) {
|
||||
case (resolveTruceAc, fr) =>
|
||||
selectionForResolveTruceOfferSelectedCommand(
|
||||
actingFactionId = actingFactionId,
|
||||
ac = resolveTruceAc,
|
||||
@@ -177,9 +177,7 @@ object ResolveDiplomacyCommandSelector {
|
||||
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
|
||||
val actingFid = actingFactionId
|
||||
val bestOffer = ac.offers
|
||||
.maxBy(inv =>
|
||||
truceOfferAcceptanceChance(inv.originatingFactionId, actingFid, gs)
|
||||
)
|
||||
.maxBy(inv => truceOfferAcceptanceChance(inv.originatingFactionId, actingFid, gs))
|
||||
|
||||
internalRequire(
|
||||
bestOffer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
|
||||
@@ -225,7 +223,7 @@ object ResolveDiplomacyCommandSelector {
|
||||
originatingFid: FactionId,
|
||||
targetFid: FactionId,
|
||||
gs: GameState
|
||||
): Int = {
|
||||
): Int =
|
||||
// Always reject if the faction already has an alliance
|
||||
if gs
|
||||
.factions(targetFid)
|
||||
@@ -235,8 +233,7 @@ object ResolveDiplomacyCommandSelector {
|
||||
|
||||
// reject if this is our only neighbor and we have no expansion possibilities
|
||||
else if LegacyFactionUtils.provinces(targetFid, gs).forall { province =>
|
||||
province.neighbors.map { n => gs.provinces(n.provinceId) }.forall {
|
||||
neighborProvince =>
|
||||
province.neighbors.map(n => gs.provinces(n.provinceId)).forall { neighborProvince =>
|
||||
neighborProvince.rulingFactionId.contains(
|
||||
originatingFid
|
||||
) || neighborProvince.rulingFactionId.contains(targetFid)
|
||||
@@ -254,15 +251,13 @@ object ResolveDiplomacyCommandSelector {
|
||||
gameState = gs
|
||||
)
|
||||
).floor.toInt.max(0)
|
||||
}
|
||||
|
||||
private def resolveRansomOfferSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
selectionForType[ResolveRansomOfferAvailableCommand](availableCommands) {
|
||||
resolveRansomAc =>
|
||||
selectionForType[ResolveRansomOfferAvailableCommand](availableCommands) { resolveRansomAc =>
|
||||
CommandSelection(
|
||||
actingFactionId = actingFactionId,
|
||||
actingProvinceId = 0,
|
||||
@@ -300,7 +295,8 @@ object ResolveDiplomacyCommandSelector {
|
||||
randomSelectionForType[ResolveAllianceOfferAvailableCommand](
|
||||
availableCommands,
|
||||
functionalRandom
|
||||
) { case (resolveAllianceAc, fr) =>
|
||||
) {
|
||||
case (resolveAllianceAc, fr) =>
|
||||
selectionForResolveAllianceOfferSelectedCommand(
|
||||
actingFactionId = actingFactionId,
|
||||
ac = resolveAllianceAc,
|
||||
@@ -328,7 +324,8 @@ object ResolveDiplomacyCommandSelector {
|
||||
randomSelectionForType[ResolveBreakAllianceAvailableCommand](
|
||||
availableCommands,
|
||||
functionalRandom
|
||||
) { case (resolveBreakAllianceAc, fr) =>
|
||||
) {
|
||||
case (resolveBreakAllianceAc, fr) =>
|
||||
selectionForResolveBreakAllianceSelectedCommand(
|
||||
actingFactionId = actingFactionId,
|
||||
ac = resolveBreakAllianceAc,
|
||||
@@ -355,9 +352,7 @@ object ResolveDiplomacyCommandSelector {
|
||||
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
|
||||
val actingFid = actingFactionId
|
||||
val bestOffer = ac.offers
|
||||
.maxBy(inv =>
|
||||
allianceOfferAcceptanceChance(inv.originatingFactionId, actingFid, gs)
|
||||
)
|
||||
.maxBy(inv => allianceOfferAcceptanceChance(inv.originatingFactionId, actingFid, gs))
|
||||
|
||||
internalRequire(
|
||||
bestOffer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
package net.eagle0.eagle.ai
|
||||
|
||||
import net.eagle0.common.MoreOption
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
AvailableCommand,
|
||||
MarchAvailableCommand
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{AvailableCommand, MarchAvailableCommand}
|
||||
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
LoyaltyGainFromFeast,
|
||||
MinimumLoyaltyForSwearBrotherhood
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.{LoyaltyGainFromFeast, MinimumLoyaltyForSwearBrotherhood}
|
||||
import net.eagle0.eagle.library.util.{CommandSelection, ProvinceDistances}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.{
|
||||
CommandChoiceHelpers,
|
||||
@@ -49,7 +43,8 @@ object SeekMoreLeadersCommandChooser {
|
||||
val bestDestination =
|
||||
opmc.availableDestinationProvinces
|
||||
.map(dest => (opmc, gameState.provinces(dest.provinceId)))
|
||||
.flatMap { case (opmc, p) =>
|
||||
.flatMap {
|
||||
case (opmc, p) =>
|
||||
ProvinceDistances
|
||||
.distanceThroughFriendliesOption(
|
||||
factionHeadProvince.id,
|
||||
@@ -88,7 +83,7 @@ object SeekMoreLeadersCommandChooser {
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
acs: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] = {
|
||||
): Option[CommandSelection] =
|
||||
LegacyFactionUtils
|
||||
.provinces(actingFactionId, gameState)
|
||||
.find(
|
||||
@@ -106,15 +101,13 @@ object SeekMoreLeadersCommandChooser {
|
||||
province.rulingFactionHeroIds.map(gameState.heroes),
|
||||
gameState.factions(actingFactionId).leaders.toVector
|
||||
)
|
||||
.map { hero => Candidate(hero, province) }
|
||||
.map(hero => Candidate(hero, province))
|
||||
}
|
||||
|
||||
val marchACs = acs
|
||||
.collect { case ac: MarchAvailableCommand => ac }
|
||||
val marchACs = acs.collect { case ac: MarchAvailableCommand => ac }
|
||||
|
||||
val marchableCandidates = candidates.flatMap { c =>
|
||||
marchACs
|
||||
.find { mac =>
|
||||
marchACs.find { mac =>
|
||||
mac.oneProvinceCommands.exists(opmc =>
|
||||
opmc.originProvinceId == c.province.id && opmc.availableHeroIds.toVector
|
||||
.contains(c.hero.id)
|
||||
@@ -125,7 +118,8 @@ object SeekMoreLeadersCommandChooser {
|
||||
|
||||
marchableCandidates
|
||||
.maxByOption(x => LegacyHeroUtils.power(x._2.hero))
|
||||
.flatMap { case (command, candidate) =>
|
||||
.flatMap {
|
||||
case (command, candidate) =>
|
||||
maybeMoveTowardDestination(
|
||||
command = command,
|
||||
hero = candidate.hero,
|
||||
@@ -135,7 +129,6 @@ object SeekMoreLeadersCommandChooser {
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def maybeSeekMoreLeadersCommand(
|
||||
actingFactionId: FactionId,
|
||||
@@ -154,8 +147,7 @@ object SeekMoreLeadersCommandChooser {
|
||||
faction.leaders.toVector
|
||||
)
|
||||
|
||||
desiredHero
|
||||
.flatMap { hero =>
|
||||
desiredHero.flatMap { hero =>
|
||||
SwearBrotherhoodCommandSelector
|
||||
.chosenCommandForSpecificHero(
|
||||
actingFactionId = actingFactionId,
|
||||
|
||||
@@ -9,11 +9,7 @@ import scala.util.Try
|
||||
|
||||
import net.eagle0.common.ProtoParser
|
||||
import net.eagle0.eagle.{ClientTextId, FactionId}
|
||||
import net.eagle0.eagle.internal.client_text.{
|
||||
IncompleteText,
|
||||
IncompleteTextStore,
|
||||
UnrequestedText
|
||||
}
|
||||
import net.eagle0.eagle.internal.client_text.{IncompleteText, IncompleteTextStore, UnrequestedText}
|
||||
import net.eagle0.eagle.internal.generated_text_request.GeneratedTextRequest
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
@@ -73,8 +69,7 @@ case class ClientTextStoreImpl(
|
||||
id = id,
|
||||
partialText = "",
|
||||
llmRequest = unrequested.llmRequest,
|
||||
requestedAfterHistoryCount =
|
||||
unrequested.requestedAfterHistoryCount
|
||||
requestedAfterHistoryCount = unrequested.requestedAfterHistoryCount
|
||||
)),
|
||||
unrequestedTexts = unrequestedTexts - id,
|
||||
incompleteTextsAreSaved = false
|
||||
@@ -98,7 +93,7 @@ case class ClientTextStoreImpl(
|
||||
.orElse {
|
||||
completeTexts.get(id).map(_.text)
|
||||
}
|
||||
.map { text => TextGenerationSuccess(text) }
|
||||
.map(text => TextGenerationSuccess(text))
|
||||
.getOrElse {
|
||||
incompleteTexts
|
||||
.get(id)
|
||||
@@ -218,8 +213,8 @@ object ClientTextStoreImpl {
|
||||
if clientTextStore.completeTexts.size > clientTextStore.savedCompleteCount
|
||||
then {
|
||||
// FIXME: make this append, will require Persister changes
|
||||
val saveData = clientTextStore.completeTexts
|
||||
.map { case (_: String, ct: CompleteClientText) =>
|
||||
val saveData = clientTextStore.completeTexts.map {
|
||||
case (_: String, ct: CompleteClientText) =>
|
||||
s"${ct.id}\n${ct.requestedAfterHistoryCount}\n${ct.text}$separator"
|
||||
}
|
||||
.mkString("")
|
||||
@@ -231,9 +226,7 @@ object ClientTextStoreImpl {
|
||||
)
|
||||
}
|
||||
|
||||
clientTextStore.copy(savedCompleteCount =
|
||||
clientTextStore.completeTexts.size
|
||||
)
|
||||
clientTextStore.copy(savedCompleteCount = clientTextStore.completeTexts.size)
|
||||
} else clientTextStore
|
||||
|
||||
val incompleteSaved =
|
||||
@@ -270,8 +263,8 @@ object ClientTextStoreImpl {
|
||||
|
||||
val accessibleToSaved =
|
||||
if !incompleteSaved.accessibleToIsSaved then {
|
||||
val accessibleToData = incompleteSaved.accessibleTo
|
||||
.map { case (id, factions) =>
|
||||
val accessibleToData = incompleteSaved.accessibleTo.map {
|
||||
case (id, factions) =>
|
||||
s"$id\n${factions.mkString(",")}$separator"
|
||||
}
|
||||
.mkString("")
|
||||
@@ -316,7 +309,8 @@ object ClientTextStoreImpl {
|
||||
}
|
||||
.toVector
|
||||
}
|
||||
.recover { case _: FileNotFoundException =>
|
||||
.recover {
|
||||
case _: FileNotFoundException =>
|
||||
println("No complete text store found")
|
||||
Vector()
|
||||
}
|
||||
@@ -350,7 +344,8 @@ object ClientTextStoreImpl {
|
||||
}
|
||||
.getOrElse((Vector(), Vector()))
|
||||
}
|
||||
.recover { case _: FileNotFoundException =>
|
||||
.recover {
|
||||
case _: FileNotFoundException =>
|
||||
println("No incomplete text store found")
|
||||
(Vector(), Vector())
|
||||
}
|
||||
@@ -376,7 +371,8 @@ object ClientTextStoreImpl {
|
||||
}
|
||||
.toMap
|
||||
}
|
||||
.recover { case _: FileNotFoundException =>
|
||||
.recover {
|
||||
case _: FileNotFoundException =>
|
||||
println("No accessibleTo store found")
|
||||
Map()
|
||||
}
|
||||
@@ -389,19 +385,15 @@ object ClientTextStoreImpl {
|
||||
loadedCompletedTexts <- loadedCompleteTexts(persister)
|
||||
loadedIncompleteTexts <- loadedIncompleteTexts(persister)
|
||||
loadedAccessibleTo <- loadedAccessibleTo(persister)
|
||||
} yield {
|
||||
ClientTextStoreImpl(
|
||||
} yield ClientTextStoreImpl(
|
||||
pregenerated = pregenerated,
|
||||
persister = persister,
|
||||
completeTexts = loadedCompletedTexts.map(ct => ct.id -> ct).toMap,
|
||||
incompleteTexts =
|
||||
loadedIncompleteTexts._1.map(ict => ict.id -> ict).toMap,
|
||||
unrequestedTexts =
|
||||
loadedIncompleteTexts._2.map(ut => ut.id -> ut).toMap,
|
||||
incompleteTexts = loadedIncompleteTexts._1.map(ict => ict.id -> ict).toMap,
|
||||
unrequestedTexts = loadedIncompleteTexts._2.map(ut => ut.id -> ut).toMap,
|
||||
accessibleTo = loadedAccessibleTo,
|
||||
savedCompleteCount = loadedCompletedTexts.size,
|
||||
incompleteTextsAreSaved = true,
|
||||
accessibleToIsSaved = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,16 +59,14 @@ case class TextGenerationDependencyInProgress(
|
||||
s"Text generation dependency in progress: $inProgressTextId"
|
||||
)
|
||||
}
|
||||
case class TextGenerationDependencyWaiting(notSatisfiedTextId: String)
|
||||
extends TextGenerationResult {
|
||||
case class TextGenerationDependencyWaiting(notSatisfiedTextId: String) extends TextGenerationResult {
|
||||
override def resolved: Boolean = false
|
||||
override def get: String =
|
||||
throw new EagleInternalException(
|
||||
s"Text generation dependency not satisfied: $notSatisfiedTextId"
|
||||
)
|
||||
}
|
||||
case class TextGenerationDependencyUnknown(unknownTextId: String)
|
||||
extends TextGenerationResult {
|
||||
case class TextGenerationDependencyUnknown(unknownTextId: String) extends TextGenerationResult {
|
||||
override def resolved: Boolean = false
|
||||
override def get: String =
|
||||
throw new EagleInternalException(
|
||||
|
||||
@@ -97,8 +97,7 @@ object ActionResultFilter {
|
||||
GameStateViewDiffer.diff(
|
||||
before = GameStateViewFilter
|
||||
.filteredGameState(gs = before, factionId = factionId),
|
||||
after =
|
||||
GameStateViewFilter.filteredGameState(gs = after, factionId = factionId)
|
||||
after = GameStateViewFilter.filteredGameState(gs = after, factionId = factionId)
|
||||
)
|
||||
|
||||
private def transformOne(
|
||||
@@ -115,9 +114,7 @@ object ActionResultFilter {
|
||||
)
|
||||
|
||||
def shouldInclude(note: Notification): Boolean =
|
||||
note.targetFactions.isEmpty || factionId.exists(fid =>
|
||||
note.targetFactions.exists(_.factionId == fid)
|
||||
)
|
||||
note.targetFactions.isEmpty || factionId.exists(fid => note.targetFactions.exists(_.factionId == fid))
|
||||
|
||||
if gsDiff.isDefined &&
|
||||
result.actionResult.player.isDefined &&
|
||||
@@ -143,8 +140,7 @@ object ActionResultFilter {
|
||||
province = actionResult.province,
|
||||
leader = actionResult.leader,
|
||||
gameStateDiff = gsDiff,
|
||||
notifications =
|
||||
actionResult.notificationsToDeliver.filter(shouldInclude)
|
||||
notifications = actionResult.notificationsToDeliver.filter(shouldInclude)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -154,8 +150,7 @@ object ActionResultFilter {
|
||||
results: Vector[ActionWithResultingState],
|
||||
factionId: Option[FactionId]
|
||||
): Vector[ActionResultView] =
|
||||
factionId
|
||||
.map { pid =>
|
||||
factionId.map { pid =>
|
||||
filterForPlayer(startingState, results, pid)
|
||||
}
|
||||
.getOrElse(filterForObserver(startingState, results))
|
||||
@@ -182,7 +177,5 @@ object ActionResultFilter {
|
||||
startingState: GameState,
|
||||
results: Vector[ActionWithResultingState]
|
||||
): Vector[ActionResultView] =
|
||||
results.flatMap(res =>
|
||||
observeOne(result = res, startingState = startingState)
|
||||
)
|
||||
results.flatMap(res => observeOne(result = res, startingState = startingState))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
package net.eagle0.eagle.library
|
||||
|
||||
class EagleValidationException(message: String)
|
||||
extends EagleInternalException(message) {}
|
||||
class EagleValidationException(message: String) extends EagleInternalException(message) {}
|
||||
|
||||
@@ -8,24 +8,15 @@ import net.eagle0.eagle.api.command.AvailableCommands
|
||||
import net.eagle0.eagle.api.selected_command.SelectedCommand
|
||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.actions.applier.{
|
||||
ActionResultProtoApplier,
|
||||
ActionResultProtoApplierImpl
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.applier.{ActionResultProtoApplier, ActionResultProtoApplierImpl}
|
||||
import net.eagle0.eagle.library.actions.availability.AvailableCommandsFactory
|
||||
import net.eagle0.eagle.library.actions.impl.action.{
|
||||
CheckForFulfilledQuestsAction,
|
||||
HeroBackstoryUpdateActionGenerator,
|
||||
ResolveBattleAction
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.command.{
|
||||
AvailableCommandTypeMap,
|
||||
CommandFactory
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.common.{
|
||||
ActionWithResultingState,
|
||||
RandomStateProtoSequencer
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.command.{AvailableCommandTypeMap, CommandFactory}
|
||||
import net.eagle0.eagle.library.actions.impl.common.{ActionWithResultingState, RandomStateProtoSequencer}
|
||||
import net.eagle0.eagle.library.util.hero_generator.HeroGenerator
|
||||
import net.eagle0.eagle.library.util.validations.RuntimeValidator
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
@@ -83,11 +74,9 @@ object EngineImpl {
|
||||
.toVector,
|
||||
battalions = eng.currentState.battalions.values.toVector
|
||||
.map(BattalionConverter.fromProto),
|
||||
getHero =
|
||||
hid => eng.currentState.heroes.get(hid).map(HeroConverter.fromProto),
|
||||
getHero = hid => eng.currentState.heroes.get(hid).map(HeroConverter.fromProto),
|
||||
battalionTypes = eng.currentState.battalionTypes.toVector,
|
||||
heroBackstoryTextIdLookup =
|
||||
hid => eng.currentState.heroes(hid).backstoryVersions.last.textId
|
||||
heroBackstoryTextIdLookup = hid => eng.currentState.heroes(hid).backstoryVersions.last.textId
|
||||
).results
|
||||
)
|
||||
|
||||
@@ -99,8 +88,7 @@ object EngineImpl {
|
||||
withPhaseAdvancement(engineAndResults)
|
||||
)
|
||||
|
||||
if newEar.results.length > engineAndResults.results.length then
|
||||
withUpdateChecks(newEar)
|
||||
if newEar.results.length > engineAndResults.results.length then withUpdateChecks(newEar)
|
||||
else newEar
|
||||
}
|
||||
|
||||
@@ -110,8 +98,7 @@ object EngineImpl {
|
||||
): EngineAndResults = withUpdateChecks(
|
||||
EngineAndResultsImpl(
|
||||
engine = engine.copy(
|
||||
currentState =
|
||||
results.lastOption.map(_.gameState).getOrElse(engine.currentState),
|
||||
currentState = results.lastOption.map(_.gameState).getOrElse(engine.currentState),
|
||||
history = engine.history.withNewResults(results)
|
||||
),
|
||||
results = results.map(_.actionResult)
|
||||
@@ -146,14 +133,14 @@ final case class EngineAndResultsImpl(
|
||||
|
||||
def recursiveTransformT(
|
||||
f: EngineImpl => Vector[ActionResultT]
|
||||
): EngineAndResultsImpl = recursiveTransform(eng => {
|
||||
): EngineAndResultsImpl = recursiveTransform { eng =>
|
||||
val results = f(eng)
|
||||
RandomStateProtoSequencer(
|
||||
initialStateProto = eng.currentState,
|
||||
actionResultProtoApplier = eng.actionResultProtoApplier,
|
||||
functionalRandom = SeededRandom(eng.currentState.randomSeed)
|
||||
).withActionResultTs(_ => results).results.newValue
|
||||
})
|
||||
}
|
||||
|
||||
def saveNow: EngineAndResultsImpl =
|
||||
this.copy(engine = this.engine.saveNow)
|
||||
@@ -169,8 +156,7 @@ case class EngineImpl(
|
||||
override val currentState: GameState,
|
||||
heroGenerator: HeroGenerator,
|
||||
override val history: GameHistory,
|
||||
actionResultProtoApplier: ActionResultProtoApplier =
|
||||
new ActionResultProtoApplierImpl(validator = RuntimeValidator)
|
||||
actionResultProtoApplier: ActionResultProtoApplier = new ActionResultProtoApplierImpl(validator = RuntimeValidator)
|
||||
) extends Engine {
|
||||
import net.eagle0.eagle.library.util.EagleRequire.commandRequire
|
||||
|
||||
@@ -326,9 +312,7 @@ case class EngineImpl(
|
||||
)
|
||||
|
||||
results
|
||||
}.withProtolessSequentialResultsAction(gs =>
|
||||
HeroBackstoryUpdateActionGenerator.fromGameState(gs)
|
||||
)
|
||||
}.withProtolessSequentialResultsAction(gs => HeroBackstoryUpdateActionGenerator.fromGameState(gs))
|
||||
|
||||
appliedResults(
|
||||
engine = this,
|
||||
|
||||
@@ -17,8 +17,7 @@ trait GameHistory {
|
||||
def all: Vector[ActionWithResultingState]
|
||||
def since(count: Int): Vector[ActionWithResultingState]
|
||||
def sinceCapped(count: Int, resultSizeCap: Int): CappedResults =
|
||||
if this.count - count <= resultSizeCap then
|
||||
CappedResults(since(count), None)
|
||||
if this.count - count <= resultSizeCap then CappedResults(since(count), None)
|
||||
else
|
||||
CappedResults(
|
||||
since(this.count - resultSizeCap),
|
||||
|
||||
@@ -6,10 +6,7 @@ import net.eagle0.common.SeededRandom
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase.*
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.actions.applier.{
|
||||
ActionResultProtoApplier,
|
||||
ActionResultTApplierImpl
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.applier.{ActionResultProtoApplier, ActionResultTApplierImpl}
|
||||
import net.eagle0.eagle.library.actions.availability.AvailableCommandsFactory
|
||||
import net.eagle0.eagle.library.actions.impl.action.*
|
||||
import net.eagle0.eagle.library.actions.impl.command.CommandFactory
|
||||
@@ -270,8 +267,7 @@ object RoundPhaseAdvancer {
|
||||
currentRoundId = currentState.currentRoundId,
|
||||
currentDate = DateConverter.fromProto(currentState.currentDate),
|
||||
battleCounter = currentState.battleCounter,
|
||||
heroes =
|
||||
currentState.heroes.view.mapValues(HeroConverter.fromProto).toMap,
|
||||
heroes = currentState.heroes.view.mapValues(HeroConverter.fromProto).toMap,
|
||||
battalions = currentState.battalions.view
|
||||
.mapValues(BattalionConverter.fromProto)
|
||||
.toMap,
|
||||
@@ -337,8 +333,7 @@ object RoundPhaseAdvancer {
|
||||
currentState,
|
||||
EndDiplomacyResolutionPhaseAction(
|
||||
currentState,
|
||||
actionResultTApplier =
|
||||
ActionResultTApplierImpl(actionResultProtoApplier)
|
||||
actionResultTApplier = ActionResultTApplierImpl(actionResultProtoApplier)
|
||||
).randomResults(SeededRandom(currentState.randomSeed))
|
||||
.newValue
|
||||
.map(ActionResultProtoConverter.toProto)
|
||||
|
||||
+42
-69
@@ -31,10 +31,7 @@ import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.{
|
||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.army.{HostileArmyGroup, MovingArmy}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.changed_faction.{
|
||||
ChangedFaction,
|
||||
TrustLevelUpdate
|
||||
}
|
||||
import net.eagle0.eagle.internal.changed_faction.{ChangedFaction, TrustLevelUpdate}
|
||||
import net.eagle0.eagle.internal.changed_hero.ChangedHero
|
||||
import net.eagle0.eagle.internal.changed_hero.ChangedHero.{Loyalty, Vigor}
|
||||
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
||||
@@ -49,24 +46,19 @@ import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.run_status.RunStatus
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle
|
||||
import net.eagle0.eagle.library.actions.impl.common.ActionWithResultingState
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
ExtraXpForStatBumpOver100,
|
||||
XpForStatBump
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.{ExtraXpForStatBumpOver100, XpForStatBump}
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.validations.Validator
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.library.util.ProvinceEventUtils
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
|
||||
class ActionResultProtoApplierImpl(validator: Validator)
|
||||
extends ActionResultProtoApplier {
|
||||
class ActionResultProtoApplierImpl(validator: Validator) extends ActionResultProtoApplier {
|
||||
import validator.validate
|
||||
|
||||
override def xpForStatBump(stat: Int): Int =
|
||||
if stat <= 99 then XpForStatBump.intValue
|
||||
else
|
||||
XpForStatBump.intValue + ExtraXpForStatBumpOver100.intValue * (stat - 99)
|
||||
else XpForStatBump.intValue + ExtraXpForStatBumpOver100.intValue * (stat - 99)
|
||||
|
||||
private val trustMax: Int = 100
|
||||
|
||||
@@ -93,7 +85,8 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
if changedProvinces.isEmpty then gameState
|
||||
else
|
||||
changedProvinces
|
||||
.foldLeft(gameState) { case (gs, cp) =>
|
||||
.foldLeft(gameState) {
|
||||
case (gs, cp) =>
|
||||
val after = gs.applyChangedProvince(cp)
|
||||
validate(after.provinces(cp.id), gs.currentPhase)
|
||||
after
|
||||
@@ -149,9 +142,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
da.map { nda =>
|
||||
nda.withUnits(
|
||||
nda.units
|
||||
.filterNot(u =>
|
||||
cp.removedRulingPlayerHeroIds.contains(u.heroId)
|
||||
)
|
||||
.filterNot(u => cp.removedRulingPlayerHeroIds.contains(u.heroId))
|
||||
.map {
|
||||
case CombatUnit(
|
||||
pid,
|
||||
@@ -185,17 +176,11 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
newF
|
||||
},
|
||||
_.priceIndex.setIfDefined(cp.newPriceIndex),
|
||||
_.economy.modify(e =>
|
||||
(e + cp.economyDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
||||
),
|
||||
_.economy.modify(e => (e + cp.economyDelta.getOrElse(0.0)).max(0.0).min(100.0)),
|
||||
_.agriculture
|
||||
.modify(a =>
|
||||
(a + cp.agricultureDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
||||
),
|
||||
.modify(a => (a + cp.agricultureDelta.getOrElse(0.0)).max(0.0).min(100.0)),
|
||||
_.infrastructure
|
||||
.modify(i =>
|
||||
(i + cp.infrastructureDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
||||
),
|
||||
.modify(i => (i + cp.infrastructureDelta.getOrElse(0.0)).max(0.0).min(100.0)),
|
||||
_.economyDevastation.modify(d =>
|
||||
(d + cp.economyDevastationDelta.getOrElse(0.0))
|
||||
.max(0.0)
|
||||
@@ -211,16 +196,12 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
.max(0.0)
|
||||
.min(provinceBefore.infrastructure)
|
||||
),
|
||||
_.support.modify(s =>
|
||||
(s + cp.supportDelta.getOrElse(0.0)).max(0.0).min(100.0)
|
||||
),
|
||||
_.support.modify(s => (s + cp.supportDelta.getOrElse(0.0)).max(0.0).min(100.0)),
|
||||
_.hasActed.setIfDefined(cp.setHasActed),
|
||||
_.rulerIsTraveling.setIfDefined(cp.setRulerIsTraveling),
|
||||
_.unaffiliatedHeroes.modify(uhs =>
|
||||
uhs
|
||||
.filterNot(uh =>
|
||||
cp.removedUnaffiliatedHeroIds.contains(uh.heroId)
|
||||
)
|
||||
.filterNot(uh => cp.removedUnaffiliatedHeroIds.contains(uh.heroId))
|
||||
.filterNot(uh =>
|
||||
cp.changedUnaffiliatedHeroes
|
||||
.map(_.heroId)
|
||||
@@ -247,14 +228,14 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
cp.newProvinceEvents.exists(
|
||||
_.newEvents.exists(ProvinceEventUtils.isBeastsEvent)
|
||||
)
|
||||
) { gameState.currentDate.get }
|
||||
)(gameState.currentDate.get)
|
||||
),
|
||||
_.lastRiotDate.setIfDefined(
|
||||
Option.when(
|
||||
cp.newProvinceEvents.exists(
|
||||
_.newEvents.exists(ProvinceEventUtils.isImminentRiotEvent)
|
||||
)
|
||||
) { gameState.currentDate.get }
|
||||
)(gameState.currentDate.get)
|
||||
),
|
||||
_.incomingEndTurnActions :++= cp.addedIncomingEndTurnActions,
|
||||
_.incomingEndTurnActions.modify(
|
||||
@@ -265,9 +246,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
.map(idx => dcs.patch(idx, Nil, 1))
|
||||
.getOrElse(dcs) ++ cp.addedDeferredChange.asNonEmpty
|
||||
),
|
||||
_.battleRevelations.modify(brs =>
|
||||
brs.diff(cp.removedBattleRevelations) ++ cp.addedBattleRevelations
|
||||
)
|
||||
_.battleRevelations.modify(brs => brs.diff(cp.removedBattleRevelations) ++ cp.addedBattleRevelations)
|
||||
)
|
||||
|
||||
val provinceFixedForRuler =
|
||||
@@ -329,7 +308,8 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
.map(_.id)
|
||||
.reduceOption(_ max _)
|
||||
.getOrElse(0) + 1
|
||||
holdovers.toVector ++ addedArmies.zipWithIndex.map { case (army, index) =>
|
||||
holdovers.toVector ++ addedArmies.zipWithIndex.map {
|
||||
case (army, index) =>
|
||||
army.withId(index + nextId)
|
||||
}
|
||||
}
|
||||
@@ -339,7 +319,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
removedFactionIds: Seq[FactionId],
|
||||
addedArmies: Seq[HostileArmyGroup],
|
||||
statusChanges: Seq[HostileArmyGroupStatusChange]
|
||||
): Vector[HostileArmyGroup] = {
|
||||
): Vector[HostileArmyGroup] =
|
||||
statusChanges
|
||||
.foldLeft(
|
||||
existingGroups
|
||||
@@ -360,7 +340,6 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
}
|
||||
}
|
||||
.toVector ++ addedArmies
|
||||
}
|
||||
|
||||
def applyProvinceActed(actedProvince: Option[Int]): GameState = {
|
||||
internalRequire(
|
||||
@@ -396,8 +375,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
): GameState =
|
||||
if newBattalions.isEmpty then gameState
|
||||
else
|
||||
provinceId
|
||||
.map { pid =>
|
||||
provinceId.map { pid =>
|
||||
val maxCurrentId =
|
||||
if gameState.battalions.isEmpty then 0
|
||||
else gameState.battalions.keys.max
|
||||
@@ -444,8 +422,9 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
_.battalions := gameState.battalions -- destroyedBattalionIds,
|
||||
_.destroyedBattalions :++= destroyedBattalionIds
|
||||
.filterNot(_ == -1)
|
||||
.map { bid => bid -> gameState.battalions(bid) },
|
||||
_.provinces := gameState.provinces.map { case (pid, p) =>
|
||||
.map(bid => bid -> gameState.battalions(bid)),
|
||||
_.provinces := gameState.provinces.map {
|
||||
case (pid, p) =>
|
||||
pid -> p.update(
|
||||
_.battalionIds := p.battalionIds
|
||||
.filterNot(destroyedBattalionIds.contains),
|
||||
@@ -484,14 +463,14 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
def applyChangedHeroes(
|
||||
changedHeroes: Seq[ChangedHero],
|
||||
date: Date
|
||||
): GameState = {
|
||||
): GameState =
|
||||
if changedHeroes.isEmpty then gameState
|
||||
else
|
||||
changedHeroes
|
||||
.foldLeft(gameState) { case (gs, ch) =>
|
||||
.foldLeft(gameState) {
|
||||
case (gs, ch) =>
|
||||
gs.applyChangedHero(ch, date)
|
||||
}
|
||||
}
|
||||
|
||||
private def statBump(
|
||||
stat: Int,
|
||||
@@ -633,8 +612,9 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
)
|
||||
}
|
||||
|
||||
def applyRemovedFactions(removedFactionIds: Set[FactionId]): GameState = {
|
||||
gameState.factions.partition { case (fid, _) =>
|
||||
def applyRemovedFactions(removedFactionIds: Set[FactionId]): GameState =
|
||||
gameState.factions.partition {
|
||||
case (fid, _) =>
|
||||
removedFactionIds(fid)
|
||||
} match {
|
||||
case (removed, remaining) =>
|
||||
@@ -643,15 +623,16 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
_.factions := remaining
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def applyRemovedHeroes(removedHeroIds: Set[HeroId]): GameState =
|
||||
gameState.heroes.partition { case (k, _) =>
|
||||
gameState.heroes.partition {
|
||||
case (k, _) =>
|
||||
removedHeroIds(k)
|
||||
} match {
|
||||
case (removed, remaining) =>
|
||||
gameState.update(
|
||||
_.killedHeroes :++= removed.map { case (hid, hero) =>
|
||||
_.killedHeroes :++= removed.map {
|
||||
case (hid, hero) =>
|
||||
(hid, hero.clearFactionId)
|
||||
},
|
||||
_.heroes := remaining
|
||||
@@ -670,8 +651,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
actionResultType: ActionResultType,
|
||||
notification: Option[Notification]
|
||||
): GameState =
|
||||
notification
|
||||
.map { note =>
|
||||
notification.map { note =>
|
||||
gameState.update(
|
||||
_.deferredNotifications :+= note
|
||||
)
|
||||
@@ -727,8 +707,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
)
|
||||
) ++ cf.updatedReconnedProvinces
|
||||
},
|
||||
_.factionRelationships
|
||||
.modify { (relationships: Seq[FactionRelationship]) =>
|
||||
_.factionRelationships.modify { (relationships: Seq[FactionRelationship]) =>
|
||||
cf.trustLevelUpdates
|
||||
.foldLeft(relationships) {
|
||||
(
|
||||
@@ -743,8 +722,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
.getOrElse(
|
||||
FactionRelationship(
|
||||
targetFactionId = update.targetFactionId,
|
||||
relationshipLevel =
|
||||
FactionRelationship.RelationshipLevel.HOSTILE,
|
||||
relationshipLevel = FactionRelationship.RelationshipLevel.HOSTILE,
|
||||
trustValue = 0
|
||||
)
|
||||
)
|
||||
@@ -835,8 +813,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
)
|
||||
|
||||
def applyNewBattle(battle: Option[ShardokBattle]): GameState =
|
||||
battle
|
||||
.map { b =>
|
||||
battle.map { b =>
|
||||
gameState.update(
|
||||
_.outstandingBattles.modify(_ :+ b),
|
||||
_.battleCounter.modify(_.max(b.battleIndex))
|
||||
@@ -847,19 +824,16 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
def applyResolvedBattle(shardokGameId: Option[ShardokGameId]): GameState =
|
||||
gameState
|
||||
.withOutstandingBattles(
|
||||
gameState.outstandingBattles.filterNot(batt =>
|
||||
shardokGameId.contains(batt.shardokGameId)
|
||||
)
|
||||
gameState.outstandingBattles.filterNot(batt => shardokGameId.contains(batt.shardokGameId))
|
||||
)
|
||||
|
||||
def applyNewSeed(newSeed: Option[Long]): GameState =
|
||||
newSeed.map { s => gameState.withRandomSeed(s) }.getOrElse(gameState)
|
||||
newSeed.map(s => gameState.withRandomSeed(s)).getOrElse(gameState)
|
||||
|
||||
def applyChronicleEntry(
|
||||
chronicleEntry: Option[ChronicleEntry]
|
||||
): GameState =
|
||||
chronicleEntry
|
||||
.map { ce =>
|
||||
chronicleEntry.map { ce =>
|
||||
gameState.update(
|
||||
_.chronicleEntries.modify { ces =>
|
||||
ces.indexWhere(_.date == ce.date) match {
|
||||
@@ -872,8 +846,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
.getOrElse(gameState)
|
||||
|
||||
def applyCommandCountUpdate(actingFactionId: Option[FactionId]): GameState =
|
||||
actingFactionId
|
||||
.map { factionId =>
|
||||
actingFactionId.map { factionId =>
|
||||
val existingCount =
|
||||
gameState.factionCommandCounts.getOrElse(factionId, 0)
|
||||
gameState.update(
|
||||
@@ -886,7 +859,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
override def applyActionResults(
|
||||
startingState: GameState,
|
||||
results: Iterable[ActionResult]
|
||||
): Vector[ActionWithResultingState] = {
|
||||
): Vector[ActionWithResultingState] =
|
||||
results
|
||||
.foldLeft((startingState, Vector.empty[ActionWithResultingState])) {
|
||||
case ((gameState, acc), result) =>
|
||||
@@ -896,7 +869,7 @@ class ActionResultProtoApplierImpl(validator: Validator)
|
||||
}
|
||||
}
|
||||
._2
|
||||
}.toVector
|
||||
.toVector
|
||||
|
||||
override def applyActionResult(
|
||||
startingState: GameState,
|
||||
|
||||
+3
-3
@@ -13,8 +13,7 @@ object ActionResultTApplierImpl {
|
||||
new ActionResultTApplierImpl(protoApplier)
|
||||
}
|
||||
|
||||
class ActionResultTApplierImpl(protoApplier: ActionResultProtoApplier)
|
||||
extends ActionResultTApplier {
|
||||
class ActionResultTApplierImpl(protoApplier: ActionResultProtoApplier) extends ActionResultTApplier {
|
||||
override def xpForStatBump(stat: Int): Int = protoApplier.xpForStatBump(stat)
|
||||
|
||||
override def applyActionResults(
|
||||
@@ -26,7 +25,8 @@ class ActionResultTApplierImpl(protoApplier: ActionResultProtoApplier)
|
||||
results.map(ActionResultProtoConverter.toProto)
|
||||
)
|
||||
.zip(results)
|
||||
.map { case (actionWithResultingState, actionResult) =>
|
||||
.map {
|
||||
case (actionWithResultingState, actionResult) =>
|
||||
ActionResultTWithResultingState(
|
||||
actionResult = actionResult,
|
||||
resultingState = actionWithResultingState.gameState
|
||||
|
||||
+2
-5
@@ -12,9 +12,7 @@ object AvailableAlmsCommandFactory extends AvailableCommandsFactoryForType {
|
||||
.rulingFactionHeroIds
|
||||
.toVector
|
||||
.filter(hid => gs.heroes(hid).vigor >= MinVigorForAlms.doubleValue)
|
||||
.sortBy(hid =>
|
||||
(!gs.heroes(hid).profession.isPaladin, -gs.heroes(hid).vigor)
|
||||
)
|
||||
.sortBy(hid => (!gs.heroes(hid).profession.isPaladin, -gs.heroes(hid).vigor))
|
||||
|
||||
override def availableCommand(
|
||||
gameState: GameState,
|
||||
@@ -28,8 +26,7 @@ object AvailableAlmsCommandFactory extends AvailableCommandsFactoryForType {
|
||||
AlmsAvailableCommand(
|
||||
availableHeroIds = hids,
|
||||
actingProvinceId = provinceId,
|
||||
foodAvailable =
|
||||
Math.min(MaxAlmsFood.intValue, gameState.provinces(provinceId).food)
|
||||
foodAvailable = Math.min(MaxAlmsFood.intValue, gameState.provinces(provinceId).food)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-6
@@ -1,10 +1,7 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
ApprehendOutlawAvailableCommand,
|
||||
ResidentOutlaw
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{ApprehendOutlawAvailableCommand, ResidentOutlaw}
|
||||
import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.UNAFFILIATED_HERO_OUTLAW
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
@@ -13,8 +10,7 @@ import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
||||
import net.eagle0.eagle.library.settings.MinVigorForApprehendOutlaw
|
||||
import net.eagle0.eagle.library.util.view_filters.HeroViewFilter
|
||||
|
||||
object AvailableApprehendOutlawCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableApprehendOutlawCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
private def availableToCapture(province: Province): Vector[UnaffiliatedHero] =
|
||||
province.unaffiliatedHeroes
|
||||
|
||||
+3
-8
@@ -1,16 +1,12 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
ArmTroopsAvailableCommand,
|
||||
ArmamentCost
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{ArmTroopsAvailableCommand, ArmamentCost}
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.library.util.LegacyBattalionUtils
|
||||
|
||||
object AvailableArmTroopsCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableArmTroopsCommandFactory extends AvailableCommandsFactoryForType {
|
||||
override def availableCommand(
|
||||
gameState: GameState,
|
||||
factionId: FactionId,
|
||||
@@ -59,8 +55,7 @@ object AvailableArmTroopsCommandFactory
|
||||
actingProvinceId = provinceId,
|
||||
availableBattalions = affordableBattalions.map(_.id),
|
||||
armamentCosts = armamentCosts,
|
||||
maxArmament =
|
||||
LegacyProvinceUtils.effectiveInfrastructure(province).floor.toInt,
|
||||
maxArmament = LegacyProvinceUtils.effectiveInfrastructure(province).floor.toInt,
|
||||
availableBattalionTypeIds = gameState.battalionTypes.map(_.typeId)
|
||||
)
|
||||
)
|
||||
|
||||
+4
-9
@@ -19,8 +19,7 @@ import net.eagle0.eagle.library.util.{IncomingArmyUtils, ProvinceDistances}
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
|
||||
object AvailableAttackDecisionCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableAttackDecisionCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
// We need to include the armies that have already withdrawn or advanced, in a consistent sort order,
|
||||
// so that the player doesn't learn what others have done by the shifts.
|
||||
@@ -72,8 +71,7 @@ object AvailableAttackDecisionCommandFactory
|
||||
factionId = defenderFid,
|
||||
heroCount = heroCount,
|
||||
troopCount = troopCount,
|
||||
hostility =
|
||||
LegacyFactionUtils.hostilityStatus(defenderFid, attackerFid, gs),
|
||||
hostility = LegacyFactionUtils.hostilityStatus(defenderFid, attackerFid, gs),
|
||||
originProvinceId = pid
|
||||
)
|
||||
}
|
||||
@@ -219,8 +217,7 @@ object AvailableAttackDecisionCommandFactory
|
||||
s"Incoming armies in attack decision phase are not mutually allied in province $provinceId"
|
||||
)
|
||||
|
||||
myDecidingHostileArmies(provinceId, factionId, gameState).flatMap {
|
||||
hostileArmyGroup =>
|
||||
myDecidingHostileArmies(provinceId, factionId, gameState).flatMap { hostileArmyGroup =>
|
||||
decisionOptions(gameState, provinceId, factionId) match {
|
||||
case items if items.isEmpty => None
|
||||
case nonemptyDecisionOptions =>
|
||||
@@ -230,9 +227,7 @@ object AvailableAttackDecisionCommandFactory
|
||||
armies = armyStats(factionId, provinceId, gameState),
|
||||
actingUnits = hostileArmyGroup.armies
|
||||
.flatMap(_.getArmy.units)
|
||||
.map(cu =>
|
||||
ExpandedCombatUnitUtils.expandedCombatUnit(cu, gameState)
|
||||
),
|
||||
.map(cu => ExpandedCombatUnitUtils.expandedCombatUnit(cu, gameState)),
|
||||
availableDecisions = nonemptyDecisionOptions
|
||||
)
|
||||
)
|
||||
|
||||
+14
-31
@@ -74,8 +74,7 @@ object AvailableCommandsFactory {
|
||||
}
|
||||
|
||||
class AvailableCommandsFactory(
|
||||
private val travelingFactories: Vector[AvailableCommandsFactoryForType] =
|
||||
Vector(
|
||||
private val travelingFactories: Vector[AvailableCommandsFactoryForType] = Vector(
|
||||
AvailableRecruitHeroesCommandFactory,
|
||||
AvailableDeclineQuestCommandsFactory,
|
||||
AvailableDivineCommandsFactory,
|
||||
@@ -84,8 +83,7 @@ class AvailableCommandsFactory(
|
||||
AvailableManagePrisonersCommandFactory,
|
||||
AvailableReturnCommandsFactory
|
||||
),
|
||||
private val handleRiotFactories: Vector[AvailableCommandsFactoryForType] =
|
||||
Vector(
|
||||
private val handleRiotFactories: Vector[AvailableCommandsFactoryForType] = Vector(
|
||||
AvailableHandleRiotCrackDownCommandFactory,
|
||||
AvailableHandleRiotDoNothingCommandFactory,
|
||||
AvailableHandleRiotGiveCommandFactory
|
||||
@@ -100,13 +98,11 @@ class AvailableCommandsFactory(
|
||||
] = Vector(
|
||||
AvailableAttackDecisionCommandFactory
|
||||
),
|
||||
private val defensePhaseFactories: Vector[AvailableCommandsFactoryForType] =
|
||||
Vector(
|
||||
private val defensePhaseFactories: Vector[AvailableCommandsFactoryForType] = Vector(
|
||||
AvailableResolveTributeCommandsFactory,
|
||||
AvailableDefendCommandsFactory
|
||||
),
|
||||
private val commandPhaseFactories: Vector[AvailableCommandsFactoryForType] =
|
||||
Vector(
|
||||
private val commandPhaseFactories: Vector[AvailableCommandsFactoryForType] = Vector(
|
||||
AvailableApprehendOutlawCommandFactory,
|
||||
AvailableControlWeatherCommandsFactory,
|
||||
AvailableDiplomacyCommandsFactory,
|
||||
@@ -181,8 +177,8 @@ class AvailableCommandsFactory(
|
||||
pid: ProvinceId,
|
||||
commands: Vector[AvailableCommand]
|
||||
): Int =
|
||||
commands.zipWithIndex
|
||||
.find { case (ac, _) =>
|
||||
commands.zipWithIndex.find {
|
||||
case (ac, _) =>
|
||||
gs.provinces
|
||||
.get(pid)
|
||||
.map(_.lastCommand)
|
||||
@@ -220,8 +216,7 @@ class AvailableCommandsFactory(
|
||||
gs = gs,
|
||||
pid = pid,
|
||||
commands =
|
||||
if gs.provinces(pid).rulerIsTraveling then
|
||||
allCommands(travelingFactories, gs, fid(gs, pid).get, pid)
|
||||
if gs.provinces(pid).rulerIsTraveling then allCommands(travelingFactories, gs, fid(gs, pid).get, pid)
|
||||
else
|
||||
allCommands(
|
||||
commandPhaseFactories,
|
||||
@@ -235,8 +230,7 @@ class AvailableCommandsFactory(
|
||||
gs: GameState,
|
||||
pid: ProvinceId
|
||||
): Boolean =
|
||||
if gs.provinces(pid).rulerIsTraveling then
|
||||
hasCommand(travelingFactories, gs, fid(gs, pid).get, pid)
|
||||
if gs.provinces(pid).rulerIsTraveling then hasCommand(travelingFactories, gs, fid(gs, pid).get, pid)
|
||||
else
|
||||
hasCommand(
|
||||
commandPhaseFactories,
|
||||
@@ -440,27 +434,19 @@ class AvailableCommandsFactory(
|
||||
gs: GameState,
|
||||
fid: FactionId
|
||||
): Boolean =
|
||||
factionLeaderProvinces(gs, fid).exists(p =>
|
||||
defensePhaseCommandsForProvince(gs, p.id).isDefined
|
||||
)
|
||||
factionLeaderProvinces(gs, fid).exists(p => defensePhaseCommandsForProvince(gs, p.id).isDefined)
|
||||
|
||||
def hasAvailableHandleRiotPhaseCommands(gs: GameState): Boolean =
|
||||
gs.factions.keys.exists(fid =>
|
||||
handleRiotPhaseCommandsForFactionLeader(gs, fid).nonEmpty
|
||||
)
|
||||
gs.factions.keys.exists(fid => handleRiotPhaseCommandsForFactionLeader(gs, fid).nonEmpty)
|
||||
|
||||
def hasAvailablePlayerCommandsPhaseCommands(gs: GameState): Boolean =
|
||||
gs.factions.keys.exists(fid => hasCommandsPhaseCommandsForPlayer(gs, fid))
|
||||
|
||||
def hasAvailableFreeForAllDecisionPhaseCommands(gs: GameState): Boolean =
|
||||
gs.factions.keys.exists(fid =>
|
||||
freeForAllDecisionPhasePlayerCommands(gs, fid).nonEmpty
|
||||
)
|
||||
gs.factions.keys.exists(fid => freeForAllDecisionPhasePlayerCommands(gs, fid).nonEmpty)
|
||||
|
||||
def hasAvailableAttackDecisionPhaseCommands(gs: GameState): Boolean =
|
||||
gs.factions.keys.exists(fid =>
|
||||
attackDecisionPhasePlayerCommands(gs, fid).nonEmpty
|
||||
)
|
||||
gs.factions.keys.exists(fid => attackDecisionPhasePlayerCommands(gs, fid).nonEmpty)
|
||||
|
||||
def hasAvailablePlayerDefenseCommands(gs: GameState): Boolean =
|
||||
gs.factions.keys.exists(fid => hasPlayerDefenseCommandsForPlayer(gs, fid))
|
||||
@@ -527,9 +513,7 @@ class AvailableCommandsFactory(
|
||||
LegacyFactionUtils
|
||||
.provinces(fid, gs)
|
||||
.map(_.id)
|
||||
.flatMap(pid =>
|
||||
availableAftermathCommandsForProvince(gs, pid).map(pid -> _)
|
||||
)*
|
||||
.flatMap(pid => availableAftermathCommandsForProvince(gs, pid).map(pid -> _))*
|
||||
)
|
||||
|
||||
def availablePlayerCommands(
|
||||
@@ -550,7 +534,7 @@ class AvailableCommandsFactory(
|
||||
case _ => SortedMap.empty[ProvinceId, OneProvinceAvailableCommands]
|
||||
}
|
||||
|
||||
def hasAvailablePlayerCommands(gs: GameState): Boolean = {
|
||||
def hasAvailablePlayerCommands(gs: GameState): Boolean =
|
||||
gs.currentPhase match {
|
||||
case DIPLOMACY_RESOLUTION =>
|
||||
hasAvailableDiplomacyResolutionPhaseCommands(gs)
|
||||
@@ -574,5 +558,4 @@ class AvailableCommandsFactory(
|
||||
}
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-6
@@ -14,8 +14,7 @@ import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.MinVigorForControlWeather
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
object AvailableControlWeatherCommandsFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableControlWeatherCommandsFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
private def makeControlWeatherCommand(
|
||||
gameState: GameState,
|
||||
@@ -34,12 +33,10 @@ object AvailableControlWeatherCommandsFactory
|
||||
TargetProvinceOptions(
|
||||
provinceId = pid,
|
||||
controlWeatherTypes = (
|
||||
if LegacyProvinceUtils.hasBlizzard(gameState.provinces(pid)) then
|
||||
Vector(CONTROL_WEATHER_END_BLIZZARD)
|
||||
if LegacyProvinceUtils.hasBlizzard(gameState.provinces(pid)) then Vector(CONTROL_WEATHER_END_BLIZZARD)
|
||||
else Vector(CONTROL_WEATHER_START_BLIZZARD)
|
||||
) ++ (
|
||||
if LegacyProvinceUtils.hasDrought(gameState.provinces(pid)) then
|
||||
Vector(CONTROL_WEATHER_END_DROUGHT)
|
||||
if LegacyProvinceUtils.hasDrought(gameState.provinces(pid)) then Vector(CONTROL_WEATHER_END_DROUGHT)
|
||||
else Vector(CONTROL_WEATHER_START_DROUGHT)
|
||||
)
|
||||
)
|
||||
|
||||
+2
-5
@@ -8,8 +8,7 @@ import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.actions.availability.ExpandedUnaffiliatedHeroUtils.expandedUnaffiliatedHero
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
object AvailableDeclineQuestCommandsFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableDeclineQuestCommandsFactory extends AvailableCommandsFactoryForType {
|
||||
override def availableCommand(
|
||||
gameState: GameState,
|
||||
factionId: FactionId,
|
||||
@@ -27,9 +26,7 @@ object AvailableDeclineQuestCommandsFactory
|
||||
Option.when(withQuests.nonEmpty) {
|
||||
DeclineQuestAvailableCommand(
|
||||
actingProvinceId = provinceId,
|
||||
declinableHeroes = withQuests.map(uh =>
|
||||
expandedUnaffiliatedHero(gs = gameState, uh = uh)
|
||||
)
|
||||
declinableHeroes = withQuests.map(uh => expandedUnaffiliatedHero(gs = gameState, uh = uh))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-14
@@ -6,14 +6,8 @@ import net.eagle0.eagle.api.command.util.battalion_with_food_cost.BattalionWithF
|
||||
import net.eagle0.eagle.internal.army.Attacking
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
MaxCombatUnitCountPerSide,
|
||||
MinVigorForDefend
|
||||
}
|
||||
import net.eagle0.eagle.library.util.{
|
||||
BattalionSuitability,
|
||||
LegacyBattalionUtils
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.{MaxCombatUnitCountPerSide, MinVigorForDefend}
|
||||
import net.eagle0.eagle.library.util.{BattalionSuitability, LegacyBattalionUtils}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.CombatUnitSelector
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
|
||||
@@ -76,8 +70,7 @@ object AvailableDefendCommandsFactory extends AvailableCommandsFactoryForType {
|
||||
)
|
||||
|
||||
val recommendedUnits = CombatUnitSelector.selectedCombatBattalions(
|
||||
desiredCount =
|
||||
availableHeroes.size.min(MaxCombatUnitCountPerSide.intValue),
|
||||
desiredCount = availableHeroes.size.min(MaxCombatUnitCountPerSide.intValue),
|
||||
heroes = availableHeroes.toVector,
|
||||
battalions = availableBattalions.toVector,
|
||||
battalionTypes = gameState.battalionTypes.toVector
|
||||
@@ -93,15 +86,13 @@ object AvailableDefendCommandsFactory extends AvailableCommandsFactoryForType {
|
||||
.toVector
|
||||
.map(_.id),
|
||||
actingProvinceId = province.id,
|
||||
suitableBattalionsForHeroes =
|
||||
BattalionSuitability.suitableBattalionsForHeroes(
|
||||
suitableBattalionsForHeroes = BattalionSuitability.suitableBattalionsForHeroes(
|
||||
hs = availableHeroes.toVector,
|
||||
bs = availableBattalions.toVector,
|
||||
bts = gameState.battalionTypes.toVector
|
||||
),
|
||||
availableBattalions = availableBattalionsWithCosts,
|
||||
availableFleeProvinceIds =
|
||||
sortedFleeProvinces(from = province, gs = gameState),
|
||||
availableFleeProvinceIds = sortedFleeProvinces(from = province, gs = gameState),
|
||||
recommendedUnits = recommendedUnits,
|
||||
hostileFactionIds = hostileArmies.toVector.map(_.factionId).distinct,
|
||||
hostileHeroCount = hostileUnits.size,
|
||||
|
||||
+8
-17
@@ -31,8 +31,7 @@ import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.library.util.ProvinceDistances
|
||||
|
||||
object AvailableDiplomacyCommandsFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableDiplomacyCommandsFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
def availableHeroIds(gs: GameState, pid: ProvinceId): Vector[HeroId] =
|
||||
gs.provinces(pid)
|
||||
@@ -42,9 +41,7 @@ object AvailableDiplomacyCommandsFactory
|
||||
|
||||
def recommendedHeroId(gs: GameState, availableHids: Seq[HeroId]): HeroId =
|
||||
availableHids
|
||||
.sortBy(hid =>
|
||||
(LegacyHeroUtils.fatigue(gs.heroes(hid)), -gs.heroes(hid).vigor)
|
||||
) match {
|
||||
.sortBy(hid => (LegacyHeroUtils.fatigue(gs.heroes(hid)), -gs.heroes(hid).vigor)) match {
|
||||
case sortedHeroes =>
|
||||
sortedHeroes
|
||||
.find(hid => !LegacyFactionUtils.isFactionHead(hid, gs))
|
||||
@@ -123,9 +120,8 @@ object AvailableDiplomacyCommandsFactory
|
||||
pid: ProvinceId,
|
||||
actingFid: FactionId,
|
||||
targetFid: FactionId
|
||||
): Boolean = {
|
||||
LegacyFactionUtils.provinces(targetFid, gs).headOption.exists {
|
||||
targetProvince =>
|
||||
): Boolean =
|
||||
LegacyFactionUtils.provinces(targetFid, gs).headOption.exists { targetProvince =>
|
||||
val eligibleNeighbors: ProvinceId => Vector[ProvinceId] =
|
||||
p =>
|
||||
gs.provinces(p)
|
||||
@@ -154,7 +150,6 @@ object AvailableDiplomacyCommandsFactory
|
||||
.distance(pid, targetProvince.id, eligibleNeighbors)
|
||||
.isDefined
|
||||
}
|
||||
}
|
||||
|
||||
private def inviteOptionForTargetFaction(
|
||||
gs: GameState,
|
||||
@@ -200,8 +195,7 @@ object AvailableDiplomacyCommandsFactory
|
||||
gs,
|
||||
factionId = actingFid,
|
||||
targetFactionId = targetFid
|
||||
)
|
||||
.map { prisonerToBeRansomed =>
|
||||
).map { prisonerToBeRansomed =>
|
||||
RansomOfferOption(
|
||||
targetFactionId = targetFid,
|
||||
ransomOffer = Some(
|
||||
@@ -264,10 +258,9 @@ object AvailableDiplomacyCommandsFactory
|
||||
gameState: GameState,
|
||||
factionId: FactionId,
|
||||
provinceId: ProvinceId
|
||||
): Option[DiplomacyAvailableCommand] = {
|
||||
): Option[DiplomacyAvailableCommand] =
|
||||
if availableHeroIds(gameState, provinceId).isEmpty then None
|
||||
else if gameState.provinces(provinceId).rulingFactionHeroIds.size < 2 then
|
||||
None
|
||||
else if gameState.provinces(provinceId).rulingFactionHeroIds.size < 2 then None
|
||||
else {
|
||||
gameState.factions.keys
|
||||
.filterNot(_ == factionId)
|
||||
@@ -328,7 +321,6 @@ object AvailableDiplomacyCommandsFactory
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def availablePrisoners(
|
||||
province: Province
|
||||
@@ -365,7 +357,7 @@ object AvailableDiplomacyCommandsFactory
|
||||
gameState: GameState,
|
||||
factionId: FactionId,
|
||||
targetFactionId: FactionId
|
||||
): Vector[PrisonerToBeRansomed] = {
|
||||
): Vector[PrisonerToBeRansomed] =
|
||||
for {
|
||||
province <- gameState.provinces.values
|
||||
.filter(_.rulingFactionId.contains(targetFactionId))
|
||||
@@ -379,5 +371,4 @@ object AvailableDiplomacyCommandsFactory
|
||||
prisonerHeroId = prisoner.heroId,
|
||||
provinceIdForPrisoner = province.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -7,8 +7,7 @@ import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
object AvailableExileVassalCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableExileVassalCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
def exilableHeroIds(
|
||||
gameState: GameState,
|
||||
|
||||
+7
-22
@@ -3,22 +3,13 @@ package net.eagle0.eagle.library.actions.availability
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.FreeForAllDecisionAvailableCommand
|
||||
import net.eagle0.eagle.api.command.util.army_stats.ArmyStats
|
||||
import net.eagle0.eagle.api.command.util.attack_decision_type.{
|
||||
AdvanceDecision,
|
||||
AttackDecisionType,
|
||||
WithdrawDecision
|
||||
}
|
||||
import net.eagle0.eagle.api.command.util.attack_decision_type.{AdvanceDecision, AttackDecisionType, WithdrawDecision}
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.internal.army.{
|
||||
AwaitingDecision,
|
||||
HostileArmyGroup,
|
||||
MovingArmy
|
||||
}
|
||||
import net.eagle0.eagle.internal.army.{AwaitingDecision, HostileArmyGroup, MovingArmy}
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.IncomingArmyUtils
|
||||
|
||||
object AvailableFreeForAllDecisionCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableFreeForAllDecisionCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
private def decisionOptions(
|
||||
armies: Seq[MovingArmy]
|
||||
@@ -58,17 +49,14 @@ object AvailableFreeForAllDecisionCommandFactory
|
||||
pid: ProvinceId,
|
||||
gs: GameState
|
||||
): Vector[ArmyStats] =
|
||||
relevantArmies(pid, gs).map(ia =>
|
||||
IncomingArmyUtils.stats(toFid, ia.getArmy, gs, ia.originProvince)
|
||||
)
|
||||
relevantArmies(pid, gs).map(ia => IncomingArmyUtils.stats(toFid, ia.getArmy, gs, ia.originProvince))
|
||||
|
||||
override def availableCommand(
|
||||
gameState: GameState,
|
||||
factionId: FactionId,
|
||||
provinceId: ProvinceId
|
||||
): Option[FreeForAllDecisionAvailableCommand] = {
|
||||
myHostileArmies(provinceId, factionId, gameState).flatMap {
|
||||
hostileArmyGroup =>
|
||||
): Option[FreeForAllDecisionAvailableCommand] =
|
||||
myHostileArmies(provinceId, factionId, gameState).flatMap { hostileArmyGroup =>
|
||||
Option.when(
|
||||
gameState.currentPhase == RoundPhase.FREE_FOR_ALL_DECISION
|
||||
&& !IncomingArmyUtils.incomingArmiesAreMutuallyAllied(
|
||||
@@ -81,12 +69,9 @@ object AvailableFreeForAllDecisionCommandFactory
|
||||
armies = armyStats(factionId, provinceId, gameState),
|
||||
actingUnits = hostileArmyGroup.armies
|
||||
.flatMap(_.getArmy.units)
|
||||
.map(cu =>
|
||||
ExpandedCombatUnitUtils.expandedCombatUnit(cu, gameState)
|
||||
),
|
||||
.map(cu => ExpandedCombatUnitUtils.expandedCombatUnit(cu, gameState)),
|
||||
availableDecisions = decisionOptions(hostileArmyGroup.armies)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-15
@@ -1,10 +1,7 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
ExpandedCapturedHero,
|
||||
HandleCapturedHeroAvailableCommand
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{ExpandedCapturedHero, HandleCapturedHeroAvailableCommand}
|
||||
import net.eagle0.eagle.api.command.util.captured_hero_option.CapturedHeroOption.{
|
||||
EXECUTE_CAPTURED_HERO_OPTION,
|
||||
EXILE_CAPTURED_HERO_OPTION,
|
||||
@@ -17,16 +14,14 @@ import net.eagle0.eagle.internal.unaffiliated_hero.CapturedHero
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.view_filters.HeroViewFilter
|
||||
|
||||
object AvailableHandleCapturedHeroCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableHandleCapturedHeroCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
private val optionsWithoutRecruit = {
|
||||
private val optionsWithoutRecruit =
|
||||
Vector(
|
||||
IMPRISON_CAPTURED_HERO_OPTION,
|
||||
EXILE_CAPTURED_HERO_OPTION,
|
||||
EXECUTE_CAPTURED_HERO_OPTION
|
||||
)
|
||||
}
|
||||
private val optionsWithRecruit =
|
||||
RECRUIT_CAPTURED_HERO_OPTION +: optionsWithoutRecruit
|
||||
private val optionsForFactionLeader = Vector(
|
||||
@@ -72,14 +67,11 @@ object AvailableHandleCapturedHeroCommandFactory
|
||||
provinceId: ProvinceId
|
||||
): Option[HandleCapturedHeroAvailableCommand] =
|
||||
capturedHeroes
|
||||
.find(ch =>
|
||||
ch.recruitmentRefusedMessageId.nonEmpty || ch.messageId.nonEmpty
|
||||
)
|
||||
.find(ch => ch.recruitmentRefusedMessageId.nonEmpty || ch.messageId.nonEmpty)
|
||||
.map { capturedHero =>
|
||||
HandleCapturedHeroAvailableCommand(
|
||||
actingProvinceId = provinceId,
|
||||
availableHeroes =
|
||||
Vector(expandedCapturedHero(gameState, capturedHero))
|
||||
availableHeroes = Vector(expandedCapturedHero(gameState, capturedHero))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -87,7 +79,7 @@ object AvailableHandleCapturedHeroCommandFactory
|
||||
gameState: GameState,
|
||||
factionId: FactionId,
|
||||
provinceId: ProvinceId
|
||||
): Option[HandleCapturedHeroAvailableCommand] = {
|
||||
): Option[HandleCapturedHeroAvailableCommand] =
|
||||
// If we've already attempted to recruit one of the heroes, only show available commands for that one
|
||||
gameState
|
||||
.provinces(provinceId)
|
||||
@@ -108,5 +100,4 @@ object AvailableHandleCapturedHeroCommandFactory
|
||||
provinceId = provinceId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -7,17 +7,14 @@ import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.MinVigorForCrackDown
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
object AvailableHandleRiotCrackDownCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableHandleRiotCrackDownCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
def crackDownHeroIds(
|
||||
province: Province,
|
||||
gameState: GameState
|
||||
): Vector[HeroId] =
|
||||
province.rulingFactionHeroIds
|
||||
.filter(hid =>
|
||||
gameState.heroes(hid).vigor >= MinVigorForCrackDown.doubleValue
|
||||
)
|
||||
.filter(hid => gameState.heroes(hid).vigor >= MinVigorForCrackDown.doubleValue)
|
||||
.toVector
|
||||
|
||||
override def availableCommand(
|
||||
|
||||
+1
-2
@@ -5,8 +5,7 @@ import net.eagle0.eagle.api.available_command.HandleRiotDoNothingAvailableComman
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
object AvailableHandleRiotDoNothingCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableHandleRiotDoNothingCommandFactory extends AvailableCommandsFactoryForType {
|
||||
override def availableCommand(
|
||||
gameState: GameState,
|
||||
factionId: FactionId,
|
||||
|
||||
+1
-2
@@ -6,8 +6,7 @@ import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.settings.{RiotMaxFood, RiotMaxGold}
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
object AvailableHandleRiotGiveCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableHandleRiotGiveCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
override def availableCommand(
|
||||
gameState: GameState,
|
||||
|
||||
+2
-6
@@ -1,10 +1,7 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
EligibleGift,
|
||||
HeroGiftAvailableCommand
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{EligibleGift, HeroGiftAvailableCommand}
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.MaxGiftGold
|
||||
@@ -31,7 +28,7 @@ object AvailableHeroGiftCommandFactory extends AvailableCommandsFactoryForType {
|
||||
gameState: GameState,
|
||||
factionId: FactionId,
|
||||
provinceId: ProvinceId
|
||||
): Option[HeroGiftAvailableCommand] = {
|
||||
): Option[HeroGiftAvailableCommand] =
|
||||
consideredProvinces(gameState, factionId, provinceId)
|
||||
.filterNot(_.gold == 0)
|
||||
.flatMap(p => eligibleRecipients(p, gameState)) match {
|
||||
@@ -44,5 +41,4 @@ object AvailableHeroGiftCommandFactory extends AvailableCommandsFactoryForType {
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-9
@@ -2,12 +2,7 @@ package net.eagle0.eagle.library.actions.availability
|
||||
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.ImproveAvailableCommand
|
||||
import net.eagle0.eagle.common.improvement_type.ImprovementType.{
|
||||
AGRICULTURE,
|
||||
DEVASTATION,
|
||||
ECONOMY,
|
||||
INFRASTRUCTURE
|
||||
}
|
||||
import net.eagle0.eagle.common.improvement_type.ImprovementType.{AGRICULTURE, DEVASTATION, ECONOMY, INFRASTRUCTURE}
|
||||
import net.eagle0.eagle.common.profession.Profession.ENGINEER
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
@@ -42,14 +37,14 @@ object AvailableImproveCommandsFactory extends AvailableCommandsFactoryForType {
|
||||
.filterNot(_.vigor < MinVigorForImprove.doubleValue)
|
||||
|
||||
val availableTypes =
|
||||
Option.when(province.economy < IMPROVEMENT_MAX) { ECONOMY } ++
|
||||
Option.when(province.agriculture < IMPROVEMENT_MAX) { AGRICULTURE } ++
|
||||
Option.when(province.economy < IMPROVEMENT_MAX)(ECONOMY) ++
|
||||
Option.when(province.agriculture < IMPROVEMENT_MAX)(AGRICULTURE) ++
|
||||
Option.when(province.infrastructure < IMPROVEMENT_MAX) {
|
||||
INFRASTRUCTURE
|
||||
} ++
|
||||
Option.when(
|
||||
province.economyDevastation > 0 || province.agricultureDevastation > 0 || province.infrastructureDevastation > 0
|
||||
) { DEVASTATION }
|
||||
)(DEVASTATION)
|
||||
|
||||
Option.when(availableHeroes.nonEmpty && availableTypes.nonEmpty) {
|
||||
ImproveAvailableCommand(
|
||||
|
||||
+2
-5
@@ -7,8 +7,7 @@ import net.eagle0.eagle.common.province_order_type.ProvinceOrderType.*
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
|
||||
object AvailableIssueOrdersCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableIssueOrdersCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
override def availableCommand(
|
||||
gameState: GameState,
|
||||
@@ -28,9 +27,7 @@ object AvailableIssueOrdersCommandFactory
|
||||
.filter(_.rulingFactionId.contains(factionId))
|
||||
.toVector
|
||||
.sortBy(_.name)
|
||||
.map(p =>
|
||||
ProvinceOrders(provinceId = p.id, orders = p.provinceOrders)
|
||||
),
|
||||
.map(p => ProvinceOrders(provinceId = p.id, orders = p.provinceOrders)),
|
||||
availableOrders = Vector(DEVELOP, MOBILIZE, EXPAND, ENTRUST)
|
||||
)
|
||||
}
|
||||
|
||||
+3
-9
@@ -1,10 +1,7 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
import net.eagle0.common.MoreOption
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
ManagePrisonersAvailableCommand,
|
||||
PrisonerToManage
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{ManagePrisonersAvailableCommand, PrisonerToManage}
|
||||
import net.eagle0.eagle.api.command.util.prisoner_management_type.{
|
||||
PrisonerManagementOption,
|
||||
PrisonerManagementOptionExecute,
|
||||
@@ -17,8 +14,7 @@ import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.UNAFF
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
||||
|
||||
object AvailableManagePrisonersCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableManagePrisonersCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
private def availableMoveToProvinceIds(
|
||||
fid: FactionId,
|
||||
@@ -51,9 +47,7 @@ object AvailableManagePrisonersCommandFactory
|
||||
_.rulingFactionId.contains(factionWithThisPrisonerAsLeader.get.id)
|
||||
)
|
||||
)(
|
||||
PrisonerManagementOptionReturn(toFactionId =
|
||||
factionWithThisPrisonerAsLeader.get.id
|
||||
)
|
||||
PrisonerManagementOptionReturn(toFactionId = factionWithThisPrisonerAsLeader.get.id)
|
||||
)
|
||||
.toVector
|
||||
} else {
|
||||
|
||||
+4
-16
@@ -10,19 +10,9 @@ import net.eagle0.eagle.api.available_command.{
|
||||
import net.eagle0.eagle.api.command.util.battalion_with_food_cost.BattalionWithFoodCost
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
MaxCombatUnitCountPerSide,
|
||||
MinVigorForMarch
|
||||
}
|
||||
import net.eagle0.eagle.library.util.{
|
||||
BattalionSuitability,
|
||||
LegacyBattalionUtils,
|
||||
ShardokMapInfo
|
||||
}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.{
|
||||
CombatUnitSelector,
|
||||
MarchSuppliesHelpers
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.{MaxCombatUnitCountPerSide, MinVigorForMarch}
|
||||
import net.eagle0.eagle.library.util.{BattalionSuitability, LegacyBattalionUtils, ShardokMapInfo}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.{CombatUnitSelector, MarchSuppliesHelpers}
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
@@ -128,9 +118,7 @@ object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
||||
): Option[MarchAvailableCommand] =
|
||||
LegacyHeroUtils
|
||||
.consideredProvinces(gameState, factionId, provinceId)
|
||||
.flatMap(p =>
|
||||
oneOriginProvinceCommand(gameState = gameState, originProvince = p)
|
||||
) match {
|
||||
.flatMap(p => oneOriginProvinceCommand(gameState = gameState, originProvince = p)) match {
|
||||
case items if items.isEmpty => None
|
||||
case opcs =>
|
||||
Some(
|
||||
|
||||
+4
-10
@@ -1,16 +1,12 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
OrganizeTroopsAvailableCommand,
|
||||
TroopCost
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{OrganizeTroopsAvailableCommand, TroopCost}
|
||||
import net.eagle0.eagle.api.available_command.OrganizeTroopsAvailableCommand.BattalionTypeStatus
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
object AvailableOrganizeTroopsCommandsFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableOrganizeTroopsCommandsFactory extends AvailableCommandsFactoryForType {
|
||||
val maxBattalions = 10 // FIXME
|
||||
|
||||
override def availableCommand(
|
||||
@@ -20,8 +16,7 @@ object AvailableOrganizeTroopsCommandsFactory
|
||||
): Option[OrganizeTroopsAvailableCommand] = {
|
||||
val province = gameState.provinces(provinceId)
|
||||
|
||||
val availableTypeStatuses = gameState.battalionTypes.toVector
|
||||
.map { bt =>
|
||||
val availableTypeStatuses = gameState.battalionTypes.toVector.map { bt =>
|
||||
BattalionTypeStatus(
|
||||
typeId = bt.typeId,
|
||||
minimumAgriculture = bt.minimumAgriculture,
|
||||
@@ -44,8 +39,7 @@ object AvailableOrganizeTroopsCommandsFactory
|
||||
)
|
||||
|
||||
if troopCosts.isEmpty then return None
|
||||
if troopCosts.map(_.costPerTroop).min.ceil.toInt > province.gold then
|
||||
return None
|
||||
if troopCosts.map(_.costPerTroop).min.ceil.toInt > province.gold then return None
|
||||
|
||||
val existingEligibleBattalions = province.battalionIds
|
||||
.map(bid => gameState.battalions(bid))
|
||||
|
||||
+2
-6
@@ -1,9 +1,6 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
OneProvincePleaseRecruitMe,
|
||||
PleaseRecruitMeAvailableCommand
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{OneProvincePleaseRecruitMe, PleaseRecruitMeAvailableCommand}
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.MinSupportForTaxes
|
||||
@@ -25,14 +22,13 @@ object AvailablePleaseRecruitMeCommandFactory {
|
||||
unaffiliatedHero = uh
|
||||
)
|
||||
)
|
||||
} yield {
|
||||
} yield
|
||||
// FIXME: We should be generating the LLM request and its textID here instead of earlier in the round
|
||||
ExpandedUnaffiliatedHeroUtils
|
||||
.expandedUnaffiliatedHero(
|
||||
gs = gs,
|
||||
uh = targetUH
|
||||
)
|
||||
}
|
||||
|
||||
Option.when(availableHeroes.nonEmpty)(
|
||||
OneProvincePleaseRecruitMe(
|
||||
|
||||
+2
-4
@@ -8,8 +8,7 @@ import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.library.util.recruitment_odds.LegacyRecruitmentOdds
|
||||
|
||||
object AvailableRecruitHeroesCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableRecruitHeroesCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
override def availableCommand(
|
||||
gameState: GameState,
|
||||
@@ -21,8 +20,7 @@ object AvailableRecruitHeroesCommandFactory
|
||||
if !province.rulerIsTraveling then return None
|
||||
|
||||
val faction = gameState.factions(factionId)
|
||||
if !LegacyProvinceUtils.ruledByFactionLeader(province, gameState) then
|
||||
return None
|
||||
if !LegacyProvinceUtils.ruledByFactionLeader(province, gameState) then return None
|
||||
|
||||
val expandedRecruitable = province.unaffiliatedHeroes
|
||||
.filterNot(uh => LegacyFactionUtils.isFactionLeader(uh.heroId, gameState))
|
||||
|
||||
+2
-6
@@ -1,9 +1,6 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
import net.eagle0.eagle.api.available_command.ResolveAllianceOfferAvailableCommand
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
||||
AllianceOfferDetails,
|
||||
DiplomacyOffer
|
||||
}
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{AllianceOfferDetails, DiplomacyOffer}
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.FactionId
|
||||
@@ -52,8 +49,7 @@ object AvailableResolveAllianceOfferCommandFactory {
|
||||
messengerHeroId = messengerHeroId,
|
||||
messengerOriginProvinceId = messengerOriginProvinceId,
|
||||
status = status,
|
||||
eligibleStatuses =
|
||||
EligibleDiplomacyStatuses.acceptRejectStatuses ++ EligibleDiplomacyStatuses
|
||||
eligibleStatuses = EligibleDiplomacyStatuses.acceptRejectStatuses ++ EligibleDiplomacyStatuses
|
||||
.maybeImprisonStatus(
|
||||
actingFactionId = factionId,
|
||||
gameState = gameState
|
||||
|
||||
+1
-4
@@ -1,9 +1,6 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
import net.eagle0.eagle.api.available_command.ResolveBreakAllianceAvailableCommand
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
||||
BreakAllianceDetails,
|
||||
DiplomacyOffer
|
||||
}
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{BreakAllianceDetails, DiplomacyOffer}
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
||||
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
||||
|
||||
+1
-4
@@ -1,9 +1,6 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
import net.eagle0.eagle.api.available_command.ResolveInvitationAvailableCommand
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
||||
DiplomacyOffer,
|
||||
InvitationDetails
|
||||
}
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{DiplomacyOffer, InvitationDetails}
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
||||
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
||||
DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
||||
|
||||
+1
-5
@@ -1,10 +1,6 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
import net.eagle0.eagle.api.available_command.ResolveRansomOfferAvailableCommand
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
||||
DiplomacyOffer,
|
||||
InvitationDetails,
|
||||
RansomOfferDetails
|
||||
}
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{DiplomacyOffer, InvitationDetails, RansomOfferDetails}
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
||||
DIPLOMACY_OFFER_STATUS_REJECTED,
|
||||
DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
||||
|
||||
+4
-10
@@ -1,17 +1,13 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
ResolveTributeAvailableCommand,
|
||||
TributeAndFaction
|
||||
}
|
||||
import net.eagle0.eagle.api.available_command.{ResolveTributeAvailableCommand, TributeAndFaction}
|
||||
import net.eagle0.eagle.internal.army.{HostileArmyGroup, HostileArmyGroupStatus}
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.util.ArmyUtils
|
||||
|
||||
object AvailableResolveTributeCommandsFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableResolveTributeCommandsFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
private def makeResolveTributeCommand(
|
||||
province: Province,
|
||||
@@ -25,8 +21,7 @@ object AvailableResolveTributeCommandsFactory
|
||||
Option.when(incomingTributeDemands.nonEmpty) {
|
||||
ResolveTributeAvailableCommand(
|
||||
actingProvinceId = province.id,
|
||||
demands = incomingTributeDemands
|
||||
.map {
|
||||
demands = incomingTributeDemands.map {
|
||||
case ag @ HostileArmyGroup(
|
||||
fid: FactionId,
|
||||
_,
|
||||
@@ -35,8 +30,7 @@ object AvailableResolveTributeCommandsFactory
|
||||
) =>
|
||||
TributeAndFaction(
|
||||
demandingFactionId = fid,
|
||||
tributeDemanded =
|
||||
status.asMessage.getTributeDemanded.tributeAmount,
|
||||
tributeDemanded = status.asMessage.getTributeDemanded.tributeAmount,
|
||||
heroCount = ArmyUtils.heroCount(ag),
|
||||
troopCount = ArmyUtils.troopCount(ag, gs)
|
||||
)
|
||||
|
||||
+2
-6
@@ -1,9 +1,6 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
import net.eagle0.eagle.api.available_command.ResolveTruceOfferAvailableCommand
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
||||
DiplomacyOffer,
|
||||
TruceOfferDetails
|
||||
}
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{DiplomacyOffer, TruceOfferDetails}
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.settings.TruceMonths
|
||||
@@ -59,8 +56,7 @@ object AvailableResolveTruceOfferCommandFactory {
|
||||
messengerHeroId = messengerHeroId,
|
||||
messengerOriginProvinceId = messengerOriginProvinceId,
|
||||
status = status,
|
||||
eligibleStatuses =
|
||||
(EligibleDiplomacyStatuses.acceptRejectStatuses ++ EligibleDiplomacyStatuses
|
||||
eligibleStatuses = (EligibleDiplomacyStatuses.acceptRejectStatuses ++ EligibleDiplomacyStatuses
|
||||
.maybeImprisonStatus(
|
||||
actingFactionId = factionId,
|
||||
gameState = gameState
|
||||
|
||||
+1
-2
@@ -7,8 +7,7 @@ import net.eagle0.eagle.library.settings.MinVigorForSendSupplies
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
object AvailableSendSuppliesCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableSendSuppliesCommandFactory extends AvailableCommandsFactoryForType {
|
||||
override def availableCommand(
|
||||
gameState: GameState,
|
||||
factionId: FactionId,
|
||||
|
||||
+2
-5
@@ -8,8 +8,7 @@ import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.MinVigorForStartEpidemic
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
object AvailableStartEpidemicCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableStartEpidemicCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
private def makeStartEpidemicCommand(
|
||||
gameState: GameState,
|
||||
@@ -38,9 +37,7 @@ object AvailableStartEpidemicCommandFactory
|
||||
availableHeroIds = necromancersWithSufficientVigor
|
||||
.sortBy(-_.vigor)
|
||||
.map(_.id),
|
||||
options = targetProvinceIds.map(pid =>
|
||||
StartEpidemicOptions(targetProvinceId = pid)
|
||||
),
|
||||
options = targetProvinceIds.map(pid => StartEpidemicOptions(targetProvinceId = pid)),
|
||||
actingProvinceId = province.id
|
||||
)
|
||||
}
|
||||
|
||||
+2
-5
@@ -7,17 +7,14 @@ import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.MinVigorForSuppressBeasts
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
object AvailableSuppressBeastsCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableSuppressBeastsCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
private def makeSuppressBeastsCommand(
|
||||
gameState: GameState,
|
||||
province: Province
|
||||
): Option[SuppressBeastsAvailableCommand] = {
|
||||
val availableHeroIds = province.rulingFactionHeroIds
|
||||
.filter(hid =>
|
||||
gameState.heroes(hid).vigor >= MinVigorForSuppressBeasts.doubleValue
|
||||
)
|
||||
.filter(hid => gameState.heroes(hid).vigor >= MinVigorForSuppressBeasts.doubleValue)
|
||||
|
||||
Option.when(
|
||||
LegacyProvinceUtils.hasBeasts(province) && availableHeroIds.nonEmpty
|
||||
|
||||
+2
-6
@@ -4,13 +4,9 @@ import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
|
||||
import net.eagle0.eagle.api.available_command.SwearBrotherhoodAvailableCommand
|
||||
import net.eagle0.eagle.api.available_command.SwearBrotherhoodAvailableCommand.HeroAndBackstory
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
MaximumFactionLeaders,
|
||||
MinimumLoyaltyForSwearBrotherhood
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.{MaximumFactionLeaders, MinimumLoyaltyForSwearBrotherhood}
|
||||
|
||||
object AvailableSwearBrotherhoodCommandFactory
|
||||
extends AvailableCommandsFactoryForType {
|
||||
object AvailableSwearBrotherhoodCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
def canHaveMoreLeaders(gameState: GameState, factionId: FactionId): Boolean =
|
||||
gameState
|
||||
|
||||
+1
-4
@@ -3,10 +3,7 @@ package net.eagle0.eagle.library.actions.availability
|
||||
import net.eagle0.eagle.api.command.util.expanded_combat_unit.ExpandedCombatUnit
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.view_filters.{
|
||||
HeroViewFilter,
|
||||
LegacyBattalionViewFilter
|
||||
}
|
||||
import net.eagle0.eagle.library.util.view_filters.{HeroViewFilter, LegacyBattalionViewFilter}
|
||||
|
||||
object ExpandedCombatUnitUtils {
|
||||
def expandedCombatUnit(
|
||||
|
||||
+19
-28
@@ -7,11 +7,7 @@ import net.eagle0.eagle.library.util.province.ProvinceUtils
|
||||
import net.eagle0.eagle.library.util.ransom_validity.RansomValidity
|
||||
import net.eagle0.eagle.library.util.ReturningHeroes
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.concrete.{
|
||||
ActionResultC,
|
||||
ChangedFactionC,
|
||||
ChangedHeroC
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC, ChangedHeroC}
|
||||
import net.eagle0.eagle.model.action_result.types.{
|
||||
FactionDestroyedResultType,
|
||||
FactionLeaderRemovedResultType,
|
||||
@@ -25,10 +21,7 @@ import net.eagle0.eagle.model.state.faction.concrete.FactionC
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.{
|
||||
RecruitmentInfo,
|
||||
UnaffiliatedHeroT
|
||||
}
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.{RecruitmentInfo, UnaffiliatedHeroT}
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.concrete.UnaffiliatedHeroC
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.Traveler
|
||||
import net.eagle0.eagle.model.state.Army
|
||||
@@ -95,7 +88,8 @@ case class CheckForFactionChangesAction(
|
||||
case (faction: FactionC, fr) =>
|
||||
applyRemovedFactionResult(faction, fr)
|
||||
}
|
||||
.continue { case (results, fr) =>
|
||||
.continue {
|
||||
case (results, fr) =>
|
||||
maybeRansomInvalidResults(notDestroyed, fr).map { rirs =>
|
||||
results ++ rirs
|
||||
}
|
||||
@@ -114,12 +108,12 @@ case class CheckForFactionChangesAction(
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ReturningHeroes.ReturningResult]] =
|
||||
functionalRandom
|
||||
.nextMap(destroyedFaction.incomingDiplomacyOffers) { case (offer, fr) =>
|
||||
.nextMap(destroyedFaction.incomingDiplomacyOffers) {
|
||||
case (offer, fr) =>
|
||||
ReturningHeroes.heroesReturningToFaction(
|
||||
hids = Vector(offer.messengerHeroId),
|
||||
factionId = offer.originatingFactionId,
|
||||
originProvince =
|
||||
provinces.find(_.id == offer.messengerOriginProvinceId).get,
|
||||
originProvince = provinces.find(_.id == offer.messengerOriginProvinceId).get,
|
||||
provinces = provinces,
|
||||
functionalRandom = fr
|
||||
)
|
||||
@@ -140,7 +134,8 @@ case class CheckForFactionChangesAction(
|
||||
provinceId = p.id,
|
||||
removedIncomingArmyIds = removedFactionArmies.map(_.id)
|
||||
)
|
||||
) { case (cp, ma) =>
|
||||
) {
|
||||
case (cp, ma) =>
|
||||
cp.withNewUnaffiliatedHeroes(
|
||||
armyToUnaffiliatedHeroes(ma.army)
|
||||
).copy(
|
||||
@@ -193,9 +188,7 @@ case class CheckForFactionChangesAction(
|
||||
): Option[ChangedFactionC] =
|
||||
Option.when(
|
||||
faction.factionRelationships.exists(_.targetFactionId == otherFid)
|
||||
|| faction.incomingDiplomacyOffers.exists(ioff =>
|
||||
ioff.originatingFactionId == otherFid
|
||||
)
|
||||
|| faction.incomingDiplomacyOffers.exists(ioff => ioff.originatingFactionId == otherFid)
|
||||
)(
|
||||
ChangedFactionC(
|
||||
factionId = faction.id,
|
||||
@@ -272,16 +265,16 @@ case class CheckForFactionChangesAction(
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] =
|
||||
functionalRandom
|
||||
.nextFlatMap(notDestroyedFactions.toVector) { case (faction, fr) =>
|
||||
.nextFlatMap(notDestroyedFactions.toVector) {
|
||||
case (faction, fr) =>
|
||||
fr.nextMap(
|
||||
faction.incomingDiplomacyOffers
|
||||
.collect { case diploOffer: RansomOffer =>
|
||||
faction.incomingDiplomacyOffers.collect {
|
||||
case diploOffer: RansomOffer =>
|
||||
diploOffer
|
||||
}
|
||||
.filterNot(ransomOffer =>
|
||||
RansomValidity.isStillValid(ransomOffer, provinces)
|
||||
)
|
||||
) { case (ransomOffer, innerFr) =>
|
||||
.filterNot(ransomOffer => RansomValidity.isStillValid(ransomOffer, provinces))
|
||||
) {
|
||||
case (ransomOffer, innerFr) =>
|
||||
// Ransom offers don't remove the hero from the province
|
||||
RandomState(
|
||||
newValue = ActionResultC(
|
||||
@@ -289,10 +282,8 @@ case class CheckForFactionChangesAction(
|
||||
changedFactions = Vector(
|
||||
ChangedFactionC(
|
||||
factionId = faction.id,
|
||||
removedIncomingDiplomacyOfferFactionIds =
|
||||
Vector(ransomOffer.originatingFactionId),
|
||||
newIncomingDiplomacyOffers =
|
||||
Vector(ransomOffer.withStatus(Invalidated))
|
||||
removedIncomingDiplomacyOfferFactionIds = Vector(ransomOffer.originatingFactionId),
|
||||
newIncomingDiplomacyOffers = Vector(ransomOffer.withStatus(Invalidated))
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
+1
-2
@@ -54,8 +54,7 @@ case class CheckForFailedQuestsAction(
|
||||
// check for the province being owned by a different faction
|
||||
else if imprisonedProvince.rulingFactionId != questFactionId then true
|
||||
// check for the hero being no longer a prisoner in the province
|
||||
else if !imprisonedProvince.unaffiliatedHeroes
|
||||
.exists { uh =>
|
||||
else if !imprisonedProvince.unaffiliatedHeroes.exists { uh =>
|
||||
uh.heroId == prisonerHeroId && uh.unaffiliatedHeroType == Prisoner
|
||||
}
|
||||
then true
|
||||
|
||||
+4
-19
@@ -1,29 +1,16 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.eagle.{
|
||||
BattalionId,
|
||||
FactionId,
|
||||
GameId,
|
||||
HeroId,
|
||||
ProvinceId,
|
||||
RoundId
|
||||
}
|
||||
import net.eagle0.eagle.{BattalionId, FactionId, GameId, HeroId, ProvinceId, RoundId}
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionType
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessSequentialResultsAction
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
MinSupportForTaxes,
|
||||
PortionOfCapacityForUpgradeBattalionQuest
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.{MinSupportForTaxes, PortionOfCapacityForUpgradeBattalionQuest}
|
||||
import net.eagle0.eagle.library.util.province.ProvinceUtils
|
||||
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentUtils
|
||||
import net.eagle0.eagle.library.util.BattalionTypeFinder
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.faction.FactionRelationship.RelationshipLevel.{
|
||||
Ally,
|
||||
Hostile
|
||||
}
|
||||
import net.eagle0.eagle.model.state.faction.FactionRelationship.RelationshipLevel.{Ally, Hostile}
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
@@ -155,9 +142,7 @@ case class CheckForFulfilledQuestsAction(
|
||||
.exists(fr => fr.relationshipLevel == Ally)
|
||||
case TruceWithFactionQuest(targetFactionId) =>
|
||||
factionWithId(province.getRulingFactionId).get.factionRelationships
|
||||
.exists(fr =>
|
||||
fr.targetFactionId == targetFactionId && fr.relationshipLevel != Hostile
|
||||
)
|
||||
.exists(fr => fr.targetFactionId == targetFactionId && fr.relationshipLevel != Hostile)
|
||||
case TruceCountQuest(truceCount) =>
|
||||
factionWithId(province.getRulingFactionId).get.factionRelationships
|
||||
.count(_.relationshipLevel != Hostile) >= truceCount
|
||||
|
||||
+1
-2
@@ -407,9 +407,8 @@ object ChronicleEventGenerator {
|
||||
def eventTextEntries(
|
||||
gameHistory: GameHistory,
|
||||
since: Date
|
||||
): Vector[EventForChronicle] = {
|
||||
): Vector[EventForChronicle] =
|
||||
gameHistory.sinceDate(since).flatMap { (awrs: ActionWithResultingState) =>
|
||||
notificationEntries(awrs) ++ basicActionResultEntries(awrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -51,7 +51,8 @@ case class EndAttackDecisionPhaseAction(
|
||||
private def isFailedTruceQuest(q: QuestT, p: ProvinceT): Boolean =
|
||||
q match {
|
||||
case TruceWithFactionQuest(targetFactionId) =>
|
||||
incomingAttacks.exists { case (defendingFid, attackingFid) =>
|
||||
incomingAttacks.exists {
|
||||
case (defendingFid, attackingFid) =>
|
||||
defendingFid == targetFactionId && p.rulingFactionId.contains(
|
||||
attackingFid
|
||||
)
|
||||
|
||||
+21
-56
@@ -26,10 +26,7 @@ import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultTApplier
|
||||
import net.eagle0.eagle.library.actions.generated_text_request_generators.captured_hero_helpers.CapturedHeroPleaGenerator
|
||||
import net.eagle0.eagle.library.actions.impl.action.EndBattleAftermathPhaseAction.RevelationChange
|
||||
import net.eagle0.eagle.library.actions.impl.common.{
|
||||
ProtolessRandomSequentialResultsAction,
|
||||
RandomStateTSequencer
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.common.{ProtolessRandomSequentialResultsAction, RandomStateTSequencer}
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
FactionBiasAgainstFormerOnExile,
|
||||
FactionBiasFromExile,
|
||||
@@ -41,22 +38,10 @@ import net.eagle0.eagle.library.util.unaffiliated_hero.LegacyUnaffiliatedHeroUti
|
||||
import net.eagle0.eagle.library.util.view_filters.ProvinceViewFilter
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
import net.eagle0.eagle.model.action_result.{
|
||||
ActionResultT,
|
||||
NotificationDetails,
|
||||
NotificationT
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.{ActionResultT, NotificationDetails, NotificationT}
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.concrete.{
|
||||
ActionResultC,
|
||||
ChangedFactionC,
|
||||
ChangedHeroC,
|
||||
NotificationC
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.types.{
|
||||
CapturedHeroResolvedResultType,
|
||||
EndAftermathPhaseResultType
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC, ChangedHeroC, NotificationC}
|
||||
import net.eagle0.eagle.model.action_result.types.{CapturedHeroResolvedResultType, EndAftermathPhaseResultType}
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
BattleRevelationConverter,
|
||||
NotificationConverter,
|
||||
@@ -74,11 +59,7 @@ import net.eagle0.eagle.model.state.hero.{
|
||||
CapturedHeroReturnedBackstoryEvent,
|
||||
EventForHeroBackstoryT
|
||||
}
|
||||
import net.eagle0.eagle.model.state.BattleRevelationType.{
|
||||
DidBattle,
|
||||
Unknown,
|
||||
Withdrew
|
||||
}
|
||||
import net.eagle0.eagle.model.state.BattleRevelationType.{DidBattle, Unknown, Withdrew}
|
||||
|
||||
object EndBattleAftermathPhaseAction {
|
||||
case class RevelationChange(
|
||||
@@ -112,15 +93,13 @@ object EndBattleAftermathPhaseAction {
|
||||
`type` = uhType,
|
||||
lastFaction = gameState.heroes(capturedHeroId).factionId,
|
||||
factionBiases = (
|
||||
newFactionBias.map(b => actingFactionId -> b) ++ oldFactionBias.map(
|
||||
b => capturedHeroFactionId -> b
|
||||
)
|
||||
newFactionBias.map(b => actingFactionId -> b) ++ oldFactionBias.map(b => capturedHeroFactionId -> b)
|
||||
).toMap
|
||||
),
|
||||
hero = gameState.heroes(capturedHeroId),
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.map { UnaffiliatedHeroConverter.fromProto }
|
||||
.map(UnaffiliatedHeroConverter.fromProto)
|
||||
.map { uh =>
|
||||
ActionResultC(
|
||||
actionResultType = CapturedHeroResolvedResultType,
|
||||
@@ -129,8 +108,7 @@ object EndBattleAftermathPhaseAction {
|
||||
changedHeroes = Vector(
|
||||
ChangedHeroC(
|
||||
heroId = capturedHeroId,
|
||||
newEventsForHeroBackstory =
|
||||
Vector(newEventForHeroBackstoryDetails)
|
||||
newEventsForHeroBackstory = Vector(newEventForHeroBackstoryDetails)
|
||||
)
|
||||
),
|
||||
changedProvinces = Vector(
|
||||
@@ -274,8 +252,7 @@ object EndBattleAftermathPhaseAction {
|
||||
oldFactionBias = None,
|
||||
gameState = gameState,
|
||||
functionalRandom = functionalRandom,
|
||||
newEventForHeroBackstoryDetails =
|
||||
CapturedHeroImprisonedBackstoryEvent(
|
||||
newEventForHeroBackstoryDetails = CapturedHeroImprisonedBackstoryEvent(
|
||||
date = DateConverter.fromProto(gameState.currentDate),
|
||||
capturingFactionId = imprisoningFactionId,
|
||||
capturingHeroId = imprisoningHeroId,
|
||||
@@ -352,8 +329,7 @@ object EndBattleAftermathPhaseAction {
|
||||
factions = allFactionTs
|
||||
)
|
||||
.copy(
|
||||
relationshipLevel =
|
||||
FactionRelationship.RelationshipLevel.Truce,
|
||||
relationshipLevel = FactionRelationship.RelationshipLevel.Truce,
|
||||
resetDate = Some(truceEndDate)
|
||||
)
|
||||
)
|
||||
@@ -368,8 +344,7 @@ object EndBattleAftermathPhaseAction {
|
||||
factions = allFactionTs
|
||||
)
|
||||
.copy(
|
||||
relationshipLevel =
|
||||
FactionRelationship.RelationshipLevel.Truce,
|
||||
relationshipLevel = FactionRelationship.RelationshipLevel.Truce,
|
||||
resetDate = Some(truceEndDate)
|
||||
)
|
||||
)
|
||||
@@ -394,9 +369,8 @@ object EndBattleAftermathPhaseAction {
|
||||
throw new EagleInternalException("Empty deferred change")
|
||||
|
||||
// the rest are not for this phase
|
||||
case _: EpidemicStarted | _: DroughtStarted | _: DroughtEnded |
|
||||
_: PrisonerMoved | _: PrisonerReturned | _: BlizzardStarted |
|
||||
_: BlizzardEnded =>
|
||||
case _: EpidemicStarted | _: DroughtStarted | _: DroughtEnded | _: PrisonerMoved | _: PrisonerReturned |
|
||||
_: BlizzardStarted | _: BlizzardEnded =>
|
||||
throw new EagleInternalException(
|
||||
"Event should not be present in EndBattleAftermathPhaseAction"
|
||||
)
|
||||
@@ -466,9 +440,7 @@ case class EndBattleAftermathPhaseAction(
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.foldIn(
|
||||
EndBattleAftermathPhaseAction.allDeferredChanges(gameState =
|
||||
initialState
|
||||
)
|
||||
EndBattleAftermathPhaseAction.allDeferredChanges(gameState = initialState)
|
||||
)(
|
||||
EndBattleAftermathPhaseAction.deferredChangeAR
|
||||
)
|
||||
@@ -495,18 +467,15 @@ case class EndBattleAftermathPhaseAction(
|
||||
.withRandomActionResults((gs, fr) =>
|
||||
CheckForFactionChangesAction(
|
||||
gameId = gs.gameId,
|
||||
factions =
|
||||
gs.factions.values.toVector.map(FactionConverter.fromProto),
|
||||
provinces =
|
||||
gs.provinces.values.toVector.map(ProvinceConverter.fromProto),
|
||||
factions = gs.factions.values.toVector.map(FactionConverter.fromProto),
|
||||
provinces = gs.provinces.values.toVector.map(ProvinceConverter.fromProto),
|
||||
heroes = gs.heroes.values.toVector.map(HeroConverter.fromProto),
|
||||
killedHeroIds = gs.killedHeroes.keys.toVector
|
||||
).randomResults(fr)
|
||||
)
|
||||
.withProtolessSequentialResultsAction(gs =>
|
||||
HeroBackstoryUpdateActionGenerator.fromGameState(gs)
|
||||
)
|
||||
.withRandomActionResult { case (gs, fr) =>
|
||||
.withProtolessSequentialResultsAction(gs => HeroBackstoryUpdateActionGenerator.fromGameState(gs))
|
||||
.withRandomActionResult {
|
||||
case (gs, fr) =>
|
||||
RandomState(
|
||||
ActionResultC(
|
||||
actionResultType = EndAftermathPhaseResultType,
|
||||
@@ -514,14 +483,10 @@ case class EndBattleAftermathPhaseAction(
|
||||
changedProvinces = revelationChanges(gs).map(_.changedProvince),
|
||||
newRoundPhase = Some(RoundPhase.DiplomacyResolution),
|
||||
removedNotifications = gs.deferredNotifications
|
||||
.map(note =>
|
||||
NotificationConverter.fromProto(note, deferred = true)
|
||||
)
|
||||
.map(note => NotificationConverter.fromProto(note, deferred = true))
|
||||
.toVector,
|
||||
newNotifications = gs.deferredNotifications
|
||||
.map(note =>
|
||||
NotificationConverter.fromProto(note, deferred = false)
|
||||
)
|
||||
.map(note => NotificationConverter.fromProto(note, deferred = false))
|
||||
.toVector
|
||||
),
|
||||
fr
|
||||
|
||||
+3
-3
@@ -9,11 +9,11 @@ import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.actions.impl.common.DeterministicSingleResultAction
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
|
||||
case class EndBattleRequestPhaseAction(gameState: GameState)
|
||||
extends DeterministicSingleResultAction(gameState) {
|
||||
case class EndBattleRequestPhaseAction(gameState: GameState) extends DeterministicSingleResultAction(gameState) {
|
||||
override def immediateExecute: ActionResult = {
|
||||
internalRequire(
|
||||
gameState.provinces.forall { case (_, province) =>
|
||||
gameState.provinces.forall {
|
||||
case (_, province) =>
|
||||
province.hostileArmies.isEmpty
|
||||
},
|
||||
s"Province ${gameState.provinces.find(_._2.hostileArmies.nonEmpty).get._2.name} still has attacking armies!"
|
||||
|
||||
+1
-2
@@ -7,8 +7,7 @@ import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.actions.impl.common.DeterministicSingleResultAction
|
||||
|
||||
case class EndBattleResolutionPhaseAction(gameState: GameState)
|
||||
extends DeterministicSingleResultAction(gameState) {
|
||||
case class EndBattleResolutionPhaseAction(gameState: GameState) extends DeterministicSingleResultAction(gameState) {
|
||||
override def immediateExecute: ActionResult =
|
||||
ActionResult(
|
||||
`type` = END_RESOLUTION_PHASE,
|
||||
|
||||
+1
-3
@@ -32,8 +32,7 @@ case class EndDefenseDecisionPhaseAction(
|
||||
val attackerCps = for {
|
||||
ag <- tributePaidArmies
|
||||
army <- ag.armies
|
||||
} yield {
|
||||
ChangedProvince(
|
||||
} yield ChangedProvince(
|
||||
id = army.originProvince,
|
||||
addedIncomingArmies = Vector(
|
||||
army.update(
|
||||
@@ -44,7 +43,6 @@ case class EndDefenseDecisionPhaseAction(
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if attackerCps.isEmpty then Vector.empty
|
||||
else
|
||||
|
||||
+23
-56
@@ -10,47 +10,23 @@ import net.eagle0.eagle.library.actions.impl.action.diplomacy_helpers.{
|
||||
RansomResolutionHelpers,
|
||||
TruceResolutionHelpers
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.common.{
|
||||
ProtolessRandomSequentialResultsAction,
|
||||
RandomStateTSequencer
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.common.{ProtolessRandomSequentialResultsAction, RandomStateTSequencer}
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentChecker
|
||||
import net.eagle0.eagle.library.util.ransom_validity.RansomValidity
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
import net.eagle0.eagle.model.action_result.concrete.{
|
||||
ActionResultC,
|
||||
ChangedFactionC
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.types.{
|
||||
EndDiplomacyResolutionPhaseResultType,
|
||||
RansomInvalidatedResultType
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC}
|
||||
import net.eagle0.eagle.model.action_result.types.{EndDiplomacyResolutionPhaseResultType, RansomInvalidatedResultType}
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
BattalionConverter,
|
||||
NotificationConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.{BattalionConverter, 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.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.{
|
||||
AllianceOffer,
|
||||
BreakAlliance,
|
||||
Invitation,
|
||||
RansomOffer,
|
||||
TruceOffer
|
||||
}
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.{
|
||||
Accepted,
|
||||
Imprisoned,
|
||||
Invalidated,
|
||||
Rejected,
|
||||
Unresolved
|
||||
}
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.{AllianceOffer, BreakAlliance, Invitation, RansomOffer, TruceOffer}
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.{Accepted, Imprisoned, Invalidated, Rejected, Unresolved}
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
@@ -120,19 +96,15 @@ case class EndDiplomacyResolutionPhaseAction(
|
||||
|
||||
}
|
||||
|
||||
/** Companion object for EndDiplomacyResolutionPhaseAction containing helper
|
||||
* methods.
|
||||
/**
|
||||
* Companion object for EndDiplomacyResolutionPhaseAction containing helper methods.
|
||||
*
|
||||
* IMPORTANT: This is intentionally named differently from the case class to
|
||||
* avoid Scala naming conflicts that prevent the class from being imported in
|
||||
* other packages. When a case class and its companion object share the same
|
||||
* name, the compiler can become confused about which one is being referenced
|
||||
* during imports.
|
||||
* IMPORTANT: This is intentionally named differently from the case class to avoid Scala naming conflicts that prevent
|
||||
* the class from being imported in other packages. When a case class and its companion object share the same name, the
|
||||
* compiler can become confused about which one is being referenced during imports.
|
||||
*
|
||||
* All helper methods take explicit parameters for game state components to
|
||||
* ensure they always operate on the current state as provided by
|
||||
* RandomStateTSequencer, preventing use of stale data from the initial game
|
||||
* state.
|
||||
* All helper methods take explicit parameters for game state components to ensure they always operate on the current
|
||||
* state as provided by RandomStateTSequencer, preventing use of stale data from the initial game state.
|
||||
*/
|
||||
private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
|
||||
@@ -151,17 +123,14 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
diploOffer <- faction.incomingDiplomacyOffers.filter(
|
||||
_.status == Unresolved
|
||||
)
|
||||
} yield {
|
||||
diploOffer match {
|
||||
case ro: RansomOffer
|
||||
if !RansomValidity.isStillValid(diploOffer, provinces) =>
|
||||
} yield diploOffer match {
|
||||
case ro: RansomOffer if !RansomValidity.isStillValid(diploOffer, provinces) =>
|
||||
ActionResultC(
|
||||
actionResultType = RansomInvalidatedResultType,
|
||||
changedFactions = Vector(
|
||||
ChangedFactionC(
|
||||
factionId = faction.id,
|
||||
removedIncomingDiplomacyOfferFactionIds =
|
||||
Vector(ro.originatingFactionId),
|
||||
removedIncomingDiplomacyOfferFactionIds = Vector(ro.originatingFactionId),
|
||||
newIncomingDiplomacyOffers = Vector(ro.withStatus(Invalidated))
|
||||
)
|
||||
)
|
||||
@@ -173,7 +142,6 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def endPhaseResultForState(
|
||||
currentGameState: GameState
|
||||
@@ -207,8 +175,10 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
resolver: (A, FunctionalRandom) => RandomState[Vector[ActionResultT]],
|
||||
functionalRandom: FunctionalRandom
|
||||
)(factions: Vector[FactionT]): RandomState[Vector[ActionResultT]] =
|
||||
functionalRandom.nextFlatMap(factions) { case (faction, fr) =>
|
||||
fr.nextMap(getter(faction)) { case (outgoingDiplo, innerFr) =>
|
||||
functionalRandom.nextFlatMap(factions) {
|
||||
case (faction, fr) =>
|
||||
fr.nextMap(getter(faction)) {
|
||||
case (outgoingDiplo, innerFr) =>
|
||||
resolver(outgoingDiplo, innerFr)
|
||||
}.map(_.flatten)
|
||||
}
|
||||
@@ -346,8 +316,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
gameId = gameState.gameId,
|
||||
currentDate = currentDate,
|
||||
currentRoundId = gameState.currentRoundId,
|
||||
previousBackstoryTextIdLookup =
|
||||
hid => gameState.heroes(hid).backstoryVersions.toVector.last.textId
|
||||
previousBackstoryTextIdLookup = hid => gameState.heroes(hid).backstoryVersions.toVector.last.textId
|
||||
)
|
||||
)
|
||||
case Rejected =>
|
||||
@@ -429,7 +398,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
factions: Vector[FactionT],
|
||||
heroes: Vector[HeroT],
|
||||
currentDate: Date
|
||||
): RandomState[Vector[ActionResultT]] = {
|
||||
): RandomState[Vector[ActionResultT]] =
|
||||
allianceOffer.status match {
|
||||
case Accepted =>
|
||||
AllianceResolutionHelpers
|
||||
@@ -474,7 +443,6 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
"UNRESOLVED alliance offer at the end of the round"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def resultsForBreakAlliance(
|
||||
breakAllianceOffer: BreakAlliance,
|
||||
@@ -482,7 +450,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
provinces: Vector[ProvinceT],
|
||||
factions: Vector[FactionT],
|
||||
currentDate: Date
|
||||
): RandomState[Vector[ActionResultT]] = {
|
||||
): RandomState[Vector[ActionResultT]] =
|
||||
breakAllianceOffer.status match {
|
||||
case Accepted =>
|
||||
BreakAllianceResolutionHelpers
|
||||
@@ -526,7 +494,6 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
)
|
||||
.map(Vector(_))
|
||||
}
|
||||
}
|
||||
|
||||
private def resultsForInvitation(
|
||||
invitation: Invitation,
|
||||
|
||||
+1
-2
@@ -12,7 +12,6 @@ case class EndFreeForAllBattleRequestPhaseAction(gameState: GameState)
|
||||
override def immediateExecute: ActionResult =
|
||||
ActionResult(
|
||||
`type` = END_FREE_FOR_ALL_BATTLE_REQUEST_PHASE,
|
||||
newRoundPhase =
|
||||
Some(NewRoundPhase(value = FREE_FOR_ALL_BATTLE_RESOLUTION))
|
||||
newRoundPhase = Some(NewRoundPhase(value = FREE_FOR_ALL_BATTLE_RESOLUTION))
|
||||
)
|
||||
}
|
||||
|
||||
+1
-2
@@ -8,8 +8,7 @@ import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.model.state.RoundPhase
|
||||
|
||||
case class EndFreeForAllDecisionPhaseAction(gameState: GameState)
|
||||
extends ProtolessSequentialResultsAction {
|
||||
case class EndFreeForAllDecisionPhaseAction(gameState: GameState) extends ProtolessSequentialResultsAction {
|
||||
|
||||
override def results: Vector[ActionResultT] =
|
||||
WithdrawnArmiesReturnHomeAction(
|
||||
|
||||
+8
-11
@@ -9,14 +9,8 @@ import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplier
|
||||
import net.eagle0.eagle.library.actions.impl.command.{
|
||||
CommandFactory,
|
||||
LegacyHandleRiotUtils
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.common.{
|
||||
RandomSequentialResultsAction,
|
||||
RandomStateProtoSequencer
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.command.{CommandFactory, LegacyHandleRiotUtils}
|
||||
import net.eagle0.eagle.library.actions.impl.common.{RandomSequentialResultsAction, RandomStateProtoSequencer}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChoiceHelpers
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
@@ -34,9 +28,11 @@ case class EndHandleRiotsPhaseAction(
|
||||
ars.lastStateProto.provinces.values
|
||||
.filter(LegacyProvinceUtils.hasImminentRiot)
|
||||
.filterNot(_.hasActed)
|
||||
.foldLeft(ars) { case (sequencer, p) =>
|
||||
.foldLeft(ars) {
|
||||
case (sequencer, p) =>
|
||||
commandsForProvince(p.id).map { opac =>
|
||||
sequencer.withRandomAction { case (gs, fr) =>
|
||||
sequencer.withRandomAction {
|
||||
case (gs, fr) =>
|
||||
CommandChoiceHelpers
|
||||
.handleRiotSelectedCommand(
|
||||
actingFactionId = p.getRulingFactionId,
|
||||
@@ -64,7 +60,8 @@ case class EndHandleRiotsPhaseAction(
|
||||
): RandomStateProtoSequencer =
|
||||
ars.lastStateProto.provinces.values
|
||||
.filter(LegacyProvinceUtils.hasImminentRiot)
|
||||
.foldLeft(ars) { case (ars, p) =>
|
||||
.foldLeft(ars) {
|
||||
case (ars, p) =>
|
||||
ars.withActionResult(_ =>
|
||||
LegacyHandleRiotUtils
|
||||
.riotOccurredAr(
|
||||
|
||||
+14
-35
@@ -17,11 +17,7 @@ import net.eagle0.eagle.library.util.ProvinceEventUtils
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.changed_province.ChangedProvinceT
|
||||
import net.eagle0.eagle.model.action_result.concrete.{
|
||||
ActionResultC,
|
||||
ChangedFactionC,
|
||||
ChangedHeroC
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC, ChangedHeroC}
|
||||
import net.eagle0.eagle.model.action_result.types.{
|
||||
EndPlayerCommandsPhaseResultType,
|
||||
EpidemicTookEffectResultType,
|
||||
@@ -30,27 +26,14 @@ import net.eagle0.eagle.model.action_result.types.{
|
||||
WeatherTookEffectResultType
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
NotificationConverter,
|
||||
UnaffiliatedHeroConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.{NotificationConverter, UnaffiliatedHeroConverter}
|
||||
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,
|
||||
ProvinceEventConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.state.province.{
|
||||
BlizzardEvent,
|
||||
DroughtEvent,
|
||||
EpidemicEvent
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.province.{ProvinceConverter, ProvinceEventConverter}
|
||||
import net.eagle0.eagle.model.state.province.{BlizzardEvent, DroughtEvent, EpidemicEvent}
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.RecruitmentInfo
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.{
|
||||
Outlaw,
|
||||
Prisoner
|
||||
}
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.{Outlaw, Prisoner}
|
||||
import net.eagle0.eagle.model.state.RoundPhase
|
||||
import net.eagle0.eagle.ProvinceId
|
||||
|
||||
@@ -148,8 +131,7 @@ case class EndPlayerCommandsPhaseAction(
|
||||
actionResultType = PrisonerMoveTookEffectResultType,
|
||||
provinceId = Some(pid),
|
||||
actingFactionId = Some(prisonerReturned.fromFactionId),
|
||||
affectedFactionIds =
|
||||
Vector(prisonerReturned.fromFactionId, prisonerReturned.toFactionId),
|
||||
affectedFactionIds = Vector(prisonerReturned.fromFactionId, prisonerReturned.toFactionId),
|
||||
changedHeroes = Vector(
|
||||
ChangedHeroC(
|
||||
heroId = prisonerReturned.heroId,
|
||||
@@ -186,8 +168,7 @@ case class EndPlayerCommandsPhaseAction(
|
||||
case _: BlizzardStarted => WeatherTookEffectResultType
|
||||
case _: DroughtStarted => WeatherTookEffectResultType
|
||||
case _: DroughtEnded => WeatherTookEffectResultType
|
||||
case _: PrisonerMoved | _: PrisonerReturned |
|
||||
_: CapturedHeroImprisoned | _: CapturedHeroExecuted |
|
||||
case _: PrisonerMoved | _: PrisonerReturned | _: CapturedHeroImprisoned | _: CapturedHeroExecuted |
|
||||
_: CapturedHeroExiled | _: CapturedHeroReturned =>
|
||||
throw new EagleInternalException(
|
||||
"Prisoner management changes should not be here"
|
||||
@@ -297,7 +278,8 @@ case class EndPlayerCommandsPhaseAction(
|
||||
): RandomStateTSequencer =
|
||||
arsRS.lastStateProto.provinces(pid).deferredChanges.foldLeft(arsRS) {
|
||||
case (acc, dc) =>
|
||||
acc.withRandomActionResult { case (gs, fr) =>
|
||||
acc.withRandomActionResult {
|
||||
case (gs, fr) =>
|
||||
oneDeferredProvinceChange(pid, dc, gs, fr)
|
||||
}
|
||||
}
|
||||
@@ -306,7 +288,8 @@ case class EndPlayerCommandsPhaseAction(
|
||||
ars: RandomStateTSequencer
|
||||
): RandomStateTSequencer =
|
||||
ars.lastStateProto.provinces.keys
|
||||
.foldLeft(ars) { case (newArs, pid) =>
|
||||
.foldLeft(ars) {
|
||||
case (newArs, pid) =>
|
||||
deferredProvinceChangesResultsForProvince(pid, newArs)
|
||||
}
|
||||
|
||||
@@ -328,18 +311,14 @@ case class EndPlayerCommandsPhaseAction(
|
||||
.withRandomActionResults((gs, fr) =>
|
||||
CheckForFactionChangesAction(
|
||||
gameId = gs.gameId,
|
||||
factions =
|
||||
gs.factions.values.toVector.map(FactionConverter.fromProto),
|
||||
provinces =
|
||||
gs.provinces.values.toVector.map(ProvinceConverter.fromProto),
|
||||
factions = gs.factions.values.toVector.map(FactionConverter.fromProto),
|
||||
provinces = gs.provinces.values.toVector.map(ProvinceConverter.fromProto),
|
||||
heroes = gs.heroes.values.toVector.map(HeroConverter.fromProto),
|
||||
killedHeroIds = gs.killedHeroes.keys.toVector
|
||||
).randomResults(fr)
|
||||
)
|
||||
.withContinuance(deferredProvinceChangesResults)
|
||||
.withProtolessSequentialResultsAction(gs =>
|
||||
HeroBackstoryUpdateActionGenerator.fromGameState(gs)
|
||||
)
|
||||
.withProtolessSequentialResultsAction(gs => HeroBackstoryUpdateActionGenerator.fromGameState(gs))
|
||||
.withActionResult(endPhaseResult)
|
||||
.actionResults
|
||||
}
|
||||
|
||||
+1
-2
@@ -7,8 +7,7 @@ import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.actions.impl.common.DeterministicSingleResultAction
|
||||
|
||||
case class EndPleaseRecruitMePhaseAction(gameState: GameState)
|
||||
extends DeterministicSingleResultAction(gameState) {
|
||||
case class EndPleaseRecruitMePhaseAction(gameState: GameState) extends DeterministicSingleResultAction(gameState) {
|
||||
override def immediateExecute: ActionResult =
|
||||
ActionResult(
|
||||
`type` = END_PLEASE_RECRUIT_ME_PHASE,
|
||||
|
||||
+4
-9
@@ -38,22 +38,18 @@ case class EndUnaffiliatedHeroActionsPhaseAction(
|
||||
|
||||
private def oneProvincePleaseRecruitMeRequests(
|
||||
province: ProvinceT
|
||||
): Vector[(LlmRequestT, UnaffiliatedHeroT)] = {
|
||||
province.rulingFactionId
|
||||
.map { fid =>
|
||||
): Vector[(LlmRequestT, UnaffiliatedHeroT)] =
|
||||
province.rulingFactionId.map { fid =>
|
||||
val actingFaction = factions.find(_.id == fid).get
|
||||
province.unaffiliatedHeroes
|
||||
// Filter out outlaws that are going to rejoin the faction anyway
|
||||
.filterNot(uh =>
|
||||
uh.unaffiliatedHeroType == Outlaw && uh.lastFactionId == province.rulingFactionId
|
||||
)
|
||||
.filterNot(uh => uh.unaffiliatedHeroType == Outlaw && uh.lastFactionId == province.rulingFactionId)
|
||||
.filter(uh =>
|
||||
UnaffiliatedHeroUtils.willPleaseRecruitMe(
|
||||
targetHero = heroes.find(_.id == uh.heroId).get,
|
||||
unaffiliatedHero = uh,
|
||||
actingFaction = actingFaction,
|
||||
actingFactionLeader =
|
||||
heroes.find(_.id == actingFaction.factionHeadId).get,
|
||||
actingFactionLeader = heroes.find(_.id == actingFaction.factionHeadId).get,
|
||||
allProvinces = provinces,
|
||||
allFactions = factions,
|
||||
currentDate = currentDate
|
||||
@@ -76,7 +72,6 @@ case class EndUnaffiliatedHeroActionsPhaseAction(
|
||||
}
|
||||
}
|
||||
.getOrElse(Vector())
|
||||
}
|
||||
|
||||
override def results: Vector[ActionResultT] = {
|
||||
val cpsAndLlmRequests = provinces.map { p =>
|
||||
|
||||
+10
-25
@@ -7,22 +7,15 @@ import net.eagle0.eagle.common.round_phase.RoundPhase.PLAYER_COMMANDS
|
||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplier
|
||||
import net.eagle0.eagle.library.actions.impl.common.{
|
||||
RandomSequentialResultsAction,
|
||||
RandomStateProtoSequencer
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.common.{RandomSequentialResultsAction, RandomStateProtoSequencer}
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
ActionResultProtoConverter,
|
||||
BattalionConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.{ActionResultProtoConverter, BattalionConverter}
|
||||
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
|
||||
|
||||
case class EndVassalCommandsPhaseAction(gameState: GameState)
|
||||
extends RandomSequentialResultsAction(gameState) {
|
||||
case class EndVassalCommandsPhaseAction(gameState: GameState) extends RandomSequentialResultsAction(gameState) {
|
||||
override def randomResults(
|
||||
functionalRandom: FunctionalRandom,
|
||||
actionResultProtoApplier: ActionResultProtoApplier
|
||||
@@ -44,15 +37,12 @@ case class EndVassalCommandsPhaseAction(gameState: GameState)
|
||||
gameId = gs.gameId,
|
||||
currentDate = DateConverter.fromProto(gs.currentDate),
|
||||
currentRoundId = gs.currentRoundId,
|
||||
provinces =
|
||||
gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
||||
provinces = gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
||||
factions = gs.factions.values
|
||||
.map(FactionConverter.fromProto)
|
||||
.toVector,
|
||||
battalions =
|
||||
gs.battalions.values.toVector.map(BattalionConverter.fromProto),
|
||||
getHero =
|
||||
hid => gameState.heroes.get(hid).map(HeroConverter.fromProto),
|
||||
battalions = gs.battalions.values.toVector.map(BattalionConverter.fromProto),
|
||||
getHero = hid => gameState.heroes.get(hid).map(HeroConverter.fromProto),
|
||||
battalionTypes = gs.battalionTypes.toVector,
|
||||
hid => gs.heroes(hid).backstoryVersions.last.textId
|
||||
)
|
||||
@@ -62,8 +52,7 @@ case class EndVassalCommandsPhaseAction(gameState: GameState)
|
||||
gameId = gs.gameId,
|
||||
currentDate = DateConverter.fromProto(gs.currentDate),
|
||||
currentRoundId = gs.currentRoundId,
|
||||
provinces =
|
||||
gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
||||
provinces = gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
||||
factions = gs.factions.values
|
||||
.map(FactionConverter.fromProto)
|
||||
.toVector,
|
||||
@@ -73,17 +62,13 @@ case class EndVassalCommandsPhaseAction(gameState: GameState)
|
||||
.withRandomActionResults((gs, fr) =>
|
||||
CheckForFactionChangesAction(
|
||||
gameId = gs.gameId,
|
||||
factions =
|
||||
gs.factions.values.map(FactionConverter.fromProto).toVector,
|
||||
provinces =
|
||||
gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
||||
factions = gs.factions.values.map(FactionConverter.fromProto).toVector,
|
||||
provinces = gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
|
||||
heroes = gs.heroes.values.map(HeroConverter.fromProto).toVector,
|
||||
killedHeroIds = gs.killedHeroes.keys.toVector
|
||||
).randomResults(fr).map(_.map(ActionResultProtoConverter.toProto))
|
||||
)
|
||||
.withProtolessSequentialResultsAction(gs =>
|
||||
HeroBackstoryUpdateActionGenerator.fromGameState(gs)
|
||||
)
|
||||
.withProtolessSequentialResultsAction(gs => HeroBackstoryUpdateActionGenerator.fromGameState(gs))
|
||||
.withActionWithResultingState(gs =>
|
||||
actionResultProtoApplier.applyActionResult(
|
||||
gs,
|
||||
|
||||
+4
-6
@@ -20,7 +20,8 @@ case class FreeForAllDrawAction(
|
||||
|
||||
mas
|
||||
.groupBy(_.destinationProvinceId)
|
||||
.map { case (pid, ms) =>
|
||||
.map {
|
||||
case (pid, ms) =>
|
||||
ChangedProvinceC(
|
||||
provinceId = pid,
|
||||
newIncomingArmies = ms.toVector
|
||||
@@ -29,13 +30,12 @@ case class FreeForAllDrawAction(
|
||||
.toVector
|
||||
}
|
||||
|
||||
override def immediateExecute: ActionResultT = {
|
||||
override def immediateExecute: ActionResultT =
|
||||
ActionResultC(
|
||||
actionResultType = FreeForAllDrawResultType,
|
||||
provinceId = Some(defenderProvince),
|
||||
changedProvinces = changedProvincesFromRemainingUnits
|
||||
)
|
||||
}
|
||||
|
||||
private def reversedMovingArmy(ma: MovingArmy): MovingArmy =
|
||||
ma.copy(
|
||||
@@ -43,9 +43,7 @@ case class FreeForAllDrawAction(
|
||||
destinationProvinceId = ma.destinationProvinceId,
|
||||
arrivalRound = ma.arrivalRound + 1,
|
||||
army = ma.army.copy(
|
||||
units = ma.army.units.filter(cu =>
|
||||
remainingUnits.exists(_.hero.id == cu.heroId)
|
||||
),
|
||||
units = ma.army.units.filter(cu => remainingUnits.exists(_.hero.id == cu.heroId)),
|
||||
fleeProvinceId = None
|
||||
)
|
||||
)
|
||||
|
||||
+4
-7
@@ -2,10 +2,7 @@ package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.eagle.{FactionId, GameId, HeroId, RoundId}
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessSequentialResultsAction
|
||||
import net.eagle0.eagle.model.action_result.concrete.{
|
||||
ActionResultC,
|
||||
ChangedHeroC
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedHeroC}
|
||||
import net.eagle0.eagle.model.action_result.generated_text_request.LlmRequestT
|
||||
import net.eagle0.eagle.model.action_result.generated_text_request.LlmRequestT.HeroBackstoryUpdateRequest
|
||||
import net.eagle0.eagle.model.action_result.types.HeroBackstoriesUpdatedResultType
|
||||
@@ -41,7 +38,8 @@ case class HeroBackstoryUpdateAction(
|
||||
Vector(
|
||||
ActionResultC(
|
||||
actionResultType = HeroBackstoriesUpdatedResultType,
|
||||
changedHeroes = heroesWithUpdates.map { case (heroId, llmRequest) =>
|
||||
changedHeroes = heroesWithUpdates.map {
|
||||
case (heroId, llmRequest) =>
|
||||
ChangedHeroC(
|
||||
heroId = heroId,
|
||||
newBackstoryTextId = Some(llmRequest.requestId),
|
||||
@@ -58,8 +56,7 @@ case class HeroBackstoryUpdateAction(
|
||||
heroFactionId: Option[FactionId],
|
||||
visibleToFactionIds: FactionId => Vector[FactionId]
|
||||
): Vector[FactionId] =
|
||||
heroFactionId
|
||||
.map { hid =>
|
||||
heroFactionId.map { hid =>
|
||||
visibleToFactionIds(hid) :+ hid
|
||||
}
|
||||
.getOrElse(Vector.empty)
|
||||
|
||||
+1
-2
@@ -11,8 +11,7 @@ object HeroBackstoryUpdateActionGenerator {
|
||||
gameId = gameState.gameId,
|
||||
roundId = gameState.currentRoundId,
|
||||
heroes = gameState.heroes.values.map(HeroConverter.fromProto).toVector,
|
||||
visibleToFactionIds =
|
||||
fid => LegacyFactionUtils.alliedFactions(fid, gameState),
|
||||
visibleToFactionIds = fid => LegacyFactionUtils.alliedFactions(fid, gameState),
|
||||
heroInProvinceOwnedBy = heroId =>
|
||||
gameState.provinces.values
|
||||
.find(province =>
|
||||
|
||||
@@ -5,26 +5,17 @@ import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.round_phase.NewRoundPhase
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase.PRISONER_EXCHANGE
|
||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.changed_faction.{
|
||||
ChangedFaction,
|
||||
TrustLevelUpdate
|
||||
}
|
||||
import net.eagle0.eagle.internal.changed_faction.{ChangedFaction, TrustLevelUpdate}
|
||||
import net.eagle0.eagle.internal.changed_hero.ChangedHero
|
||||
import net.eagle0.eagle.internal.changed_hero.ChangedHero.{Loyalty, Vigor}
|
||||
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
||||
import net.eagle0.eagle.internal.chronicle_entry.ChronicleEntry
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship.RelationshipLevel.HOSTILE
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.generated_text_request.{
|
||||
ChronicleUpdateMessage,
|
||||
GeneratedTextRequest
|
||||
}
|
||||
import net.eagle0.eagle.internal.generated_text_request.{ChronicleUpdateMessage, GeneratedTextRequest}
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplier
|
||||
import net.eagle0.eagle.library.actions.impl.common.{
|
||||
Action,
|
||||
ActionWithResultingState
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.common.{Action, ActionWithResultingState}
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
EmptyProvinceMonthlyDevastationDelta,
|
||||
FactionBiasMinimumAdjustmentPerRound,
|
||||
@@ -41,8 +32,7 @@ import net.eagle0.eagle.library.util.PriceIndexUtils
|
||||
import net.eagle0.eagle.library.GameHistory
|
||||
import net.eagle0.eagle.RoundId
|
||||
|
||||
case class NewRoundAction(startingState: GameState, gameHistory: GameHistory)
|
||||
extends Action {
|
||||
case class NewRoundAction(startingState: GameState, gameHistory: GameHistory) extends Action {
|
||||
import net.eagle0.eagle.library.util.DateProtoUtils.*
|
||||
|
||||
private val maxMonth = 12
|
||||
@@ -57,7 +47,8 @@ case class NewRoundAction(startingState: GameState, gameHistory: GameHistory)
|
||||
val newRoundId = startingState.currentRoundId + 1
|
||||
|
||||
// Verify no old incoming armies
|
||||
startingState.provinces.foreach { case (pid, province) =>
|
||||
startingState.provinces.foreach {
|
||||
case (pid, province) =>
|
||||
internalRequire(
|
||||
province.incomingArmies.forall(_.arrivalRound >= newRoundId),
|
||||
s"Province $pid has an army that should have already arrived"
|
||||
@@ -178,8 +169,7 @@ case class NewRoundAction(startingState: GameState, gameHistory: GameHistory)
|
||||
changedUnaffiliatedHeroes = changedUHs,
|
||||
foodDelta = Option.when(capFoodLoss != 0)(-capFoodLoss),
|
||||
goldDelta = Option.when(capGoldLoss != 0)(-capGoldLoss),
|
||||
newPriceIndex =
|
||||
Option.when(newPriceIndex != p.priceIndex)(newPriceIndex),
|
||||
newPriceIndex = Option.when(newPriceIndex != p.priceIndex)(newPriceIndex),
|
||||
economyDevastationDelta = Option.when(
|
||||
p.rulingFactionId.isEmpty && p.economyDevastation > 0
|
||||
)(
|
||||
@@ -216,8 +206,8 @@ case class NewRoundAction(startingState: GameState, gameHistory: GameHistory)
|
||||
val heroesAfterStipend =
|
||||
changes.flatMap(_.changedHeroes).map(h => h.id -> h).toMap
|
||||
|
||||
val uppedVigorHeroes = startingState.heroes
|
||||
.map { case (hid, h) =>
|
||||
val uppedVigorHeroes = startingState.heroes.map {
|
||||
case (hid, h) =>
|
||||
heroesAfterStipend
|
||||
.getOrElse(hid, ChangedHero(id = hid))
|
||||
.withVigor(
|
||||
@@ -242,9 +232,7 @@ case class NewRoundAction(startingState: GameState, gameHistory: GameHistory)
|
||||
changedFactionRelationships = removedTruces.map(
|
||||
_.update(_.optionalResetDate := None, _.relationshipLevel := HOSTILE)
|
||||
),
|
||||
trustLevelUpdates =
|
||||
startingState.factions.keys.filterNot(_ == f.id).toVector.map {
|
||||
targetFid =>
|
||||
trustLevelUpdates = startingState.factions.keys.filterNot(_ == f.id).toVector.map { targetFid =>
|
||||
TrustLevelUpdate(targetFid, TrustDeltaPerRound.intValue)
|
||||
},
|
||||
clearLastActedProvinceId = true
|
||||
@@ -278,9 +266,7 @@ case class NewRoundAction(startingState: GameState, gameHistory: GameHistory)
|
||||
val notRecentHeroCount = province.rulingFactionHeroIds
|
||||
.map(gameState.heroes)
|
||||
.flatMap(_.roundIdJoined)
|
||||
.count(roundId =>
|
||||
gameState.currentRoundId - roundId > MinimumRoundsBeforeLoyaltyDegrades.intValue
|
||||
)
|
||||
.count(roundId => gameState.currentRoundId - roundId > MinimumRoundsBeforeLoyaltyDegrades.intValue)
|
||||
if province.heroCap >= notRecentHeroCount then Vector.empty
|
||||
else {
|
||||
val loyaltyHit =
|
||||
|
||||
@@ -21,8 +21,7 @@ import net.eagle0.eagle.library.settings.{
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
case class NewYearAction(gameState: GameState)
|
||||
extends DeterministicSingleResultAction(gameState) {
|
||||
case class NewYearAction(gameState: GameState) extends DeterministicSingleResultAction(gameState) {
|
||||
private def degradationMultiplier: Double =
|
||||
PercentDegradationPerYear.doubleValue / 100.0
|
||||
|
||||
@@ -36,8 +35,7 @@ case class NewYearAction(gameState: GameState)
|
||||
(
|
||||
LegacyHeroUtils.discordance(vassal, factionLeader)
|
||||
+ (if isProvinceLeader then 0
|
||||
else
|
||||
AmbitionFactorForLoyaltyDegradation.doubleValue * vassal.ambition)
|
||||
else AmbitionFactorForLoyaltyDegradation.doubleValue * vassal.ambition)
|
||||
- LoyaltyDiscordanceThreshold.doubleValue
|
||||
) * LoyaltyDecreasePerDiscordance.doubleValue
|
||||
|
||||
@@ -60,10 +58,10 @@ case class NewYearAction(gameState: GameState)
|
||||
hero = gameState.heroes(heroId)
|
||||
roundIdJoined <- hero.roundIdJoined
|
||||
currentRoundId = gameState.currentRoundId
|
||||
if (currentRoundId - roundIdJoined > MinimumRoundsBeforeLoyaltyDegrades.intValue)
|
||||
if currentRoundId - roundIdJoined > MinimumRoundsBeforeLoyaltyDegrades.intValue
|
||||
factionId <- hero.factionId
|
||||
faction = gameState.factions(factionId)
|
||||
if (!faction.leaders.contains(heroId))
|
||||
if !faction.leaders.contains(heroId)
|
||||
factionLeader = gameState.heroes(faction.factionHeadId)
|
||||
} yield ChangedHero(
|
||||
id = hero.id,
|
||||
|
||||
+3
-8
@@ -14,8 +14,7 @@ import net.eagle0.eagle.library.settings.MaxBattalionReductionForFoodShortage
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.library.util.LegacyBattalionUtils
|
||||
|
||||
case class PerformFoodConsumptionPhaseAction(gs: GameState)
|
||||
extends DeterministicSingleResultAction(gs) {
|
||||
case class PerformFoodConsumptionPhaseAction(gs: GameState) extends DeterministicSingleResultAction(gs) {
|
||||
|
||||
override def immediateExecute: ActionResult = {
|
||||
val (cps, cbs) = gs.provinces.values.map { p =>
|
||||
@@ -62,12 +61,9 @@ case class PerformFoodConsumptionPhaseAction(gs: GameState)
|
||||
)
|
||||
val availableFood = movingArmy.supplies.map(_.food).getOrElse(0)
|
||||
|
||||
val remainingSupplies = movingArmy.supplies.map(s =>
|
||||
s.withFood(Math.max(0, availableFood - foodConsumption))
|
||||
)
|
||||
val remainingSupplies = movingArmy.supplies.map(s => s.withFood(Math.max(0, availableFood - foodConsumption)))
|
||||
|
||||
if foodConsumption <= availableFood then
|
||||
(movingArmy.update(_.optionalSupplies := remainingSupplies), Vector.empty)
|
||||
if foodConsumption <= availableFood then (movingArmy.update(_.optionalSupplies := remainingSupplies), Vector.empty)
|
||||
else {
|
||||
val remainingProportion =
|
||||
(availableFood.toDouble / foodConsumption.toDouble)
|
||||
@@ -81,7 +77,6 @@ case class PerformFoodConsumptionPhaseAction(gs: GameState)
|
||||
newBattalions
|
||||
)
|
||||
}
|
||||
end if
|
||||
}
|
||||
|
||||
private def foodDeltaAndChangedBattalionsAfterConsumingFood(
|
||||
|
||||
+5
-12
@@ -20,14 +20,10 @@ import net.eagle0.eagle.library.actions.util.ShatteredArmyUtils
|
||||
import net.eagle0.eagle.library.settings.WinterSuppliesLoss
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.model.action_result.NotificationDetails.ShatteredArmy.Reason.Blizzard
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
ActionResultProtoConverter,
|
||||
ArmyConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.{ActionResultProtoConverter, ArmyConverter}
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
|
||||
case class PerformForcedTurnBackAction(gs: GameState)
|
||||
extends DeterministicSequentialResultsAction(gs) {
|
||||
case class PerformForcedTurnBackAction(gs: GameState) extends DeterministicSequentialResultsAction(gs) {
|
||||
|
||||
val endPhaseResult: ActionResult = ActionResult(
|
||||
`type` = END_FORCED_TURN_BACK_PHASE,
|
||||
@@ -35,8 +31,7 @@ case class PerformForcedTurnBackAction(gs: GameState)
|
||||
)
|
||||
|
||||
private def oneTurnBackSuppliesResult(is: MovingSupplies, p: Province) =
|
||||
is.originProvinceId
|
||||
.map { originPid =>
|
||||
is.originProvinceId.map { originPid =>
|
||||
ActionResult(
|
||||
`type` = WEATHER_FORCED_SUPPLIES_BACK,
|
||||
player = Some(is.factionId),
|
||||
@@ -91,14 +86,12 @@ case class PerformForcedTurnBackAction(gs: GameState)
|
||||
)
|
||||
|
||||
private def oneTurnBackArmyResult(ia: MovingArmy, p: Province): ActionResult =
|
||||
ia.getArmy.fleeProvinceId
|
||||
.map { fleePid =>
|
||||
ia.getArmy.fleeProvinceId.map { fleePid =>
|
||||
ActionResult(
|
||||
`type` = WEATHER_FORCED_TURN_BACK,
|
||||
player = Some(ia.getArmy.factionId),
|
||||
province = Some(ia.destinationProvince),
|
||||
affectedPlayers =
|
||||
(Vector(ia.getArmy.factionId) ++ p.rulingFactionId).distinct,
|
||||
affectedPlayers = (Vector(ia.getArmy.factionId) ++ p.rulingFactionId).distinct,
|
||||
changedProvinces = Vector(
|
||||
ChangedProvince(id = p.id, removedIncomingArmyIds = Vector(ia.id)),
|
||||
ChangedProvince(
|
||||
|
||||
+15
-29
@@ -2,15 +2,9 @@ package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.common.{FunctionalRandom, RandomState}
|
||||
import net.eagle0.eagle.{HeroId, ProvinceId, RoundId}
|
||||
import net.eagle0.eagle.common.action_result_notification_details.{
|
||||
HeroDepartureDetails,
|
||||
Notification
|
||||
}
|
||||
import net.eagle0.eagle.common.action_result_notification_details.{HeroDepartureDetails, Notification}
|
||||
import net.eagle0.eagle.common.action_result_notification_details.Notification.Llm
|
||||
import net.eagle0.eagle.common.action_result_type.ActionResultType.{
|
||||
END_HERO_DEPARTURE_PHASE,
|
||||
HEROES_DEPARTED
|
||||
}
|
||||
import net.eagle0.eagle.common.action_result_type.ActionResultType.{END_HERO_DEPARTURE_PHASE, HEROES_DEPARTED}
|
||||
import net.eagle0.eagle.common.recruitment_info.RecruitmentInfo
|
||||
import net.eagle0.eagle.common.recruitment_info.RecruitmentStatus.RECRUITMENT_STATUS_TRAVELER
|
||||
import net.eagle0.eagle.common.round_phase.NewRoundPhase
|
||||
@@ -19,22 +13,13 @@ import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.UNAFF
|
||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.changed_hero.ChangedHero
|
||||
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
||||
import net.eagle0.eagle.internal.event_for_hero_backstory.{
|
||||
EventForHeroBackstory,
|
||||
HeroDepartedBackstoryEvent
|
||||
}
|
||||
import net.eagle0.eagle.internal.event_for_hero_backstory.{EventForHeroBackstory, HeroDepartedBackstoryEvent}
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.generated_text_request.{
|
||||
GeneratedTextRequest,
|
||||
HeroDepartureMessage
|
||||
}
|
||||
import net.eagle0.eagle.internal.generated_text_request.{GeneratedTextRequest, HeroDepartureMessage}
|
||||
import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplier
|
||||
import net.eagle0.eagle.library.actions.impl.common.DeterministicSequentialResultsAction
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
FactionBiasFromDeparture,
|
||||
LoyaltyThreshold
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.{FactionBiasFromDeparture, LoyaltyThreshold}
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
|
||||
@@ -100,7 +85,7 @@ object PerformHeroDeparturesAction {
|
||||
Math.max(0, LoyaltyThreshold.doubleValue - loyalty) / 100.0
|
||||
|
||||
private def notificationLlmId(currentRoundId: RoundId, hid: HeroId): String =
|
||||
s"hero departed ${currentRoundId} $hid"
|
||||
s"hero departed $currentRoundId $hid"
|
||||
|
||||
private def notificationForHero(
|
||||
hid: HeroId,
|
||||
@@ -146,20 +131,23 @@ case class PerformHeroDeparturesAction(
|
||||
Vector[Option[(ChangedProvince, Vector[HeroId])]](),
|
||||
functionalRandom
|
||||
)
|
||||
) { case (rs, p) =>
|
||||
rs.continue { case (acc, fr) =>
|
||||
) {
|
||||
case (rs, p) =>
|
||||
rs.continue {
|
||||
case (acc, fr) =>
|
||||
PerformHeroDeparturesAction
|
||||
.provinceWithDepartedHeroes(
|
||||
startingState,
|
||||
p.id,
|
||||
fr
|
||||
)
|
||||
.map { tup => acc :+ tup }
|
||||
.map(tup => acc :+ tup)
|
||||
}
|
||||
}
|
||||
.map(_.flatten)
|
||||
.map {
|
||||
_.map { case (cp, hs) =>
|
||||
_.map {
|
||||
case (cp, hs) =>
|
||||
ActionResult(
|
||||
`type` = HEROES_DEPARTED,
|
||||
province = Some(cp.id),
|
||||
@@ -173,8 +161,7 @@ case class PerformHeroDeparturesAction(
|
||||
EventForHeroBackstory(
|
||||
date = startingState.currentDate,
|
||||
details = HeroDepartedBackstoryEvent(
|
||||
departedFromFactionId =
|
||||
startingState.provinces(cp.id).getRulingFactionId,
|
||||
departedFromFactionId = startingState.provinces(cp.id).getRulingFactionId,
|
||||
departedFromProvinceId = cp.id
|
||||
)
|
||||
)
|
||||
@@ -203,8 +190,7 @@ case class PerformHeroDeparturesAction(
|
||||
case RandomState(ars, fr) =>
|
||||
ars :+ ActionResult(
|
||||
`type` = END_HERO_DEPARTURE_PHASE,
|
||||
newRoundPhase =
|
||||
Some(NewRoundPhase(value = UNAFFILIATED_HERO_ACTIONS)),
|
||||
newRoundPhase = Some(NewRoundPhase(value = UNAFFILIATED_HERO_ACTIONS)),
|
||||
newRandomSeed = Some(fr.seed)
|
||||
)
|
||||
}
|
||||
|
||||
+3
-3
@@ -30,7 +30,8 @@ case class PerformHostileArmySetupAction(startingState: GameState)
|
||||
_.arrivalRound == startingState.currentRoundId
|
||||
)
|
||||
.groupBy(_.getArmy.factionId)
|
||||
.map { case (fid, movingArmies) =>
|
||||
.map {
|
||||
case (fid, movingArmies) =>
|
||||
HostileArmyGroup(
|
||||
factionId = fid,
|
||||
armies = movingArmies.toVector.map { ma =>
|
||||
@@ -58,8 +59,7 @@ case class PerformHostileArmySetupAction(startingState: GameState)
|
||||
|
||||
override def immediateExecute: ActionResult = ActionResult(
|
||||
`type` = ActionResultType.SET_UP_HOSTILE_ARMIES,
|
||||
changedProvinces =
|
||||
startingState.provinces.values.flatMap(oneProvinceResult).toVector,
|
||||
changedProvinces = startingState.provinces.values.flatMap(oneProvinceResult).toVector,
|
||||
newRoundPhase = Some(NewRoundPhase(RoundPhase.FREE_FOR_ALL_DECISION))
|
||||
)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user