Add Alliance and BreakAlliance diplomacy options (#3076)

* add alliance and break alliance options

* tests for alliance and break alliance availability

* alliance offer and resolution messages

* more prompt generators

* fix ActionResultApplier

* alliance offer / break availability

* finish the helpers

* enable Alliance and Break Alliance in client

* ResolveAllianceOfferCommand

* ResolveBreakAllianceCommand

* update command choice helpers for new commands

* fix imports after rebase

* fix client notifications

* need resolve selectors

* resolve command selectors for Alliance and BreakAlliance

* hook up missing options

* hook up all the buttons

* exception text too

* fix the helper and reenable settings

* bad rebase

* redundant braces

* bad rebase

* Revert "bad rebase"

This reverts commit d4c67ba5af90c6f6f980e276f430ae51998e1c1d.

* bad rebase

* unneeded change

* bad rebase

* add one more test

* small simplification
This commit is contained in:
2023-11-26 13:33:54 -08:00
committed by GitHub
parent b51ee96d45
commit 72a2ff6b6a
36 changed files with 7720 additions and 837 deletions
@@ -72,7 +72,9 @@
<Compile Include="Assets/Eagle/Notifications/ARNNotifications/OutlawApprehendedDetailsNotificationGenerator.cs" />
<Compile Include="Assets/Shardok/HexMetrics.cs" />
<Compile Include="Assets/Eagle/CommandSelectors/ResolveInvitationCommandSelector.cs" />
<Compile Include="Assets/Eagle/CommandSelectors/ResolveBreakAllianceCommandSelector.cs" />
<Compile Include="Assets/Modern UI Pack/Scripts/Slider/SliderManager.cs" />
<Compile Include="Assets/Eagle/CommandSelectors/ResolveAllianceCommandSelector.cs" />
<Compile Include="Assets/Eagle/CommandSelectors/OrganizeTroopsCommandSelector.cs" />
<Compile Include="Assets/Eagle/PanelPositions.cs" />
<Compile Include="Assets/Eagle/Notifications/NotificationGenerator.cs" />
@@ -99,6 +101,7 @@
<Compile Include="Assets/Eagle/Table Rows/CustomUnitRowController.cs" />
<Compile Include="Assets/Modern UI Pack/Scripts/Modal Window/ModalWindowManagerEditor.cs" />
<Compile Include="Assets/GUI Pro Kit Fantasy RPG/Scripts/CtrPanel.cs" />
<Compile Include="Assets/Eagle/Notifications/ARNNotifications/AllianceRejectedNotificationGenerator.cs" />
<Compile Include="Assets/Eagle/Notifications/ARNNotifications/InvitationAcceptedDetailsNotificationGenerator.cs" />
<Compile Include="Assets/Eagle/CommandSelectors/HandleRiotGiveCommandSelector.cs" />
<Compile Include="Assets/Eagle/Notifications/ProvinceEventsNotificationGenerator.cs" />
@@ -143,6 +146,7 @@
<Compile Include="Assets/Shardok/TurnHistoryPanelController.cs" />
<Compile Include="Assets/Eagle/CommandSelectors/ResolveRansomOfferCommandSelector.cs" />
<Compile Include="Assets/Eagle/Notifications/ARNNotifications/HeroDepartureDetailsNotificationGenerator.cs" />
<Compile Include="Assets/Eagle/Notifications/ARNNotifications/BreakAllianceAcceptedNotificationGenerator.cs" />
<Compile Include="Assets/Modern UI Pack/Scripts/Progress Bar/PBFilled.cs" />
<Compile Include="Assets/Eagle/Notifications/ArmyWithdrewNotificationGenerator.cs" />
<Compile Include="Assets/ProtoExtensions.cs" />
@@ -176,6 +180,7 @@
<Compile Include="Assets/Eagle/MapController.cs" />
<Compile Include="Assets/Modern UI Pack/Scripts/Button/ButtonManagerBasic.cs" />
<Compile Include="Assets/common/DisclosureTriangle.cs" />
<Compile Include="Assets/Eagle/Notifications/ARNNotifications/BreakAllianceAmbassadorImprisonedNotificationGenerator.cs" />
<Compile Include="Assets/Modern UI Pack/Scripts/Dropdown/DropdownMultiSelect.cs" />
<Compile Include="Assets/Eagle/Table Rows/MovingArmyTableRow.cs" />
<Compile Include="Assets/Modern UI Pack/Scripts/Horizontal Selector/HorizontalSelectorEditor.cs" />
@@ -194,6 +199,7 @@
<Compile Include="Assets/Modern UI Pack/Scripts/Fixes/UIElementInFront.cs" />
<Compile Include="Assets/Modern UI Pack/Scripts/UI Manager/UIManagerButtonEditor.cs" />
<Compile Include="Assets/Eagle/PopupPanelController.cs" />
<Compile Include="Assets/Eagle/Notifications/ARNNotifications/AllianceAmbassadorImprisonedNotificationGenerator.cs" />
<Compile Include="Assets/Eagle/CommandSelectors/StartEpidemicCommandSelector.cs" />
<Compile Include="Assets/ConnectionHandler/WaitingGameItem.cs" />
<Compile Include="Assets/Eagle/CommandSelectors/RansomCommandSelector.cs" />
@@ -241,6 +247,7 @@
<Compile Include="Assets/Eagle/Table Rows/OrganizeTroopsComponentRow.cs" />
<Compile Include="Assets/Modern UI Pack/Scripts/Notification/NotificationManagerEditor.cs" />
<Compile Include="Assets/Eagle/Notifications/ARNNotifications/RansomRejectedDetailsNotificationGenerator.cs" />
<Compile Include="Assets/Eagle/Notifications/ARNNotifications/AllianceAcceptedNotificationGenerator.cs" />
<Compile Include="Assets/Modern UI Pack/Scripts/UI Manager/UIManagerDropdown.cs" />
<Compile Include="Assets/Modern UI Pack/Scripts/Button/ButtonManagerBasicEditor.cs" />
<Compile Include="Assets/ConnectionHandler/RunningGameItem.cs" />
@@ -29,9 +29,11 @@ namespace eagle {
public TMP_Text costText;
public Toggle truceToggle;
public Toggle allianceToggle;
public Toggle inviteToggle;
public TMP_Text truceLabel;
public TMP_Text allianceLabel;
public TMP_Text inviteLabel;
public override AvailableCommand.SealedValueOneofCase CommandType =>
@@ -42,8 +44,12 @@ namespace eagle {
switch (SelectedDiplomacyOptionType) {
case DiplomacyOption.SealedValueOneofCase.TruceOption:
return "Commit Truce Offer";
case DiplomacyOption.SealedValueOneofCase.AllianceOption:
return "Commit Alliance Offer";
case DiplomacyOption.SealedValueOneofCase.BreakAllianceOption:
return "Commit Break Alliance";
case DiplomacyOption.SealedValueOneofCase.InvitationOption:
return "Commit Invite";
return "Commit Invitation";
default: throw new KeyNotFoundException("Unknown diplomacy option");
}
}
@@ -72,10 +78,14 @@ namespace eagle {
private DiplomacyOption.SealedValueOneofCase SelectedDiplomacyOptionType {
get {
if (truceToggle.isOn) return DiplomacyOption.SealedValueOneofCase.TruceOption;
else if (inviteToggle.isOn)
return DiplomacyOption.SealedValueOneofCase.InvitationOption;
else
throw new KeyNotFoundException("Unknown diplomacy option");
if (allianceToggle.isOn) {
if (HasBreakAlliance())
return DiplomacyOption.SealedValueOneofCase.BreakAllianceOption;
else
return DiplomacyOption.SealedValueOneofCase.AllianceOption;
}
if (inviteToggle.isOn) return DiplomacyOption.SealedValueOneofCase.InvitationOption;
throw new KeyNotFoundException("Unknown diplomacy option");
}
}
@@ -101,6 +111,12 @@ namespace eagle {
case DiplomacyOption.SealedValueOneofCase.TruceOption:
return option.TruceOption.TargetFactionId;
case DiplomacyOption.SealedValueOneofCase.AllianceOption:
return option.AllianceOption.TargetFactionId;
case DiplomacyOption.SealedValueOneofCase.BreakAllianceOption:
return option.BreakAllianceOption.TargetFactionId;
case DiplomacyOption.SealedValueOneofCase.InvitationOption:
return option.InvitationOption.TargetFactionId;
@@ -113,6 +129,12 @@ namespace eagle {
case DiplomacyOption.SealedValueOneofCase.TruceOption:
return option.TruceOption.GoldCost;
case DiplomacyOption.SealedValueOneofCase.AllianceOption:
return option.AllianceOption.GoldCost;
case DiplomacyOption.SealedValueOneofCase.BreakAllianceOption:
return option.BreakAllianceOption.GoldCost;
case DiplomacyOption.SealedValueOneofCase.InvitationOption:
return option.InvitationOption.GoldCost;
@@ -159,13 +181,33 @@ namespace eagle {
public void TargetDropdownChanged(int value) { SetOptions(); }
private bool HasInvitation() {
return DiplomacyCommand.Options.Any(
opt => opt.InvitationOption?.TargetFactionId == SelectedFactionId);
}
private bool HasAlliance() {
return DiplomacyCommand.Options.Any(
opt => opt.AllianceOption?.TargetFactionId == SelectedFactionId);
}
private bool HasBreakAlliance() {
return DiplomacyCommand.Options.Any(
opt => opt.BreakAllianceOption?.TargetFactionId == SelectedFactionId);
}
private void SetOptions() {
var targetFid = SelectedFactionId;
var enableInvite = DiplomacyCommand.Options.Any(
opt => opt.InvitationOption != null && TargetFactionId(opt) == targetFid);
var enableInvite = HasInvitation();
if (!enableInvite) truceToggle.isOn = true;
ConditionallyEnable(inviteToggle, inviteLabel, enableInvite);
if (HasBreakAlliance()) {
allianceLabel.text = "Break Alliance";
ConditionallyEnable(allianceToggle, allianceLabel, true);
} else {
allianceLabel.text = "Alliance";
ConditionallyEnable(allianceToggle, allianceLabel, HasAlliance());
}
ConditionallyEnable(truceToggle, truceLabel, true);
costText.text = SelectedOptionCost.ToString();
}
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Linq;
using Net.Eagle0.Eagle.Common;
using Net.Eagle0.Eagle.Api;
namespace eagle {
using ProvinceId = System.Int32;
public class ResolveAllianceCommandSelector : ResolveDiplomacyCommandSelector {
protected override IList<DiplomacyOffer> Offers =>
_availableCommand?.ResolveAllianceOfferCommand?.Offers.ToList() ??
new List<DiplomacyOffer>();
protected override string MainText(DiplomacyOffer offer) {
var messengerName = Model.Heroes[MessengerHeroId].Name;
return $"{Model.FactionName(OriginatingFactionId)}'s representative {messengerName}:\n{OfferMessage}";
}
protected override string TitleText => "Alliance Proposed";
public override SelectedCommand selectedCommand(DiplomacyOfferStatus resolution) =>
new SelectedCommand {
ResolveAllianceOfferCommand =
new ResolveAllianceOfferSelectedCommand {
Resolution = resolution,
OriginatingFactionId = OriginatingFactionId
}
};
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: abb9f435ac4f4fb89019ed39ccaef00f
timeCreated: 1700614421
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Linq;
using Net.Eagle0.Eagle.Common;
using Net.Eagle0.Eagle.Api;
namespace eagle {
using ProvinceId = System.Int32;
public class ResolveBreakAllianceCommandSelector : ResolveDiplomacyCommandSelector {
protected override IList<DiplomacyOffer> Offers =>
_availableCommand?.ResolveBreakAllianceCommand?.Offers.ToList() ??
new List<DiplomacyOffer>();
protected override string MainText(DiplomacyOffer offer) {
var messengerName = Model.Heroes[MessengerHeroId].Name;
return $"{Model.FactionName(OriginatingFactionId)}'s representative {messengerName}:\n{OfferMessage}";
}
protected override string TitleText => "Alliance Broken";
public override SelectedCommand selectedCommand(DiplomacyOfferStatus resolution) =>
new SelectedCommand {
ResolveBreakCommand =
new ResolveBreakAllianceSelectedCommand {
Resolution = resolution,
OriginatingFactionId = OriginatingFactionId
}
};
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dbf970cd53634619b63f5c943d60375b
timeCreated: 1700614555
@@ -22,9 +22,10 @@ namespace eagle {
protected abstract string MainText(DiplomacyOffer offer);
protected abstract string TitleText { get; }
protected abstract IList<DiplomacyOffer> Offers { get; }
protected abstract DiplomacyOffer Offer { get; }
protected abstract int OfferCount { get; }
private DiplomacyOffer Offer => Offers[DisplayedIndex];
private int OfferCount => Offers.Count;
protected HeroId MessengerHeroId => Offer.MessengerHeroId;
protected FactionId OriginatingFactionId => Offer.OriginatingFactionId;
protected string OfferMessage => Offer.OfferMessage;
@@ -40,14 +41,12 @@ namespace eagle {
var offeringFactionHead = Model.Heroes[offeringFaction.FactionHeadId];
PopupInfos =
_availableCommand.ResolveTruceOfferCommand.Offers
.Select(off => new PopupInfo {
titleText = TitleText,
mainText = MainText(off),
affectedProvinceIds = new List<ProvinceId>(),
displayedHeroes = new List<HeroView> { offeringFactionHead }
})
.ToList();
Offers.Select(off => new PopupInfo {
titleText = TitleText,
mainText = MainText(off),
affectedProvinceIds = new List<ProvinceId>(),
displayedHeroes = new List<HeroView> { offeringFactionHead }
}).ToList();
}
SetUpPanel();
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Net.Eagle0.Eagle.Api;
using Net.Eagle0.Eagle.Common;
@@ -6,10 +8,9 @@ namespace eagle {
using ProvinceId = Int32;
public class ResolveInvitationCommandSelector : ResolveDiplomacyCommandSelector {
protected override DiplomacyOffer Offer =>
_availableCommand.ResolveInvitationCommand.Invitations[DisplayedIndex];
protected override int OfferCount =>
_availableCommand?.ResolveInvitationCommand?.Invitations.Count ?? 0;
protected override IList<DiplomacyOffer> Offers =>
_availableCommand?.ResolveInvitationCommand?.Invitations.ToList() ??
new List<DiplomacyOffer>();
protected override string MainText(DiplomacyOffer offer) =>
$"{Model.Heroes[MessengerHeroId].Name} brings an invitation to join {Model.FactionName(OriginatingFactionId)}.\n{OfferMessage}";
@@ -20,9 +21,7 @@ namespace eagle {
ResolveInvitationCommand =
new ResolveInvitationSelectedCommand {
Resolution = resolution,
OriginatingFactionId = AvailableCommand.ResolveTruceOfferCommand
.Offers[DisplayedIndex]
.OriginatingFactionId
OriginatingFactionId = OriginatingFactionId
}
};
}
@@ -1,14 +1,15 @@
using Net.Eagle0.Eagle.Common;
using System.Collections.Generic;
using System.Linq;
using Net.Eagle0.Eagle.Common;
using Net.Eagle0.Eagle.Api;
namespace eagle {
using ProvinceId = System.Int32;
public class ResolveTruceCommandSelector : ResolveDiplomacyCommandSelector {
protected override DiplomacyOffer Offer =>
_availableCommand.ResolveTruceOfferCommand.Offers[DisplayedIndex];
protected override int OfferCount =>
_availableCommand?.ResolveTruceOfferCommand?.Offers.Count ?? 0;
protected override IList<DiplomacyOffer> Offers =>
_availableCommand?.ResolveTruceOfferCommand?.Offers.ToList() ??
new List<DiplomacyOffer>();
protected override string MainText(DiplomacyOffer offer) {
var messengerName = Model.Heroes[MessengerHeroId].Name;
@@ -21,9 +22,7 @@ namespace eagle {
ResolveTruceOfferCommand =
new ResolveTruceOfferSelectedCommand {
Resolution = resolution,
OriginatingFactionId = AvailableCommand.ResolveTruceOfferCommand
.Offers[DisplayedIndex]
.OriginatingFactionId
OriginatingFactionId = OriginatingFactionId
}
};
}
@@ -50,6 +50,8 @@ namespace eagle {
public PleaseRecruitMeCommandSelector pleaseRecruitMeCommandSelector;
public ResolveTruceCommandSelector resolveTruceCommandSelector;
public ResolveAllianceCommandSelector resolveAllianceCommandSelector;
public ResolveBreakAllianceCommandSelector resolveBreakAllianceCommandSelector;
public ResolveInvitationCommandSelector resolveInvitationCommandSelector;
public ResolveRansomOfferCommandSelector resolveRansomOfferCommandSelector;
public CommandWarningPanelController commandWarningPanelController;
@@ -158,6 +160,9 @@ namespace eagle {
_commandPanelController.Model = Model;
resolveTruceCommandSelector.submissionAction = sc => PostCommittedCommand(0, sc);
resolveAllianceCommandSelector.submissionAction = sc => PostCommittedCommand(0, sc);
resolveBreakAllianceCommandSelector.submissionAction = sc =>
PostCommittedCommand(0, sc);
resolveInvitationCommandSelector.submissionAction = sc => PostCommittedCommand(0, sc);
resolveRansomOfferCommandSelector.submissionAction = sc => PostCommittedCommand(0, sc);
pleaseRecruitMeCommandSelector.submissionAction = sc => PostCommittedCommand(0, sc);
@@ -379,13 +384,28 @@ namespace eagle {
}
throw new InvalidOperationException(str);
}
switch (Model.AvailableCommandsByProvince.First()
.Value.Commands.First()
.SealedValueCase) {
var firstCommand = Model.AvailableCommandsByProvince.First().Value.Commands.First();
switch (firstCommand.SealedValueCase) {
case AvailableCommand.SealedValueOneofCase.ResolveTruceOfferCommand:
resolveTruceCommandSelector.Model = Model;
resolveTruceCommandSelector.AvailableCommand =
Model.AvailableCommandsByProvince.First().Value.Commands.First();
resolveTruceCommandSelector.AvailableCommand = firstCommand;
_commandPanelController.SetAvailableCommandAndSelector(null, null);
mapController.TargetedProvinces = new List<ProvinceId>();
mapController.SelectedProvinceId = null;
break;
case AvailableCommand.SealedValueOneofCase.ResolveAllianceOfferCommand:
resolveAllianceCommandSelector.Model = Model;
resolveAllianceCommandSelector.AvailableCommand = firstCommand;
_commandPanelController.SetAvailableCommandAndSelector(null, null);
mapController.TargetedProvinces = new List<ProvinceId>();
mapController.SelectedProvinceId = null;
break;
case AvailableCommand.SealedValueOneofCase.ResolveBreakAllianceCommand:
resolveBreakAllianceCommandSelector.Model = Model;
resolveBreakAllianceCommandSelector.AvailableCommand = firstCommand;
_commandPanelController.SetAvailableCommandAndSelector(null, null);
mapController.TargetedProvinces = new List<ProvinceId>();
mapController.SelectedProvinceId = null;
@@ -393,8 +413,7 @@ namespace eagle {
case AvailableCommand.SealedValueOneofCase.ResolveInvitationCommand:
resolveInvitationCommandSelector.Model = Model;
resolveInvitationCommandSelector.AvailableCommand =
Model.AvailableCommandsByProvince.First().Value.Commands.First();
resolveInvitationCommandSelector.AvailableCommand = firstCommand;
_commandPanelController.SetAvailableCommandAndSelector(null, null);
mapController.TargetedProvinces = new List<ProvinceId>();
mapController.SelectedProvinceId = null;
@@ -402,8 +421,7 @@ namespace eagle {
case AvailableCommand.SealedValueOneofCase.ResolveRansomOfferCommand:
resolveRansomOfferCommandSelector.Model = Model;
resolveRansomOfferCommandSelector.AvailableCommand =
Model.AvailableCommandsByProvince.First().Value.Commands.First();
resolveRansomOfferCommandSelector.AvailableCommand = firstCommand;
_commandPanelController.SetAvailableCommandAndSelector(null, null);
mapController.TargetedProvinces = new List<ProvinceId>();
mapController.SelectedProvinceId = null;
@@ -411,8 +429,7 @@ namespace eagle {
case AvailableCommand.SealedValueOneofCase.PleaseRecruitMeCommand:
pleaseRecruitMeCommandSelector.Model = Model;
pleaseRecruitMeCommandSelector.AvailableCommand =
Model.AvailableCommandsByProvince.First().Value.Commands.First();
pleaseRecruitMeCommandSelector.AvailableCommand = firstCommand;
_commandPanelController.SetAvailableCommandAndSelector(null, null);
mapController.TargetedProvinces = new List<ProvinceId>();
mapController.SelectedProvinceId = null;
@@ -21,6 +21,10 @@ namespace eagle.Notifications {
TruceAmbassadorImprisonedDetailsNotificationGenerator.Generator },
{ InvitationAmbassadorImprisonedDetails,
InvitationAmbassadorImprisonedDetailsNotificationGenerator.Generator },
{ AllianceAmbassadorImprisonedDetails,
AllianceAmbassadorImprisonedDetailsNotificationGenerator.Generator },
{ BreakAllianceAmbassadorImprisonedDetails,
BreakAllianceAmbassadorImprisonedDetailsNotificationGenerator.Generator },
{ CapturedHeroExecutedDetails,
CapturedHeroExecutedDetailsNotificationGenerator.Generator },
{ CapturedHeroExiledDetails,
@@ -56,6 +60,12 @@ namespace eagle.Notifications {
SwearBrotherhoodDetailsNotificationGenerator.Generator },
{ TruceAcceptedDetails, TruceAcceptedDetailsNotificationGenerator.Generator },
{ TruceRejectedDetails, TruceRejectedDetailsNotificationGenerator.Generator },
{ AllianceAcceptedDetails,
AllianceAcceptedDetailsNotificationGenerator.Generator },
{ AllianceRejectedDetails,
AllianceRejectedDetailsNotificationGenerator.Generator },
{ BreakAllianceAcceptedDetails,
BreakAllianceAcceptedNotificationGenerator.Generator },
{ VassalExiledDetails, VassalExiledDetailsNotificationGenerator.Generator },
{ WithdrewForTruce, WithdrewForTruceDetailsNotificationGenerator.Generator }
};
@@ -0,0 +1,57 @@
using System.Collections.Generic;
using Net.Eagle0.Eagle.Api;
namespace eagle.Notifications.ARNNotifications {
using ProvinceId = System.Int32;
using ARN = Net.Eagle0.Eagle.Common.Notification;
public static class AllianceAcceptedDetailsNotificationGenerator {
public static readonly ARNNotificationGenerator Generator = GenerateNotifications;
private static IEnumerable<Notification> GenerateNotifications(
Net.Eagle0.Eagle.Common.Notification notification,
GameModel currentModel) {
var playerId = currentModel.PlayerId;
var notif = notification.Details.AllianceAcceptedDetails;
var offeringFactionId = notif.OfferingFactionId;
var acceptingFactionId = notif.TargetFactionId;
var llmMessage = notification.LlmMessage;
var affectedProvinces = currentModel.ProvincesForFaction(acceptingFactionId);
affectedProvinces.AddRange(currentModel.ProvincesForFaction(offeringFactionId));
var ambassador = currentModel.Heroes[notif.AmbassadorHeroId];
var offeringFactionHeadId =
currentModel.MaybeDestroyedFaction(offeringFactionId).FactionHeadId;
var offeringFactionHead = currentModel.Heroes[offeringFactionHeadId];
var acceptingFactionHeadId =
currentModel.MaybeDestroyedFaction(acceptingFactionId).FactionHeadId;
var acceptingFactionHead = currentModel.Heroes[acceptingFactionHeadId];
if (playerId.HasValue && playerId.Value == acceptingFactionId) {
yield return new Notification(
title: "Alliance Accepted",
text:
$"You have formed an alliance with {currentModel.FactionName(offeringFactionId)}.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { offeringFactionHead, ambassador});
} else if (playerId.HasValue && playerId.Value == offeringFactionId) {
yield return new Notification(
title: "Alliance Accepted",
text:
$"{currentModel.FactionName(acceptingFactionId)} has agreed to an alliance!\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { acceptingFactionHead, ambassador});
} else {
yield return new Notification(
title: "Alliance Formed",
text:
$"{currentModel.FactionName(acceptingFactionId)} has formed an alliance with {currentModel.FactionName(offeringFactionId)}.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { acceptingFactionHead, offeringFactionHead, ambassador });
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: facd869001084512bcabb670b7c84f08
timeCreated: 1700546495
@@ -0,0 +1,57 @@
using System.Collections.Generic;
using Net.Eagle0.Eagle.Api;
namespace eagle.Notifications.ARNNotifications {
using ProvinceId = System.Int32;
using ARN = Net.Eagle0.Eagle.Common.Notification;
public static class AllianceAmbassadorImprisonedDetailsNotificationGenerator {
public static readonly ARNNotificationGenerator Generator = GenerateNotifications;
private static IEnumerable<Notification> GenerateNotifications(
Net.Eagle0.Eagle.Common.Notification notification,
GameModel currentModel) {
var playerId = currentModel.PlayerId;
var llmMessage = notification.LlmMessage;
var notif = notification.Details.AllianceAmbassadorImprisonedDetails;
var offeringFactionId = notif.OfferingFactionId;
var imprisoningFactionId = notif.TargetFactionId;
var imprisonedHeroId = notif.AmbassadorHeroId;
var imprisonedHero = currentModel.Heroes[imprisonedHeroId];
var affectedProvinces = currentModel.ProvincesForFaction(imprisoningFactionId);
affectedProvinces.AddRange(currentModel.ProvincesForFaction(offeringFactionId));
var offeringFactionHeadId =
currentModel.MaybeDestroyedFaction(offeringFactionId).FactionHeadId;
var offeringFactionHead = currentModel.Heroes[offeringFactionHeadId];
var imprisoningFactionHeadId =
currentModel.MaybeDestroyedFaction(imprisoningFactionId).FactionHeadId;
var imprisoningFactionHead = currentModel.Heroes[imprisoningFactionHeadId];
if (playerId.HasValue && playerId.Value == imprisoningFactionId) {
yield return new Notification(
title: "Ambassador Imprisoned",
text:
$"You have rejected an alliance with {currentModel.FactionName(offeringFactionId)} and imprisoned their ambassador {imprisonedHero.Name}.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { offeringFactionHead, imprisonedHero});
} else if (playerId.HasValue && playerId.Value == offeringFactionId) {
yield return new Notification(
title: "Ambassador Imprisoned",
text:
$"{currentModel.FactionName(imprisoningFactionId)} has imprisoned our ambassador {imprisonedHero.Name}!\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { imprisoningFactionHead, imprisonedHero});
} else {
yield return new Notification(
title: "Ambassador Imprisoned",
text:
$"{currentModel.FactionName(imprisoningFactionId)} has rejected an alliance from {currentModel.FactionName(offeringFactionId)} and imprisoned their ambassador {imprisonedHero.Name}.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { imprisoningFactionHead, offeringFactionHead, imprisonedHero });
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 08e8f63059f149208fc160d0e5ce8e52
timeCreated: 1700546875
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using Net.Eagle0.Eagle.Api;
namespace eagle.Notifications.ARNNotifications {
using ProvinceId = System.Int32;
using ARN = Net.Eagle0.Eagle.Common.Notification;
public static class AllianceRejectedDetailsNotificationGenerator {
public static readonly ARNNotificationGenerator Generator = GenerateNotifications;
private static IEnumerable<Notification> GenerateNotifications(
Net.Eagle0.Eagle.Common.Notification notification,
GameModel currentModel) {
var playerId = currentModel.PlayerId;
var notif = notification.Details.AllianceRejectedDetails;
var offeringFactionId = notif.OfferingFactionId;
var rejectingFactionId = notif.TargetFactionId;
var llmMessage = notification.LlmMessage;
var affectedProvinces = currentModel.ProvincesForFaction(rejectingFactionId);
affectedProvinces.AddRange(currentModel.ProvincesForFaction(offeringFactionId));
var ambassador = currentModel.Heroes[notif.AmbassadorHeroId];
var offeringFactionHeadId =
currentModel.MaybeDestroyedFaction(offeringFactionId).FactionHeadId;
var offeringFactionHead = currentModel.Heroes[offeringFactionHeadId];
var rejectingFactionHeadId =
currentModel.MaybeDestroyedFaction(rejectingFactionId).FactionHeadId;
var rejectingFactionHead = currentModel.Heroes[rejectingFactionHeadId];
if (playerId.HasValue && playerId.Value == rejectingFactionId) {
// Player is rejecting a treaty
yield return new Notification(
title: "Alliance Rejected",
text:
$"You have rejected an alliance with {currentModel.FactionName(offeringFactionId)}.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { offeringFactionHead, ambassador});
} else if (playerId.HasValue && playerId.Value == offeringFactionId) {
// Player's treaty offer was rejected
yield return new Notification(
title: "Alliance Rejected",
text:
$"{currentModel.FactionName(rejectingFactionId)} has rejected your alliance proposal.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { rejectingFactionHead, ambassador});
} else {
yield return new Notification(
title: "Alliance Rejected",
text:
$"{currentModel.FactionName(rejectingFactionId)} refused to form an alliance with {currentModel.FactionName(offeringFactionId)}.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { rejectingFactionHead, offeringFactionHead, ambassador });
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 041aa9a5bf9641d6b590500bb43079dd
timeCreated: 1700546588
@@ -0,0 +1,57 @@
using System.Collections.Generic;
using Net.Eagle0.Eagle.Api;
namespace eagle.Notifications.ARNNotifications {
using ProvinceId = System.Int32;
using ARN = Net.Eagle0.Eagle.Common.Notification;
public static class BreakAllianceAcceptedNotificationGenerator {
public static readonly ARNNotificationGenerator Generator = GenerateNotifications;
private static IEnumerable<Notification> GenerateNotifications(
Net.Eagle0.Eagle.Common.Notification notification,
GameModel currentModel) {
var playerId = currentModel.PlayerId;
var llmMessage = notification.LlmMessage;
var notif = notification.Details.BreakAllianceAcceptedDetails;
var offeringFactionId = notif.OfferingFactionId;
var imprisoningFactionId = notif.TargetFactionId;
var imprisonedHeroId = notif.AmbassadorHeroId;
var imprisonedHero = currentModel.Heroes[imprisonedHeroId];
var affectedProvinces = currentModel.ProvincesForFaction(imprisoningFactionId);
affectedProvinces.AddRange(currentModel.ProvincesForFaction(offeringFactionId));
var offeringFactionHeadId =
currentModel.MaybeDestroyedFaction(offeringFactionId).FactionHeadId;
var offeringFactionHead = currentModel.Heroes[offeringFactionHeadId];
var imprisoningFactionHeadId =
currentModel.MaybeDestroyedFaction(imprisoningFactionId).FactionHeadId;
var imprisoningFactionHead = currentModel.Heroes[imprisoningFactionHeadId];
if (playerId.HasValue && playerId.Value == imprisoningFactionId) {
yield return new Notification(
title: "Alliance Broken",
text:
$"{currentModel.FactionName(offeringFactionId)} broke their alliance with us.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { offeringFactionHead, imprisonedHero});
} else if (playerId.HasValue && playerId.Value == offeringFactionId) {
yield return new Notification(
title: "Alliance Broken",
text:
$"We broke our alliance with {currentModel.FactionName(imprisoningFactionId)}.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { imprisoningFactionHead, imprisonedHero});
} else {
yield return new Notification(
title: "Alliance Broken",
text:
$"{currentModel.FactionName(offeringFactionId)} broke their alliance with {currentModel.FactionName(imprisoningFactionId)}.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { imprisoningFactionHead, offeringFactionHead, imprisonedHero });
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5530138020a24fa8a6d621acb7b4ddb8
timeCreated: 1700547102
@@ -0,0 +1,57 @@
using System.Collections.Generic;
using Net.Eagle0.Eagle.Api;
namespace eagle.Notifications.ARNNotifications {
using ProvinceId = System.Int32;
using ARN = Net.Eagle0.Eagle.Common.Notification;
public static class BreakAllianceAmbassadorImprisonedDetailsNotificationGenerator {
public static readonly ARNNotificationGenerator Generator = GenerateNotifications;
private static IEnumerable<Notification> GenerateNotifications(
Net.Eagle0.Eagle.Common.Notification notification,
GameModel currentModel) {
var playerId = currentModel.PlayerId;
var llmMessage = notification.LlmMessage;
var notif = notification.Details.BreakAllianceAmbassadorImprisonedDetails;
var offeringFactionId = notif.OfferingFactionId;
var imprisoningFactionId = notif.TargetFactionId;
var imprisonedHeroId = notif.AmbassadorHeroId;
var imprisonedHero = currentModel.Heroes[imprisonedHeroId];
var affectedProvinces = currentModel.ProvincesForFaction(imprisoningFactionId);
affectedProvinces.AddRange(currentModel.ProvincesForFaction(offeringFactionId));
var offeringFactionHeadId =
currentModel.MaybeDestroyedFaction(offeringFactionId).FactionHeadId;
var offeringFactionHead = currentModel.Heroes[offeringFactionHeadId];
var imprisoningFactionHeadId =
currentModel.MaybeDestroyedFaction(imprisoningFactionId).FactionHeadId;
var imprisoningFactionHead = currentModel.Heroes[imprisoningFactionHeadId];
if (playerId.HasValue && playerId.Value == imprisoningFactionId) {
yield return new Notification(
title: "Ambassador Imprisoned",
text:
$"{currentModel.FactionName(offeringFactionId)} broke their alliance with us, and we and imprisoned their ambassador {imprisonedHero.Name}.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { offeringFactionHead, imprisonedHero});
} else if (playerId.HasValue && playerId.Value == offeringFactionId) {
yield return new Notification(
title: "Ambassador Imprisoned",
text:
$"{currentModel.FactionName(imprisoningFactionId)} has imprisoned our ambassador {imprisonedHero.Name} in response to us breaking the alliance!\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { imprisoningFactionHead, imprisonedHero});
} else {
yield return new Notification(
title: "Ambassador Imprisoned",
text:
$"{currentModel.FactionName(offeringFactionId)} broke their alliance with {currentModel.FactionName(imprisoningFactionId)}, and {currentModel.FactionName(imprisoningFactionId)} imprisoned their ambassador {imprisonedHero.Name}.\n\n{llmMessage}",
provinceIds: affectedProvinces,
displayedHeroes: new List<HeroView> { imprisoningFactionHead, offeringFactionHead, imprisonedHero });
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b176d91f9d8c4b15827938104aaebee2
timeCreated: 1700546699
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -58,6 +58,8 @@ message AvailableCommand {
PleaseRecruitMeAvailableCommand please_recruit_me_command = 22;
ReconAvailableCommand recon_command = 23;
RecruitHeroesAvailableCommand recruit_heroes_command = 24;
ResolveAllianceOfferAvailableCommand resolve_alliance_offer_command = 40;
ResolveBreakAllianceAvailableCommand resolve_break_alliance_command = 39;
ResolveInvitationAvailableCommand resolve_invitation_command = 25;
ResolveRansomOfferAvailableCommand resolve_ransom_offer_command = 37;
ResolveTruceOfferAvailableCommand resolve_truce_offer_command = 26;
@@ -53,6 +53,8 @@ message SelectedCommand {
PleaseRecruitMeSelectedCommand please_recruit_me_command = 22;
ReconSelectedCommand recon_selected_command = 23;
RecruitHeroesSelectedCommand recruit_heroes_command = 24;
ResolveAllianceOfferSelectedCommand resolve_alliance_offer_command = 40;
ResolveBreakAllianceSelectedCommand resolve_break_command = 39;
ResolveInvitationSelectedCommand resolve_invitation_command = 25;
ResolveRansomOfferSelectedCommand resolve_ransom_offer_command = 37;
ResolveTruceOfferSelectedCommand resolve_truce_offer_command = 26;
@@ -478,6 +478,14 @@ class AvailableCommandsFactory(
AvailableResolveRansomOfferCommandFactory
.availableCommand(gs, faction.id)
)
.orElse(
AvailableResolveAllianceOfferCommandFactory
.availableCommand(gs, faction.id)
)
.orElse(
AvailableResolveBreakAllianceCommandFactory
.availableCommand(gs, faction.id)
)
oneProvinceAvailableCommand <- optionalOneProvinceAvailableCommands(
gs = gs,
0,
@@ -83,6 +83,46 @@ object AvailableDiplomacyCommandsFactory
)
)
private def allianceOptionForTargetFaction(
gs: GameState,
pid: ProvinceId,
actingFid: FactionId,
targetFid: FactionId
): Option[DiplomacyOption] =
Option.when(
gs.provinces(pid).gold >= allianceGoldCost
&& LegacyProvinceUtils.ruledByFactionLeader(
province = gs.provinces(pid),
gameState = gs
) && !FactionUtils
.hasAlliance(fid1 = actingFid, fid2 = targetFid, gs = gs)
)(
AllianceOption(
targetFactionId = targetFid,
goldCost = allianceGoldCost
)
)
private def breakAllianceOptionForTargetFaction(
gs: GameState,
pid: ProvinceId,
actingFid: FactionId,
targetFid: FactionId
): Option[DiplomacyOption] =
Option.when(
gs.provinces(pid).gold >= allianceGoldCost
&& LegacyProvinceUtils.ruledByFactionLeader(
province = gs.provinces(pid),
gameState = gs
) && FactionUtils
.hasAlliance(fid1 = actingFid, fid2 = targetFid, gs = gs)
)(
BreakAllianceOption(
targetFactionId = targetFid,
goldCost = breakAllianceGoldCost
)
)
private def hasClearPath(
gs: GameState,
pid: ProvinceId,
@@ -188,6 +228,16 @@ object AvailableDiplomacyCommandsFactory
pid = pid,
actingFid = actingFid,
targetFid = targetFid
) ++ allianceOptionForTargetFaction(
gs = gs,
pid = pid,
actingFid = actingFid,
targetFid = targetFid
) ++ breakAllianceOptionForTargetFaction(
gs = gs,
pid = pid,
actingFid = actingFid,
targetFid = targetFid
)
private def hasIncomingDiplomacyOffer(
@@ -0,0 +1,59 @@
package net.eagle0.eagle.library.actions.availability
import net.eagle0.eagle.FactionId
import net.eagle0.eagle.api.available_command.ResolveAllianceOfferAvailableCommand
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
object AvailableResolveAllianceOfferCommandFactory {
private def unresolvedAllianceOffers(
gameState: GameState,
factionId: FactionId
): Vector[DiplomacyOffer] =
gameState
.factions(factionId)
.incomingDiplomacyOffers
.filter(_.status == DIPLOMACY_OFFER_STATUS_UNRESOLVED)
.filter(_.offerMessage.isDefined)
.collect { diploOffer =>
diploOffer.offerDetails match {
case _: AllianceOfferDetails => diploOffer
}
}
def availableCommand(
gameState: GameState,
factionId: FactionId
): Option[ResolveAllianceOfferAvailableCommand] =
unresolvedAllianceOffers(gameState, factionId) match {
case Vector() => None
case offers =>
Some(
ResolveAllianceOfferAvailableCommand(
offers = offers.collect {
case DiplomacyOffer(
originatingFactionId,
_ /* targetFactionId */,
messengerHeroId,
messengerOriginProvinceId,
status,
offerMessage,
AllianceOfferDetails()
) =>
DiplomacyOffer(
originatingFactionId = originatingFactionId,
targetFactionId = factionId,
offerDetails = AllianceOfferDetails(),
messengerHeroId = messengerHeroId,
messengerOriginProvinceId = messengerOriginProvinceId,
status = status,
offerMessage = offerMessage
)
}
)
)
}
}
@@ -0,0 +1,60 @@
package net.eagle0.eagle.library.actions.availability
import net.eagle0.eagle.FactionId
import net.eagle0.eagle.api.available_command.ResolveBreakAllianceAvailableCommand
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_UNRESOLVED
import net.eagle0.eagle.internal.game_state.GameState
object AvailableResolveBreakAllianceCommandFactory {
private def unresolvedBreakAllianceOffers(
gameState: GameState,
factionId: FactionId
): Vector[DiplomacyOffer] =
gameState
.factions(factionId)
.incomingDiplomacyOffers
.filter(_.status == DIPLOMACY_OFFER_STATUS_UNRESOLVED)
.filter(_.offerMessage.isDefined)
.collect { diploOffer =>
diploOffer.offerDetails match {
case _: BreakAllianceDetails => diploOffer
}
}
def availableCommand(
gameState: GameState,
factionId: FactionId
): Option[ResolveBreakAllianceAvailableCommand] =
unresolvedBreakAllianceOffers(gameState, factionId) match {
case Vector() => None
case offers =>
Some(
ResolveBreakAllianceAvailableCommand(
offers = offers.collect {
case DiplomacyOffer(
originatingFactionId: FactionId,
_ /* targetFactionId */,
messengerHeroId: FactionId,
messengerOriginProvinceId: FactionId,
status: DiplomacyOfferStatus,
offerMessage: Option[String],
details: BreakAllianceDetails
) =>
DiplomacyOffer(
originatingFactionId = originatingFactionId,
targetFactionId = factionId,
offerDetails = details,
messengerHeroId = messengerHeroId,
messengerOriginProvinceId = messengerOriginProvinceId,
status = status,
offerMessage = offerMessage
)
}
)
)
}
}
@@ -14,7 +14,7 @@ object AvailableResolveTruceOfferCommandFactory {
def truceMonths: Int = Settings.getInt(SettingsKeys.truceMonths)
def unresolvedTruceOffers(
private def unresolvedTruceOffers(
gameState: GameState,
factionId: FactionId
): Vector[DiplomacyOffer] =
@@ -37,6 +37,8 @@ scala_library(
":available_please_recruit_me_command_factory",
":available_recon_command_factory",
":available_recruit_heroes_command_factory",
":available_resolve_alliance_offer_command_factory",
":available_resolve_break_alliance_command_factory",
":available_resolve_invitation_command_factory",
":available_resolve_ransom_offer_command_factory",
":available_resolve_tribute_commands_factory",
@@ -66,7 +68,6 @@ scala_library(
name = "available_apprehend_outlaw_command_factory",
srcs = ["AvailableApprehendOutlawCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -84,7 +85,6 @@ scala_library(
name = "available_arm_troops_command_factory",
srcs = ["AvailableArmTroopsCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -101,7 +101,6 @@ scala_library(
name = "available_attack_decision_command_factory",
srcs = ["AvailableAttackDecisionCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
exports = [
@@ -124,7 +123,6 @@ scala_library(
name = "available_commands_factory_for_type",
srcs = ["AvailableCommandsFactoryForType.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -138,7 +136,6 @@ scala_library(
name = "available_control_weather_commands_factory",
srcs = ["AvailableControlWeatherCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -156,7 +153,6 @@ scala_library(
name = "available_defend_commands_factory",
srcs = ["AvailableDefendCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -177,7 +173,6 @@ scala_library(
name = "available_diplomacy_commands_factory",
srcs = ["AvailableDiplomacyCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -198,7 +193,6 @@ scala_library(
name = "available_divine_commands_factory",
srcs = ["AvailableDivineCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -219,7 +213,6 @@ scala_library(
name = "available_exile_vassal_command_factory",
srcs = ["AvailableExileVassalCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -237,7 +230,6 @@ scala_library(
name = "available_feast_command_factory",
srcs = ["AvailableFeastCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -255,7 +247,6 @@ scala_library(
name = "available_free_for_all_decision_command_factory",
srcs = ["AvailableFreeForAllDecisionCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
exports = [
@@ -277,7 +268,6 @@ scala_library(
name = "available_handle_captured_hero_command_factory",
srcs = ["AvailableHandleCapturedHeroCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -296,7 +286,6 @@ scala_library(
name = "available_handle_riot_crack_down_command_factory",
srcs = ["AvailableHandleRiotCrackDownCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
@@ -319,7 +308,6 @@ scala_library(
name = "available_handle_riot_do_nothing_command_factory",
srcs = ["AvailableHandleRiotDoNothingCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
@@ -341,7 +329,6 @@ scala_library(
name = "available_handle_riot_give_command_factory",
srcs = ["AvailableHandleRiotGiveCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
@@ -364,7 +351,6 @@ scala_library(
name = "available_hero_gift_command_factory",
srcs = ["AvailableHeroGiftCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -383,7 +369,6 @@ scala_library(
name = "available_improve_command_factory",
srcs = ["AvailableImproveCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -402,7 +387,6 @@ scala_library(
name = "available_manage_prisoners_command_factory",
srcs = ["AvailableManagePrisonersCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -421,7 +405,6 @@ scala_library(
name = "available_march_command_factory",
srcs = ["AvailableMarchCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -444,7 +427,6 @@ scala_library(
name = "available_orders_command_factory",
srcs = ["AvailableOrdersCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -461,7 +443,6 @@ scala_library(
name = "available_organize_troops_command_factory",
srcs = ["AvailableOrganizeTroopsCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -478,7 +459,6 @@ scala_library(
name = "available_please_recruit_me_command_factory",
srcs = ["AvailablePleaseRecruitMeCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -496,7 +476,6 @@ scala_library(
name = "available_alms_command_factory",
srcs = ["AvailableAlmsCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -514,7 +493,6 @@ scala_library(
name = "available_recon_command_factory",
srcs = ["AvailableReconCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -531,7 +509,6 @@ scala_library(
name = "available_recruit_heroes_command_factory",
srcs = ["AvailableRecruitHeroesCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -554,7 +531,6 @@ scala_library(
name = "available_resolve_invitation_command_factory",
srcs = ["AvailableResolveInvitationCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -567,11 +543,38 @@ scala_library(
],
)
scala_library(
name = "available_resolve_alliance_offer_command_factory",
srcs = ["AvailableResolveAllianceOfferCommandFactory.scala"],
visibility = [
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
":pkg",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
],
)
scala_library(
name = "available_resolve_break_alliance_command_factory",
srcs = ["AvailableResolveBreakAllianceCommandFactory.scala"],
visibility = [
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
":pkg",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
],
)
scala_library(
name = "available_resolve_ransom_offer_command_factory",
srcs = ["AvailableResolveRansomOfferCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -589,7 +592,6 @@ scala_library(
name = "available_resolve_truce_offer_command_factory",
srcs = ["AvailableResolveTruceOfferCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -606,7 +608,6 @@ scala_library(
name = "available_resolve_tribute_commands_factory",
srcs = ["AvailableResolveTributeCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -623,7 +624,6 @@ scala_library(
name = "available_rest_commands_factory",
srcs = ["AvailableRestCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -639,7 +639,6 @@ scala_library(
name = "available_return_commands_factory",
srcs = ["AvailableReturnCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -655,7 +654,6 @@ scala_library(
name = "available_send_supplies_command_factory",
srcs = ["AvailableSendSuppliesCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -674,7 +672,6 @@ scala_library(
name = "available_start_epidemic_command_factory",
srcs = ["AvailableStartEpidemicCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -692,7 +689,6 @@ scala_library(
name = "available_suppress_beasts_command_factory",
srcs = ["AvailableSuppressBeastsCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -710,7 +706,6 @@ scala_library(
name = "available_swear_brotherhood_command_factory",
srcs = ["AvailableSwearBrotherhoodCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -727,7 +722,6 @@ scala_library(
name = "available_trade_command_factory",
srcs = ["AvailableTradeCommandFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -744,7 +738,6 @@ scala_library(
name = "available_train_command_factory",
srcs = ["AvailableTrainCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -761,7 +754,6 @@ scala_library(
name = "available_travel_commands_factory",
srcs = ["AvailableTravelCommandsFactory.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -808,7 +800,6 @@ scala_library(
name = "pkg",
srcs = ["package.scala"],
visibility = [
":__pkg__",
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
],
deps = [
@@ -99,6 +99,8 @@ scala_library(
":random_simple_action_wrapper",
":recon_command",
":recruit_heroes_command",
":resolve_alliance_offer_command",
":resolve_break_alliance_command",
":resolve_invitation_command",
":resolve_ransom_offer_command",
":resolve_tribute_command",
@@ -265,6 +265,30 @@ class CommandFactory {
currentRoundId = gameState.currentRoundId
)
case (
ac: ResolveAllianceOfferAvailableCommand,
sc: ResolveAllianceOfferSelectedCommand
) =>
ResolveAllianceOfferCommand.make(
actingFactionId = actingFactionId,
availableCommand = ac,
selectedCommand = sc,
gameId = gameState.gameId,
currentRoundId = gameState.currentRoundId
)
case (
ac: ResolveBreakAllianceAvailableCommand,
sc: ResolveBreakAllianceSelectedCommand
) =>
ResolveBreakAllianceCommand.make(
actingFactionId = actingFactionId,
availableCommand = ac,
selectedCommand = sc,
gameId = gameState.gameId,
currentRoundId = gameState.currentRoundId
)
case (
ac: ResolveTributeAvailableCommand,
sc: ResolveTributeSelectedCommand
@@ -4,11 +4,15 @@ import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.{FactionId, SettingsKeys}
import net.eagle0.eagle.api.available_command.{
AvailableCommand,
ResolveAllianceOfferAvailableCommand,
ResolveBreakAllianceAvailableCommand,
ResolveInvitationAvailableCommand,
ResolveRansomOfferAvailableCommand,
ResolveTruceOfferAvailableCommand
}
import net.eagle0.eagle.api.selected_command.{
ResolveAllianceOfferSelectedCommand,
ResolveBreakAllianceSelectedCommand,
ResolveInvitationSelectedCommand,
ResolveRansomOfferSelectedCommand,
ResolveTruceOfferSelectedCommand,
@@ -17,6 +21,7 @@ import net.eagle0.eagle.api.selected_command.{
import net.eagle0.eagle.common.diplomacy_offer.RansomOfferDetails
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
DIPLOMACY_OFFER_STATUS_ACCEPTED,
DIPLOMACY_OFFER_STATUS_IMPRISONED,
DIPLOMACY_OFFER_STATUS_REJECTED
}
import net.eagle0.eagle.internal.game_state.GameState
@@ -43,6 +48,8 @@ object ResolveDiplomacyCommandSelector {
availableCommands = availableCommands,
rankedChoosers = Vector[CommandChooser](
resolveInvitationSelectedCommand _,
resolveAllianceOfferSelectedCommand _,
resolveBreakAllianceSelectedCommand _,
resolveTruceOfferSelectedCommand _,
resolveRansomOfferSelectedCommand _
),
@@ -54,7 +61,7 @@ object ResolveDiplomacyCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = {
): RandomState[Option[CommandSelection]] =
randomSelectionForType[ResolveInvitationAvailableCommand](
availableCommands,
functionalRandom
@@ -75,7 +82,6 @@ object ResolveDiplomacyCommandSelector {
)
}
}
}
private def selectionForResolveInvitationCommand(
actingFactionId: FactionId,
@@ -219,4 +225,84 @@ object ResolveDiplomacyCommandSelector {
)
case _ => throw new EagleInternalException("Not a ransom offer")
}
private def resolveAllianceOfferSelectedCommand(
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
randomSelectionForType[ResolveAllianceOfferAvailableCommand](
availableCommands,
functionalRandom
) { case (resolveAllianceAc, fr) =>
selectionForResolveAllianceOfferSelectedCommand(
actingFactionId = actingFactionId,
ac = resolveAllianceAc,
gs = gameState,
functionalRandom = fr
).map { resolveAllianceOfferSelection =>
Some(
CommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = 0,
available = resolveAllianceAc,
selected = resolveAllianceOfferSelection
)
)
}
}
private def resolveBreakAllianceSelectedCommand(
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
randomSelectionForType[ResolveBreakAllianceAvailableCommand](
availableCommands,
functionalRandom
) { case (resolveBreakAllianceAc, fr) =>
selectionForResolveBreakAllianceSelectedCommand(
actingFactionId = actingFactionId,
ac = resolveBreakAllianceAc,
gs = gameState,
functionalRandom = fr
).map { resolveBreakAllianceSelection =>
Some(
CommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = 0,
available = resolveBreakAllianceAc,
selected = resolveBreakAllianceSelection
)
)
}
}
// FIXME: always reject alliances for now
private def selectionForResolveAllianceOfferSelectedCommand(
actingFactionId: FactionId,
ac: ResolveAllianceOfferAvailableCommand,
gs: GameState,
functionalRandom: FunctionalRandom
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
ResolveAllianceOfferSelectedCommand(
originatingFactionId = ac.offers.head.originatingFactionId,
resolution = DIPLOMACY_OFFER_STATUS_REJECTED
)
}
// FIXME: always imprison ambassadors for now
private def selectionForResolveBreakAllianceSelectedCommand(
actingFactionId: FactionId,
ac: ResolveBreakAllianceAvailableCommand,
gs: GameState,
functionalRandom: FunctionalRandom
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
ResolveBreakAllianceSelectedCommand(
originatingFactionId = ac.offers.head.originatingFactionId,
resolution = DIPLOMACY_OFFER_STATUS_IMPRISONED
)
}
}
@@ -3,6 +3,8 @@ package net.eagle0.eagle.library.actions.availability
import net.eagle0.common.ProtoMatchers.equalProto
import net.eagle0.eagle.SettingsKeys
import net.eagle0.eagle.api.command.util.diplomacy_option.{
AllianceOption,
BreakAllianceOption,
InvitationOption,
RansomOfferOption,
TruceOption
@@ -21,6 +23,7 @@ import net.eagle0.eagle.common.unaffiliated_hero.UnaffiliatedHero.Type.{
RESIDENT
}
import net.eagle0.eagle.internal.faction.{Faction, PrestigeModifier}
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship
import net.eagle0.eagle.internal.game_state.GameState
import net.eagle0.eagle.internal.hero.Hero
import net.eagle0.eagle.internal.province.{Neighbor, Province}
@@ -133,6 +136,8 @@ class AvailableDiplomacyCommandsFactoryTest
Map(
SettingsKeys.minVigorForDiplomacy -> "55",
SettingsKeys.truceGoldCost -> "150",
SettingsKeys.allianceGoldCost -> "200",
SettingsKeys.breakAllianceGoldCost -> "250",
SettingsKeys.inviteGoldCost -> "1000",
SettingsKeys.maxProvincesOnTargetForInvite -> "1",
SettingsKeys.minSupportForTaxes -> "40",
@@ -176,6 +181,112 @@ class AvailableDiplomacyCommandsFactoryTest
)
}
it should "an alliance option for each other faction, but no break alliance options, if there are no alliances" in {
val diploCmd = AvailableDiplomacyCommandsFactory
.availableCommand(
gameState = gameState,
factionId = actingFid,
provinceId = actingProvinceId
)
.get
diploCmd.availableHeroIds shouldBe Vector(8, 9, 10, 11)
diploCmd.options should contain.allOf(
AllianceOption(
targetFactionId = 4,
goldCost = 200
),
AllianceOption(
targetFactionId = 5,
goldCost = 200
)
)
diploCmd.options.collect { case bao @ BreakAllianceOption(_, _) =>
bao
} shouldBe empty
}
it should "not contain an alliance option if there is already an alliance" in {
val diploCmd = AvailableDiplomacyCommandsFactory
.availableCommand(
gameState = gameState.update(
_.factions(actingFid).factionRelationships := Vector(
FactionRelationship(
targetFactionId = 5,
relationshipLevel =
net.eagle0.eagle.internal.faction_relationship.FactionRelationship.RelationshipLevel.ALLY,
trustValue = 0,
resetDate = None
)
)
),
factionId = actingFid,
provinceId = actingProvinceId
)
.get
diploCmd.availableHeroIds shouldBe Vector(8, 9, 10, 11)
diploCmd.options should contain(
AllianceOption(
targetFactionId = 4,
goldCost = 200
)
)
diploCmd.options should not contain (
AllianceOption(
targetFactionId = 5,
goldCost = 200
)
)
diploCmd.options should contain(
BreakAllianceOption(
targetFactionId = 5,
goldCost = 250
)
)
}
it should "still contain an alliance option if there is already a truce" in {
val diploCmd = AvailableDiplomacyCommandsFactory
.availableCommand(
gameState = gameState.update(
_.factions(actingFid).factionRelationships := Vector(
FactionRelationship(
targetFactionId = 5,
relationshipLevel =
net.eagle0.eagle.internal.faction_relationship.FactionRelationship.RelationshipLevel.TRUCE,
trustValue = 0,
resetDate = None
)
)
),
factionId = actingFid,
provinceId = actingProvinceId
)
.get
diploCmd.availableHeroIds shouldBe Vector(8, 9, 10, 11)
diploCmd.options should contain(
AllianceOption(
targetFactionId = 4,
goldCost = 200
)
)
diploCmd.options should contain(
AllianceOption(
targetFactionId = 5,
goldCost = 200
)
)
diploCmd.options should not contain (
BreakAllianceOption(
targetFactionId = 5,
goldCost = 250
)
)
}
it should "include an invite option for an appropriate faction" in {
val diploCmd = AvailableDiplomacyCommandsFactory
.availableCommand(