diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 90181d0c..ef32b8a3 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -9,6 +9,9 @@ on: # Publish `main` as Docker `latest` image. branches: [playtest/*] + # Publish `v1.2.3` tags as releases. + tags: [v*] + env: IMAGE_NAME: edwardcarmack/gearforce-web diff --git a/assets/data/units/new_jerusalem.json b/assets/data/units/new_jerusalem.json new file mode 100644 index 00000000..e9a08644 --- /dev/null +++ b/assets/data/units/new_jerusalem.json @@ -0,0 +1,817 @@ +{ + "frames": [ + { + "name": "Initiate", + "variants": [ + { + "model": "Initiate", + "tv": 8, + "role": "GP+, SK, FS", + "mr": "W:6", + "arm": 6, + "h/s": "4/2", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "LPL, LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Nahum Initiate", + "tv": 8, + "role": "GP, SK, FS", + "mr": "W:6", + "arm": 6, + "h/s": "4/2", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "MFL, LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Tobit Initiate", + "tv": 9, + "role": "GP, SK, FS", + "mr": "W:6", + "arm": 6, + "h/s": "4/2", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "LSC (Corrosion), LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Judges Initiate", + "tv": 10, + "role": "GP, SK, FS", + "mr": "W:6", + "arm": 6, + "h/s": "4/2", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "MFC (Advanced), LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Esdras Initiate", + "tv": 8, + "role": "GP, SK, FS", + "mr": "W:6", + "arm": 6, + "h/s": "4/2", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "MSMG, LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Jump Jets:2", + "type": "Gear", + "height": 1.5 + } + ] + }, + { + "name": "Apostle", + "variants": [ + { + "model": "Apostle", + "tv": 12, + "role": "SK, FS", + "mr": "W:7", + "arm": 7, + "h/s": "4/2", + "a": 1, + "gu": "3+", + "pi": "3+", + "ew": "5+", + "react-weapons": "LPL, LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Osee Apostle", + "tv": 12, + "role": "SK, FS", + "mr": "W:7", + "arm": 7, + "h/s": "4/2", + "a": 1, + "gu": "3+", + "pi": "3+", + "ew": "5+", + "react-weapons": "LLC, LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Isaias Apostle", + "tv": 12, + "role": "SK, FS", + "mr": "W:7", + "arm": 7, + "h/s": "4/2", + "a": 1, + "gu": "3+", + "pi": "3+", + "ew": "5+", + "react-weapons": "LPA, LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Malachias Apostle", + "tv": 13, + "role": "SK, FS", + "mr": "W:7", + "arm": 7, + "h/s": "4/2", + "a": 1, + "gu": "3+", + "pi": "3+", + "ew": "5+", + "react-weapons": "MRL, LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Jonas Apostle", + "tv": 13, + "role": "SK, FS", + "mr": "W:7", + "arm": 7, + "h/s": "4/2", + "a": 1, + "gu": "3+", + "pi": "3+", + "ew": "5+", + "react-weapons": "MGL (AI), LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Jump Jets:2", + "type": "Gear", + "height": 1.5 + } + ] + }, + { + "name": "Disciple", + "variants": [ + { + "model": "Disciple", + "tv": 11, + "role": "RC, FS", + "mr": "W:8", + "arm": 5, + "h/s": "3/3", + "a": 1, + "gu": "4+", + "pi": "3+", + "ew": "4+", + "react-weapons": "LP (Advanced), LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Comms, ECM+, ECCM, Faithful, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Abdias Disciple", + "tv": 12, + "role": "RC, FS", + "mr": "W:8", + "arm": 5, + "h/s": "3/3", + "a": 1, + "gu": "4+", + "pi": "3+", + "ew": "4+", + "react-weapons": "LP (Advanced), LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Comms, ECM+, ECCM, Faithful, Jump Jets:2, Sensor Boom, TD, SatUp, Sensors:36", + "type": "Gear", + "height": 1.5 + } + ] + }, + { + "name": "Paladin", + "variants": [ + { + "model": "Paladin", + "tv": 15, + "role": "SK, FS", + "mr": "W:6", + "arm": 8, + "h/s": "4/2", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "MP (Advanced Link), LVB", + "mounted-weapons": "LFG", + "traits": "Hands, React+, Jump Jets:2", + "type": "Gear", + "height": 2 + }, + { + "model": "Songs Paladin", + "tv": 16, + "role": "SK, FS", + "mr": "W:6", + "arm": 8, + "h/s": "4/2", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "MP (Advanced Link), LVB", + "mounted-weapons": "MRC (Link)", + "traits": "Hands, React+, Jump Jets:2", + "type": "Gear", + "height": 2 + }, + { + "model": "Ezechial Paladin", + "tv": 15, + "role": "SK, FS", + "mr": "W:6", + "arm": 8, + "h/s": "4/2", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "MP (Advanced Link), LVB", + "mounted-weapons": "LRG", + "traits": "Hands, React+, Jump Jets:2", + "type": "Gear", + "height": 2 + }, + { + "model": "Tobit Paladin", + "tv": 15, + "role": "SK, FS", + "mr": "W:6", + "arm": 8, + "h/s": "4/2", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "MP (Advanced Link), LVB", + "mounted-weapons": "MSC (Link Corrosion)", + "traits": "Hands, React+, Jump Jets:2", + "type": "Gear", + "height": 2 + } + ] + }, + { + "name": "Templar", + "variants": [ + { + "model": "Templar", + "tv": 10, + "role": "SK, FS", + "mr": "W:8", + "arm": 7, + "h/s": "4/2", + "a": 2, + "gu": "4+", + "pi": "3+", + "ew": "5+", + "react-weapons": "MVB (Reach:1 Fire:1)", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Shield, Brawl:2, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Psalms Templar", + "tv": 11, + "role": "SK, FS", + "mr": "W:8", + "arm": 7, + "h/s": "4/2", + "a": 2, + "gu": "4+", + "pi": "3+", + "ew": "5+", + "react-weapons": "MVB (Link Fire:1)", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Brawl:2, Jump Jets:2", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Proverbs Templar", + "tv": 11, + "role": "SK, FS", + "mr": "W:8", + "arm": 7, + "h/s": "4/2", + "a": 2, + "gu": "4+", + "pi": "3+", + "ew": "5+", + "react-weapons": "HVB (Reach:2 Fire:2)", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Brawl:2, Faithful, Jump Jets:2", + "type": "Gear", + "height": 1.5 + } + ] + }, + { + "name": "Hakham", + "variants": [ + { + "model": "Hakham", + "tv": 21, + "role": "SK, FS", + "mr": "W:6", + "arm": 9, + "h/s": "4/2", + "a": 2, + "gu": "4+", + "pi": "4+", + "ew": "4+", + "react-weapons": "MPL, MVB", + "mounted-weapons": "MFM, MAPGL", + "traits": "Hands, AMS, ECCM, Comms, SatUp, Jump Jets:2, Sensors:24 (Aux)", + "type": "Gear", + "height": 2 + }, + { + "model": "Isaias Hakham", + "tv": 22, + "role": "SK, FS", + "mr": "W:6", + "arm": 9, + "h/s": "4/2", + "a": 2, + "gu": "4+", + "pi": "4+", + "ew": "4+", + "react-weapons": "MPA, MVB", + "mounted-weapons": "MFM, MAPGL", + "traits": "Hands, AMS, ECCM, Comms, SatUp, Jump Jets:2, Sensors:24 (Aux)", + "type": "Gear", + "height": 2 + }, + { + "model": "Malachias Hakham", + "tv": 23, + "role": "SK, FS", + "mr": "W:6", + "arm": 9, + "h/s": "4/2", + "a": 2, + "gu": "4+", + "pi": "4+", + "ew": "4+", + "react-weapons": "HRL, MVB", + "mounted-weapons": "MFM, MAPGL", + "traits": "Hands, AMS, ECCM, Comms, SatUp, Jump Jets:2, Sensors:24 (Aux)", + "type": "Gear", + "height": 2 + } + ] + }, + { + "name": "Ghazi", + "variants": [ + { + "model": "Ghzai", + "tv": 13, + "role": "SK, SO", + "mr": "W:8", + "arm": 7, + "h/s": "3/3", + "a": 1, + "gu": "3+", + "pi": "3+", + "ew": "5+", + "react-weapons": "MPL (Silent), LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Airdrop, Stealth, Jump Jets:2, Vet", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Ezechial Ghzai", + "tv": 15, + "role": "SK, SO", + "mr": "W:8", + "arm": 7, + "h/s": "3/3", + "a": 1, + "gu": "3+", + "pi": "3+", + "ew": "5+", + "react-weapons": "LRG (Silent), LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Airdrop, Stealth, Jump Jets:2, Vet", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Jonas Ghzai", + "tv": 13, + "role": "SK, SO", + "mr": "W:8", + "arm": 7, + "h/s": "3/3", + "a": 1, + "gu": "3+", + "pi": "3+", + "ew": "5+", + "react-weapons": "MGL (AI), LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Airdrop, Stealth, Jump Jets:2, Vet", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Wisdom Ghzai", + "tv": 13, + "role": "SK, SO", + "mr": "W:8", + "arm": 7, + "h/s": "3/3", + "a": 1, + "gu": "3+", + "pi": "3+", + "ew": "5+", + "react-weapons": "LPL (Link AA), LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Airdrop, Stealth, Jump Jets:2, Vet, AMS", + "type": "Gear", + "height": 1.5 + }, + { + "model": "Osee Ghzai", + "tv": 13, + "role": "SK, SO", + "mr": "W:8", + "arm": 7, + "h/s": "3/3", + "a": 1, + "gu": "3+", + "pi": "3+", + "ew": "5+", + "react-weapons": "MLC (Silent), LVB", + "mounted-weapons": "LAPGL", + "traits": "Hands, Agile, Airdrop, Stealth, Jump Jets:2, Vet", + "type": "Gear", + "height": 1.5 + } + ] + }, + { + "name": "Prioress", + "variants": [ + { + "model": "Prioress", + "tv": 25, + "role": "SK, FS", + "mr": "W:6", + "arm": 10, + "h/s": "4/4", + "a": 2, + "gu": "4+", + "pi": "5+", + "ew": "6+", + "react-weapons": "LRL (T AA), MCW", + "mounted-weapons": "LRG (T), MATM (T)", + "traits": "React+, Stable", + "type": "Strider", + "height": 1.5 + }, + { + "model": "Nahum Prioress", + "tv": 24, + "role": "SK, FS", + "mr": "W:6", + "arm": 10, + "h/s": "4/4", + "a": 2, + "gu": "4+", + "pi": "5+", + "ew": "6+", + "react-weapons": "LRL (T AA), MCW", + "mounted-weapons": "HFL (T Apex), MAPR (T Fire:1)", + "traits": "React+, Stable", + "type": "Strider", + "height": 1.5 + }, + { + "model": "Songs Prioress", + "tv": 25, + "role": "SK, FS", + "mr": "W:6", + "arm": 10, + "h/s": "4/4", + "a": 2, + "gu": "4+", + "pi": "5+", + "ew": "6+", + "react-weapons": "LRL (T AA), MCW", + "mounted-weapons": "HRC (T), HRP (T)", + "traits": "React+, Stable", + "type": "Strider", + "height": 1.5 + } + ] + }, + { + "name": "Abbess", + "variants": [ + { + "model": "Abbess", + "tv": 34, + "role": "FS", + "mr": "W:5", + "arm": 12, + "h/s": "4/4", + "a": 3, + "gu": "4+", + "pi": "6+", + "ew": "5+", + "react-weapons": "LRL (T AA), MFL (T), HCW", + "mounted-weapons": "MRG (T Link), HRP (T Link)", + "traits": "Lumbering, Stable", + "type": "Strider", + "height": 2 + }, + { + "model": "Jeremias Abbess", + "tv": 35, + "role": "FS", + "mr": "W:5", + "arm": 12, + "h/s": "4/4", + "a": 3, + "gu": "4+", + "pi": "6+", + "ew": "5+", + "react-weapons": "LRL (T AA), MFL (T), HCW", + "mounted-weapons": "MRG (T Link), MATM (T Link)", + "traits": "Lumbering, Stable", + "type": "Strider", + "height": 2 + }, + { + "model": "Habacuc Abbess", + "tv": 35, + "role": "FS", + "mr": "W:5", + "arm": 12, + "h/s": "4/4", + "a": 3, + "gu": "4+", + "pi": "6+", + "ew": "5+", + "react-weapons": "LRL (T AA), MFL (T), HCW", + "mounted-weapons": "MRG (T Link), HABM (T Link)", + "traits": "Lumbering, Stable", + "type": "Strider", + "height": 2 + }, + { + "model": "Ezra Abbess", + "tv": 36, + "role": "FS", + "mr": "W:5", + "arm": 12, + "h/s": "4/4", + "a": 3, + "gu": "4+", + "pi": "6+", + "ew": "5+", + "react-weapons": "LRL (T AA), MFL (T), HCW", + "mounted-weapons": "MRG (T Link), HAR (T Link)", + "traits": "Lumbering, Stable", + "type": "Strider", + "height": 2 + } + ] + }, + { + "name": "Kwaja", + "variants": [ + { + "model": "Kwaja", + "tv": 5, + "role": "RC, SK", + "mr": "H:8", + "arm": 2, + "h/s": "2/2", + "a": 1, + "gu": "4+", + "pi": "3+", + "ew": "6+", + "react-weapons": "LIW, LICW", + "mounted-weapons": "", + "traits": "VTOL", + "type": "Cavalry", + "height": 1 + }, + { + "model": "Abidas Kwaja", + "tv": 7, + "role": "RC, SK", + "mr": "H:8", + "arm": 3, + "h/s": "1/2", + "a": 1, + "gu": "4+", + "pi": "3+", + "ew": "6+", + "react-weapons": "LIW, LIGL, LICW", + "mounted-weapons": "", + "traits": "VTOL, Sensors:18, TD", + "type": "Cavalry", + "height": 1 + } + ] + }, + { + "name": "Zealot", + "variants": [ + { + "model": "Zealot", + "tv": 7, + "role": "SK, RC", + "mr": "H:8", + "arm": 3, + "h/s": "3/3", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "MMG (Link), LIW, LICW", + "mounted-weapons": "", + "traits": "VTOL", + "type": "Vehicle", + "height": 1.5 + }, + { + "model": "Judith Zealot", + "tv": 7, + "role": "SK, RC", + "mr": "H:8", + "arm": 3, + "h/s": "3/3", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "MIL (Link), LIW, LICW", + "mounted-weapons": "", + "traits": "VTOL", + "type": "Vehicle", + "height": 1.5 + }, + { + "model": "Tobit Zealot", + "tv": 7, + "role": "SK, RC", + "mr": "H:8", + "arm": 3, + "h/s": "3/3", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "LPZ (Link), LIW, LICW", + "mounted-weapons": "", + "traits": "VTOL", + "type": "Vehicle", + "height": 1.5 + } + ] + }, + { + "name": "Tenet", + "variants": [ + { + "model": "Tenet", + "tv": 8, + "role": "GP, SK, FS", + "mr": "G:7", + "arm": 6, + "h/s": "4/4", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "LSMG (T)", + "mounted-weapons": "", + "traits": "Offroad, Transport: 2 Squads", + "type": "Vehicle", + "height": 1 + }, + { + "model": "Numbers Tenet", + "tv": 8, + "role": "GP, SK, FS", + "mr": "G:7", + "arm": 6, + "h/s": "4/4", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "LSMG (T)", + "mounted-weapons": "", + "traits": "Offroad, Medic, Transport: 1 Squads", + "type": "Vehicle", + "height": 1 + }, + { + "model": "Tiferet", + "tv": 8, + "role": "GP, SK, FS", + "mr": "G:7", + "arm": 6, + "h/s": "4/4", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "6+", + "react-weapons": "LSMG (T)", + "mounted-weapons": "LCW (Link)", + "traits": "Offroad, Repair, Supply", + "type": "Vehicle", + "height": 1 + }, + { + "model": "ArchTenet", + "tv": 10, + "role": "GP, SK, FS", + "mr": "G:7", + "arm": 6, + "h/s": "4/4", + "a": 1, + "gu": "4+", + "pi": "4+", + "ew": "5+", + "react-weapons": "LSMG (T)", + "mounted-weapons": "", + "traits": "Offroad, ECCM, Comms, SatUp", + "type": "Vehicle", + "height": 1 + } + ] + }, + { + "name": "Prophet", + "variants": [ + { + "model": "Prophet", + "tv": 75, + "role": "FS", + "mr": "W:4", + "arm": 12, + "h/s": "10/6", + "a": 6, + "gu": "3+", + "pi": "6+", + "ew": "5+", + "react-weapons": "2 x HPA, 2 x HLC, 2 x MFL, HCW", + "mounted-weapons": "", + "traits": "Comms, SatUp, ECCM, Lumbering, Resist:H, Stable", + "type": "Strider", + "height": 4 + } + ] + } + ] +} \ No newline at end of file diff --git a/lib/data/data.dart b/lib/data/data.dart index 1ce8b5e0..dd9b3d32 100644 --- a/lib/data/data.dart +++ b/lib/data/data.dart @@ -14,6 +14,7 @@ const Map _factionUnitFiles = { FactionType.CEF: 'assets/data/units/cef.json', FactionType.Caprice: 'assets/data/units/caprice.json', FactionType.Eden: 'assets/data/units/eden.json', + FactionType.NewJerusalem: 'assets/data/units/new_jerusalem.json', FactionType.North: 'assets/data/units/north.json', FactionType.NuCoal: 'assets/data/units/nucoal.json', FactionType.PeaceRiver: 'assets/data/units/peace_river.json', @@ -150,6 +151,7 @@ class Data { Faction.cef(this), Faction.eden(this), Faction.leagueless(this), + Faction.new_jerusalem(this), Faction.north(this), Faction.nucoal(this), Faction.peaceRiver(this), diff --git a/lib/models/factions/faction.dart b/lib/models/factions/faction.dart index 857bfa9e..b87686c8 100644 --- a/lib/models/factions/faction.dart +++ b/lib/models/factions/faction.dart @@ -5,6 +5,7 @@ import 'package:gearforce/models/rules/caprice/caprice.dart'; import 'package:gearforce/models/rules/cef/cef.dart'; import 'package:gearforce/models/rules/eden/eden.dart'; import 'package:gearforce/models/rules/leagueless/leagueless.dart'; +import 'package:gearforce/models/rules/new_jerusalem/new_jerusalem.dart'; import 'package:gearforce/models/rules/north/north.dart'; import 'package:gearforce/models/rules/nucoal/nucoal.dart'; import 'package:gearforce/models/rules/peace_river/peace_river.dart'; @@ -121,6 +122,14 @@ class Faction { return Faction(FactionType.Leagueless, rulesets, rulesets.first); } + factory Faction.new_jerusalem(Data data) { + List rulesets = [ + NewJerusalem(data, name: _emptySubFactionName), + ]; + + return Faction(FactionType.NewJerusalem, rulesets, rulesets.first); + } + factory Faction.fromType(FactionType faction, Data data) { switch (faction) { case FactionType.North: @@ -155,6 +164,8 @@ class Faction { break; case FactionType.None: break; + case FactionType.NewJerusalem: + return Faction.new_jerusalem(data); } throw FormatException('Cannot create a faction from ${faction.name}'); } diff --git a/lib/models/factions/faction_type.dart b/lib/models/factions/faction_type.dart index afd8c924..2b15444e 100644 --- a/lib/models/factions/faction_type.dart +++ b/lib/models/factions/faction_type.dart @@ -8,6 +8,7 @@ enum FactionType { Caprice('Caprice'), Utopia('Utopia'), Eden('Eden'), + NewJerusalem('New Jerusalem'), Universal('Universal'), Universal_TerraNova('Terra Nova Universal'), Universal_Non_TerraNova('Non Terra Nova Universal'), diff --git a/lib/models/mods/unitUpgrades/new_jerusalem.dart b/lib/models/mods/unitUpgrades/new_jerusalem.dart new file mode 100644 index 00000000..6b690349 --- /dev/null +++ b/lib/models/mods/unitUpgrades/new_jerusalem.dart @@ -0,0 +1,326 @@ +import 'package:gearforce/models/mods/mods.dart'; +import 'package:gearforce/models/mods/unitUpgrades/unit_modification.dart'; +import 'package:gearforce/models/traits/trait.dart'; +import 'package:gearforce/models/unit/movement.dart'; +import 'package:gearforce/models/unit/unit.dart'; +import 'package:gearforce/models/unit/unit_attribute.dart'; +import 'package:gearforce/models/weapons/weapons.dart'; + +final UnitModification faithful = UnitModification(name: 'Faithful') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Faithful')) + ..addMod(UnitAttribute.ew, createSetIntMod(5, onlyIfLessThan: true), + description: 'EW 5+') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.Faithful()), + description: '+Faithful'); + +final UnitModification archUpgrade4 = UnitModification(name: 'Arch Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Arch')) + ..addMod(UnitAttribute.ew, createSetIntMod(4, onlyIfLessThan: true), + description: 'EW 4+') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.Comms()), + description: '+Comms'); + +final UnitModification archUpgrade5 = UnitModification(name: 'Arch Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Arch')) + ..addMod(UnitAttribute.ew, createSetIntMod(5, onlyIfLessThan: true), + description: 'EW 5+') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.Comms()), + description: '+Comms'); + +final UnitModification archUpgradeGhazi = UnitModification(name: 'Arch Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(2), description: 'TV +2') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Arch')) + ..addMod(UnitAttribute.ew, createSetIntMod(4, onlyIfLessThan: true), + description: 'EW 4+') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.Comms()), + description: '+Comms') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.SatUp()), + description: '+SatUp') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.ECCM()), + description: '+ECCM'); + +final UnitModification archUpgradeAbbess = + UnitModification(name: 'Arch Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(2), description: 'TV +2') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Arch')) + ..addMod(UnitAttribute.ew, createSetIntMod(4, onlyIfLessThan: true), + description: 'EW 4+') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.Comms()), + description: '+Comms') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.ECCM()), + description: '+ECCM'); + +final UnitModification archUpgradeZealot = + UnitModification(name: 'Arch Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Arch')) + ..addMod(UnitAttribute.armor, createSetIntMod(4), description: 'Arm:4') + ..addMod(UnitAttribute.hull, createSetIntMod(5), description: 'H:5') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.Comms()), + description: '+Comms'); + +final UnitModification genesisUpgrade = + UnitModification(name: 'Genesis Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Genesis')) + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.ECM()), + description: '+ECM') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.ECCM()), + description: '+ECCM'); + +final UnitModification genesisUpgradePrioress = + UnitModification(name: 'Genesis Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(2), description: 'TV +2') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Genesis')) + ..addMod(UnitAttribute.ew, createSetIntMod(4, onlyIfLessThan: true), + description: 'EW 4+') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.ECM()), + description: '+ECM') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.ECCM()), + description: '+ECCM'); + +final UnitModification samsonUpgrade = UnitModification(name: 'Samson Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Samson')) + ..addMod( + UnitAttribute.traits, + createReplaceTraitInList( + oldValue: Trait.Brawl(2), + newValue: Trait.Brawl(4), + ), + description: '-Brawl:2, +Brawl:4'); + +final UnitModification neshUpgrade = UnitModification(name: 'Nesh Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Nesh')) + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.CBS()), + description: '+CBS'); + +final UnitModification zacchaeusUpgrade = UnitModification( + name: 'Zacchaeus Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Zacchaeus')) + ..addMod( + UnitAttribute.weapons, + createAddWeaponToList(buildWeapon('MCW', hasReact: true)!), + description: '+MCW', + ) + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.Climber()), + description: '+Climber') + ..addMod(UnitAttribute.traits, createAddOrCombineTraitToList(Trait.Brawl(1)), + description: '+Brawl:1'); + +final UnitModification sayfUpgrade = UnitModification(name: 'Sayf Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Sayf')) + ..addMod( + UnitAttribute.weapons, + createAddWeaponToList(buildWeapon('LVB (Link)', hasReact: true)!), + description: '+LVB (Link)', + ) + ..addMod(UnitAttribute.traits, createAddOrCombineTraitToList(Trait.Brawl(1)), + description: '+Brawl:1'); + +final UnitModification nissiUpgrade = UnitModification(name: 'Nissi Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(true, 'Nissi')) + ..addMod(UnitAttribute.ew, createSetIntMod(4, onlyIfLessThan: true), + description: 'EW 4+') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.ECM()), + description: '+ECM'); + +final UnitModification enhancedEngine = UnitModification( + name: 'Enhanced Engine') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod( + UnitAttribute.name, createSimpleStringMod(false, 'with Enhanced Engine')) + ..addMod(UnitAttribute.movement, + createSetMovementMod(Movement(type: 'H', rate: 9)), + description: 'H:9'); + +final UnitModification squadKwaja = UnitModification(name: 'Squad Upgrade') + ..addMod(UnitAttribute.tv, createSimpleIntMod(1), description: 'TV +1') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'Squad')) + ..addMod(UnitAttribute.hull, createSetIntMod(4)) + ..addMod(UnitAttribute.structure, createSetIntMod(4), description: 'H/S 4/4'); + +final UnitModification milchamaPack = UnitModification( + name: 'Milchama Pack', + requirementCheck: (rs, ur, cg, unit) { + return hasLessThenTwoPackMods(unit, milchamaPack.name); + }, +) + ..addMod(UnitAttribute.tv, createSimpleIntMod(3), description: 'TV +3') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'w/ Milchama Pack')) + ..addMod(UnitAttribute.weapons, + createAddWeaponToList(buildWeapon('MAG', hasReact: false)!), + description: '+MAG'); + +final UnitModification raavPack = UnitModification( + name: 'Ra\'av Pack', + requirementCheck: (rs, ur, cg, unit) { + return hasLessThenTwoPackMods(unit, raavPack.name); + }, +) + ..addMod(UnitAttribute.tv, createSimpleIntMod(4), description: 'TV +4') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'w/ Ra\'av Pack')) + ..addMod(UnitAttribute.weapons, + createAddWeaponToList(buildWeapon('HRG', hasReact: false)!), + description: '+HRG'); + +final UnitModification mavethPack = UnitModification( + name: 'Maveth Pack', + requirementCheck: (rs, ur, cg, unit) { + return hasLessThenTwoPackMods(unit, mavethPack.name); + }, +) + ..addMod(UnitAttribute.tv, createSimpleIntMod(2), description: 'TV +2') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'w/ Maveth Pack')) + ..addMod(UnitAttribute.weapons, + createAddWeaponToList(buildWeapon('LFG (Link)', hasReact: false)!), + description: '+LFG (Link)'); + +final UnitModification deverPack = UnitModification( + name: 'Dever Pack', + requirementCheck: (rs, ur, cg, unit) { + return hasLessThenTwoPackMods(unit, deverPack.name); + }, +) + ..addMod(UnitAttribute.tv, createSimpleIntMod(3), description: 'TV +3') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'w/ Dever Pack')) + ..addMod(UnitAttribute.weapons, + createAddWeaponToList(buildWeapon('MAM', hasReact: false)!), + description: '+MAM'); + +final UnitModification kibushPack = UnitModification( + name: 'Kibush Pack', + requirementCheck: (rs, ur, cg, unit) { + return hasLessThenTwoPackMods(unit, kibushPack.name); + }, +) + ..addMod(UnitAttribute.tv, createSimpleIntMod(5), description: 'TV +5') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'w/ Kibush Pack')) + ..addMod(UnitAttribute.ew, createSetIntMod(4, onlyIfLessThan: true), + description: 'EW 4+') + ..addMod(UnitAttribute.weapons, + createAddWeaponToList(buildWeapon('MRL (AA)', hasReact: true)!), + description: '+MRL (AA)') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.ECM()), + description: '+ECM') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.Sensors(24)), + description: '+Sensors:24'); + +final UnitModification milchamaPack2 = UnitModification( + name: 'Milchama Pack 2', + requirementCheck: (rs, ur, cg, unit) { + return hasLessThenTwoPackMods(unit, milchamaPack2.name) && + unit.hasMod('Milchama Pack'); + }, +) + ..addMod(UnitAttribute.tv, createSimpleIntMod(3), description: 'TV +3') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'w/ Milchama Pack')) + ..addMod( + UnitAttribute.weapons, + createReplaceWeaponInList( + oldValue: buildWeapon('MAG', hasReact: false)!, + newValue: buildWeapon('2 x MAG', hasReact: false)!), + description: '+MAG'); + +final UnitModification raavPack2 = UnitModification( + name: 'Ra\'av Pack 2', + requirementCheck: (rs, ur, cg, unit) { + return hasLessThenTwoPackMods(unit, raavPack2.name) && + unit.hasMod('Ra\'av Pack'); + }, +) + ..addMod(UnitAttribute.tv, createSimpleIntMod(4), description: 'TV +4') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'w/ Ra\'av Pack')) + ..addMod( + UnitAttribute.weapons, + createReplaceWeaponInList( + oldValue: buildWeapon('HRG', hasReact: false)!, + newValue: buildWeapon('2 x HRG', hasReact: false)!), + description: '+HRG'); + +final UnitModification mavethPack2 = UnitModification( + name: 'Maveth Pack 2', + requirementCheck: (rs, ur, cg, unit) { + return hasLessThenTwoPackMods(unit, mavethPack2.name) && + unit.hasMod('Maveth Pack'); + }, +) + ..addMod(UnitAttribute.tv, createSimpleIntMod(2), description: 'TV +2') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'w/ Maveth Pack')) + ..addMod( + UnitAttribute.weapons, + createReplaceWeaponInList( + oldValue: buildWeapon('LFG (Link)', hasReact: false)!, + newValue: buildWeapon('2 x LFG (Link)', hasReact: false)!), + description: '+LFG (Link)'); + +final UnitModification deverPack2 = UnitModification( + name: 'Dever Pack 2', + requirementCheck: (rs, ur, cg, unit) { + return hasLessThenTwoPackMods(unit, deverPack2.name) && + unit.hasMod('Dever Pack'); + }, +) + ..addMod(UnitAttribute.tv, createSimpleIntMod(3), description: 'TV +3') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'w/ Dever Pack')) + ..addMod( + UnitAttribute.weapons, + createReplaceWeaponInList( + oldValue: buildWeapon('MAM', hasReact: false)!, + newValue: buildWeapon('2 x MAM', hasReact: false)!), + description: '+MAM'); + +final UnitModification kibushPack2 = UnitModification( + name: 'Kibush Pack 2', + requirementCheck: (rs, ur, cg, unit) { + return hasLessThenTwoPackMods(unit, kibushPack2.name) && + unit.hasMod('Kibush Pack'); + }, +) + ..addMod(UnitAttribute.tv, createSimpleIntMod(5), description: 'TV +5') + ..addMod(UnitAttribute.name, createSimpleStringMod(false, 'w/ Kibush Pack')) + ..addMod(UnitAttribute.ew, createSetIntMod(3, onlyIfLessThan: true), + description: 'EW 3+') + ..addMod( + UnitAttribute.weapons, + createReplaceWeaponInList( + oldValue: buildWeapon('MRL (AA)', hasReact: true)!, + newValue: buildWeapon('2 x MRL (AA)', hasReact: true)!), + description: '+MRL (AA)') + ..addMod( + UnitAttribute.traits, + createReplaceTraitInList( + oldValue: Trait.ECM(), newValue: Trait.ECMPlus()), + description: '+ECM+') + ..addMod(UnitAttribute.traits, createAddTraitToList(Trait.Sensors(36)), + description: '+Sensors:36'); + +final _packModNames = [ + milchamaPack.name, + raavPack.name, + mavethPack.name, + deverPack.name, + kibushPack.name, + milchamaPack2.name, + raavPack2.name, + mavethPack2.name, + deverPack2.name, + kibushPack2.name, +]; + +bool hasLessThenTwoPackMods(Unit unit, String exludedPackName) { + final trimmedModNames = + _packModNames.where((name) => name != exludedPackName); + + final modsWithPack = + unit.getMods().where((mod) => trimmedModNames.contains(mod.name)); + final hasLessThanTwo = modsWithPack.length < 2; + return hasLessThanTwo; +} diff --git a/lib/models/mods/unitUpgrades/unit_upgrades.dart b/lib/models/mods/unitUpgrades/unit_upgrades.dart index bc54486b..63bfe257 100644 --- a/lib/models/mods/unitUpgrades/unit_upgrades.dart +++ b/lib/models/mods/unitUpgrades/unit_upgrades.dart @@ -11,6 +11,8 @@ import 'package:gearforce/models/mods/unitUpgrades/peace_river.dart' import 'package:gearforce/models/mods/unitUpgrades/south.dart' as south; import 'package:gearforce/models/mods/unitUpgrades/universal.dart' as universal; import 'package:gearforce/models/mods/unitUpgrades/utopia.dart' as utopia; +import 'package:gearforce/models/mods/unitUpgrades/new_jerusalem.dart' + as newJerusalem; import 'package:gearforce/models/unit/unit.dart'; List getUnitMods(String frameName, Unit unit) { @@ -288,6 +290,61 @@ List getUnitMods(String frameName, Unit unit) { case 'huni riders': return [eden.team]; + // New Jerusalem + case 'initiate': + return [newJerusalem.faithful, newJerusalem.archUpgrade5]; + case 'apostle': + return [newJerusalem.faithful, newJerusalem.archUpgrade4]; + case 'disciple': + return [newJerusalem.faithful]; + case 'paladin': + return [newJerusalem.faithful, newJerusalem.archUpgrade5]; + case 'templar': + return [ + newJerusalem.genesisUpgrade, + newJerusalem.samsonUpgrade, + newJerusalem.faithful, + newJerusalem.archUpgrade4, + ]; + case 'hakham': + return [newJerusalem.faithful, newJerusalem.neshUpgrade]; + case 'ghazi': + return [ + newJerusalem.zacchaeusUpgrade, + newJerusalem.sayfUpgrade, + newJerusalem.nissiUpgrade, + newJerusalem.faithful, + newJerusalem.archUpgradeGhazi, + ]; + case 'prioress': + return [newJerusalem.faithful, newJerusalem.genesisUpgradePrioress]; + case 'abbess': + return [newJerusalem.faithful, newJerusalem.archUpgradeAbbess]; + case 'kwaja': + return [ + newJerusalem.faithful, + newJerusalem.enhancedEngine, + newJerusalem.squadKwaja, + ]; + case 'zealot': + return [newJerusalem.faithful, newJerusalem.archUpgradeZealot]; + case 'tenet': + return [newJerusalem.faithful]; + case 'prophet': + return [ + newJerusalem.faithful, + newJerusalem.milchamaPack, + newJerusalem.milchamaPack2, + newJerusalem.raavPack, + newJerusalem.raavPack2, + newJerusalem.mavethPack, + newJerusalem.mavethPack2, + newJerusalem.deverPack, + newJerusalem.deverPack2, + newJerusalem.kibushPack, + newJerusalem.kibushPack2, + ]; + // Universal case 'chargeur': return [universal.chainswordSwap, universal.clawSwap]; diff --git a/lib/models/rules/new_jerusalem/new_jerusalem.dart b/lib/models/rules/new_jerusalem/new_jerusalem.dart new file mode 100644 index 00000000..a6b2a887 --- /dev/null +++ b/lib/models/rules/new_jerusalem/new_jerusalem.dart @@ -0,0 +1,53 @@ +import 'package:gearforce/data/data.dart'; +import 'package:gearforce/data/unit_filter.dart'; +import 'package:gearforce/models/factions/faction_type.dart'; +import 'package:gearforce/models/rules/faction_rule.dart'; +import 'package:gearforce/models/rules/options/combat_group_options.dart'; +import 'package:gearforce/models/rules/rule_set.dart'; +import 'package:gearforce/models/rules/options/special_unit_filter.dart'; + +//const String _baseRuleId = 'rule::nj::core'; + +/* + All New Jerusalem models have the following rules: +*/ + +class NewJerusalem extends RuleSet { + NewJerusalem( + Data data, { + super.description, + required super.name, + List subFactionRules = const [], + }) : super(FactionType.NewJerusalem, data, + factionRules: [], + subFactionRules: subFactionRules, + specialRules: [ + 'Order - Inspiring Oratory: This order may be issued at any' + + ' time by a New Jerusalem commander. Affected models' + + ' immediately refresh their Faithful SP. A CG can only be' + + ' affected by this order once per round ' + ]); + + @override + List availableUnitFilters( + List? cgOptions, + ) { + final filters = [ + SpecialUnitFilter( + text: type.name, + id: coreTag, + filters: const [ + const UnitFilter(FactionType.NewJerusalem), + const UnitFilter(FactionType.Airstrike), + const UnitFilter(FactionType.Universal), + const UnitFilter(FactionType.Terrain), + ], + ), + ]; + return [...filters, ...super.availableUnitFilters(cgOptions)]; + } + + // factory NewJerusalem.OSG(Data data) => OSG(data); + // factory NewJerusalem.OSM(Data data) => OSM(data); + // factory NewJerusalem.ODC(Data data) => ODC(data); +} diff --git a/lib/models/traits/trait.dart b/lib/models/traits/trait.dart index 68d2a735..85aed441 100644 --- a/lib/models/traits/trait.dart +++ b/lib/models/traits/trait.dart @@ -165,6 +165,8 @@ class Trait { return Trait.ECMPlus(isAux: auxCheck); case 'ECCM': return Trait.ECCM(isAux: auxCheck); + case 'Faithful': + return Trait.Faithful(isAux: auxCheck); case 'Field Armor': return Trait.FieldArmor(isAux: auxCheck); case 'Fire': @@ -611,6 +613,16 @@ class Trait { ); } + factory Trait.Faithful({bool isAux = false}) { + return Trait( + name: 'Faithful', + isAux: isAux, + description: 'This model begins the game with 1 SP, this SP does not' + + ' become a CP for commanders and does not refresh during the' + + ' Cleanup phase. It can only have 1 SP due to faithful at a time.', + ); + } + factory Trait.FieldArmor({bool isAux = false}) { return Trait( name: 'Field Armor', diff --git a/lib/screens/roster/pdf/pdf.dart b/lib/screens/roster/pdf/pdf.dart index b39d297a..830d7589 100644 --- a/lib/screens/roster/pdf/pdf.dart +++ b/lib/screens/roster/pdf/pdf.dart @@ -12,7 +12,7 @@ import 'package:pdf/widgets.dart' as pw; import 'package:printing/printing.dart'; const String _defaultRosterFileName = 'hg-roster'; -const String _webURL = 'https://gearforce.metadiversions.com'; +const String _webURL = 'https://nj.playtest.gearforce.metadiversions.com'; const double _unitCardMargins = 0; diff --git a/lib/screens/roster/roster.dart b/lib/screens/roster/roster.dart index 081e32f3..04aa2c0d 100644 --- a/lib/screens/roster/roster.dart +++ b/lib/screens/roster/roster.dart @@ -21,7 +21,7 @@ import 'package:url_launcher/url_launcher_string.dart'; const double _leftPanelWidth = 670.0; const double _titleHeight = 40.0; const double _menuTitleHeight = 50.0; -const String _version = '1.4.6'; +const String _version = '1.4.6-playtest.4'; const String _bugEmailAddress = 'gearforce@metadiversions.com'; const String _dp9URL = 'https://www.dp9.com/'; const String _sourceCodeURL = 'https://github.com/Ariemeth/gearforce-flutter'; diff --git a/lib/screens/roster/show_roster_id.dart b/lib/screens/roster/show_roster_id.dart index 9d8b0a23..f2012072 100644 --- a/lib/screens/roster/show_roster_id.dart +++ b/lib/screens/roster/show_roster_id.dart @@ -28,7 +28,8 @@ class RosterIdDialog extends StatelessWidget { @override Widget build(BuildContext context) { - final link = 'https://gearforce.metadiversions.com/?id=$rosterId'; + final link = + 'https://nj.playtest.gearforce.metadiversions.com/?id=$rosterId'; return SimpleDialog( children: [ Row(