Compare commits

..
1 Commits
Author SHA1 Message Date
admin c8d7e03afe lock streaming text 2025-06-05 22:22:34 -07:00
50 changed files with 12077 additions and 495 deletions
-1
View File
@@ -6,4 +6,3 @@
*.bytes filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.tsv filter=lfs diff=lfs merge=lfs -text
@@ -48,8 +48,9 @@ public class AvailableGameItem : MonoBehaviour {
joinButton.interactable = true;
buttonText.text = "JOIN";
List<HeroView> heroOptions =
new List<HeroView> { new() { Id = 0, Profession = Profession.NoProfession } };
List<HeroView> heroOptions = new List<HeroView> {
new() { Name = "Random", Id = 0, Profession = Profession.NoProfession }
};
for (int i = 0; i < leaders.Count; i++) {
var leader = leaders[i];
heroOptions.Add(new HeroView {
@@ -59,7 +60,6 @@ public class AvailableGameItem : MonoBehaviour {
});
}
heroDropdownController.FallbackText = "Random";
heroDropdownController.AvailableHeroes = heroOptions;
}
}
@@ -60,8 +60,9 @@ public class CreateGameItem : MonoBehaviour {
buttonText.text = "CREATE";
List<HeroView> heroOptions =
new List<HeroView> { new() { Id = 0, Profession = Profession.NoProfession } };
List<HeroView> heroOptions = new List<HeroView> {
new HeroView { Name = "Random", Id = 0, Profession = Profession.NoProfession }
};
for (int i = 0; i < leaders.Count; i++) {
var leader = leaders[i];
heroOptions.Add(new HeroView {
@@ -72,7 +73,6 @@ public class CreateGameItem : MonoBehaviour {
});
}
heroDropdownController.FallbackText = "Random";
heroDropdownController.AvailableHeroes = heroOptions;
heroDropdownController.HeadshotImage.gameObject.SetActive(false);
@@ -26,48 +26,58 @@ namespace eagle {
private readonly HashSet<IClientTextListener> _listeners = new();
public void Clear() { _streamingTexts.Clear(); }
public void Clear() {
lock (this) { _streamingTexts.Clear(); }
}
public Dictionary<String, TextEntry> All() {
return new Dictionary<string, TextEntry>(_streamingTexts);
lock (this) { return new Dictionary<string, TextEntry>(_streamingTexts); }
}
public String
HandleNewStreamingText(String llmId, String newText, Int32 knownByteCount, bool completed) {
var currentText = "";
if (_streamingTexts.TryGetValue(llmId, out var entry)) { currentText = entry.Text; }
lock (this) {
var currentText = "";
if (_streamingTexts.TryGetValue(llmId, out var entry)) { currentText = entry.Text; }
var currentTextBytes = Encoding.UTF8.GetBytes(currentText);
var truncatedBytes = currentTextBytes.Take(knownByteCount).ToArray();
var currentTextBytes = Encoding.UTF8.GetBytes(currentText);
var truncatedBytes = currentTextBytes.Take(knownByteCount).ToArray();
var updatedText = Encoding.UTF8.GetString(truncatedBytes) + newText;
_streamingTexts[llmId] = new TextEntry(updatedText, completed);
var updatedText = Encoding.UTF8.GetString(truncatedBytes) + newText;
_streamingTexts[llmId] = new TextEntry(updatedText, completed);
_listeners.Where(x => x.TextId() == llmId)
.ToList()
.ForEach(x => x.OnTextUpdate(updatedText, completed));
_listeners.Where(x => x.TextId() == llmId)
.ToList()
.ForEach(x => x.OnTextUpdate(updatedText, completed));
return updatedText;
return updatedText;
}
}
public TextEntry GetTextEntry(string streamId) {
if (String.IsNullOrEmpty(streamId)) return new TextEntry("", false);
lock (this) {
if (String.IsNullOrEmpty(streamId)) return new TextEntry("", false);
if (ClientPregeneratedText.Provider.TryGetText(streamId, out var text)) {
return new TextEntry(text, true);
if (ClientPregeneratedText.Provider.TryGetText(streamId, out var text)) {
return new TextEntry(text, true);
}
return _streamingTexts.GetValueOrDefault(streamId, null);
}
return _streamingTexts.GetValueOrDefault(streamId, null);
}
public void AddListener(IClientTextListener listener) {
_listeners.Add(listener);
if (listener.TextId() is {} textId && !String.IsNullOrEmpty(textId)) {
var entry = GetTextEntry(textId);
if (entry != null) { listener.OnTextUpdate(entry.Text, entry.Completed); }
lock (this) {
if (listener.TextId() is {} textId && !String.IsNullOrEmpty(textId)) {
_listeners.Add(listener);
var entry = GetTextEntry(textId);
if (entry != null) { listener.OnTextUpdate(entry.Text, entry.Completed); }
}
}
}
public void RemoveListener(IClientTextListener listener) { _listeners.Remove(listener); }
public void RemoveListener(IClientTextListener listener) {
lock (this) { _listeners.Remove(listener); }
}
}
}
@@ -39,7 +39,12 @@ namespace eagle {
? $" ({GUIUtils.ConditionString(ConditionAccessor(hero), ConditionName)})"
: "",
i,
SetGeneratedText);
(index, text) => {
if (index < Dropdown.options.Count) {
options[index].text = text;
AssignOptions();
}
});
listeners.Add(listener);
ClientTextProvider.Provider.AddListener(listener);
@@ -49,14 +54,6 @@ namespace eagle {
}
}
public string FallbackText = "";
private void SetGeneratedText(int index, string text) {
if (index >= Dropdown.options.Count) return;
options[index].text = text;
AssignOptions();
}
private List<TMP_Dropdown.OptionData> options = new();
public void AssignOptions() {
Dropdown.ClearOptions();
@@ -135,7 +132,7 @@ namespace eagle {
}
private TMP_Dropdown.OptionData HeroOption(HeroView hv) {
var texture = !Textures ? null : Textures.Profession(hv.Profession);
var texture = Textures.Profession(hv.Profession);
var op = new TMP_Dropdown.OptionData {
image = !texture ? null
@@ -143,7 +140,7 @@ namespace eagle {
texture,
new Rect(0, 0, texture.width, texture.height),
new Vector2(0.5f, 0.5f)),
text = String.IsNullOrEmpty(hv.NameTextId) ? FallbackText : ""
text = ""
};
return op;
@@ -260,7 +260,6 @@ namespace eagle {
switch (quest.Details.SealedValueCase) {
case SealedValueOneofCase.DefeatFactionQuest: return "Attack Faction";
case SealedValueOneofCase.DismissSpecificVassalQuest: return "Dismiss Vassal";
case SealedValueOneofCase.AllianceQuest: return "Secure Alliance";
case SealedValueOneofCase.AlmsToProvinceQuest: return "Give Alms in Province";
case SealedValueOneofCase.AlmsAcrossRealmQuest: return "Give Alms Across Realm";
case SealedValueOneofCase.GiveToHeroesInProvinceQuest:
@@ -15,14 +15,10 @@ namespace eagle {
_currentCompleted = false;
var scrollRect = GetComponentInParent<ScrollRect>(includeInactive: true);
if (scrollRect) { scrollRect.normalizedPosition = new Vector2(0, 1); }
if (scrollRect != null) { scrollRect.normalizedPosition = new Vector2(0, 1); }
}
if (!String.IsNullOrEmpty(_textId)) {
// If the text ID is set, we want to update the view immediately
// to reflect any existing text.
OnTextUpdate(ClientTextProvider.Provider.GetTextEntry(TextId));
}
OnTextUpdate(ClientTextProvider.Provider.GetTextEntry(TextId));
}
}
Binary file not shown.
1 version https://git-lfs.github.com/spec/v1 singularName pluralName likelihood maxCountMultiplier relativePower economyDevastation agricultureDevastation infrastructureDevastation averageGoldPer averageFoodPer
1 size 2344 singularName pluralName likelihood maxCountMultiplier relativePower economyDevastation agricultureDevastation infrastructureDevastation averageGoldPer averageFoodPer
2 agitator agitators 0.1 1 1 5 0 0 1 0
3 asshole assholes 0.05 5 1 2 2 2 0.5 0
4 bandit bandits 0.2 1 1 5 0 0 1 0
5 bear bears 0.04 0.1 10 0 5 5 1 3
6 boar boars 0.04 0.2 5 0 5 5 0 10
7 brigand brigands 0.2 1 1 5 0 0 1 0
8 buccaneer buccaneers 0.05 1 2 5 0 0 2 0
9 chimpanzee chimpanzees 0.04 3 1 0 0 5 0 0
10 clown clowns 0.04 1 5 5 0 0 1 0
11 cossack cossacks 0.04 1 5 5 0 0 1 0
12 crocodile crocodiles 0.04 0.04 10 0 5 0 0 1
13 crook crooks 0.1 1 1 5 0 0 1 0
14 cultist cultists 0.05 1 3 5 0 0 1 0
15 dacoit dacoits 0.05 1 1 5 0 0 1 0
16 demon demons 0.01 0.5 50 5 5 0 50 0
17 desperado desperadoes 0.1 1 1 5 0 0 1 0
18 dragon dragons 0.04 0.001 1000 10 10 10 5000 200
19 elephant elephants 0.04 0.1 15 0 5 5 0 8
20 freebooter freebooters 0.05 1 1 5 0 0 1 0
21 heretic heretics 0.05 1 3 5 0 0 1 0
22 highwayman highwaymen 0.1 1 1.5 5 0 0 1 0
23 hippogryph hippogryphs 0.04 0.05 50 5 5 5 1000 100
24 hippopotamus hippopotamuses 0.04 0.1 10 0 0 5 0 5
25 honey badger honey badgers 0.3 3 0.3 0 5 0 0 0.5
26 hooligan hooligans 0.1 1 1 5 0 0 1 0
27 instigator instigators 0.1 1 1 5 0 0 1 0
28 lion lions 0.04 0.2 5 0 5 0 2 1.5
29 mammoth mammoths 0.1 1 1.5 5 0 0 1 0
30 marauder marauders 0.1 1 1.5 5 0 0 1 0
31 miscreant miscreants 0.01 1 1 5 0 0 1 0
32 mobster mobsters 0.05 1 2 5 0 0 2 0
33 ne'er-do-well ne'er-do-wells 0.1 1 0.8 5 0 0 1 0
34 nihilist nihilists 0.1 1 1 5 0 0 1 0
35 particularist particularists 0.1 1 1 5 0 0 1 0
36 pirate pirates 0.05 1 2 5 0 0 2 0
37 rabble-rouser rabble-rousers 0.1 1 1 5 0 0 1 0
38 raccoon raccoons 0.2 20 0.2 0 5 0 0 0.1
39 rebel rebels 0.04 2 1 5 0 0 1 0
40 robber robbers 0.1 1 1 5 0 0 1 0
41 ruffian ruffians 0.1 1 1 5 0 0 1 0
42 scalawag scalawags 0.1 1 0.7 5 0 0 0.5 0
43 separatist separatists 0.1 1 1 5 0 0 1 0
44 skeleton skeletons 0.01 1 1 5 0 0 1 0
45 snake snakes 0.2 20 0.2 0 5 0 0 0.1
46 spider spiders 0.1 500 0.01 0 5 0 0 0.001
47 street tough street toughs 0.1 1 1 5 0 0 1 0
48 terrorist terrorists 0.04 1 5 5 0 0 1 0
49 thief thieves 0.1 1 1 5 0 0 1 0
50 tiger tigers 0.1 0.2 3 0 5 0 1 1
51 traitor traitors 0.1 1 2 5 0 0 1 0
52 unknown unknown 0 0 0 5 0 0 0 0
53 vampire vampires 0.01 0.5 50 5 5 0 50 0
54 wolf wolves 0.3 3 0.3 0 5 0 0 0.5
55 wolverine wolverines 0.4 3 0.3 0 5 0 0 0.5
56 zombie zombies 0.01 1 1 5 0 0 1 0
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
Can't render this file because it contains an unexpected character in line 5 and column 5.
Binary file not shown.
1 version https://git-lfs.github.com/spec/v1 id name neighbors neighborPositions hexMapName neighborNames castleCount
1 size 3135 id name neighbors neighborPositions hexMapName neighborNames castleCount
2 1 Shumal 18.36 7.5 Shumal Alah.Pemeria 3
3 2 Motcia 12.31.27.29.5 5.2.4.1.7 Motcia Pozia.Nikemi.Chapellia.Mesh.Musland 3
4 3 Laufarvia 40.6.19 3.7.1 Laufarvia Faluria.Soria.Hella 3
5 4 Berkorszag 28.37.26.30.16.25.38 2.0.6.5.4.7.3 Berkorszag Kezonoria.Yuetia.Reigia.Tegrot.Oscasland.Fluria.Wichel 3
6 5 Musland 38.8.2.12.29 7.6.3.4.1 Musland Wichel.Atfordia.Motcia.Pozia.Mesh 3
7 6 Soria 32.10.11.19.3 7.1.2.3.4 Soria Ingia.Vovimaria.Garholtia.Hella.Laufarvia 2
8 7 Oryslia 29.36.28.34.38 3.2.7.1.5 Oryslia Mesh.Pemeria.Kezonoria.Turton.Wichel 3
9 8 Atfordia 5.38.22.16 3.1.7.0 Atfordia Musland.Wichel.Chipingia.Oscasland 3
10 9 Bolve 26.25.23 1.0.7 Bolve Reigia.Fluria.Eivikia 3
11 10 Vovimaria 41.11.6.32.37 7.3.4.5.1 Vovimaria Parnia.Garholtia.Soria.Ingia.Yuetia 5
12 11 Garholtia 37.40.6.10.19 1.3.6.7.5 Garholtia Yuetia.Faluria.Soria.Vovimaria.Hella 4
13 12 Pozia 5.2.27 0.1.2 Pozia Musland.Motcia.Chapellia 3
14 13 Al Raala 31.18.29.36 4.1.6.7 AlRaala Nikemi.Alah.Mesh.Pemeria 3
15 14 Onmaa 31.15 7.6 Onmaa Nikemi.Pieksa 3
16 15 Pieksa 31.14.39 7.2.5 Pieksa Nikemi.Onmaa.Kojaria 3
17 16 Oscasland 21.8.38.22.4.30 7.3.2.4.1.0 Oscasland Sigkarl.Atfordia.Wichel.Chipingia.Berkorszag.Tegrot 4
18 17 Hofolen 42.40.23.25 6.7.3.1 Hofolen Grytrand.Faluria.Eivikia.Fluria 3
19 18 Alah 13.35.1.36 5.3.0.6 Alah Al Raala.Tumala.Shumal.Pemeria 3
20 19 Hella 11.3.40.6 1.5.3.7 Hella Garholtia.Laufarvia.Faluria.Soria 3
21 20 Elekes 24.34.28 1.3.5 Elekes Tatoros.Turton.Kezonoria 3
22 21 Sigkarl 16.30.26 3.1.7 Sigkarl Oscasland.Tegrot.Reigia 3
23 22 Chipingia 16.8 7.3 Chipingia Oscasland.Atfordia 3
24 23 Eivikia 17.25.9 7.1.3 Eivikia Hofolen.Fluria.Bolve 2
25 24 Tatoros 20.34 5.3 Tatoros Elekes.Turton 3
26 25 Fluria 9.40.23.26.4.37.17 4.7.5.3.2.1.6 Fluria Bolve.Faluria.Eivikia.Reigia.Berkorszag.Yuetia.Hofolen 5
27 26 Reigia 25.21.9.4.30 7.4.5.1.2 Reigia Fluria.Sigkarl.Bolve.Berkorszag.Tegrot 4
28 27 Chapellia 2.12.31 7.5.1 Chapellia Motcia.Pozia.Nikemi 4
29 28 Kezonoria 20.4.7.34.38 1.7.3.2.5 Kezonoria Elekes.Berkorszag.Oryslia.Turton.Wichel 3
30 29 Mesh 13.2.5.7.36.31.38 2.4.5.7.1.3.6 Mesh Al Raala.Motcia.Musland.Oryslia.Pemeria.Nikemi.Wichel 7
31 30 Tegrot 16.21.4.26 4.5.1.7 Tegrot Oscasland.Sigkarl.Berkorszag.Reigia 4
32 31 Nikemi 15.39.13.2.29.14.27 3.4.1.7.0.2.6 Nikemi Pieksa.Kojaria.Al Raala.Motcia.Mesh.Onmaa.Chapellia 4
33 32 Ingia 6.10.41 3.1.7 Ingia Soria.Vovimaria.Parnia 3
34 33 Usvol 40 1 Usvol Faluria 2
35 34 Turton 36.24.7.28.20 3.0.4.5.7 Turton Pemeria.Tatoros.Oryslia.Kezonoria.Elekes 3
36 35 Tumala 18 7 Tumala Alah 3
37 36 Pemeria 7.34.1.18.13.29 6.0.1.2.3.5 Pemeria Oryslia.Turton.Shumal.Alah.Al Raala.Mesh 4
38 37 Yuetia 10.25.40.41.11.4.43 7.4.5.0.6.3.1 Yuetia Vovimaria.Fluria.Faluria.Parnia.Garholtia.Berkorszag.Chia 4
39 38 Wichel 29.4.7.28.16.5.8 2.7.1.0.6.3.5 Wichel Mesh.Berkorszag.Oryslia.Kezonoria.Oscasland.Musland.Atfordia 6
40 39 Kojaria 15.31 2.0 Kojaria Pieksa.Nikemi 4
41 40 Faluria 37.25.17.42.3.33.19.11 1.2.3.4.6.5.7.0 Faluria Yuetia.Fluria.Hofolen.Grytrand.Laufarvia.Usvol.Hella.Garholtia 3
42 41 Parnia 32.37.10 4.2.3 Parnia Ingia.Yuetia.Vovimaria 3
43 42 Grytrand 40.17 1.2 Grytrand Faluria.Hofolen 2
44 43 Chia 37 5 Chia Yuetia 3
Binary file not shown.
1 version https://git-lfs.github.com/spec/v1 actionVigorCost 15 Int
1 size 9623 actionVigorCost 15 Int
2 ambitionAgainstDiscordForSwearBrotherhoodFactor 1 Double
3 ambitionFactorForLoyaltyDegradation 0.4 Double
4 baseFoodBuyPrice 0.5 Double
5 baseFoodSellPrice 0.25 Double
6 baseResourceLimit 5000 Int
7 engineerImprovementBonus 2 Double
8 feastGoldCostPerHero 20 Int
9 foodIncreasePerAgriculture 50 Double
10 goldIncreasePerEconomy 35 Double
11 goldPerHeroHeldBack 100 Double
12 improvementPerStat 0.08 Double
13 improvementReductionMax 0.5 Double
14 improvementSupportGain 0.75 Double
15 loyaltyDecreasePerDiscordance 0.3 Double
16 loyaltyDiscordanceThreshold 10 Double
17 loyaltyGainFromFeast 10 Double
18 loyaltyIncreasePerGold 0.25 Double
19 loyaltyThreshold 50 Double
20 maxBattalionReductionForFoodShortage 0.2 Double
21 maxGiftGold 100 Int
22 maximumFactionLeaders 5 Int
23 minimumLoyaltyForSwearBrotherhood 95 Double
24 minSupportForTaxes 40 Double
25 minVigorForMarch 50 Double
26 minVigorForDefend 40 Double
27 minVigorForFlee 20 Double
28 minVigorForImprove 40 Double
29 minVigorForSendSupplies 50 Double
30 minVigorForTrain 40 Double
31 minVigorForTravel 40 Double
32 minVigorForRecon 50 Double
33 newHeroChance 0.001 Double
34 newRoundVigorGain 5 Double
35 overResourceLimitLoss 0.2 Double
36 percentDegradationPerYear 4 Double
37 perDevelopmentResourceLimit 50 Int
38 prestigePerSupportedProvince 10 Double
39 prisonerEscapeChance 0.01 Double
40 recruitmentAmbitionFactor 0.5 Double
41 recruitmentBaseOdds 80 Double
42 recruitmentCharismaFactor 1 Double
43 recruitmentCompatibilityFactor 0.2 Double
44 recruitmentPowerFactor 0.5 Double
45 recruitmentPrestigeFactor 1 Double
46 residentToTravelerChance 0.005 Double
47 restVigorGain 15 Double
48 shipSuppliesLoss 0.1 Double
49 startingLoyalty 60 Double
50 thresholdForSwearBrotherhood 20 Int
51 trainStatDivisor 8 Double
52 travelerMoveChance 0.2 Double
53 travelerToResidentChance 0.1 Double
54 turnsToResolveConquest 1 Int
55 turnsToResolveShipment 1 Int
56 vigorGainFromFeast 10 Double
57 factionBiasFromDeparture -200 Double
58 vassalCommandsMaxFatigueLevelBeforeResting 15 Double
59 vassalCommandsMinOddsForRecruitment 50 Double
60 maxCombatUnitCountPerSide 9999 Int
61 maxAlmsFood 1000 Int
62 almsSupportIncreasePerFood 0.01 Double
63 almsPaladinSupportMultiplier 4 Double
64 foodPerProvinceHeldBack 1000 Int
65 factionBiasFromImprisonment -100 Double
66 factionBiasFromExile -50 Double
67 recruitmentAdjustmentForTraveler 20 Double
68 recruitmentAdjustmentForResident 0 Double
69 recruitmentAdjustmentForOutlaw 50 Double
70 recruitmentAdjustmentPerTurnForPrisoner 5 Double
71 factionBiasFromFailedRecruitment -50 Double
72 factionBiasMultiplierPerRound 0.9 Double
73 factionBiasMinimumAdjustmentPerRound 10 Double
74 factionBiasAgainstFormerOnExile -500 Double
75 vassalExecutesOnCaptureChance 0.1 Double
76 minGoldSurplusForArm 500 Int
77 maxSupportedPlayers 12 Int
78 beastsEventChance 0.02 Double
79 riotEventChance 0.02 Double
80 minVigorForSuppressBeasts 50 Double
81 suppressBeastsPrestigeBonus 2 Int
82 beastsDurationMonths 4 Int
83 beastsSupportDamage 3 Double
84 suppressBeastsSupportBonus 5 Double
85 maxInfrastructureForbeasts 50 Int
86 failedSuppressBeastsPrestigePenalty 5 Int
87 baseBeastsCount 150 Int
88 maxBeastsIncreasePerEconomy 2 Double
89 minOddsForRecruitment 80 Double
90 minOddsForQuest -50 Double
91 questRecruitmentBonus 250 Double
92 minOddsForPleaseRecruitMe 260 Double
93 minDesiredImproveAmountInQuest 15 Int
94 goldCostForDivine 100 Int
95 desiredLoyaltyOverThreshold 15 Double
96 monthsBetweenBeastsInfrastructureMultiplier 0.2 Double
97 minVigorForDiplomacy 50 Double
98 breakAllianceGoldCost 200 Int
99 allianceGoldCost 200 Int
100 truceGoldCost 200 Int
101 truceMonths 12 Int
102 maxProvincesOnTargetForInvite 1 Int
103 inviteGoldCost 1000 Int
104 freedPrisonerLoyalty 90 Double
105 maxRequiredImproveAmountInQuest 30 Int
106 minimumRoundsBeforeLoyaltyDegrades 3 Int
107 reconVigorCost 30 Double
108 jailbreakVigorCost 30 Double
109 minVigorForJailbreak 50 Double
110 factionBiasForLiberator 150 Double
111 jailbreakSuccessBaseChance 30 Int
112 jailbreakBonusFromGold 0.05 Double
113 jailbreakActorCapturedChance 5 Int
114 maxGoldTribute 10000 Int
115 maxFoodTribute 10000 Int
116 loyaltyToOldFactionFactor 0.25 Double
117 minimumPrestigeDifferenceToInvite 10 Double
118 minDesiredIncreaseForWealthQuest 5000 Int
119 maxDesiredIncreaseForWealthQuest 15000 Int
120 minDesiredIncreaseForUpgradeBattalionQuest 30 Int
121 maxDesiredIncreaseForUpgradeBattalionQuest 50 Int
122 portionOfCapacityForUpgradeBattalionQuest 0.75 Double
123 maxDesiredIncreaseForGrandArmyQuest 5000 Int
124 minDesiredIncreaseForGrandArmyQuest 2500 Int
125 minVigorForAlms 20 Double
126 questFailedRecruitmentPenalty 50 Int
127 xpForStatBump 100 Int
128 extraXpForStatBumpOver100 50 Int
129 improvementXpPerStat 5 Int
130 suppressBeastsStrengthXp 50 Int
131 suppressBeastsAgilityXp 50 Int
132 marchWisdomXp 7 Int
133 swearBrotherhoodCharismaXp 75 Int
134 acceptBrotherhoodCharismaXp 50 Int
135 trainStrengthXp 5 Int
136 trainAgilityXp 5 Int
137 trainCharismaXp 10 Int
138 truceSuccessCharismaXp 25 Int
139 truceSuccessWisdomXp 20 Int
140 truceFailureCharismaXp 10 Int
141 truceFailureWisdomXp 20 Int
142 allianceSuccessCharismaXp 50 Int
143 allianceSuccessWisdomXp 40 Int
144 allianceFailureCharismaXp 10 Int
145 allianceFailureWisdomXp 20 Int
146 breakAllianceCharismaXp 50 Int
147 breakAllianceWisdomXp 40 Int
148 ransomSuccessCharismaXp 40 Int
149 ransomSuccessWisdomXp 30 Int
150 ransomFailureCharismaXp 20 Int
151 ransomFailureWisdomXp 20 Int
152 inviteSuccessCharismaXp 40 Int
153 inviteSuccessWisdomXp 30 Int
154 inviteFailureCharismaXp 20 Int
155 inviteFailureWisdomXp 20 Int
156 reconAgilityXp 20 Int
157 reconWisdomXp 10 Int
158 jailbreakAgilityXp 20 Double
159 jailbreakWisdomXp 20 Double
160 jailbreakCharismaXp 50 Double
161 sendSuppliesWisdomXp 15 Int
162 minimumPercentageOfEnemyToAttack 0.6 Double
163 almsMaxCharismaXp 10 Int
164 minMonthsAfterPleaseRecruitMeRejectionBeforeTryingAgain 12 Int
165 exiledHeroFactionBias -500 Double
166 minVigorForApprehendOutlaw 50 Double
167 apprehendOutlawVigorCost 30 Double
168 battleEconomyDevastationDelta 10 Double
169 battleAgricultureDevastationDelta 10 Double
170 battleInfrastructureDevastationDelta 10 Double
171 minVigorForCrackDown 50 Double
172 riotSupportDelta -25 Double
173 riotEconomyDevastationDelta 25 Double
174 riotInfrastructureDevastationDelta 25 Double
175 riotMaxFood 1000 Int
176 riotMaxGold 500 Int
177 riotCharismaFactor 0.1 Double
178 maxRiotSize 1500 Int
179 minRiotSize 100 Int
180 crackDownStrengthXp 75 Int
181 crackDownAgilityXp 25 Int
182 riotRollCharismaReduction 0.01 Double
183 monthsBetweenRiotsSupportMultiplier 0.5 Double
184 blizzardEventChance 0.01 Double
185 maxBlizzardDurationMonths 4 Int
186 winterSuppliesLoss 0.25 Double
187 blizzardEconomyDevastationDelta 4 Double
188 blizzardInfrastructureDevastationDelta 4 Double
189 blizzardAgricultureDevastationDelta 4 Double
190 festivalEventChance 0.01 Double
191 maxFestivalDurationMonths 3 Int
192 festivalSupportDelta 8 Double
193 festivalPrestigeDelta 3 Double
194 freeHeroMoveVigorCost 15 Double
195 minVigorForFreeHeroMove 50 Double
196 maxDesiredTruceCountForQuest 5 Int
197 minDesiredNewTruceCountForQuest 2 Int
198 floodEventChance 0.01 Double
199 floodDurationMonths 1 Int
200 maxFloodInfrastructureDevastationDelta 20 Double
201 maxFloodAgricultureDevastationDelta 20 Double
202 floodDevastationDeltaReductionPerInfrastructure 0.3 Double
203 epidemicBreakoutChance 0.0015 Double
204 epidemicSpreadChance 0.03 Double
205 epidemicEconomyDevastationDelta 10 Double
206 epidemicBattalionLossPercentage 0.05 Double
207 epidemicVigorDelta -10 Double
208 epidemicEndChance 0.35 Double
209 controlWeatherVigorDelta -40 Double
210 minVigorForControlWeather 60 Double
211 controlWeatherWisdomXp 25 Int
212 controlWeatherBlizzardDurationMonths 2 Int
213 minVigorForStartEpidemic 50 Double
214 startEpidemicCharismaXp 25 Int
215 startEpidemicVigorDelta -30 Double
216 droughtEventChance 0.01 Double
217 droughtDurationMonths 3 Int
218 droughtAgricultureDevastationDelta 10 Double
219 controlWeatherDroughtDurationMonths 3 Int
220 emptyProvinceMonthlyDevastationDelta -2 Double
221 vigorToConstitutionXpMultiplier 0.2 Double
222 trustDeltaPerRound 1 Int
223 trustDeltaForImprisoningOwnHero -150 Int
224 trustDeltaForImprisoningOtherHero -40 Int
225 trustDeltaFromAcceptingRansom 25 Int
226 trustDeltaFromRejectingRansom -25 Int
227 minimumTrustToOfferRansom 0 Int
228 minimumTrustToOfferTruce 0 Int
229 minimumTrustToInvite 20 Int
230 minimumTrustToOfferAlliance 30 Int
231 requiredPowerMultiplierForDismiss 1.5 Double
232 heroCapPerCastle 5 Int
233 overHeroCapLoyaltyDelta -1 Double
234 minimumAgilityForArchery 75 Double
235 minimumAgilityForStartFire 70 Double
236 minimumWisdomForStartFire 70 Double
237 championTrainingBonus 4 Double
238 requiredRatioToAttack 1.5 Double
239 requiredRatioToAttackWithoutTakingCastles 2.5 Double
240 monthsReconConsideredRecent 12 Int
241 aiMinimumCharismaForSwornBrother 65 Double
242 aiMinimumConstitutionForSwornBrother 65 Double
243 vassalMinimumGoldToSendSupplies 100 Int
244 vassalMinimumFoodToSendSupplies 200 Int
245 truceMonthsFromReturningLeader 12 Int
246 truceMonthsAfterBreakingAlliance 12 Int
247 ransomOfferScorePerPrisoner 1000 Int
248 ransomOfferScorePerHostage 2000 Int
249 ransomOfferScorePerGold 1 Int
250 ransomOfferBaseNegative -5000 Int
251 ransomOfferMaxExcess 1000 Int
252 prisonerEscapeOnMoveChance 0.1 Double
253 minimumMonthsBeforeRepeatTruceOffer 12 Int
254 minimumMonthsBeforeRepeatInvitation 12 Int
255 minimumMonthsBeforeRepeatRansomOffer 12 Int
256 minimumMonthsBeforeFirstProvinceEvents 6 Int
257 minDesiredAlmsToProvinceGiven 3000 Int
258 maxDesiredAlmsToProvinceGiven 5000 Int
259 minDesiredGoldInProvinceGiven 300 Int
260 maxDesiredGoldInProvinceGiven 500 Int
261 resourcePerBeastSwing 0.2 Double
262 almsProvinceCountExponent 0.5 Double
263 giftProvinceCountExponent 0.5 Double
264 minChanceForAIInvite 70 Int
265 chronicleWordCount 200 Int
266 maxDistanceForDefeatFactionQuest 3 Int
267 minimumPriceIndex 0.75 Double
268 maximumPriceIndex 1.5 Double
269 priceIndexReturnRate 0.1 Double
270 priceIndexShiftPerGold 0.0001 Double
271 aiTruceAcceptanceBaseChance 100 Double
272 aiAllianceAcceptanceBaseChance 100 Double
Binary file not shown.
1 version https://git-lfs.github.com/spec/v1
2 oid sha256:22e059ffe373d2d493c207f7a16450880d9a3e3abc9281f647d630df5c01ebc6
3 size 7563200
Binary file not shown.
1 version https://git-lfs.github.com/spec/v1 TypeName Light Infantry Heavy Infantry Light Cavalry Heavy Cavalry Longbowmen Undead
1 size 6244 TypeName Light Infantry Heavy Infantry Light Cavalry Heavy Cavalry Longbowmen Undead
2 TypeId 0 1 2 3 4 5
3 BaseCost 1 1.5 2.5 4 1.5 9999999
4 MinimumAgriculture 0 0 60 60 0 999
5 MinimumEconomy 0 60 0 60 0 999
6 TrainingDifficulty 1 1.5 1.5 2 2 9999999
7 BaseArmamentCostPerTroop 0.02 0.035 0.035 0.05 0.03 0
8 MonthlyFoodCost 0.065 0.08 0.1 0.15 0.08 0
9 Capacity 1000 800 800 600 800 1200
10 RandomnessSwing 20 20 20 20 20 30
11 MoraleSwing 50 50 50 50 50 0
12 CharismaSwing 50 50 50 50 50 50
13 BraverySwing 20 20 20 20 20 0
14 FlankDamageMultiplier 1.1 1.1 1.1 1.1 1.1 1.2
15 RearDamageMultiplier 1.25 1.25 1.25 1.25 1.25 1.5
16 ChargeMultiplier 1 1 1.2 1.5 1 1
17 AllowsCasting true false true false true false
18 AllowsStealth true false false false true false
19 AllowsBraveWater true false false false true false
20 AllowsMoveAfterCharge false false true true false false
21 AdjustsMorale true true true true true false
22 AlwaysHiddenInWater false false false false false true
23 AlwaysArcheryCapable false false false false true false
24 DamagedByHolyWave false false false false false true
25 BonusVolleys 0 0 0 0 2 0
26 CostToEnterPlains 3 3 2 2 3 3
27 CostToEnterHill 5 5 3 3 5 4
28 CostToEnterForest 4 4 4 4 4 4
29 CostToEnterMountain -1 -1 -1 -1 -1 -1
30 CostToEnterSwamp 5 5 5 5 5 5
31 CostToEnterCastle 3 3 3 3 3 4
32 CostToEnterCity 3 3 3 3 3 4
33 CostToEnterOcean -1 -1 -1 -1 -1 5
34 CostToEnterIce 5 5 6 6 5 5
35 MinimumCostToEnterBridge 2 2 3 4 2 4
36 SnowPenalty 0.1 0.1 0.1 0.1 0.1 0.1
37 ResistanceMultiplierForPlains 1 1 1 1 1 1
38 ResistanceMultiplierForHill 0.7 0.75 0.75 0.75 0.7 0.7
39 ResistanceMultiplierForForest 0.8 0.85 0.85 0.85 0.8 0.8
40 ResistanceMultiplierForMountain 0 0 0 0 0 0
41 ResistanceMultiplierForSwamp 1.3 1.5 1.5 1.5 1.3 1.3
42 ResistanceMultiplierForCastle 0.5 0.65 0.65 0.65 0.5 0.5
43 ResistanceMultiplierForCity 0.6 0.85 0.85 0.85 0.6 0.6
44 ResistanceMultiplierForOcean 2 2 2 2 2 2
45 BaseSlashingDamage 12 18 12 18 0 24
46 BasePuncturingDamage 0 0 0 0 0 0
47 BaseStabbingDamage 6 6 12 12 6 24
48 BaseCrushingDamage 6 18 6 18 0 100
49 BaseFireDamage 0 0 0 0 0 0
50 BaseColdDamage 0 0 0 0 0 0
51 BaseLightningDamage 0 0 0 0 0 0
52 BaseAessenceDamage 0 0 0 0 0 0
53 BaseImpactDamage 0 0 0 0 0 0
54 MaxTrainingAugmentSlashingDamage 60 60 60 60 30 0
55 MaxTrainingAugmentPuncturingDamage 0 0 0 0 0 0
56 MaxTrainingAugmentStabbingDamage 30 18 30 18 30 0
57 MaxTrainingAugmentCrushingDamage 24 60 24 60 12 0
58 MaxTrainingAugmentFireDamage 0 0 0 0 0 0
59 MaxTrainingAugmentColdDamage 0 0 0 0 0 0
60 MaxTrainingAugmentLightningDamage 0 0 0 0 0 0
61 MaxTrainingAugmentAessenceDamage 0 0 0 0 0 0
62 MaxTrainingAugmentImpactDamage 0 0 0 0 0 0
63 BasePenetratingSlashingDamage 0 0 0 0 0 0
64 BasePenetratingPuncturingDamage 0 0 0 0 0 0
65 BasePenetratingStabbingDamage 0 0 0 0 0 0
66 BasePenetratingCrushingDamage 0 0 0 0 0 0
67 BasePenetratingFireDamage 0 0 0 0 0 0
68 BasePenetratingColdDamage 0 0 0 0 0 0
69 BasePenetratingLightningDamage 0 0 0 0 0 0
70 BasePenetratingAessenceDamage 0 0 0 0 0 0
71 BasePenetratingImpactDamage 0 0 0 0 0 0
72 MaxTrainingAugmentPenetratingSlashingDamage 2 12 3 12 0 0
73 MaxTrainingAugmentPenetratingPuncturingDamage 2 6 3 6 0 0
74 MaxTrainingAugmentPenetratingStabbingDamage 2 6 3 6 0 0
75 MaxTrainingAugmentPenetratingCrushingDamage 2 18 3 18 0 0
76 MaxTrainingAugmentPenetratingFireDamage 0 0 0 0 0 0
77 MaxTrainingAugmentPenetratingColdDamage 0 0 0 0 0 0
78 MaxTrainingAugmentPenetratingLightningDamage 0 0 0 0 0 0
79 MaxTrainingAugmentPenetratingAessenceDamage 0 0 0 0 0 0
80 MaxTrainingAugmentPenetratingImpactDamage 0 2 2 2 0 0
81 BaseMissileSlashingDamage 0 0 0 0 0 0
82 BaseMissilePuncturingDamage 10 10 10 10 20 0
83 BaseMissileStabbingDamage 0 0 0 0 0 0
84 BaseMissileCrushingDamage 0 0 0 0 0 0
85 BaseMissileFireDamage 0 0 0 0 0 0
86 BaseMissileColdDamage 0 0 0 0 0 0
87 BaseMissileLightningDamage 0 0 0 0 0 0
88 BaseMissileAessenceDamage 0 0 0 0 0 0
89 BaseMissileImpactDamage 0 0 0 0 0 0
90 MaxTrainingAugmentMissileSlashingDamage 0 0 0 0 0 0
91 MaxTrainingAugmentMissilePuncturingDamage 50 50 50 50 20 0
92 MaxTrainingAugmentMissileStabbingDamage 0 0 0 0 0 0
93 MaxTrainingAugmentMissileCrushingDamage 0 0 0 0 0 0
94 MaxTrainingAugmentMissileFireDamage 0 0 0 0 0 0
95 MaxTrainingAugmentMissileColdDamage 0 0 0 0 0 0
96 MaxTrainingAugmentMissileLightningDamage 0 0 0 0 0 0
97 MaxTrainingAugmentMissileAessenceDamage 0 0 0 0 0 0
98 MaxTrainingAugmentMissileImpactDamage 0 0 0 0 0 0
99 BasePenetratingMissileSlashingDamage 0 0 0 0 0 0
100 BasePenetratingMissilePuncturingDamage 0 0 0 0 15 0
101 BasePenetratingMissileStabbingDamage 0 0 0 0 0 0
102 BasePenetratingMissileCrushingDamage 0 0 0 0 0 0
103 BasePenetratingMissileFireDamage 0 0 0 0 0 0
104 BasePenetratingMissileColdDamage 0 0 0 0 0 0
105 BasePenetratingMissileLightningDamage 0 0 0 0 0 0
106 BasePenetratingMissileAessenceDamage 0 0 0 0 0 0
107 BasePenetratingMissileImpactDamage 0 0 0 0 0 0
108 MaxTrainingAugmentPenetratingMissileSlashingDamage 0 0 0 0 0 0
109 MaxTrainingAugmentPenetratingMissilePuncturingDamage 0 0 0 0 75 0
110 MaxTrainingAugmentPenetratingMissileStabbingDamage 0 0 0 0 0 0
111 MaxTrainingAugmentPenetratingMissileCrushingDamage 0 0 0 0 0 0
112 MaxTrainingAugmentPenetratingMissileFireDamage 0 0 0 0 0 0
113 MaxTrainingAugmentPenetratingMissileColdDamage 0 0 0 0 0 0
114 MaxTrainingAugmentPenetratingMissileLightningDamage 0 0 0 0 0 0
115 MaxTrainingAugmentPenetratingMissileAessenceDamage 0 0 0 0 0 0
116 MaxTrainingAugmentPenetratingMissileImpactDamage 0 0 0 0 0 0
117 BaseSlashingResistance 0 25 15 40 0 0
118 BasePuncturingResistance 0 25 10 40 0 0
119 BaseStabbingResistance 0 25 10 40 0 0
120 BaseCrushingResistance 0 25 15 40 0 0
121 BaseFireResistance 0 0 0 0 0 -50
122 BaseColdResistance 0 0 0 0 0 0
123 BaseLightningResistance 0 0 0 0 0 0
124 BaseAessenceResistance 0 0 0 0 0 0
125 BaseImpactResistance 0 0 0 0 0 0
126 MaxArmamentAugmentSlashingResistance 50 65 50 55 20 50
127 MaxArmamentAugmentPuncturingResistance 20 65 20 55 20 20
128 MaxArmamentAugmentStabbingResistance 25 60 25 55 20 25
129 MaxArmamentAugmentCrushingResistance 20 30 20 30 20 20
130 MaxArmamentAugmentFireResistance 0 0 0 0 0 0
131 MaxArmamentAugmentColdResistance 0 0 0 0 0 0
132 MaxArmamentAugmentLightningResistance 0 0 0 0 0 0
133 MaxArmamentAugmentAessenceResistance 0 0 0 0 0 0
134 MaxArmamentAugmentImpactResistance 0 0 0 0 0 0
135 AmbushOddsWithoutHero 0 0 0 0 0 50
136 BaseStunChanceOnCharge 0 10 10 30 0 0
137 TrainingDifferenceMultiplierForStunChanceOnCharge 0.3 0.5 0.5 1 0 0
Binary file not shown.
1 version https://git-lfs.github.com/spec/v1 lightningWisdomFactor 1 double
1 size 6880 lightningWisdomFactor 1 double
2 lightningAgilityFactor 0.25 double
3 lightningDamageFactor 250 double
4 meteorBaseDamage 2500 double
5 meteorSplashFactor 0.4 double
6 meteorDirectFireBaseChance 90 double
7 meteorSplashFireBaseChance 50 double
8 meteorStructureDamage 30 double
9 meteorFrozenIntegrityReduced 80 double
10 meteorSnowIntegrityReduced 80 double
11 raiseDeadBaseOdds 100 int16
12 dismissBaseOdds 60 int16
13 fearBaseOdds 75 int16
14 meleeKnowledgeIncrement 10 int8
15 scoutDirectKnowledgeIncrement 35 int8
16 scoutAdjacentKnowledgeIncrement 20 int8
17 damageReceivedMultiplierFromStun 1.2 double
18 damageGivenMultiplierFromStun 0.5 double
19 minimumCastleToPreventCharge 15 double
20 holyWaveBaseChance 75 double
21 holyWaveInspireBonus 25 double
22 holyWaveVigorBuf 5 double
23 maxInspireOverBase 10 double
24 holyWaveFailedDebuf -10 double
25 holyWaveDamagePercentage 0.7 double
26 minMoraleInCastle 15 double
27 fleeActionPointCost 5 int8
28 firePropensityCity 10 int16
29 firePropensityHill 0 int16
30 firePropensityPlains 0 int16
31 firePropensityForest 20 int16
32 firePropensityWater -500 int16
33 firePropensitySwamp -70 int16
34 firePropensityMountain -10 int16
35 firePropensityBridge 10 int16
36 firePropensityCastle 0 int16
37 firePropensityIce -500 int16
38 firePropensitySun 10 int16
39 firePropensityClouds 0 int16
40 firePropensityRain -60 int16
41 firePropensitySnow -60 int16
42 firePropensityThunderstorm -85 int16
43 firePropensityBlizzard -85 int16
44 firePropensityWindSpeedMultiplier 1 double
45 extinguishOwnTileBuf 25 int16
46 fireOutNaturallyBaseOdds 30 int16
47 extinguishBaseOdds 70 int16
48 startFireBaseOdds 40 int16
49 freezeWaterBaseOdds 60 int16
50 freezeWaterBonusSun -10 int16
51 freezeWaterBonusClouds 0 int16
52 freezeWaterBonusRain 10 int16
53 freezeWaterBonusThunderstorm 10 int16
54 freezeWaterBonusSnow 50 int16
55 freezeWaterBonusBlizzard 100 int16
56 scoutBaseOdds 75 int16
57 ambushBaseOdds 50 int16
58 moraleShiftPerCasualty 0.1 double
59 minimumMoraleToAct 10 double
60 minimumVigorToAct 10 double
61 minimumVigorForCombat 20 double
62 minimumVigorForMeteor 30 double
63 extraCostForZocMovement 1 int8
64 holyWaveActionPointCost 5 int8
65 fallInWaterDamagePerTroop 150 double
66 fallInWaterBaseEscapeOdds 50 int16
67 retreatActionPointCost 5 int8
68 reinforceActionPointCost 5 int8
69 braveWaterActionPointCost 8 int8
70 braveWaterBaseOdds 50 int16
71 braveWaterOddsRain -20 int16
72 braveWaterOddsThunderstorm -50 int16
73 braveWaterOddsSnow -35 int16
74 braveWaterOddsBlizzard -70 int16
75 braveWaterAgilityFactor 0.2 double
76 braveWaterWisdomFactor 0.1 double
77 braveWaterRangerBonus 20 double
78 braveWaterArmamentFactor -0.5 double
79 braveWaterMaxLoss 0.2 double
80 lightningRange 3 int8
81 meteorRange 3 int8
82 duelDeclinedMoraleAdjustment -15 double
83 duelWonMoraleBoost 15 double
84 minimumKnowledgeForProfession 20 int8
85 minimumKnowledgeForName 30 int8
86 minimumKnowledgeForBattalionName 30 int8
87 minimumKnowledgeForBattalionStats 50 int8
88 minimumKnowledgeForHeroStats 75 int8
89 duelBaseAcceptOdds 50 int16
90 meteorImpactStartFireBaseOdds 90 int16
91 meteorSplashStartFireBaseOdds 50 int16
92 meteorImpactCastleMinDamage 0 double
93 meteorImpactCastleMaxDamage 30 double
94 meteorImpactBridgeMinDamage 25 double
95 meteorImpactBridgeMaxDamage 100 double
96 meteorSplashBridgeMinDamage 12.5 double
97 meteorSplashBridgeMaxDamage 60 double
98 burningBridgeDamage 25 double
99 burningCastleDamage 15 double
100 buildBridgeBaseOdds 30 int16
101 buildBridgeForestBonus 30 int16
102 meleeActionPointCost 5 int8
103 archeryActionPointCost 5 int8
104 lightningActionPointCost 5 int8
105 meteorStartActionPointCost 5 int8
106 meteorCancelActionPointCost 1 int8
107 meteorTargetActionPointCost 1 int8
108 meteorCastActionPointCost 1 int8
109 extinguishActionPointCost 5 int8
110 startFireActionPointCost 5 int8
111 freezeWaterActionPointCost 5 int8
112 buildBridgeActionPointCost 5 int8
113 repairActionPointCost 5 int8
114 reduceActionPointCost 5 int8
115 scoutActionPointCost 5 int8
116 raiseDeadActionPointCost 5 int8
117 fearActionPointCost 5 int8
118 challengeDuelActionPointCost 5 int8
119 scoutRange 4 int8
120 raiseDeadRange 2 int8
121 fearRange 3 int8
122 reduceRange 3 int8
123 heroDamageFactor 12.5 double
124 moraleAdjustmentForNotEnoughFood -10 double
125 sizeMultiplierForNotEnoughFood 0.95 double
126 freezeWaterIntegrity 80 double
127 repairBuf 25 double
128 averageReduceDebuf 10 double
129 reduceForestBonus 50 double
130 unitDamagePerReduceDebuf 800 double
131 fearDebuf -25 double
132 fearFailedBuf 10 double
133 actionCostToVigorCost 0.5 double
134 moraleMeanReversion 0.1 double
135 meteorCastVigorCost 20 double
136 meleeStrengthXp 1 int8
137 archeryAgilityXp 1 int8
138 ambushAgilityXp 1 int8
139 archeryAmbushAgilityXp 2 int8
140 meteorCastWisdomXp 5 int8
141 lightningBoltWisdomXp 1 int8
142 startFireWisdomXp 1 int8
143 startFireAgilityXp 1 int8
144 extinguishAgilityXp 1 int8
145 extinguishWisdomXp 1 int8
146 duelStrengthXp 10 int8
147 duelAgilityXp 10 int8
148 duelWinnerCharismaXp 20 int8
149 minimumVigorToAcceptDuel 30 double
150 chargeAgilityXp 1 int8
151 iceIntegrityAdjustmentRiver -20 double
152 iceIntegrityAdjustmentSun -20 double
153 iceIntegrityAdjustmentClouds 0 double
154 iceIntegrityAdjustmentRain 0 double
155 iceIntegrityAdjustmentThunderstorm 0 double
156 iceIntegrityAdjustmentSnow 10 double
157 iceIntegrityAdjustmentBlizzard 25 double
158 iceIntegrityAdjustmentFire -50 double
159 initialWinterIceIntegrity 30 double
160 armamentToVolleys 0.1 double
161 defenderBaseKnowledgeGain 2 int8
162 attackerBaseKnowledgeGain 1 int8
163 adjacentHeroKnowledgeGain 4 int8
164 maxRounds 30 int8
165 holyWaveCharismaXp 5 int8
166 snowIntegrityAdjustmentSun -20 double
167 snowIntegrityAdjustmentClouds 0 double
168 snowIntegrityAdjustmentRain 0 double
169 snowIntegrityAdjustmentThunderstorm 0 double
170 snowIntegrityAdjustmentSnow 10 double
171 snowIntegrityAdjustmentBlizzard 20 double
172 snowIntegrityAdjustmentFire -100 double
173 freezeWaterWisdomXp 3 int8
174 buildBridgeAgilityXp 2 int8
175 buildBridgeStrengthXp 2 int8
176 repairAgilityXp 1 int8
177 repairStrengthXp 1 int8
178 reduceAgilityXp 2 int8
179 reduceStrengthXp 2 int8
180 scoutAgilityXp 2 int8
181 scoutWisdomXp 2 int8
182 hideActionPointCost 5 int8
183 fortifyStrengthXp 1 int8
184 fortifyAgilityXp 1 int8
185 hideAgilityXp 2 int8
186 fortifyActionPointCost 5 int8
187 fortifyDamageTakenMultiplier 0.9 double
188 raiseDeadWisdomXp 1 int8
189 raiseDeadCharismaXp 2 int8
190 fearCharismaXp 2 int8
191 undeadDecayRate 0.25 double
192 deadReinforceRate 0.3 double
193 raiseDeadStartingSize 100 int16
194 undeadDecayFloor 100 int16
195 winterMoraleAdjustment -20 double
196 stealthForestModifier 10 int16
197 stealthSwampModifier 10 int16
198 stealthWaterModifier -20 int16
199 lightningBoltDamageCap 0.2 double
200 vipCapturedMoraleAdjustment -20 double
201 fleeSuccessBaseChance 120 int16
202 fleeSuccessPerAdjacentEnemy -20 int16
203 fleeSuccessPerAdjacentFriendly 10 int16
204 fleeSuccessAdjustmentForTwoAway 0.5 double
205 fleeSuccessPerAdjacentImpassableTile -20 int16
206 heroBraverySwing 25 double
207 heroDamageAmbushAdjustment 50 double
208 heroMortalityFactor 0.005 double
209 baseDeadliness 0.0012 double
210 aiUtilityRepeatCount 5 int8
211 maxLookaheadTurns 1 int8
212 saveAll FALSE bool
213 holyWaveVigorCost 10 double
@@ -15,7 +15,7 @@ import java.time.format.DateTimeFormatter
import java.util.function.Consumer
object ClaudeServiceImpl {
val model: String = "claude-sonnet-4-20250514"
val model: String = "claude-3-5-sonnet-20240620"
val maxTokens = 2048
private val apiKey = ApiKeys.anthropic
@@ -43,7 +43,6 @@ object ClaudeServiceImpl {
.timeout(Duration.ofSeconds(timeoutSeconds))
.header("X-API-Key", s"${ClaudeServiceImpl.apiKey}")
.header("anthropic-version", "2023-06-01")
.header("Content-Type", "application/json")
}
class ClaudeServiceImpl(
@@ -77,6 +77,10 @@ final class ExternalTextGenerationCaller(
backoffSeconds: Double =
ExternalTextGenerationCaller.initialBackoffSeconds
): Future[HttpResponse[Unit]] = {
if (inputText.contains("Some(")) {
println("SOME!")
}
streamCompletion(
inputText = inputText,
request = serviceImpl.makeRequest(
+1 -1
View File
@@ -74,7 +74,7 @@ scala_library(
srcs = ["package.scala"],
visibility = [
":__subpackages__",
"//src/main/scala/net/eagle0/util:__subpackages__",
"//src/main/scala/net/eagle0/util:__pkg__",
"//src/test:__subpackages__",
],
deps = [
@@ -122,7 +122,7 @@ case class PerformUnaffiliatedHeroesAction(
.withActionResults { gs =>
EndUnaffiliatedHeroActionsPhaseAction(
gameId = gs.gameId,
currentDate = currentDate,
currentDate = DateConverter.fromProto(gs.currentDate),
currentRoundId = gs.currentRoundId,
provinces =
gs.provinces.values.map(ProvinceConverter.fromProto).toVector,
@@ -250,15 +250,15 @@ case class PerformUnaffiliatedHeroesAction(
.getHero(
random = nextFr,
hid = nextHid,
date = currentDate
date = gs.currentDate
)
.map {
case HeroGenerationResponse(
hero,
nameInfo
) =>
innerAcc :+ (toCheck.head, hero
.copy(id = nextHid), nameInfo)
innerAcc :+ (toCheck.head, HeroConverter
.fromProto(hero.withId(nextHid)), nameInfo)
}
)
}
@@ -8,9 +8,12 @@ scala_library(
"//src/test/scala/net/eagle0/eagle:__subpackages__",
],
exports = [
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
],
deps = [
"//src/main/protobuf/net/eagle0/eagle/common:profession_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/common/name_generation:string_construction_token_generator",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
@@ -19,8 +22,5 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/hero_generation_response",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/name_info",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/random_hero_generator",
"//src/main/scala/net/eagle0/eagle/model/state/date",
"//src/main/scala/net/eagle0/eagle/model/state/hero/backstory_version",
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
],
)
@@ -1,13 +1,13 @@
package net.eagle0.eagle.library.util.hero_generator
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.common.date.Date
import net.eagle0.eagle.internal.hero.Hero
import net.eagle0.eagle.library.EagleInternalException
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
import net.eagle0.eagle.library.util.hero_generator.hero_generation_response.HeroGenerationResponse
import net.eagle0.eagle.library.util.hero_generator.name_info.NoNameRequest
import net.eagle0.eagle.library.util.hero_generator.random_hero_generator.RandomHeroGenerator
import net.eagle0.eagle.model.state.date.Date
import net.eagle0.eagle.model.state.hero.concrete.HeroC
import net.eagle0.eagle.{HeroId, RoundId}
import scala.util.{Failure, Success}
@@ -16,7 +16,7 @@ object HeroGenerator {
private val randomChance: Double = 0.0
def apply(
pregeneratedHeroes: Vector[HeroC],
pregeneratedHeroes: Vector[Hero],
excludingNames: Vector[String],
functionalRandom: FunctionalRandom
): RandomState[HeroGenerator] =
@@ -33,7 +33,7 @@ object HeroGenerator {
class HeroGenerator(
functionalRandom: FunctionalRandom,
shuffledPregeneratedHeroes: Vector[HeroC],
shuffledPregeneratedHeroes: Vector[Hero],
excludingNames: Vector[String]
) {
private val randomHeroGenerator = new RandomHeroGenerator(
@@ -41,7 +41,7 @@ class HeroGenerator(
excludingNames = excludingNames
)
private var remainingPregeneratedHeroes: Vector[HeroC] =
private var remainingPregeneratedHeroes: Vector[Hero] =
if (
shuffledPregeneratedHeroes == null || shuffledPregeneratedHeroes.isEmpty
) Vector()
@@ -54,7 +54,7 @@ class HeroGenerator(
s"Pregenerated heroes exhausted, no hero with id $hid"
)
val nextHero = remainingPregeneratedHeroes.head
.copy(id = hid)
.withId(hid)
internalRequire(
nextHero.backstoryVersions.nonEmpty,
s"Pregenerated hero ${nextHero.name} has no backstory text"
@@ -74,7 +74,7 @@ class HeroGenerator(
.getHero(hid, date) match {
case Success(HeroGenerationResponse(hero, nameInfo)) =>
HeroGenerationResponse(
hero = hero.copy(backstoryVersions = Vector()),
hero = hero.clearBackstoryVersions,
nameInfo = nameInfo
)
case Failure(_) =>
@@ -7,12 +7,11 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:__pkg__",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator:__subpackages__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
"//src/main/scala/net/eagle0/util:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/util/hero_generator:__subpackages__",
],
deps = [
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/name_info",
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
],
)
@@ -1,9 +1,9 @@
package net.eagle0.eagle.library.util.hero_generator.hero_generation_response
import net.eagle0.eagle.internal.hero.Hero
import net.eagle0.eagle.library.util.hero_generator.name_info.NameInfo
import net.eagle0.eagle.model.state.hero.concrete.HeroC
case class HeroGenerationResponse(
hero: HeroC,
hero: Hero,
nameInfo: NameInfo
)
@@ -8,7 +8,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/actions/name_generation_request:__pkg__",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator:__subpackages__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
"//src/main/scala/net/eagle0/util:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/util/hero_generator:__subpackages__",
],
deps = [
@@ -12,11 +12,13 @@ scala_library(
"//src/test/scala/net/eagle0/eagle:__subpackages__",
],
exports = [
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
],
deps = [
":random_hero_personalities",
"//src/main/protobuf/net/eagle0/eagle/common:profession_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/common/name_generation:string_construction_token_generator",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
@@ -24,9 +26,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/hero_generation_response",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/name_info",
"//src/main/scala/net/eagle0/eagle/library/util/hero_name_fetcher",
"//src/main/scala/net/eagle0/eagle/model/state/date",
"//src/main/scala/net/eagle0/eagle/model/state/hero/backstory_version",
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
],
)
@@ -2,24 +2,29 @@ package net.eagle0.eagle.library.util.hero_generator.random_hero_generator
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.HeroId
import net.eagle0.eagle.common.date.Date
import net.eagle0.eagle.common.gender.Gender
import net.eagle0.eagle.common.gender.Gender.{
GENDER_FEMALE,
GENDER_MALE,
GENDER_OTHER
}
import net.eagle0.eagle.common.hero_backstory_version.BackstoryVersion
import net.eagle0.eagle.common.profession.Profession
import net.eagle0.eagle.common.profession.Profession.{
CHAMPION,
ENGINEER,
MAGE,
NECROMANCER,
NO_PROFESSION,
PALADIN,
RANGER
}
import net.eagle0.eagle.internal.hero.Hero
import net.eagle0.eagle.library.EagleInternalException
import net.eagle0.eagle.library.util.hero_generator.hero_generation_response.HeroGenerationResponse
import net.eagle0.eagle.library.util.hero_generator.name_info.FixedName
import net.eagle0.eagle.library.util.hero_name_fetcher.HeroNameFetcher
import net.eagle0.eagle.model.state.date.Date
import net.eagle0.eagle.model.state.hero.Gender.{Female, Male, Other}
import net.eagle0.eagle.model.state.hero.{Gender, Profession}
import net.eagle0.eagle.model.state.hero.Profession.{
Champion,
Engineer,
Mage,
Necromancer,
NoProfession,
Paladin,
Ranger
}
import net.eagle0.eagle.model.state.hero.backstory_version.BackstoryVersion
import net.eagle0.eagle.model.state.hero.concrete.HeroC
import javax.naming.InsufficientResourcesException
import scala.annotation.tailrec
@@ -35,44 +40,44 @@ object RandomHeroGenerator {
private def genderFromString(genderString: String): Gender =
genderString match {
case "female" => Gender.Female
case "male" => Gender.Male
case "other" => Gender.Other
case "female" => GENDER_FEMALE
case "male" => GENDER_MALE
case "other" => GENDER_OTHER
case _ => throw new EagleInternalException("unknown gender string")
}
private def genderToString(gender: Gender): String =
gender match {
case Gender.Female => "female"
case Gender.Male => "male"
case Gender.Other => "other"
case GENDER_FEMALE => "female"
case GENDER_MALE => "male"
case GENDER_OTHER => "other"
case _ => throw new EagleInternalException("unknown gender")
}
private def withProfessionAndPrimeStat(
bh: HeroC,
bh: Hero,
fr: FunctionalRandom
): RandomState[HeroC] =
): RandomState[Hero] =
primeStat(fr)
.continue { case (ps, nextRandom) =>
nextRandom.nextInt(0, 125).map {
case 1 | 2 => bh.withProfession(Mage).copy(wisdom = ps)
case 1 | 2 => bh.withProfession(MAGE).withWisdom(ps)
case 3 | 4 =>
bh.withProfession(Necromancer).copy(charisma = ps)
bh.withProfession(NECROMANCER).withCharisma(ps)
case 5 | 6 | 7 | 8 | 9 =>
bh.withProfession(Engineer).copy(agility = ps)
bh.withProfession(ENGINEER).withAgility(ps)
case 10 | 11 | 12 | 13 =>
bh.withProfession(Paladin).copy(charisma = ps)
bh.withProfession(PALADIN).withCharisma(ps)
case 14 | 15 | 16 | 17 =>
bh.withProfession(Ranger).copy(agility = ps)
bh.withProfession(RANGER).withAgility(ps)
case 18 | 19 | 20 | 21 =>
bh.withProfession(Champion).copy(strength = ps)
case _ => bh.withProfession(NoProfession)
bh.withProfession(CHAMPION).withStrength(ps)
case _ => bh.withProfession(NO_PROFESSION)
}
}
def withNoProfession(bh: HeroC, fr: FunctionalRandom): RandomState[HeroC] =
RandomState(bh.withProfession(NoProfession), fr)
def withNoProfession(bh: Hero, fr: FunctionalRandom): RandomState[Hero] =
RandomState(bh.withProfession(NO_PROFESSION), fr)
private def primeStat(random: FunctionalRandom): RandomState[Int] =
random
@@ -83,7 +88,7 @@ class RandomHeroGenerator(
functionalRandom: FunctionalRandom,
var excludingNames: Vector[String] = Vector.empty[String],
excludingImagePaths: Vector[String] = Vector.empty[String],
professionAdder: (HeroC, FunctionalRandom) => RandomState[HeroC] =
professionAdder: (Hero, FunctionalRandom) => RandomState[Hero] =
RandomHeroGenerator.withProfessionAndPrimeStat
) {
import RandomHeroGenerator._
@@ -91,7 +96,7 @@ class RandomHeroGenerator(
private def createHeroes(
count: Int,
functionalRandom: FunctionalRandom
): Try[RandomState[Vector[HeroC]]] =
): Try[RandomState[Vector[Hero]]] =
(1 to count)
.foldLeft(RandomState(Vector[String](), functionalRandom)) {
case (RandomState(acc, fr), _) =>
@@ -102,7 +107,7 @@ class RandomHeroGenerator(
.names(genderStrings, excludingNames)
.map(_.zip(genderStrings))
.map {
_.foldLeft(RandomState(Vector[HeroC](), fr)) {
_.foldLeft(RandomState(Vector[Hero](), fr)) {
case (RandomState(heroes, fr), (name, gendString)) =>
excludingNames = excludingNames :+ name
heroWithBaseStats(0, name, genderFromString(gendString), fr)
@@ -115,7 +120,7 @@ class RandomHeroGenerator(
hero.profession,
genderFromString(gendString)
)
.map(ip => hero.copy(imagePath = ip))
.map(hero.withImagePath)
}
.map(heroes :+ _)
}
@@ -159,10 +164,10 @@ class RandomHeroGenerator(
Success(
taken.zip(hids).map { case (hero, hid) =>
val adjustedHero = hero.copy(
id = hid,
nameTextId = s"hn_$hid",
backstoryVersions = hero.backstoryVersions :+ BackstoryVersion(
val adjustedHero = hero.update(
_.id := hid,
_.nameTextId := s"hn_$hid",
_.backstoryVersions :+= BackstoryVersion(
textId = s"hero_${hid}_backstory_0",
date = date
)
@@ -183,13 +188,13 @@ class RandomHeroGenerator(
getClass.getResource("/net/eagle0/eagle/headshots.tsv")
private val professionStringMap = Map(
"no_profession" -> NoProfession,
"mage" -> Mage,
"engineer" -> Engineer,
"necromancer" -> Necromancer,
"paladin" -> Paladin,
"ranger" -> Ranger,
"champion" -> Champion
"no_profession" -> NO_PROFESSION,
"mage" -> MAGE,
"engineer" -> ENGINEER,
"necromancer" -> NECROMANCER,
"paladin" -> PALADIN,
"ranger" -> RANGER,
"champion" -> CHAMPION
)
private def normalStat(random: FunctionalRandom): RandomState[Int] =
@@ -200,10 +205,10 @@ class RandomHeroGenerator(
gender: Gender,
fr: FunctionalRandom
): RandomState[Gender] =
if (gender == Gender.Other)
if (gender == GENDER_OTHER)
fr.nextIntInclusive(0, 1).map {
case 0 => Gender.Female
case 1 => Gender.Male
case 0 => GENDER_FEMALE
case 1 => GENDER_MALE
}
else RandomState(gender, fr)
@@ -240,9 +245,9 @@ class RandomHeroGenerator(
private def gender(random: FunctionalRandom): RandomState[Gender] =
random.nextDouble
.map { d =>
if (d < 0.47) Female
else if (d < 0.94) Male
else Other
if (d < 0.47) GENDER_FEMALE
else if (d < 0.94) GENDER_MALE
else GENDER_OTHER
}
private def heroWithBaseStats(
@@ -250,7 +255,7 @@ class RandomHeroGenerator(
name: String,
gender: Gender,
fr: FunctionalRandom
): RandomState[HeroC] =
): RandomState[Hero] =
fr.nextInt(constitutionMin, 100)
.continue { case (constitution, fr) =>
fr.nextItems(8, normalStat).continue {
@@ -269,7 +274,7 @@ class RandomHeroGenerator(
) =>
RandomHeroPersonalities.randomPersonalityWords(3, innerFr).map {
personalityWords =>
HeroC(
Hero(
id = hid,
name = name,
strength = strength,
@@ -342,13 +347,13 @@ class RandomHeroGenerator(
private val minCacheSize = 10
private val cacheFetchSize = 50
private var cache: RandomState[Vector[HeroC]] =
if (functionalRandom == null) RandomState(Vector[HeroC](), null)
private var cache: RandomState[Vector[Hero]] =
if (functionalRandom == null) RandomState(Vector[Hero](), null)
else createCache(functionalRandom)
private def createCache(
functionalRandom: FunctionalRandom
): RandomState[Vector[HeroC]] =
): RandomState[Vector[Hero]] =
createHeroes(cacheFetchSize, functionalRandom) match {
case Success(rs) => rs
case Failure(e) =>
@@ -9,7 +9,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util/quest_fulfillment:__pkg__",
"//src/main/scala/net/eagle0/eagle/model:__subpackages__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
"//src/main/scala/net/eagle0/util:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
"//src/test/scala/net/eagle0/eagle/library/util/quest_fulfillment:__pkg__",
"//src/test/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
@@ -35,7 +35,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
"//src/main/scala/net/eagle0/util:__pkg__",
],
exports = [
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
@@ -54,7 +53,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library:__subpackages__",
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
],
exports = [
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
@@ -82,7 +80,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
"//src/main/scala/net/eagle0/util:__pkg__",
],
exports = [
"//src/main/scala/net/eagle0/eagle/model/state/hero:profession",
@@ -4,7 +4,14 @@ scala_library(
name = "date",
srcs = ["Date.scala"],
visibility = [
"//visibility:public",
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
"//src/main/scala/net/eagle0/eagle/library/actions:__subpackages__",
"//src/main/scala/net/eagle0/eagle/library/util/quest_fulfillment:__pkg__",
"//src/main/scala/net/eagle0/eagle/model:__subpackages__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions:__subpackages__",
"//src/test/scala/net/eagle0/eagle/library/util/quest_fulfillment:__pkg__",
"//src/test/scala/net/eagle0/eagle/model:__subpackages__",
],
deps = [],
)
@@ -29,7 +29,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library:__subpackages__",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:__pkg__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
"//src/main/scala/net/eagle0/util/hero_generation:__pkg__",
"//src/test/scala/net/eagle0/eagle/library:__subpackages__",
"//src/test/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
],
@@ -65,7 +64,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/actions:__subpackages__",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:__pkg__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
"//src/main/scala/net/eagle0/util/hero_generation:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions:__subpackages__",
"//src/test/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
],
@@ -5,13 +5,11 @@ scala_library(
srcs = ["BackstoryVersion.scala"],
visibility = [
"//src/main/scala/net/eagle0/eagle/library/actions:__subpackages__",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator:__subpackages__",
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request:__pkg__",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:__pkg__",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/llm_request:__pkg__",
"//src/main/scala/net/eagle0/eagle/model/state/hero:__pkg__",
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete:__pkg__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions:__subpackages__",
],
deps = [
@@ -5,10 +5,7 @@ scala_library(
srcs = ["HeroC.scala"],
visibility = [
"//src/main/scala/net/eagle0/eagle/library/actions:__subpackages__",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator:__subpackages__",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:__pkg__",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
"//src/test/scala/net/eagle0/eagle/library:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions:__subpackages__",
"//src/test/scala/net/eagle0/eagle/library/util:__subpackages__",
],
@@ -31,7 +31,6 @@ import net.eagle0.eagle.library.settings.loaders.{
}
import net.eagle0.eagle.library.util.EagleRequire
import net.eagle0.eagle.library.util.hero_generator.HeroGenerator
import net.eagle0.eagle.model.state.hero.concrete.HeroC
import net.eagle0.eagle.service.controller._
import net.eagle0.eagle.service.new_game_creation.{
ExpandedGameParameters,
@@ -170,7 +169,7 @@ object GamesManager {
gamePersister: Persister,
history: GameHistory,
userToFid: Map[String, FactionId],
pregeneratedHeroes: Vector[HeroC]
pregeneratedHeroes: Vector[Hero]
): GameController = {
val existingNames = (history.last.gameState.killedHeroes ++
history.last.gameState.heroes).values.map(_.name).toVector
@@ -224,7 +223,7 @@ object GamesManager {
gamePersister = persister,
history = gh,
userToFid = runningGame.userToPid,
pregeneratedHeroes = GameParametersUtils.allPregeneratedHeroes
pregeneratedHeroes = GameParametersUtils.allPregeneratedHeroProtos
)
)
)
@@ -105,11 +105,11 @@ class LlmResolver(updateReceiver: LlmUpdateQueuingProxy, gptModelName: String) {
defaultModelName = gptModelName
)
private def newClaudeServiceImpl = new ClaudeServiceImpl(
defaultModelName = "claude-sonnet-4-20250514"
defaultModelName = "claude-3-5-sonnet-20240620"
)
private val chatGptCount = 1
private val claudeCount = 1
private val chatGptCount = 2
private val claudeCount = 0
lazy private val llmCallers = ((1 to claudeCount).map { _ =>
new ExternalTextGenerationCaller(
@@ -15,7 +15,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library:eagle_internal_exception",
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
"//src/main/scala/net/eagle0/eagle/model/state/hero:profession",
"//src/main/scala/net/eagle0/util/hero_generation:loaded_hero",
],
)
@@ -24,20 +23,17 @@ scala_library(
srcs = ["GreatPerson.scala"],
visibility = [
"//src/main/scala/net/eagle0/eagle/service:__pkg__",
"//src/main/scala/net/eagle0/util:__pkg__",
"//src/test/scala/net/eagle0/eagle/service:__pkg__",
"//src/test/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
],
exports = [
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
"//src/main/scala/net/eagle0/eagle/model/state/hero:profession",
"//src/main/scala/net/eagle0/util/hero_generation:loaded_hero",
],
deps = [
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
"//src/main/scala/net/eagle0/eagle/model/state/hero:profession",
"//src/main/scala/net/eagle0/util/hero_generation:loaded_hero",
],
)
@@ -64,8 +60,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library:eagle_internal_exception",
"//src/main/scala/net/eagle0/eagle/library/util:map_generator",
"//src/main/scala/net/eagle0/eagle/library/util:price_index_utils",
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
"//src/main/scala/net/eagle0/util/hero_generation:loaded_hero",
"@maven//:com_thesamet_scalapb_scalapb_json4s_2_13",
],
)
@@ -77,6 +71,7 @@ scala_library(
"//src/test/scala/net/eagle0/eagle/service:__subpackages__",
],
deps = [
":great_person",
":start_date",
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
@@ -84,9 +79,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:gender",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:profession",
"//src/main/scala/net/eagle0/eagle/model/state/date",
"//src/main/scala/net/eagle0/eagle/model/state/hero/backstory_version",
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
"//src/main/scala/net/eagle0/util/hero_generation:loaded_hero",
],
)
@@ -131,7 +123,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/random_hero_generator",
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
"//src/main/scala/net/eagle0/eagle/model/state/date",
"//src/main/scala/net/eagle0/eagle/service:persisted_history",
],
@@ -140,9 +131,6 @@ scala_library(
scala_library(
name = "start_date",
srcs = ["StartDate.scala"],
visibility = [
"//src/main/scala/net/eagle0/util:__pkg__",
],
deps = [
"//src/main/scala/net/eagle0/eagle/model/state/date",
],
@@ -158,10 +146,10 @@ scala_library(
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
],
deps = [
":great_person",
":loaded_hero_conversion",
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:idable",
"//src/main/scala/net/eagle0/util/hero_generation:loaded_hero",
],
)
@@ -3,7 +3,6 @@ package net.eagle0.eagle.service.new_game_creation
import net.eagle0.common.TsvUtils
import net.eagle0.eagle.library.EagleInternalException
import net.eagle0.eagle.model.state.hero.{Gender, Profession}
import net.eagle0.util.hero_generation.LoadedHero
import scala.util.Try
@@ -8,9 +8,7 @@ import net.eagle0.eagle.internal.hero.Hero
import net.eagle0.eagle.internal.province.Province
import net.eagle0.eagle.library.EagleInternalException
import net.eagle0.eagle.library.util.{MapGenerator, PriceIndexUtils}
import net.eagle0.eagle.model.state.hero.concrete.HeroC
import net.eagle0.eagle.{BattalionId, FactionId, HeroId}
import net.eagle0.util.hero_generation.LoadedHero
import scalapb.json4s.JsonFormat
import scala.io.Source
@@ -46,11 +44,6 @@ object GameParametersUtils {
val fixedOthers: Vector[LoadedHero] = fixedHeroes.others
val pregeneratedOthers: Vector[LoadedHero] = pregeneratedHeroes.others
val allPregeneratedHeroes: Vector[HeroC] = (greatPeople.map(
_.greatPerson.hero
) ++ fixedOthers ++ pregeneratedOthers)
.map(LoadedHeroConversion.toHeroC)
val allPregeneratedHeroProtos: Vector[Hero] = (greatPeople.map(
_.greatPerson.hero
) ++ fixedOthers ++ pregeneratedOthers)
@@ -1,6 +1,37 @@
package net.eagle0.eagle.service.new_game_creation
import net.eagle0.util.hero_generation.LoadedHero
import net.eagle0.eagle.model.state.hero.{Gender, Profession}
import net.eagle0.eagle.{FactionId, HeroId}
import scala.util.hashing.MurmurHash3
case class LoadedHero(
name: String,
factionId: Option[FactionId] = None,
id: HeroId,
backstory: String = "",
strength: Int = 0,
agility: Int = 0,
constitution: Int = 0,
charisma: Int = 0,
wisdom: Int = 0,
integrity: Int = 0,
ambition: Int = 0,
gregariousness: Int = 0,
bravery: Int = 0,
vigor: Int = 0,
profession: Profession = Profession.NoProfession,
imagePath: String = "",
pronounGender: Gender = Gender.Other,
personalityWords: Vector[String] = Vector.empty
) {
def withId(newId: HeroId): LoadedHero = copy(id = newId)
def withFactionId(newFactionId: FactionId): LoadedHero =
copy(factionId = Some(newFactionId))
def nameTextId =
s"fhn_${MurmurHash3.stringHash(name).toHexString}"
def backstoryTextId = s"${name}_backstory 0"
}
case class GreatPerson(
hero: LoadedHero,
@@ -1,70 +1,16 @@
package net.eagle0.eagle.service.new_game_creation
import net.eagle0.eagle.common.hero_backstory_version.{
BackstoryVersion => BackstoryVersionProto
}
import net.eagle0.eagle.common.hero_backstory_version.BackstoryVersion
import net.eagle0.eagle.internal.hero.Hero
import net.eagle0.eagle.model.proto_converters.date.DateConverter
import net.eagle0.eagle.model.proto_converters.hero.{
GenderConverter,
ProfessionConverter
}
import net.eagle0.eagle.model.state.hero.backstory_version.BackstoryVersion
import net.eagle0.eagle.model.state.hero.concrete.HeroC
import net.eagle0.eagle.model.state.hero.{Gender, Profession}
import net.eagle0.eagle.{FactionId, HeroId}
import net.eagle0.util.hero_generation.LoadedHero
object LoadedHeroConversion {
def toHeroC(loadedHero: LoadedHero): HeroC = loadedHero match {
case LoadedHero(
name: String,
factionId: Option[FactionId],
id: HeroId,
backstory: String,
strength: Int,
agility: Int,
constitution: Int,
charisma: Int,
wisdom: Int,
integrity: Int,
ambition: Int,
gregariousness: Int,
bravery: Int,
vigor: Int,
profession: Profession,
imagePath: String,
pronounGender: Gender,
personalityWords: Vector[String]
) =>
HeroC(
name = name,
nameTextId = loadedHero.nameTextId,
factionId = factionId,
id = id,
backstoryVersions = Vector(
BackstoryVersion(
textId = loadedHero.backstoryTextId,
date = StartDate.startDate
)
),
strength = strength,
agility = agility,
constitution = constitution,
charisma = charisma,
wisdom = wisdom,
integrity = integrity,
ambition = ambition,
gregariousness = gregariousness,
bravery = bravery,
vigor = vigor,
profession = profession,
imagePath = imagePath,
pronounGender = pronounGender,
personalityWords = personalityWords
)
}
def toHeroProto(loadedHero: LoadedHero): Hero = loadedHero match {
case LoadedHero(
name: String,
@@ -92,7 +38,7 @@ object LoadedHeroConversion {
factionId = factionId,
id = id,
backstoryVersions = Vector(
BackstoryVersionProto(
BackstoryVersion(
textId = loadedHero.backstoryTextId,
date = DateConverter.toProto(StartDate.startDate)
)
@@ -47,11 +47,9 @@ import net.eagle0.eagle.model.action_result.generated_text_request.{
GeneratedTextRequestT
}
import net.eagle0.eagle.model.proto_converters.date.DateConverter
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
import net.eagle0.eagle.service.PersistedHistory
import net.eagle0.eagle.service.persistence.Persister
import net.eagle0.eagle.{FactionId, GameId, HeroId, ProvinceId}
import net.eagle0.util.hero_generation.LoadedHero
import scala.annotation.tailrec
@@ -190,15 +188,15 @@ object FixedNewGameCreation extends NewGameCreation {
val newHeroes = heroGenerator
.getHeroes(
hids,
StartDate.startDate
DateConverter.toProto(StartDate.startDate)
)
.get
.map { case HeroGenerationResponse(hero, nameInfo) =>
(
hero.copy(
factionId = Some(fid),
loyalty = 75,
roundIdJoined = Some(0)
hero.update(
_.factionId := fid,
_.loyalty := 75,
_.roundIdJoined := 0
),
NameGenerationRequestCreator.nameGenerationRequest(
heroId = hero.id,
@@ -209,7 +207,7 @@ object FixedNewGameCreation extends NewGameCreation {
}
(
ar.withHeroes(newHeroes.map(_._1).map(HeroConverter.toProto))
ar.withHeroes(newHeroes.map(_._1))
.withGeneratedTextRequests(
newHeroes.flatMap(_._2).map(convertNameRequest(_, playerCount))
),
@@ -557,7 +555,7 @@ object FixedNewGameCreation extends NewGameCreation {
.getHero(
random = fr,
hid = heroId,
date = StartDate.startDate
date = DateConverter.toProto(StartDate.startDate)
)
.map { acc :+ _ }
}
@@ -671,8 +669,9 @@ object FixedNewGameCreation extends NewGameCreation {
val fixedHeroesARwithGenerator = withFixedHeroesAR.continue {
case (ar, fr) =>
HeroGenerator(
pregeneratedHeroes =
pregeneratedHeroes.map(LoadedHeroConversion.toHeroC),
pregeneratedHeroes = pregeneratedHeroes.map(h =>
LoadedHeroConversion.toHeroProto(loadedHero = h)
),
excludingNames = withFixedHeroesAR.newValue.newHeroes.map(_.name),
functionalRandom = fr
).map((ar, _))
@@ -696,7 +695,7 @@ object FixedNewGameCreation extends NewGameCreation {
)
}
}.map(newHeroes =>
ar.withHeroes(newHeroes.map(_._1).map(HeroConverter.toProto))
ar.withHeroes(newHeroes.map(_._1))
.withGeneratedTextRequests(
newHeroes.flatMap(_._2).map { convertNameRequest(_, playerCount) }
)
@@ -12,7 +12,6 @@ import net.eagle0.eagle.library.util.IDable.{
mapifyProvinces
}
import net.eagle0.eagle.{BattalionId, FactionId, HeroId, ProvinceId}
import net.eagle0.util.hero_generation.LoadedHero
object StartGameActionResultUtils {
implicit class _StartGameActionResult(value: ActionResult) {
+17 -25
View File
@@ -25,31 +25,23 @@ scala_binary(
],
)
scala_binary(
name = "hero_library_generator",
srcs = ["HeroLibraryGenerator.scala"],
main_class = "net.eagle0.util.HeroLibraryGenerator",
resources = [
"//src/main/resources/net/eagle0/eagle:headshots",
"//src/main/resources/net/eagle0/eagle:heroes",
],
visibility = ["//visibility:public"],
deps = [
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/common/llm_integration:claude_service_impl",
"//src/main/scala/net/eagle0/common/llm_integration:external_text_generation_caller",
"//src/main/scala/net/eagle0/common/llm_integration:external_text_generation_service_impl",
"//src/main/scala/net/eagle0/common/llm_integration:open_ai_chat_completions_service_impl",
"//src/main/scala/net/eagle0/common/llm_integration:streaming_text_results",
"//src/main/scala/net/eagle0/eagle/library/actions/llm_prompt_generators:generator_utilities",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/hero_generation_response",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/random_hero_generator",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:fixed_heroes",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:great_person",
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:start_date",
],
)
#scala_binary(
# name = "hero_library_generator",
# srcs = ["HeroLibraryGenerator.scala"],
# main_class = "net.eagle0.util.HeroLibraryGenerator",
# resources = [
# "//src/main/resources/net/eagle0/eagle:headshots",
# "//src/main/resources/net/eagle0/eagle:heroes",
# ],
# visibility = ["//visibility:public"],
# deps = [
# "//src/main/scala/net/eagle0/common:functional_random",
# "//src/main/scala/net/eagle0/common/llm_integration:chat_gpt_service_impl",
# "//src/main/scala/net/eagle0/eagle/library/actions/llm_prompt_generators:generator_utilities",
# "//src/main/scala/net/eagle0/eagle/library/util/hero_generator/random_hero_generator",
# "//src/main/scala/net/eagle0/eagle/service/new_game_creation:fixed_heroes",
# ],
#)
scala_binary(
name = "name_list_checker",
@@ -1,72 +1,59 @@
package net.eagle0.util
import net.eagle0.common.SeededRandom
import net.eagle0.common.llm_integration.{
ClaudeServiceImpl,
ExternalTextGenerationCaller,
OpenAIChatCompletionsServiceImpl,
StreamingTextResults
}
import net.eagle0.common.llm_integration.OpenAIChatCompletionsServiceImpl
import net.eagle0.common.{RandomState, SeededRandom}
import net.eagle0.eagle.common.profession.Profession
import net.eagle0.eagle.internal.hero.Hero
import net.eagle0.eagle.common.gender.Gender
import net.eagle0.eagle.library.actions.llm_prompt_generators.GeneratorUtilities.{
academyName,
conjunction
}
import net.eagle0.eagle.library.util.hero_generator.hero_generation_response.HeroGenerationResponse
import net.eagle0.eagle.library.util.hero_generator.random_hero_generator.RandomHeroGenerator
import net.eagle0.eagle.model.state.hero.concrete.HeroC
import net.eagle0.eagle.model.state.hero.{Gender, Profession}
import net.eagle0.eagle.service.new_game_creation.{FixedHeroes, StartDate}
import net.eagle0.util.hero_generation.LoadedHero
import net.eagle0.eagle.service.new_game_creation.FixedHeroes
import java.io.FileOutputStream
import java.util.function.Consumer
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, ExecutionContextExecutor}
import scala.util.{Random, Success, Using}
import scala.concurrent.duration.Duration
import scala.util.{Random, Success}
object HeroLibraryGenerator {
private val random = new Random()
private val heroFilePath: String = "/net/eagle0/eagle/heroes.tsv"
val heroFilePath = "/net/eagle0/eagle/heroes.tsv"
private val generatedHeroesFilePath = "/net/eagle0/eagle/generated_heroes.tsv"
val heroCount = 10
private val openAICaller = new OpenAIChatCompletionsServiceImpl(
timeoutSeconds = 90,
defaultModelName = OpenAIChatCompletionsServiceImpl.gpt4
)
private val claudeCaller = new ClaudeServiceImpl(
timeoutSeconds = 90,
defaultModelName = ClaudeServiceImpl.model
)
implicit val ec: ExecutionContextExecutor = ExecutionContext.global
val random = new Random()
private def randomElement[T](elts: Seq[T]): T =
elts(random.nextInt(elts.length))
def genderString(gender: Gender): String = gender match {
case Gender.Female => "female"
case Gender.Male => "male"
case Gender.Other => "other"
case Gender.GENDER_UNKNOWN => "unknown"
case Gender.GENDER_FEMALE => "female"
case Gender.GENDER_MALE => "male"
case Gender.GENDER_OTHER => "other"
case Gender.Unrecognized(_) => ???
}
private def professionString(profession: Profession) = profession match {
case Profession.Mage => "mage"
case Profession.Necromancer => "necromancer"
case Profession.Engineer => "engineer"
case Profession.Paladin => "paladin"
case Profession.Ranger => "ranger"
case Profession.Champion => "champion"
case Profession.NoProfession => "noProfession"
case Profession.UNKNOWN_PROFESSION => "unknownProfession"
case Profession.MAGE => "mage"
case Profession.NECROMANCER => "necromancer"
case Profession.ENGINEER => "engineer"
case Profession.PALADIN => "paladin"
case Profession.RANGER => "ranger"
case Profession.CHAMPION => "champion"
case Profession.NO_PROFESSION => "noProfession"
case Profession.Unrecognized(_) => ???
}
private def heroTsvString(hero: LoadedHero): String = {
private def heroTsvString(hero: Hero): String = {
val sum =
hero.strength + hero.agility + hero.constitution + hero.charisma + hero.wisdom
val personalityWords = hero.personalityWords.mkString(", ")
s"${hero.name}\t${hero.strength}\t${hero.agility}\t${hero.constitution}\t${hero.charisma}\t${hero.wisdom}\t$sum\t${hero.integrity}\t${hero.ambition}\t${hero.gregariousness}\t${hero.bravery}\t${hero.vigor}\t${professionString(
s"${hero.name}\t${hero.strength}\t${hero.agility}\t${hero.constitution}\t${hero.charisma}\t${hero.wisdom}\t$sum\t${hero.integrity}\t${hero.ambition}\t${hero.gregariousness}\t${hero.bravery}\t${hero.vigor.toInt}\t${professionString(
hero.profession
)}\tFALSE\t${hero.imagePath}\t${genderString(hero.pronounGender)}\t${hero.backstory}\t$personalityWords"
}
@@ -84,19 +71,19 @@ object HeroLibraryGenerator {
else if (value <= 15) Some(negativeWord)
else None
private def extraPersonalityWords(hero: HeroC): Vector[String] = (
private def extraPersonalityWords(hero: Hero): Vector[String] = (
personalityWord(hero.strength, "strong", "feeble") ++
personalityWord(hero.agility, "agile", "clumsy") ++
personalityWord(hero.charisma, "charismatic", "dull") ++
personalityWord(hero.wisdom, "intelligent", "dumb")
).toVector
private def personalityString(hero: HeroC): String =
private def personalityString(hero: Hero): String =
conjunction(
randomElement(hero.personalityWords) +: extraPersonalityWords(hero)
).get
private def heroLine(hero: HeroC): String =
private def heroLine(hero: Hero): String =
s"(${genderString(hero.pronounGender)}) (${professionString(
hero.profession
)}) (${personalityString(hero)}) ${hero.name}"
@@ -106,17 +93,12 @@ object HeroLibraryGenerator {
val randomHeroes = FixedHeroes.parse(generatedHeroesFilePath).get
val usedHeroes =
heroes.greatPeople.map(
_.greatPerson.hero
) ++ heroes.others ++ randomHeroes.greatPeople.map(
_.greatPerson.hero
) ++ randomHeroes.others
heroes.greatPeople ++ heroes.others ++ randomHeroes.greatPeople ++ randomHeroes.others
val usedNames = usedHeroes.map(_.name)
val usedImagePaths = usedHeroes.map(_.imagePath)
val heroGenerator = new RandomHeroGenerator(
functionalRandom = SeededRandom(seed = new Random().nextLong()),
excludingNames = usedNames,
excludingImagePaths = usedImagePaths
)
@@ -126,113 +108,57 @@ object HeroLibraryGenerator {
)
randomHeroes.others.foreach { hero => println(heroTsvString(hero)) }
val factionHeadNames = conjunction(
heroes.greatPeople.map(_.greatPerson.hero.name)
).get
val factionDescriptions =
s"Possible leaders of the factions are: $factionHeadNames."
val promptString =
s"""This is a fantasy setting with a medieval technology level. Multiple factions are engaged in a civil war for
|control of the kingdom after a mysterious stranger from afar, a powerful mage called The Eagle, invaded the realm.
|King Bregos Fyar's right-hand man, Ikhaan Tarn, betrayed the King to join with The Eagle.
|Several other factions arose under powerful heroes, allied with neither the King nor The Eagle.
|$factionDescriptions
|$academyName, where mages and other practitioners of arcane magics are trained and practice, has remained neutral,
|though some of its members take part in the war.
|
|The following are the names of $heroCount heroes that are not yet affiliated with any faction. Each name
|is preceded by their gender, their profession, if any, and a few notes about their personality or traits,
|all in parentheses. Make up a one-paragraph backstory, no more than 120 words each, for each hero. Make
|the backstory consistent with the background given above. The backstory may refer to the any of the factions
|or their leaders or to other heroes listed here, but it does not need to. Put the backstories in the same
|order as the names given here, but do not number them. Do not include any other context beyond the in-world
|text of the response itself.""".stripMargin
.replaceAll("\n", " ")
val factionHeadNames = conjunction(heroes.greatPeople.map(_.name)).get
val _ = Using(
new FileOutputStream(
"/tmp/new_generated_heroes" + random.nextLong().toHexString + ".tsv"
)
) { outputStream =>
for (_ <- 1 to 500) {
val newHeroesState =
(1 to heroCount).toVector.foldLeft(Vector[HeroGenerationResponse]()) {
case (acc, hid) =>
val heroState =
heroGenerator
.getHero(hid = 0, date = StartDate.startDate)
.getOrElse(
throw new IllegalStateException(
s"Failed to generate hero with id $hid"
)
)
acc :+ heroState
}
val chatGptCaller = new OpenAIChatCompletionsServiceImpl(
timeoutSeconds = 90,
defaultModelName = OpenAIChatCompletionsServiceImpl.gpt4
)
val newHeroes = newHeroesState.map(_.hero)
// val nameRequests = newHeroesState.map(_.nameInfo)
val caller = new ExternalTextGenerationCaller(
timeoutSeconds = 90,
serviceImpl = claudeCaller
for (_ <- 1 to 50) {
val newHeroesState = (1 to heroCount).toVector.foldLeft(
RandomState[Vector[Hero]](
newValue = Vector(),
SeededRandom(seed = new Random().nextLong())
)
) { case (RandomState(acc, nextRandom), _) =>
val heroState = heroGenerator.getHero(random = nextRandom, hid = 0)
heroState.map(h => acc :+ h)
}
var allText = ""
val consumer: Consumer[StreamingTextResults] = {
case StreamingTextResults(streamId, textValue, completed) =>
if (completed) {
val backstories = allText.split("\n").filterNot(_.isEmpty)
if (backstories.length != newHeroes.length) {
println(
s"Expected ${newHeroes.length} backstories, but got ${backstories.length}"
)
} else {
val heroesWithBackstories =
newHeroes.zip(backstories).map { case (hero, backstory) =>
LoadedHero(
name = hero.name,
id = hero.id,
factionId = None,
backstory = backstory,
strength = hero.strength,
agility = hero.agility,
constitution = hero.constitution,
charisma = hero.charisma,
wisdom = hero.wisdom,
integrity = hero.integrity,
ambition = hero.ambition,
gregariousness = hero.gregariousness,
bravery = hero.bravery,
vigor = hero.vigor.toInt,
profession = hero.profession,
imagePath = hero.imagePath,
pronounGender = hero.pronounGender,
personalityWords = hero.personalityWords
)
}
val newHeroes = newHeroesState.newValue
val tsv =
heroesWithBackstories
.map(heroTsvString)
.mkString("\r\n") + "\r\n"
val promptString =
"This is a fantasy setting with a medieval technology level. Multiple factions are engaged in a civil war for " +
"control of the kingdom after a mysterious stranger from afar, a powerful mage called The Eagle, invaded the " +
"realm. King Bregos Fyar's right-hand man, Ikhaan Tarn, betrayed the King to join with The Eagle. Several other " +
"factions arose under powerful heroes, allied with neither the King nor The Eagle. " +
s"Among the great heroes that might be leading factions are $factionHeadNames. " +
s"$academyName, where mages " +
"and other practitioners of arcane magics are trained and practice, has remained neutral, though some of its " +
"members take part in the war.\n\n" +
s"The following are the names of $heroCount heroes that are not yet affiliated with any faction. Each name " +
"is preceded by their gender, their profession, if any, and a few notes about their personality or traits, " +
"all in parentheses. Make up a one-paragraph backstory, no more than 120 words each, for each hero. Make " +
"the backstory consistent with the " +
"background given above. Put the backstories in the same order " +
"as the names given here, but do not number them. Do not include any other context beyond the in-world text " +
"of the response itself.\n\n" +
newHeroes.map(heroLine).mkString("\n")
val future =
chatGptCaller.getCompletion(promptString, 1).map { response =>
val backstories = response.value.split("\n").filterNot(_.isEmpty)
val heroesWithBackstories =
newHeroes.zip(backstories).map { case (hero, backstory) =>
hero.withBackstory(backstory)
}
outputStream.write(tsv.getBytes("UTF-8"))
outputStream.flush()
println(s"Stream completed for $streamId")
}
} else
allText += textValue
heroesWithBackstories.map(heroTsvString).mkString("\n")
}
val promptWithNames =
promptString ++ newHeroes.map(heroLine).mkString("\n")
val future = caller.streamCompletion(promptWithNames, None, consumer, 0)
Await.ready(future, Duration.Inf).value match {
case Some(Success(value)) => println(value)
case _ => throw new IllegalStateException("Failure!")
}
Await.ready(future, Duration.Inf).value match {
case Some(Success(value)) => println(value)
case _ => throw new IllegalStateException("Failure!")
}
}
}
@@ -1,18 +0,0 @@
load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library")
scala_library(
name = "loaded_hero",
srcs = ["LoadedHero.scala"],
visibility = [
"//src/main/scala/net/eagle0:__subpackages__",
"//src/test/scala/net/eagle0/eagle:__subpackages__",
],
exports = [
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
],
deps = [
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
"//src/main/scala/net/eagle0/eagle/model/state/hero:profession",
],
)
@@ -1,34 +0,0 @@
package net.eagle0.util.hero_generation
import net.eagle0.eagle.{FactionId, HeroId}
import net.eagle0.eagle.model.state.hero.{Gender, Profession}
import scala.util.hashing.MurmurHash3
case class LoadedHero(
name: String,
factionId: Option[FactionId] = None,
id: HeroId,
backstory: String = "",
strength: Int = 0,
agility: Int = 0,
constitution: Int = 0,
charisma: Int = 0,
wisdom: Int = 0,
integrity: Int = 0,
ambition: Int = 0,
gregariousness: Int = 0,
bravery: Int = 0,
vigor: Int = 0,
profession: Profession = Profession.NoProfession,
imagePath: String = "",
pronounGender: Gender = Gender.Other,
personalityWords: Vector[String] = Vector.empty
) {
def withId(newId: HeroId): LoadedHero = copy(id = newId)
def withFactionId(newFactionId: FactionId): LoadedHero =
copy(factionId = Some(newFactionId))
def nameTextId =
s"fhn_${MurmurHash3.stringHash(name).toHexString}"
def backstoryTextId = s"${name}_backstory 0"
}
@@ -12,7 +12,6 @@ scala_test(
"//src/main/scala/net/eagle0/eagle/library:engine_impl",
"//src/main/scala/net/eagle0/eagle/library:game_history",
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator",
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
"//src/main/scala/net/eagle0/eagle/service:in_memory_history",
"@maven//:org_scalamock_scalamock_2_13",
],
@@ -24,7 +24,6 @@ scala_test(
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:loaded_hero_conversion",
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_resolution",
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_update_receiver",
"//src/main/scala/net/eagle0/util/hero_generation:loaded_hero",
"//src/test/scala/net/eagle0/common:proto_matchers",
"@maven//:org_scalamock_scalamock_2_13",
],
@@ -15,11 +15,11 @@ import net.eagle0.eagle.service.new_game_creation.{
ExpandedGameParameters,
GreatPerson,
GreatPersonWithBackstory,
LoadedHero,
LoadedHeroConversion,
NewGameCreation
}
import net.eagle0.eagle.service.persistence.Persister
import net.eagle0.util.hero_generation.LoadedHero
import org.scalamock.scalatest.MockFactory
import org.scalatest.BeforeAndAfterEach
import org.scalatest.flatspec.AnyFlatSpec
@@ -1,7 +1,6 @@
package net.eagle0.eagle.service.new_game_creation
import net.eagle0.eagle.model.state.hero.{Gender, Profession}
import net.eagle0.util.hero_generation.LoadedHero
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers