Skip to content

Commit 8ceb471

Browse files
committed
Who should accumulate abilities?
Priorities, priority windows, pilots? What's the deal with imminent instructions and pending abilities?
1 parent 09daf53 commit 8ceb471

30 files changed

+248
-472
lines changed

Assets/Scripts/Model/Cards/Card.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Threading.Tasks;
34
using model.choices.trash;
45
using model.play;
@@ -11,6 +12,7 @@ namespace model.cards {
1112
public abstract class Card : ISource {
1213
public event NotifyMoved Moved = delegate { };
1314
public event NotifyInfo ChangedInfo = delegate { };
15+
public event Action<ISource> ChangedActivation = delegate { };
1416
public abstract string Name { get; }
1517
public abstract IType Type { get; }
1618
public Zone Zone { get; private set; }
@@ -37,10 +39,22 @@ public Card(Game game) {
3739
this.Zone.Add(this);
3840
}
3941

42+
async public Task BecomeActive() {
43+
await Activate();
44+
Active = true;
45+
ChangedActivation(this);
46+
}
47+
4048
protected abstract Task Activate();
4149

42-
async protected virtual Task Deactivate() {
43-
await Task.CompletedTask;
50+
async protected virtual Task BecomeInactive() {
51+
await Deactivate();
52+
Active = false;
53+
ChangedActivation(this);
54+
}
55+
56+
protected virtual Task Deactivate() {
57+
return Task.CompletedTask;
4458
}
4559

4660
async public Task MoveTo(Zone target) {

Assets/Scripts/Model/Cards/Corp/AdvancedAssemblyLines.cs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66
using model.choices.trash;
77
using model.costs;
88
using model.play;
9-
using model.player;
109
using model.timing;
1110
using model.zones;
1211

1312
namespace model.cards.corp {
1413
public class AdvancedAssemblyLines : Card {
15-
public AdvancedAssemblyLines(Game game) : base(game) { }
14+
private Ability pop;
1615
override public string FaceupArt => "advanced-assembly-lines";
1716
override public string Name => "Advanced Assembly Lines";
1817
override public Faction Faction => Factions.HAAS_BIOROID;
@@ -24,27 +23,30 @@ public AdvancedAssemblyLines(Game game) : base(game) { }
2423
new PayToTrash(1, this, game)
2524
};
2625

26+
public AdvancedAssemblyLines(Game game) : base(game) {
27+
pop = new Ability(
28+
cost: new Trash(this, game.corp.zones.archives.Zone),
29+
effect: new AdvancedAssemblyLinesInstall(game.corp),
30+
source: this,
31+
mandatory: false
32+
);
33+
}
34+
2735
async protected override Task Activate() {
2836
await game.corp.credits.Gaining(3).Resolve();
29-
game.Timing.PaidWindowDefined += DefineTrashAbility;
37+
game.Timing.PaidWindowDefined += DeferPop;
3038
}
3139

32-
async protected override Task Deactivate() {
33-
game.Timing.PaidWindowDefined -= DefineTrashAbility;
40+
protected override Task Deactivate() {
41+
game.Timing.PaidWindowDefined -= DeferPop;
42+
return Task.CompletedTask;
3443
}
3544

36-
private void DefineTrashAbility(PaidWindow paidWindow) {
37-
var archives = game.corp.zones.archives.Zone;
38-
var aalInstall = new AdvancedAssemblyLinesInstall(game.corp);
39-
var pop = new Ability(
40-
cost: new Conjunction(paidWindow.Permission(), new Trash(aal, archives), new Active(aal)),
41-
effect: aalInstall,
42-
aal
43-
).BelongingTo(aal);
44-
paidWindow.Add(pop);
45+
private void DeferPop(PaidWindow paidWindow) {
46+
paidWindow.GiveOption(game.corp.pilot, pop);
4547
}
4648

47-
private class AdvancedAssemblyLinesInstall : IEffect, IDisposable {
49+
private class AdvancedAssemblyLinesInstall : IEffect {
4850
public bool Impactful => Installables().Count > 0;
4951
public event Action<IEffect, bool> ChangedImpact = delegate { };
5052
IEnumerable<string> IEffect.Graphics => new string[] { };
@@ -55,19 +57,17 @@ public AdvancedAssemblyLinesInstall(Corp corp) {
5557
corp.zones.hq.Zone.Changed += UpdateInstallables;
5658
}
5759

58-
private IList<Card> Installables() => corp.zones.hq.Zone.Cards.Where(card => (card.Type.Installable && !(card.Type is Agenda))).ToList();
59-
60-
async Task IEffect.Resolve(IPilot pilot) {
61-
var installable = await corp.pilot.ChooseACard().Declare("Which card to install?", Installables());
62-
await corp.Installing.InstallingCard(installable).Resolve(pilot);
63-
}
64-
6560
private void UpdateInstallables(Zone hqZone) {
6661
ChangedImpact(this, Impactful);
6762
}
6863

69-
public void Dispose() {
70-
corp.zones.hq.Zone.Changed -= UpdateInstallables;
64+
private IList<Card> Installables() => corp.zones.hq.Zone.Cards
65+
.Where(card => (card.Type.Installable && !(card.Type is Agenda)))
66+
.ToList();
67+
68+
async Task IEffect.Resolve() {
69+
var installable = await corp.pilot.ChooseACard().Declare("Which card to install?", Installables());
70+
await corp.Installing.InstallingCard(installable).Resolve();
7171
}
7272
}
7373
}

Assets/Scripts/Model/Cards/Corp/PadCampaign.cs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using model.choices.trash;
55
using model.costs;
66
using model.play;
7-
using model.timing;
87
using model.timing.corp;
98

109
namespace model.cards.corp {
@@ -25,27 +24,26 @@ public class PadCampaign : Card {
2524
private IList<CorpDrawPhase> phases = new List<CorpDrawPhase>();
2625

2726
public PadCampaign(Game game) : base(game) {
28-
drip = new Ability(new Free(), game.corp.credits.Gaining(1), this);
27+
drip = new Ability(
28+
cost: new Free(),
29+
effect: game.corp.credits.Gaining(1),
30+
source: this,
31+
mandatory: true
32+
);
2933
}
3034

31-
async protected override Task Activate() {
32-
game.Timing.CorpTurnDefined += RegisterDrip;
33-
await Task.CompletedTask;
35+
protected override Task Activate() {
36+
game.Timing.CorpTurnDefined += DeferDrip;
37+
return Task.CompletedTask;
3438
}
3539

36-
private void RegisterDrip(CorpTurn turn) {
37-
turn.Began += Drip; // TODO actually this should register to a REACTION WINDOW
40+
override protected Task Deactivate() {
41+
game.Timing.CorpTurnDefined -= DeferDrip;
42+
return Task.CompletedTask;
3843
}
3944

40-
async private Task Drip(ITurn turn) {
41-
if (Active) {
42-
await drip.Resolve();
43-
}
44-
}
45-
46-
async override protected Task Deactivate() {
47-
game.Timing.CorpTurnDefined -= RegisterDrip;
48-
await Task.CompletedTask;
45+
private void DeferDrip(CorpTurn turn) {
46+
turn.Begins.Offer(game.corp.pilot, drip);
4947
}
5048
}
5149
}
Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,41 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Threading.Tasks;
1+
using System.Threading.Tasks;
42
using model.cards.types;
3+
using model.costs;
54
using model.effects;
5+
using model.play;
6+
using model.timing.runner;
67

78
namespace model.cards.runner {
89
public class Wyldside : Card {
9-
private readonly WyldsideTrigger trigger;
10-
11-
public Wyldside(Game game) : base(game) {
12-
trigger = new WyldsideTrigger(game.runner);
13-
}
14-
10+
private Ability party;
1511
override public string FaceupArt => "wyldside";
1612
override public string Name => "Wyldside";
1713
override public Faction Faction => Factions.ANARCH;
1814
override public int InfluenceCost => 3;
1915
override public ICost PlayCost => game.runner.credits.PayingForPlaying(this, 3);
2016
override public IType Type => new Resource(game);
2117

22-
protected override Task Activate() {
23-
game.runner.turn.WhenBegins(trigger);
18+
public Wyldside(Game game) : base(game) {
19+
party = new Ability(
20+
cost: new Free(),
21+
new Sequence(game.runner.zones.Drawing(2), game.runner.clicks.Losing(1)),
22+
source: this,
23+
mandatory: true
24+
);
2425
}
2526

26-
private class WyldsideActivation : IEffect {
27-
private Runner runner;
28-
29-
public bool Impactful => true;
30-
public event Action<IEffect, bool> ChangedImpact = delegate { };
31-
IEnumerable<string> IEffect.Graphics => new string[] { };
32-
33-
public WyldsideActivation(Runner runner) {
34-
this.runner = runner;
35-
trigger = new WyldsideTrigger(runner);
36-
}
37-
38-
async Task IEffect.Resolve() {
39-
runner.turn.WhenBegins(trigger);
40-
await Task.CompletedTask;
41-
}
27+
async protected override Task Activate() {
28+
game.Timing.RunnerTurnDefined += DeferParty;
29+
await Task.CompletedTask;
4230
}
4331

44-
private class WyldsideTrigger : IEffect {
45-
private IEffect sequence;
46-
public bool Impactful => sequence.Impactful;
47-
public event Action<IEffect, bool> ChangedImpact = delegate { };
48-
IEnumerable<string> IEffect.Graphics => new[] { "wyldside" };
49-
50-
public WyldsideTrigger(Runner runner) {
51-
sequence = new Sequence(runner.zones.Drawing(2), runner.clicks.Losing(1));
52-
sequence.ChangedImpact += ChangedImpact;
53-
}
32+
override protected Task Deactivate() {
33+
game.Timing.RunnerTurnDefined -= DeferParty;
34+
return Task.CompletedTask;
35+
}
5436

55-
async Task IEffect.Resolve() {
56-
await sequence.Resolve();
57-
}
37+
private void DeferParty(RunnerTurn turn) {
38+
turn.Begins.Offer(game.runner.pilot, party);
5839
}
5940
}
6041
}

Assets/Scripts/Model/Install/GenericInstall.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ async private Task PayInstallCost(IInstallDestination destination)
9898
async private Task Place(IInstallDestination destination)
9999
{
100100
await destination.Host(card);
101-
card.SetInstalled();
101+
await card.SetInstalled();
102102
if (card.Faction.Side == Side.RUNNER)
103103
{
104104
// CR: 8.2.3
105105
card.FlipFaceUp();
106-
await card.Activate();
106+
await card.BecomeActive();
107107
}
108108
}
109109

Assets/Scripts/Model/Play/Ability.cs

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,54 @@
11
using System;
22
using System.Threading.Tasks;
33

4-
namespace model.play
5-
{
6-
public class Ability : IPlayOption
7-
{
4+
namespace model.play {
5+
public class Ability : IPlayOption {
86
public readonly ICost cost;
97
public readonly IEffect effect;
108
public readonly ISource source;
9+
public bool Mandatory { get; }
10+
private bool active;
1111
public event Action<Ability, bool> UsabilityChanged = delegate { };
1212
public event Action<Ability> Resolved = delegate { };
13-
public bool Active { get; private set; }
13+
1414
public bool Usable => cost.Payable && effect.Impactful; // CR: 1.2.5
15-
public bool Legal => Active && Usable;
15+
public bool Legal => active && Usable;
1616

17-
public Ability(ICost cost, IEffect effect, ISource source)
18-
{
17+
public Ability(ICost cost, IEffect effect, ISource source, bool mandatory) {
1918
this.cost = cost;
2019
this.effect = effect;
2120
this.source = source;
22-
Active = source.Active; // CR: 9.1.8
21+
this.Mandatory = mandatory;
22+
active = source.Active; // CR: 9.1.8
23+
source.ChangedActivation += UpdateActivity;
2324
cost.ChangedPayability += UpdateCost;
2425
effect.ChangedImpact += UpdateEffect;
2526
}
2627

27-
private void UpdateCost(ICost source, bool payable)
28-
{
28+
private void UpdateActivity(ISource source) {
29+
active = source.Active;
30+
}
31+
32+
private void UpdateCost(ICost source, bool payable) {
2933
UsabilityChanged(this, Usable);
3034
}
3135

32-
private void UpdateEffect(IEffect source, bool impactful)
33-
{
36+
private void UpdateEffect(IEffect source, bool impactful) {
3437
UsabilityChanged(this, Usable);
3538
}
3639

37-
async public Task Resolve()
38-
{
40+
async public Task Resolve() {
3941
await Trigger();
4042
}
4143

42-
async public Task Trigger()
43-
{
44-
await cost.Pay();
45-
await effect.Resolve();
46-
Resolved(this);
44+
async public Task Trigger() {
45+
if (active) {
46+
await cost.Pay();
47+
await effect.Resolve();
48+
Resolved(this);
49+
} else {
50+
throw new System.Exception("Cannot trigger an inactive ability " + this);
51+
}
4752
}
4853

4954
public override string ToString() => cost + " : " + effect;
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using model.player;
34
using model.timing;
45

56
namespace model.play {
67
public class GameRule : ISource {
7-
bool Active { get; }
8-
IList<ITimingStructure> Used { get; } // CR 9.1.6
9-
IPilot Controller { get; }
8+
public bool Active { get; }
9+
public event Action<ISource> ChangedActivation;
10+
public IList<ITimingStructure> Used { get; } // CR 9.1.6
11+
public IPilot Controller { get; }
1012
}
1113
}

Assets/Scripts/View/GUI/TimeCross/FutureTrack.cs.meta renamed to Assets/Scripts/Model/Play/GameRule.cs.meta

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Scripts/Model/Play/IPlayOption.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace model.play
55
public interface IPlayOption
66
{
77
bool Legal { get; }
8+
bool Mandatory { get; }
89
Task Resolve();
910
}
1011
}

Assets/Scripts/Model/Play/ISource.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using model.player;
34
using model.timing;
45

@@ -7,6 +8,7 @@ namespace model.play
78
public interface ISource
89
{
910
bool Active { get; }
11+
event Action<ISource> ChangedActivation;
1012
IList<ITimingStructure> Used { get; } // CR 9.1.6
1113
IPilot Controller { get; }
1214
}

0 commit comments

Comments
 (0)