-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathCivilianFaction.cs
376 lines (345 loc) · 17.1 KB
/
CivilianFaction.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
using Arcen.AIW2.Core;
using Arcen.AIW2.External;
using Arcen.Universal;
using SKCivilianIndustry.Persistence;
using System;
using System.Collections.Generic;
namespace SKCivilianIndustry
{
/// <summary>
/// Invidual storage class for each faction.
/// </summary>
public class CivilianFaction
{
// Version of this class.
public int Version;
// All values stored are the index value of ships. This is done as to simply the process of saving and loading.
// We index all of our faction ships so that they can be easily looped through based on what we're doing.
// Index of this faction's Grand Station.
public int GrandStation;
// Rebuild timer for Grand Station.
public int GrandStationRebuildTimerInSeconds;
// Index of all trade stations that belong to this faction.
public List<int> TradeStations;
// Rebuild timer for Trade Stations by planet index.
public Dictionary<int, int> TradeStationRebuildTimerInSecondsByPlanet;
// Functions for setting and getting rebuild timers.
public int GetTradeStationRebuildTimer( Planet planet )
{
return GetTradeStationRebuildTimer( planet.Index );
}
public int GetTradeStationRebuildTimer( int planet )
{
if ( TradeStationRebuildTimerInSecondsByPlanet.ContainsKey( planet ) )
return TradeStationRebuildTimerInSecondsByPlanet[planet];
else
return 0;
}
public void SetTradeStationRebuildTimer( Planet planet, int timer )
{
SetTradeStationRebuildTimer( planet.Index, timer );
}
public void SetTradeStationRebuildTimer( int planet, int timer )
{
if ( TradeStationRebuildTimerInSecondsByPlanet.ContainsKey( planet ) )
TradeStationRebuildTimerInSecondsByPlanet[planet] = timer;
else
TradeStationRebuildTimerInSecondsByPlanet.Add( planet, timer );
}
// Index of all cargo ships that belong to this faction.
public List<int> CargoShips;
// List of all cargo ships by current status that belong to this faction.
public List<int> CargoShipsIdle;
public List<int> CargoShipsLoading;
public List<int> CargoShipsUnloading;
public List<int> CargoShipsBuilding;
public List<int> CargoShipsPathing;
public List<int> CargoShipsEnroute;
// Index of all Militia Construction Ships and/or Militia Buildings
public List<int> MilitiaLeaders;
// Counter used to determine when another cargo ship should be built.
public int BuildCounter;
/// <summary>
/// Last reported number of failed trade routes due to a lack of cargo ships.
/// </summary>
public (int Import, int Export) FailedCounter;
// Counter used to determine when another militia ship should be built.
public int MilitiaCounter;
// How long until the next raid?
public int NextRaidInThisSeconds;
// Index of wormholes for the next raid.
public List<int> NextRaidWormholes;
// Unlike every other value, the follow values are not stored and saved. They are simply regenerated whenever needed.
// Contains the calculated threat value on every planet.
// Calculated threat is all hostile strength - all friendly (excluding our own) strength.
public List<ThreatReport> ThreatReports;
public List<TradeRequest> ImportRequests;
public List<TradeRequest> ExportRequests;
// Get the threat value for a planet.
public (int MilitiaGuard, int MilitiaMobile, int FriendlyGuard, int FriendlyMobile, int CloakedHostile, int NonCloakedHostile, int Wave, int Total) GetThreat( Planet planet )
{
try
{
// If reports aren't generated, return 0.
if ( ThreatReports == null )
return (0, 0, 0, 0, 0, 0, 0, 0);
else
for ( int x = 0; x < ThreatReports.Count; x++ )
if ( ThreatReports[x].Planet.Index == planet.Index )
return ThreatReports[x].GetThreat();
// Planet not processed. Return 0.
return (0, 0, 0, 0, 0, 0, 0, 0);
}
catch ( Exception e )
{
// Failed to return a report, return 0. Harmless, so we don't worry about informing the player.
ArcenDebugging.SingleLineQuickDebug( e.Message );
return (0, 0, 0, 0, 0, 0, 0, 0);
}
}
// Returns the base resource cost for ships.
public int GetResourceCost( Faction faction )
{
// 51 - (Intensity ^ 1.5)
return 51 - (int)Math.Pow( faction.Ex_MinorFactionCommon_GetPrimitives().Intensity, 1.5 );
}
/// <summary>
/// Returns the ship/turret capacity. Increases based on intensity and trade station count.
/// 20 + intensity * 3 + (((5 * StationCount) ^ 0.9) ^ (1.25 + (0.025 * Intensity)))
/// </summary>
/// <returns></returns>
public int GetCap( Faction faction )
{
int baseCap = 20;
int intensity = faction.Ex_MinorFactionCommon_GetPrimitives().Intensity;
int flatBonus = 3 * intensity;
double intensityMult = 1.25 + (0.025 * intensity);
double stationMult = Math.Pow( 5.0 * TradeStations.Count, 0.9 );
double scalingBonus = Math.Pow( stationMult, intensityMult );
int cap = (int)(Math.Ceiling( baseCap + flatBonus + scalingBonus ));
return cap;
}
// Should never be used by itself, removes the cargo ship from all applicable statuses, but keeps it in the main cargo ship list.
private void RemoveCargoShipStatus( int cargoShipID )
{
if ( this.CargoShipsIdle.Contains( cargoShipID ) )
this.CargoShipsIdle.Remove( cargoShipID );
if ( this.CargoShipsLoading.Contains( cargoShipID ) )
this.CargoShipsLoading.Remove( cargoShipID );
if ( this.CargoShipsUnloading.Contains( cargoShipID ) )
this.CargoShipsUnloading.Remove( cargoShipID );
if ( this.CargoShipsBuilding.Contains( cargoShipID ) )
this.CargoShipsBuilding.Remove( cargoShipID );
if ( this.CargoShipsPathing.Contains( cargoShipID ) )
this.CargoShipsPathing.Remove( cargoShipID );
if ( this.CargoShipsEnroute.Contains( cargoShipID ) )
this.CargoShipsEnroute.Remove( cargoShipID );
}
/// <summary>
/// Remove a cargo ship from amy list it is currently in, effectively deleting it from the faction, but NOT from the world.
/// The entity itself must be killed or despawned before or after this.
/// </summary>
/// <param name="cargoShipID">The PrimaryKeyID of the ship to remove.</param>
public void RemoveCargoShip( int cargoShipID )
{
if ( this.CargoShips.Contains( cargoShipID ) )
this.CargoShips.Remove( cargoShipID );
RemoveCargoShipStatus( cargoShipID );
}
/// <summary>
/// Remove a cargo ship from whatever it is currently doing, and change its action to the requested action.
/// </summary>
/// <param name="cargoShipID">The PrimaryKeyID of the ship to modify.</param>
/// <param name="status">The status to change to. Idle, Loading, Unloading, Building, Pathing, or Enroute</param>
public void ChangeCargoShipStatus( GameEntity_Squad cargoShip, string status )
{
int cargoShipID = cargoShip.PrimaryKeyID;
if ( !this.CargoShips.Contains( cargoShipID ) )
return;
RemoveCargoShipStatus( cargoShipID );
switch ( status )
{
case "Loading":
this.CargoShipsLoading.Add( cargoShipID );
break;
case "Unloading":
this.CargoShipsUnloading.Add( cargoShipID );
break;
case "Building":
this.CargoShipsBuilding.Add( cargoShipID );
break;
case "Pathing":
this.CargoShipsPathing.Add( cargoShipID );
break;
case "Enroute":
this.CargoShipsEnroute.Add( cargoShipID );
break;
default:
this.CargoShipsIdle.Add( cargoShipID );
break;
}
}
/// <summary>
/// Returns true if we should consider the planet friendly.
/// </summary>
/// <param name="faction">The Civilian Industry faction to check.</param>
/// <param name="planet">The Planet to check.</param>
/// <returns></returns>
public bool IsPlanetFriendly( Faction faction, Planet planet )
{
if ( planet.GetControllingOrInfluencingFaction().GetIsFriendlyTowards( faction ) )
return true; // If planet is owned by a friendly faction, its friendly.
for ( int x = 0; x < TradeStations.Count; x++ )
{
GameEntity_Squad tradeStation = World_AIW2.Instance.GetEntityByID_Squad( TradeStations[x] );
if ( tradeStation == null )
continue;
if ( tradeStation.Planet.Index == planet.Index )
return true; // Planet has a trade station on it, its friendly.
}
for ( int x = 0; x < MilitiaLeaders.Count; x++ )
{
GameEntity_Squad militia = World_AIW2.Instance.GetEntityByID_Squad( MilitiaLeaders[x] );
if ( militia == null )
continue;
if ( militia.Planet.Index == planet.Index )
return true; // Planet has a militia leader on it, its friendly.
CivilianMilitia militiaData = militia.GetCivilianMilitiaExt();
if ( militiaData.Centerpiece == -1 )
continue;
GameEntity_Squad centerpiece = World_AIW2.Instance.GetEntityByID_Squad( militiaData.Centerpiece );
if ( centerpiece == null )
continue;
if ( centerpiece.Planet.Index == planet.Index )
return true; // Planet has a militia leader's centerpiece on it, its friendly.
}
// Nothing passed. Its hostile.
return false;
}
// Following three functions are used for initializing, saving, and loading data.
// Initialization function.
// Default values. Called on creation, NOT on load.
public CivilianFaction()
{
this.GrandStation = -1;
this.GrandStationRebuildTimerInSeconds = 0;
this.TradeStations = new List<int>();
this.TradeStationRebuildTimerInSecondsByPlanet = new Dictionary<int, int>();
this.CargoShips = new List<int>();
this.CargoShipsIdle = new List<int>();
this.CargoShipsLoading = new List<int>();
this.CargoShipsUnloading = new List<int>();
this.CargoShipsBuilding = new List<int>();
this.CargoShipsPathing = new List<int>();
this.CargoShipsEnroute = new List<int>();
this.MilitiaLeaders = new List<int>();
this.BuildCounter = 0;
this.MilitiaCounter = 0;
this.NextRaidInThisSeconds = 1800;
this.NextRaidWormholes = new List<int>();
this.ThreatReports = new List<ThreatReport>();
this.ImportRequests = new List<TradeRequest>();
this.ExportRequests = new List<TradeRequest>();
}
// Serialize a list.
private void SerializeList( List<int> list, ArcenSerializationBuffer Buffer )
{
// Lists require a special touch to save.
// Get the number of items in the list, and store that as well.
// This is so you know how many items you'll have to load later.
int count = list.Count;
Buffer.AddInt32( ReadStyle.NonNeg, count );
for ( int x = 0; x < count; x++ )
Buffer.AddInt32( ReadStyle.Signed, list[x] );
}
private void SerializeDictionary( Dictionary<int, int> dict, ArcenSerializationBuffer Buffer )
{
Buffer.AddInt32( ReadStyle.NonNeg, dict.Count );
foreach ( int key in dict.Keys )
{
Buffer.AddInt32( ReadStyle.NonNeg, key );
Buffer.AddInt32( ReadStyle.Signed, dict[key] );
}
}
// Saving our data.
public void SerializeTo( ArcenSerializationBuffer Buffer )
{
Buffer.AddInt32( ReadStyle.NonNeg, 2 );
Buffer.AddInt32( ReadStyle.Signed, this.GrandStation );
Buffer.AddInt32( ReadStyle.Signed, this.GrandStationRebuildTimerInSeconds );
SerializeList( TradeStations, Buffer );
SerializeDictionary( TradeStationRebuildTimerInSecondsByPlanet, Buffer );
SerializeList( CargoShips, Buffer );
SerializeList( MilitiaLeaders, Buffer );
SerializeList( CargoShipsIdle, Buffer );
SerializeList( CargoShipsLoading, Buffer );
SerializeList( CargoShipsUnloading, Buffer );
SerializeList( CargoShipsBuilding, Buffer );
SerializeList( CargoShipsPathing, Buffer );
SerializeList( CargoShipsEnroute, Buffer );
Buffer.AddInt32( ReadStyle.Signed, this.BuildCounter );
Buffer.AddInt32( ReadStyle.NonNeg, this.FailedCounter.Import );
Buffer.AddInt32( ReadStyle.NonNeg, this.FailedCounter.Export );
Buffer.AddInt32( ReadStyle.Signed, this.MilitiaCounter );
Buffer.AddInt32( ReadStyle.Signed, this.NextRaidInThisSeconds );
SerializeList( this.NextRaidWormholes, Buffer );
}
// Deserialize a list.
public List<int> DeserializeList( ArcenDeserializationBuffer Buffer )
{
// Lists require a special touch to load.
// We'll have saved the number of items stored up above to be used here to determine the number of items to load.
// ADDITIONALLY we'll need to recreate a blank list beforehand, as loading does not call the Initialization function.
// Can't add values to a list that doesn't exist, after all.
int count = Buffer.ReadInt32( ReadStyle.NonNeg );
List<int> newList = new List<int>();
for ( int x = 0; x < count; x++ )
newList.Add( Buffer.ReadInt32( ReadStyle.Signed ) );
return newList;
}
public Dictionary<int, int> DeserializeDictionary( ArcenDeserializationBuffer Buffer )
{
int count = Buffer.ReadInt32( ReadStyle.NonNeg );
Dictionary<int, int> newDict = new Dictionary<int, int>();
for ( int x = 0; x < count; x++ )
{
int key = Buffer.ReadInt32( ReadStyle.NonNeg );
int value = Buffer.ReadInt32( ReadStyle.Signed );
if ( !newDict.ContainsKey( key ) )
newDict.Add( key, value );
else
newDict[key] = value;
}
return newDict;
}
// Loading our data. Make sure the loading order is the same as the saving order.
public CivilianFaction( ArcenDeserializationBuffer Buffer )
{
this.Version = Buffer.ReadInt32( ReadStyle.NonNeg );
this.GrandStation = Buffer.ReadInt32( ReadStyle.Signed );
this.GrandStationRebuildTimerInSeconds = Buffer.ReadInt32( ReadStyle.Signed );
this.TradeStations = DeserializeList( Buffer );
this.TradeStationRebuildTimerInSecondsByPlanet = DeserializeDictionary( Buffer );
this.CargoShips = DeserializeList( Buffer );
this.MilitiaLeaders = DeserializeList( Buffer );
this.CargoShipsIdle = DeserializeList( Buffer );
this.CargoShipsLoading = DeserializeList( Buffer );
this.CargoShipsUnloading = DeserializeList( Buffer );
this.CargoShipsBuilding = DeserializeList( Buffer );
this.CargoShipsPathing = DeserializeList( Buffer );
this.CargoShipsEnroute = DeserializeList( Buffer );
this.BuildCounter = Buffer.ReadInt32( ReadStyle.Signed );
if ( this.Version >= 2 )
this.FailedCounter = (Buffer.ReadInt32( ReadStyle.NonNeg ), Buffer.ReadInt32( ReadStyle.NonNeg ));
else
this.FailedCounter = (0, 0);
this.MilitiaCounter = Buffer.ReadInt32( ReadStyle.Signed );
this.NextRaidInThisSeconds = Buffer.ReadInt32( ReadStyle.Signed );
this.NextRaidWormholes = DeserializeList( Buffer );
// Recreate an empty list on load. Will be populated when needed.
this.ThreatReports = new List<ThreatReport>();
this.ImportRequests = new List<TradeRequest>();
this.ExportRequests = new List<TradeRequest>();
}
}
}