Skip to content

Commit ff8144d

Browse files
committed
Allow AI players to try to make deals with the human player
This adds a nice bit of interactivity to the exploration phase - now the AI can act by itself.
1 parent aa77028 commit ff8144d

File tree

5 files changed

+67
-15
lines changed

5 files changed

+67
-15
lines changed

C7/Game.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ enum GameState {
3232

3333
GameState CurrentState = GameState.PreGame;
3434

35+
// True if the deal screen is open because an AI player is offering something
36+
// to the human player.
37+
private bool waitingForDealScreen = false;
38+
3539
// CurrentlySelectedUnit is a reference directly into the game state so be careful of race conditions. TODO: Consider storing a GUID instead.
3640
public MapUnit CurrentlySelectedUnit = MapUnit.NONE; //The selected unit. May be changed by clicking on a unit or the next unit being auto-selected after orders are given for the current one.
3741
private bool HasCurrentlySelectedUnit() => CurrentlySelectedUnit != MapUnit.NONE;
@@ -283,6 +287,13 @@ public void processEngineMessagesLocked(GameData gameData) {
283287
}
284288
EmitSignal(SignalName.ShowSpecificAdvisor, "F1");
285289
break;
290+
case MsgShowTradeOffer mSTO:
291+
diplomacy.ShowDealScreenForPlayer(
292+
mSTO.humanPlayer.id, mSTO.aiPlayer.id,
293+
humanGives: mSTO.aiWant,
294+
humanWants: mSTO.aiGive);
295+
waitingForDealScreen = true;
296+
break;
286297
case MsgDisplayHurryProductionPopup mDHPP:
287298
if (mDHPP.details.errorMessage != null) {
288299
popupOverlay.ShowPopup(
@@ -328,6 +339,11 @@ public void updateAnimations(GameData gameData) {
328339
}
329340

330341
public override void _Process(double delta) {
342+
if (CurrentState == GameState.ComputerTurn && waitingForDealScreen && !diplomacy.Visible) {
343+
waitingForDealScreen = false;
344+
EngineStorage.FinishUiEvent();
345+
}
346+
331347
ProcessActions();
332348
processEngineMessages();
333349

@@ -881,7 +897,6 @@ private void ProcessAction(string currentAction) {
881897
return;
882898
}
883899

884-
885900
if (currentAction == C7Action.UnitHold) {
886901
new ActionToEngineMsg(() => CurrentlySelectedUnit?.skipTurn()).send();
887902
}

C7/UIElements/Diplomacy/DealScreen.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ public partial class DealScreen : TextureRect {
4444
TradeOffer opponentOffer = new();
4545
TradeOffer humanOffer = new();
4646

47-
public DealScreen(ID humanPlayer, ID opponentPlayer) {
47+
public DealScreen(ID humanPlayer, ID opponentPlayer, TradeOffer humanGives, TradeOffer humanWants) {
4848
this.humanPlayerId = humanPlayer;
4949
this.opponentPlayerId = opponentPlayer;
50+
this.humanOffer = humanGives;
51+
this.opponentOffer = humanWants;
5052
}
5153

5254
public override void _Ready() {

C7/UIElements/Diplomacy/Diplomacy.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@ private void RemoveOtherScreens() {
3333
}
3434

3535
public void ShowDealScreenForPlayer(ID humanPlayer, ID opponentPlayer) {
36+
ShowDealScreenForPlayer(humanPlayer, opponentPlayer, new TradeOffer(), new TradeOffer());
37+
}
38+
39+
public void ShowDealScreenForPlayer(ID humanPlayer, ID opponentPlayer, TradeOffer humanGives, TradeOffer humanWants) {
3640
RemoveOtherScreens();
3741

38-
dealScreen = new DealScreen(humanPlayer, opponentPlayer);
42+
dealScreen = new DealScreen(humanPlayer, opponentPlayer, humanGives, humanWants);
3943
AddChild(dealScreen);
4044
this.Show();
4145
}

C7Engine/AI/PlayerAI.cs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,6 @@ private static void AttemptTrading(Player us) {
316316
continue;
317317
}
318318

319-
// TODO: Implement AI -> Human tech trading
320-
if (them.isHuman) {
321-
continue;
322-
}
323-
324319
// Figure out what techs are available for trading.
325320
List<Tech> techsTheyCanTrade = gD.techs.FindAll(x => {
326321
return them.knownTechs.Contains(x.id) && !us.knownTechs.Contains(x.id);
@@ -338,18 +333,25 @@ private static void AttemptTrading(Player us) {
338333
continue;
339334
}
340335

341-
// Figure out the value of what we have available to trade.
342336
TradeOffer weGive = new();
337+
Func<int> CalculateWeGiveValue = () => {
338+
return Math.Max(weGive.GoldEquivalentFor(gD, them), weGive.GoldEquivalentFor(gD, us));
339+
};
340+
TradeOffer weWant = new();
341+
Func<int> CalculateWeWantValue = () => {
342+
return Math.Min(weWant.GoldEquivalentFor(gD, them), weWant.GoldEquivalentFor(gD, us));
343+
};
344+
345+
// Figure out the value of what we have available to trade.
343346
weGive.gold = us.gold;
344347
foreach (Tech t in techsWeCanTrade) {
345348
weGive.techs.Add(t);
346349
}
347-
int ourMaxPossibleOffer = weGive.GoldEquivalentFor(gD, them);
350+
int ourMaxPossibleOffer = CalculateWeGiveValue();
348351

349352
// Going from the most to the least valuable valuable techs, see
350353
// if we can afford them. This greedy algorithm should be good
351354
// enough - we don't need perfect binpacking.
352-
TradeOffer weWant = new();
353355
foreach (Tech t in techsTheyCanTrade) {
354356
int cost = gD.TechCostFor(t, us);
355357
if (cost < ourMaxPossibleOffer) {
@@ -365,33 +367,48 @@ private static void AttemptTrading(Player us) {
365367
// from the opponent. However, we might be overpaying, possibly
366368
// by a significant amount. Keep removing techs from our offer
367369
// as long as it doesn't make our offer worse than theirs.
368-
int theirOfferValue = weWant.GoldEquivalentFor(gD, us);
370+
int theirOfferValue = CalculateWeWantValue();
369371
for (int i = 0; i < weGive.techs.Count;) {
370-
if (weGive.GoldEquivalentFor(gD, them) - gD.TechCostFor(weGive.techs[i], them) >= theirOfferValue) {
372+
if (CalculateWeGiveValue() - gD.TechCostFor(weGive.techs[i], them) >= theirOfferValue) {
371373
weGive.techs.RemoveAt(0);
372374
} else {
373375
++i;
374376
}
375377
}
376378

377379
// Now use any gold to even things out, if possible.
378-
int remainingDelta = Math.Max(0, weGive.GoldEquivalentFor(gD, them) - theirOfferValue);
380+
int remainingDelta = Math.Max(0, CalculateWeGiveValue() - theirOfferValue);
379381
weGive.gold -= Math.Min(remainingDelta, weGive.gold.Value);
380382

381383
// And ensure we minimize the total gold traded, to keep the
382384
// logs cleaner.
383385
int redundantGold = Math.Min(weGive.gold.Value, weWant.gold.Value);
384386
weGive.gold -= redundantGold;
385387
weWant.gold -= redundantGold;
388+
if (weGive.gold == 0) {
389+
weGive.gold = null;
390+
}
391+
if (weWant.gold == 0) {
392+
weWant.gold = null;
393+
}
386394

387395
// Finally if the deal is too mismatched or only contains a swap
388396
// of gold, abandon it. Otherwise we can execute the deal.
389-
if (weGive.GoldEquivalentFor(gD, them) > 1.1 * weWant.GoldEquivalentFor(gD, us)) {
397+
// TODO: Figure out how the real trade factor in the difficulty
398+
// works.
399+
float tradeFactor = them.isHuman ? 1.0f : 1.1f;
400+
if (CalculateWeGiveValue() > tradeFactor * CalculateWeWantValue()) {
390401
continue;
391402
}
392403
if (weGive.techs.Count == 0 && weWant.techs.Count == 0) {
393404
continue;
394405
}
406+
407+
if (them.isHuman) {
408+
new MsgShowTradeOffer(us, them, weWant, weGive).send();
409+
EngineStorage.WaitForUiEvent();
410+
}
411+
395412
us.ExecuteDeal(gD, them, weWant, weGive);
396413
}
397414
}

C7Engine/EntryPoints/MessageToUI.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,18 @@ public MsgShowTemporaryPopup(string message, Tile location) {
115115
this.location = location;
116116
}
117117
}
118+
119+
public class MsgShowTradeOffer : MessageToUI {
120+
public Player aiPlayer;
121+
public Player humanPlayer;
122+
public TradeOffer aiWant;
123+
public TradeOffer aiGive;
124+
125+
public MsgShowTradeOffer(Player aiPlayer, Player humanPlayer, TradeOffer aiWant, TradeOffer aiGive) {
126+
this.aiPlayer = aiPlayer;
127+
this.humanPlayer = humanPlayer;
128+
this.aiWant = aiWant;
129+
this.aiGive = aiGive;
130+
}
131+
}
118132
}

0 commit comments

Comments
 (0)