sc2ai 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -372,7 +372,7 @@
372
372
  "allow_autocast": false,
373
373
  "allow_minimap": false,
374
374
  "buff": [],
375
- "cast_range": 9.0,
375
+ "cast_range": 10.0,
376
376
  "cooldown": 0,
377
377
  "effect": [],
378
378
  "energy_cost": 0,
@@ -11239,7 +11239,7 @@
11239
11239
  "Mechanical",
11240
11240
  "Structure"
11241
11241
  ],
11242
- "gas": 100,
11242
+ "gas": 50,
11243
11243
  "id": 29,
11244
11244
  "is_addon": false,
11245
11245
  "is_flying": false,
@@ -12239,7 +12239,7 @@
12239
12239
  "cooldown": 1.5,
12240
12240
  "damage_per_hit": 5.0,
12241
12241
  "damage_splash": 0,
12242
- "range": 0.10009765625,
12242
+ "range": 0.199951171875,
12243
12243
  "target_type": "Ground"
12244
12244
  }
12245
12245
  ]
@@ -13188,7 +13188,7 @@
13188
13188
  "power_radius": 6.5,
13189
13189
  "race": "Protoss",
13190
13190
  "radius": 1.125,
13191
- "sight": 9.0,
13191
+ "sight": 10.0,
13192
13192
  "size": 0,
13193
13193
  "speed_creep_mul": 1.0,
13194
13194
  "supply": -8.0,
@@ -14293,7 +14293,6 @@
14293
14293
  "accepts_addon": false,
14294
14294
  "armor": 1.0,
14295
14295
  "attributes": [
14296
- "Light",
14297
14296
  "Mechanical",
14298
14297
  "Psionic"
14299
14298
  ],
@@ -14615,7 +14614,7 @@
14615
14614
  "is_townhall": false,
14616
14615
  "is_worker": false,
14617
14616
  "max_health": 40.0,
14618
- "max_shield": 20.0,
14617
+ "max_shield": 30.0,
14619
14618
  "minerals": 25,
14620
14619
  "name": "Observer",
14621
14620
  "needs_creep": false,
@@ -14629,7 +14628,7 @@
14629
14628
  "speed_creep_mul": 1.0,
14630
14629
  "supply": 1.0,
14631
14630
  "tech_alias": [],
14632
- "time": 480.0,
14631
+ "time": 400.0,
14633
14632
  "unit_alias": 0,
14634
14633
  "weapons": []
14635
14634
  },
@@ -14872,7 +14871,7 @@
14872
14871
  "cooldown": 1.5,
14873
14872
  "damage_per_hit": 5.0,
14874
14873
  "damage_splash": 0,
14875
- "range": 0.10009765625,
14874
+ "range": 0.199951171875,
14876
14875
  "target_type": "Ground"
14877
14876
  }
14878
14877
  ]
@@ -15006,7 +15005,14 @@
15006
15005
  "weapons": []
15007
15006
  },
15008
15007
  {
15009
- "abilities": [],
15008
+ "abilities": [
15009
+ {
15010
+ "ability": 1733
15011
+ },
15012
+ {
15013
+ "ability": 1
15014
+ }
15015
+ ],
15010
15016
  "accepts_addon": false,
15011
15017
  "armor": 0.0,
15012
15018
  "attributes": [
@@ -16127,7 +16133,7 @@
16127
16133
  "cooldown": 1.5,
16128
16134
  "damage_per_hit": 5.0,
16129
16135
  "damage_splash": 0,
16130
- "range": 0.10009765625,
16136
+ "range": 0.199951171875,
16131
16137
  "target_type": "Ground"
16132
16138
  }
16133
16139
  ]
@@ -17301,7 +17307,7 @@
17301
17307
  "normal_mode": 111,
17302
17308
  "race": "Zerg",
17303
17309
  "radius": 0.625,
17304
- "sight": 10.0,
17310
+ "sight": 8.0,
17305
17311
  "size": 0,
17306
17312
  "speed": 2.0,
17307
17313
  "speed_creep_mul": 1.0,
@@ -20011,7 +20017,7 @@
20011
20017
  "is_structure": false,
20012
20018
  "is_townhall": false,
20013
20019
  "is_worker": false,
20014
- "max_health": 110.0,
20020
+ "max_health": 130.0,
20015
20021
  "minerals": 125,
20016
20022
  "name": "Cyclone",
20017
20023
  "needs_creep": false,
@@ -20036,7 +20042,7 @@
20036
20042
  "damage": 3.0
20037
20043
  }
20038
20044
  ],
20039
- "cooldown": 0.673828125,
20045
+ "cooldown": 0.81201171875,
20040
20046
  "damage_per_hit": 11.0,
20041
20047
  "damage_splash": 0,
20042
20048
  "range": 6.0,
@@ -21422,13 +21428,13 @@
21422
21428
  "radius": 1.0,
21423
21429
  "sight": 11.0,
21424
21430
  "size": 0,
21425
- "speed": 0.78515625,
21431
+ "speed": 0.9140625,
21426
21432
  "speed_creep_mul": 1.0,
21427
21433
  "supply": -8.0,
21428
21434
  "tech_alias": [
21429
21435
  106
21430
21436
  ],
21431
- "time": 266.6796875,
21437
+ "time": 336.015625,
21432
21438
  "unit_alias": 0,
21433
21439
  "weapons": []
21434
21440
  },
@@ -21458,7 +21464,7 @@
21458
21464
  "power_radius": 6.5,
21459
21465
  "race": "Protoss",
21460
21466
  "radius": 1.125,
21461
- "sight": 9.0,
21467
+ "sight": 10.0,
21462
21468
  "size": 0,
21463
21469
  "speed_creep_mul": 1.0,
21464
21470
  "supply": -8.0,
@@ -21519,9 +21525,6 @@
21519
21525
  },
21520
21526
  {
21521
21527
  "abilities": [
21522
- {
21523
- "ability": 4
21524
- },
21525
21528
  {
21526
21529
  "ability": 4111
21527
21530
  },
@@ -21587,7 +21590,7 @@
21587
21590
  "is_townhall": false,
21588
21591
  "is_worker": false,
21589
21592
  "max_health": 40.0,
21590
- "max_shield": 20.0,
21593
+ "max_shield": 30.0,
21591
21594
  "minerals": 25,
21592
21595
  "name": "ObserverSiegeMode",
21593
21596
  "needs_creep": false,
@@ -21900,8 +21903,8 @@
21900
21903
  },
21901
21904
  {
21902
21905
  "cost": {
21903
- "gas": 175,
21904
- "minerals": 175,
21906
+ "gas": 150,
21907
+ "minerals": 150,
21905
21908
  "time": 3040.0
21906
21909
  },
21907
21910
  "id": 8,
@@ -21909,8 +21912,8 @@
21909
21912
  },
21910
21913
  {
21911
21914
  "cost": {
21912
- "gas": 250,
21913
- "minerals": 250,
21915
+ "gas": 200,
21916
+ "minerals": 200,
21914
21917
  "time": 3520.0
21915
21918
  },
21916
21919
  "id": 9,
@@ -21936,8 +21939,8 @@
21936
21939
  },
21937
21940
  {
21938
21941
  "cost": {
21939
- "gas": 175,
21940
- "minerals": 175,
21942
+ "gas": 150,
21943
+ "minerals": 150,
21941
21944
  "time": 3040.0
21942
21945
  },
21943
21946
  "id": 12,
@@ -21945,8 +21948,8 @@
21945
21948
  },
21946
21949
  {
21947
21950
  "cost": {
21948
- "gas": 250,
21949
- "minerals": 250,
21951
+ "gas": 200,
21952
+ "minerals": 200,
21950
21953
  "time": 3520.0
21951
21954
  },
21952
21955
  "id": 13,
@@ -4,7 +4,7 @@ LABEL service="bot-ruby-local"
4
4
  USER root
5
5
  WORKDIR /root/ruby-builder
6
6
 
7
- ARG RUBY_VERSION=3.3.0
7
+ ARG RUBY_VERSION=3.3.1
8
8
  ARG DEBIAN_DISABLE_RUBYGEMS_INTEGRATION=true
9
9
 
10
10
  # Deps - Ruby build
@@ -15,10 +15,10 @@ module Sc2
15
15
  # @return [Hash<Integer, Api::AbilityData>] AbilityId => AbilityData
16
16
  attr_accessor :abilities
17
17
  # @!attribute units
18
- # @return [Hash<Integer, Api::UnitTypeData>] UnitId => UnitData
18
+ # @return [Hash<Integer, Api::UnitTypeData>] UnitId => UnitTypeData
19
19
  attr_accessor :units
20
20
  # @!attribute upgrades
21
- # @return [Hash<Integer, Api::UnitTypeData>] UnitTypeId => UnitTypeData
21
+ # @return [Hash<Integer, Api::UpgradeData>] UpgradeId => UpgradeData
22
22
  attr_accessor :upgrades
23
23
  # @!attribute buffs
24
24
  # Not particularly useful data. Just use BuffId directly
@@ -38,6 +38,8 @@ module Sc2
38
38
  @upgrades = upgrades_from_proto(data.upgrades)
39
39
  @buffs = buffs_from_proto(data.buffs)
40
40
  @effects = effects_from_proto(data.effects)
41
+
42
+ override_unit_data
41
43
  end
42
44
 
43
45
  private
@@ -97,5 +99,160 @@ module Sc2
97
99
  end
98
100
  result
99
101
  end
102
+
103
+ # @private
104
+ # Overrides unit data from api to implement fixes or change context
105
+ # i.e. Api::UnitTypeId::ORBITALCOMMAND cost is cost-to-upgrade instead of CC + Orbital combined cost.
106
+ # Run once. Depends on all data already being set.
107
+ def override_unit_data
108
+ correct_unit_type_costs
109
+ correct_unit_type_sum
110
+ end
111
+
112
+ # @private
113
+ # Fixes mineral_cost, vespene_cost and food_required values
114
+ def correct_unit_type_costs
115
+ units.transform_values! do |unit_data|
116
+ # Cost corrections, hardcoded.
117
+ case unit_data.unit_id
118
+ when Api::UnitTypeId::CYCLONE # TERRAN - UNITS
119
+ unit_data.mineral_cost = 125
120
+ unit_data.vespene_cost = 50
121
+ unit_data.food_required = 2
122
+ when Api::UnitTypeId::LIBERATOR
123
+ unit_data.vespene_cost = 125
124
+ when Api::UnitTypeId::MULE
125
+ unit_data.mineral_cost = 0
126
+ when Api::UnitTypeId::RAVEN
127
+ unit_data.vespene_cost = 150
128
+ when Api::UnitTypeId::AUTOTURRET # TERRAN - STRUCTURES
129
+ unit_data.mineral_cost = 0
130
+ when Api::UnitTypeId::ORBITALCOMMAND
131
+ unit_data.mineral_cost = 150
132
+ when Api::UnitTypeId::PLANETARYFORTRESS
133
+ unit_data.mineral_cost = 150
134
+ when Api::UnitTypeId::BANELING # ZERG - UNITS
135
+ unit_data.mineral_cost = 25
136
+ unit_data.vespene_cost = 25
137
+ unit_data.food_required = 0
138
+ when Api::UnitTypeId::BROODLORD
139
+ unit_data.mineral_cost = 150
140
+ unit_data.vespene_cost = 150
141
+ unit_data.food_required = 0
142
+ when Api::UnitTypeId::LURKERMP
143
+ unit_data.mineral_cost = 50
144
+ unit_data.vespene_cost = 100
145
+ unit_data.food_required = 0
146
+ when Api::UnitTypeId::NYDUSCANAL
147
+ unit_data.mineral_cost = 75
148
+ unit_data.vespene_cost = 75
149
+ when Api::UnitTypeId::OVERSEER
150
+ unit_data.mineral_cost = 50
151
+ when Api::UnitTypeId::RAVAGER
152
+ unit_data.mineral_cost = 25
153
+ unit_data.vespene_cost = 75
154
+ unit_data.food_required = 1
155
+ when Api::UnitTypeId::OVERLORDTRANSPORT
156
+ unit_data.mineral_cost = 25
157
+ unit_data.vespene_cost = 25
158
+ when Api::UnitTypeId::ZERGLING
159
+ unit_data.mineral_cost = 50
160
+ unit_data.food_required = 1
161
+ when Api::UnitTypeId::BANELINGNEST # ZERG - STRUCTURES
162
+ unit_data.mineral_cost = 100
163
+ when Api::UnitTypeId::EVOLUTIONCHAMBER
164
+ unit_data.mineral_cost = 75
165
+ when Api::UnitTypeId::EXTRACTOR
166
+ unit_data.mineral_cost = 25
167
+ when Api::UnitTypeId::GREATERSPIRE
168
+ unit_data.mineral_cost = 100
169
+ unit_data.vespene_cost = 150
170
+ when Api::UnitTypeId::HATCHERY
171
+ unit_data.mineral_cost = 300
172
+ when Api::UnitTypeId::HIVE
173
+ unit_data.mineral_cost = 200
174
+ unit_data.vespene_cost = 150
175
+ when Api::UnitTypeId::LAIR
176
+ unit_data.mineral_cost = 150
177
+ when Api::UnitTypeId::HYDRALISKDEN
178
+ unit_data.mineral_cost = 100
179
+ when Api::UnitTypeId::INFESTATIONPIT
180
+ unit_data.mineral_cost = 100
181
+ when Api::UnitTypeId::LURKERDENMP
182
+ unit_data.mineral_cost = 100
183
+ when Api::UnitTypeId::NYDUSNETWORK
184
+ unit_data.mineral_cost = 150
185
+ when Api::UnitTypeId::ROACHWARREN
186
+ unit_data.mineral_cost = 150
187
+ when Api::UnitTypeId::SPAWNINGPOOL
188
+ unit_data.mineral_cost = 200
189
+ when Api::UnitTypeId::SPINECRAWLER
190
+ unit_data.mineral_cost = 100
191
+ when Api::UnitTypeId::SPIRE
192
+ unit_data.mineral_cost = 200
193
+ when Api::UnitTypeId::SPORECRAWLER
194
+ unit_data.mineral_cost = 75
195
+ when Api::UnitTypeId::ULTRALISKCAVERN
196
+ unit_data.mineral_cost = 150
197
+ when Api::UnitTypeId::ARCHON # PROTOSS - UNITS
198
+ unit_data.mineral_cost = 0
199
+ unit_data.vespene_cost = 0
200
+ unit_data.food_required = 0
201
+ when Api::UnitTypeId::WARPGATE # PROTOSS - STRUCTURES
202
+ unit_data.mineral_cost = 0
203
+ end
204
+
205
+ unit_data
206
+ end
207
+ end
208
+
209
+ # @private
210
+ # Fixes mineral_cost_sum, vespene_cost_sum
211
+ def correct_unit_type_sum
212
+ units.transform_values! do |unit_data|
213
+ unit_data.mineral_cost_sum = unit_data.mineral_cost
214
+ unit_data.vespene_cost_sum = unit_data.vespene_cost
215
+
216
+ # Cost sums where necessary
217
+ case unit_data.unit_id
218
+ when Api::UnitTypeId::OVERSEER, Api::UnitTypeId::OVERLORDTRANSPORT # MORPH TOTALS - UNITS
219
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::OVERLORD].mineral_cost
220
+ when Api::UnitTypeId::BANELING
221
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::ZERGLING].mineral_cost / 2.0
222
+ when Api::UnitTypeId::RAVAGER
223
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::ROACH].mineral_cost
224
+ unit_data.vespene_cost_sum += units[Api::UnitTypeId::ROACH].vespene_cost
225
+ when Api::UnitTypeId::LURKERMP
226
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::HYDRALISK].mineral_cost
227
+ unit_data.vespene_cost_sum += units[Api::UnitTypeId::HYDRALISK].vespene_cost
228
+ when Api::UnitTypeId::BROODLORD
229
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::CORRUPTOR].mineral_cost
230
+ unit_data.vespene_cost_sum += units[Api::UnitTypeId::CORRUPTOR].vespene_cost
231
+ when Api::UnitTypeId::ORBITALCOMMAND, Api::UnitTypeId::PLANETARYFORTRESS # MORPH TOTALS - STRUCTURES
232
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::COMMANDCENTER].mineral_cost
233
+ when Api::UnitTypeId::GREATERSPIRE
234
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::SPIRE].mineral_cost
235
+ unit_data.vespene_cost_sum += units[Api::UnitTypeId::SPIRE].vespene_cost
236
+ when Api::UnitTypeId::LAIR
237
+ unit_data.mineral_cost_sum += 300 + units[Api::UnitTypeId::DRONE].mineral_cost
238
+ when Api::UnitTypeId::HIVE # = Hatchery + Lair + Drone
239
+ # Hardcode, because sequence isn't guaranteed and chained
240
+ unit_data.mineral_cost_sum += 450 + units[Api::UnitTypeId::DRONE].mineral_cost
241
+ unit_data.vespene_cost_sum += 100
242
+ when Api::UnitTypeId::ARCHON
243
+ unit_data.mineral_cost_sum = units[Api::UnitTypeId::HIGHTEMPLAR].mineral_cost * 2
244
+ unit_data.vespene_cost_sum = units[Api::UnitTypeId::HIGHTEMPLAR].vespene_cost * 2
245
+ when Api::UnitTypeId::WARPGATE
246
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::GATEWAY].mineral_cost
247
+ unit_data.vespene_cost_sum += units[Api::UnitTypeId::GATEWAY].vespene_cost
248
+ else
249
+ if unit_data.race == Api::Race::Zerg && unit_data.attributes.include?(:Structure)
250
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::DRONE].mineral_cost
251
+ end
252
+ end
253
+
254
+ unit_data
255
+ end
256
+ end
100
257
  end
101
258
  end
@@ -77,6 +77,28 @@ module Api
77
77
  def upgrade_researched_from(upgrade_id:)
78
78
  upgrade_researched_from_data[upgrade_id]
79
79
  end
80
+
81
+ # Returns the ability which researches this upgrade
82
+ # @return [Integer] ability_id
83
+ def upgrade_research_ability_id(upgrade_id:)
84
+ source_unit_type_id = upgrade_researched_from(upgrade_id:)
85
+ upgrade_research_abilities_data[source_unit_type_id][upgrade_id][:ability]
86
+ end
87
+
88
+ # Returns a full list of structure unit id's which perform upgrades
89
+ # This is a useful list when checking if any updates are in progress,
90
+ # because we scan these structure types for orders
91
+ # @return [Array<Integer>] unit type ids
92
+ def upgrade_structure_unit_type_ids
93
+ @upgrade_structure_unit_type_ids ||= upgrade_research_abilities_data.keys
94
+ end
95
+
96
+ # Returns hash of upgrade info for a specific structure where the upgrade id is the key
97
+ # @param source_unit_type_id [Integer] source structure unit type id
98
+ # @return [Hash<Integer, Hash>] ability_id
99
+ def upgrade_ability_data(source_unit_type_id)
100
+ upgrade_research_abilities_data[source_unit_type_id]
101
+ end
80
102
  end
81
103
  end
82
104
  end
@@ -233,6 +233,10 @@ module Api
233
233
  Api::UnitTypeId::QUEEN =>
234
234
  {ability: Api::AbilityId::TRAINQUEEN_QUEEN,
235
235
  required_building: Api::UnitTypeId::SPAWNINGPOOL}},
236
+ Api::UnitTypeId::CREEPTUMOR =>
237
+ {Api::UnitTypeId::CREEPTUMOR =>
238
+ {ability: Api::AbilityId::BUILD_CREEPTUMOR_TUMOR,
239
+ requires_placement_position: true}},
236
240
  Api::UnitTypeId::SPIRE =>
237
241
  {Api::UnitTypeId::GREATERSPIRE =>
238
242
  {ability: Api::AbilityId::UPGRADETOGREATERSPIRE_GREATERSPIRE,
@@ -759,6 +763,7 @@ module Api
759
763
  Api::UpgradeId::LURKERRANGE =>
760
764
  {ability: Api::AbilityId::LURKERDENRESEARCH_RESEARCHLURKERRANGE,
761
765
  required_building: Api::UnitTypeId::HIVE}}}
766
+ .freeze
762
767
 
763
768
  # unit_created_from_data = {
764
769
  # UnitTypeId.ADEPT: [UnitTypeId.GATEWAY, UnitTypeId.WARPGATE],
@@ -845,6 +850,11 @@ module Api
845
850
  [Api::UnitTypeId::HATCHERY,
846
851
  Api::UnitTypeId::LAIR,
847
852
  Api::UnitTypeId::HIVE],
853
+ Api::UnitTypeId::CREEPTUMOR =>
854
+ [Api::UnitTypeId::CREEPTUMOR,
855
+ Api::UnitTypeId::QUEEN,
856
+ Api::UnitTypeId::CREEPTUMORBURROWED,
857
+ Api::UnitTypeId::CREEPTUMORQUEEN],
848
858
  Api::UnitTypeId::GREATERSPIRE => [Api::UnitTypeId::SPIRE],
849
859
  Api::UnitTypeId::NYDUSCANAL => [Api::UnitTypeId::NYDUSNETWORK],
850
860
  Api::UnitTypeId::HIVE => [Api::UnitTypeId::LAIR],
@@ -869,10 +879,6 @@ module Api
869
879
  Api::UnitTypeId::RAVAGER => [Api::UnitTypeId::ROACH],
870
880
  Api::UnitTypeId::BROODLORD => [Api::UnitTypeId::CORRUPTOR],
871
881
  Api::UnitTypeId::CREEPTUMORQUEEN => [Api::UnitTypeId::QUEEN],
872
- Api::UnitTypeId::CREEPTUMOR =>
873
- [Api::UnitTypeId::QUEEN,
874
- Api::UnitTypeId::CREEPTUMORBURROWED,
875
- Api::UnitTypeId::CREEPTUMORQUEEN],
876
882
  Api::UnitTypeId::CHANGELING =>
877
883
  [Api::UnitTypeId::OVERSEER, Api::UnitTypeId::OVERSEERSIEGEMODE],
878
884
  Api::UnitTypeId::DRONE => [Api::UnitTypeId::LARVA],
@@ -889,6 +895,7 @@ module Api
889
895
  Api::UnitTypeId::LOCUSTMPFLYING =>
890
896
  [Api::UnitTypeId::SWARMHOSTBURROWEDMP, Api::UnitTypeId::SWARMHOSTMP],
891
897
  Api::UnitTypeId::ORACLESTASISTRAP => [Api::UnitTypeId::ORACLE]}
898
+ .freeze
892
899
 
893
900
  # unit_created_from_data = {
894
901
  # UpgradeId.ADEPTPIERCINGATTACK: UnitTypeId.TWILIGHTCOUNCIL,
@@ -993,6 +1000,7 @@ module Api
993
1000
  Api::UpgradeId::TUNNELINGCLAWS => Api::UnitTypeId::ROACHWARREN,
994
1001
  Api::UpgradeId::DIGGINGCLAWS => Api::UnitTypeId::LURKERDENMP,
995
1002
  Api::UpgradeId::LURKERRANGE => Api::UnitTypeId::LURKERDENMP}
1003
+ .freeze
996
1004
 
997
1005
  def unit_abilities_data = {Api::UnitTypeId::COLOSSUS =>
998
1006
  [Api::AbilityId::STOP_STOP,
@@ -1038,7 +1046,7 @@ module Api
1038
1046
  Api::AbilityId::ATTACK_ATTACK,
1039
1047
  Api::AbilityId::EFFECT_MASSRECALL_STRATEGICRECALL,
1040
1048
  Api::AbilityId::EFFECT_TIMEWARP,
1041
- Api::AbilityId._250MMSTRIKECANNONS_CANCEL,
1049
+ Api::AbilityId._250MMSTRIKECANNONS_250MMSTRIKECANNONS,
1042
1050
  Api::AbilityId::SMART],
1043
1051
  Api::UnitTypeId::POINTDEFENSEDRONE => [],
1044
1052
  Api::UnitTypeId::CHANGELING =>
@@ -1578,7 +1586,8 @@ module Api
1578
1586
  Api::AbilityId::SMART,
1579
1587
  Api::AbilityId::UPGRADETOLAIR_LAIR,
1580
1588
  Api::AbilityId::TRAINQUEEN_QUEEN],
1581
- Api::UnitTypeId::CREEPTUMOR => [],
1589
+ Api::UnitTypeId::CREEPTUMOR =>
1590
+ [Api::AbilityId::BUILD_CREEPTUMOR_TUMOR, Api::AbilityId::SMART],
1582
1591
  Api::UnitTypeId::EXTRACTOR => [],
1583
1592
  Api::UnitTypeId::SPAWNINGPOOL =>
1584
1593
  [Api::AbilityId::RESEARCH_ZERGLINGMETABOLICBOOST,
@@ -2242,8 +2251,7 @@ module Api
2242
2251
  Api::AbilityId::ATTACK_ATTACK,
2243
2252
  Api::AbilityId::SMART],
2244
2253
  Api::UnitTypeId::SHIELDBATTERY =>
2245
- [Api::AbilityId::STOP_STOP,
2246
- Api::AbilityId::SHIELDBATTERYRECHARGEEX5_SHIELDBATTERYRECHARGE,
2254
+ [Api::AbilityId::SHIELDBATTERYRECHARGEEX5_SHIELDBATTERYRECHARGE,
2247
2255
  Api::AbilityId::SMART],
2248
2256
  Api::UnitTypeId::OBSERVERSIEGEMODE =>
2249
2257
  [Api::AbilityId::STOP_STOP, Api::AbilityId::MORPH_OBSERVERMODE],
@@ -2261,6 +2269,7 @@ module Api
2261
2269
  Api::UnitTypeId::REFINERYRICH => [],
2262
2270
  Api::UnitTypeId::ASSIMILATORRICH => [],
2263
2271
  Api::UnitTypeId::EXTRACTORRICH => []}
2272
+ .freeze
2264
2273
 
2265
2274
  def unit_alias_data = {Api::UnitTypeId::CHANGELINGZEALOT => Api::UnitTypeId::CHANGELING,
2266
2275
  Api::UnitTypeId::CHANGELINGMARINESHIELD => Api::UnitTypeId::CHANGELING,
@@ -2302,6 +2311,7 @@ module Api
2302
2311
  Api::UnitTypeId::PYLONOVERCHARGED => Api::UnitTypeId::PYLON,
2303
2312
  Api::UnitTypeId::OBSERVERSIEGEMODE => Api::UnitTypeId::OBSERVER,
2304
2313
  Api::UnitTypeId::OVERSEERSIEGEMODE => Api::UnitTypeId::OVERSEER}
2314
+ .freeze
2305
2315
 
2306
2316
  def unit_tech_alias_data = {Api::UnitTypeId::SIEGETANKSIEGED => [Api::UnitTypeId::SIEGETANK],
2307
2317
  Api::UnitTypeId::VIKINGASSAULT => [Api::UnitTypeId::VIKING],
@@ -2337,6 +2347,7 @@ module Api
2337
2347
  Api::UnitTypeId::PYLONOVERCHARGED =>
2338
2348
  [Api::UnitTypeId::PYLON, Api::UnitTypeId::PYLON],
2339
2349
  Api::UnitTypeId::OVERSEERSIEGEMODE => [Api::UnitTypeId::OVERLORD]}
2350
+ .freeze
2340
2351
  end
2341
2352
  end
2342
2353
  end
@@ -301,21 +301,21 @@ module Sc2
301
301
  end
302
302
 
303
303
  # Queries one or more pathing queries
304
- # @param queries [Array<Api::RequestQueryPathing>, Api::RequestQueryPathing] one or more pathing queries
305
- # @return [Array<Api::ResponseQueryPathing>, Api::ResponseQueryPathing] one or more results depending on input size
304
+ # @param queries [Array<Api::RequestQueryPathing>] one or more pathing queries
305
+ # @return [Array<Api::ResponseQueryPathing>] one or more results depending on input size
306
306
  def query_pathings(queries)
307
307
  arr_queries = queries.is_a?(Array) ? queries : [queries]
308
308
 
309
309
  response = send_request_for query: Api::RequestQuery.new(
310
310
  pathing: arr_queries
311
311
  )
312
- (arr_queries.size > 1) ? response.pathing : response.pathing.first
312
+ response.pathing
313
313
  end
314
314
 
315
315
  # Queries one or more ability-available checks
316
- # @param queries [Array<Api::RequestQueryAvailableAbilities>, Api::RequestQueryAvailableAbilities] one or more pathing queries
316
+ # @param queries [Array<Api::RequestQueryAvailableAbilities>] one or more pathing queries
317
317
  # @param ignore_resource_requirements [Boolean] Ignores requirements like food, minerals and so on.
318
- # @return [Array<Api::ResponseQueryAvailableAbilities>, Api::ResponseQueryAvailableAbilities] one or more results depending on input size
318
+ # @return [Array<Api::ResponseQueryAvailableAbilities>] one or more results depending on input size
319
319
  def query_abilities(queries, ignore_resource_requirements: true)
320
320
  arr_queries = queries.is_a?(Array) ? queries : [queries]
321
321
 
@@ -323,13 +323,13 @@ module Sc2
323
323
  abilities: arr_queries,
324
324
  ignore_resource_requirements:
325
325
  )
326
- (arr_queries.size > 1) ? response.abilities : response.abilities.first
326
+ response.abilities
327
327
  end
328
328
 
329
329
  # Queries available abilities for units
330
- # @param unit_tags [Array<Integer>, Integer] an array of unit tags or a single tag
330
+ # @param unit_tags [Array<Integer>] an array of unit tags or a single tag
331
331
  # @param ignore_resource_requirements [Boolean] Ignores requirements like food, minerals and so on.
332
- # @return [Array<Api::ResponseQueryAvailableAbilities>, Api::ResponseQueryAvailableAbilities] one or more results depending on input size
332
+ # @return [Array<Api::ResponseQueryAvailableAbilities>] one or more results depending on input size
333
333
  def query_abilities_for_unit_tags(unit_tags, ignore_resource_requirements: true)
334
334
  queries = []
335
335
  unit_tags = [unit_tags] unless unit_tags.is_a? Array
@@ -340,15 +340,30 @@ module Sc2
340
340
  query_abilities(queries, ignore_resource_requirements:)
341
341
  end
342
342
 
343
+ # Queries available ability ids for one unit
344
+ # Shortened response over #query_abilities_for_unit_tags, since we know the tag already
345
+ # and can just return an array of ability ids.
346
+ # Note: Querying single units are expensive and should be batched with #query_abilities_for_unit_tags
347
+ # @param unit [Api::Unit, Integer] a unit or a tag.
348
+ def query_ability_ids_for_unit(unit, ignore_resource_requirements: true)
349
+ tag = unit.is_a?(Api::Unit) ? unit.tag : unit
350
+ result = query_abilities_for_unit_tags([tag], ignore_resource_requirements:)
351
+ if result.nil?
352
+ []
353
+ else
354
+ result.first.abilities
355
+ end
356
+ end
357
+
343
358
  # Queries one or more pathing queries
344
- # @param queries [Array<Api::RequestQueryBuildingPlacement>, Api::RequestQueryBuildingPlacement] one or more placement queries
345
- # @return [Array<Api::ResponseQueryBuildingPlacement>, Api::ResponseQueryBuildingPlacement] one or more results depending on input size
359
+ # @param queries [Array<Api::RequestQueryBuildingPlacement>] one or more placement queries
360
+ # @return [Array<Api::ResponseQueryBuildingPlacement>] one or more results depending on input size
346
361
  def query_placements(queries)
347
362
  arr_queries = queries.is_a?(Array) ? queries : [queries]
348
363
 
349
364
  response = query(placements: arr_queries)
350
365
 
351
- (arr_queries.size > 1) ? response.placements : response.placements.first
366
+ response.placements
352
367
  end
353
368
 
354
369
  # Generates a replay.
@@ -55,7 +55,7 @@ module Sc2
55
55
  # do initial ping to ensure status is set and connection is working
56
56
  response_ping = ping
57
57
  Sc2.logger.debug { "Game version: #{response_ping.game_version}" }
58
- rescue Errno::ECONNREFUSED
58
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET
59
59
  raise Error, "Connection timeout. Max retry exceeded." unless (attempt += 1) < 30 # 30s attempts
60
60
 
61
61
  @listeners[ConnectionListener.name]&.each { _1.on_connection_waiting(self) }
@@ -10,7 +10,7 @@ module Sc2
10
10
 
11
11
  class << self
12
12
  extend Forwardable
13
- def_delegators :instance, :obtain, :get, :start, :stop
13
+ def_delegators :instance, :obtain, :get, :start, :stop, :stop_all
14
14
  end
15
15
 
16
16
  # Gets client for player X or starts an instance
@@ -53,6 +53,14 @@ module Sc2
53
53
  @clients[player_index] = nil
54
54
  end
55
55
 
56
+ def stop_all
57
+ @clients.compact.each do |client|
58
+ client.stop
59
+ client = nil
60
+ @clients.delete(client)
61
+ end
62
+ end
63
+
56
64
  private
57
65
 
58
66
  attr_accessor :clients, :ports
@@ -64,6 +64,7 @@ module Sc2
64
64
  end
65
65
 
66
66
  # Builds target unit type using units as source at optional target
67
+ # @param units [Array<Integer>,Integer,Api::Unit] can be an Api::Unit, array of Api::Unit#tag or single tag
67
68
  # @param unit_type_id [Integer] Api::UnitTypeId the unit type which will do the creation
68
69
  # @param target [Api::Point2D, Integer, nil] is a unit tag or a Api::Point2D. Nil for addons/orbital
69
70
  # @param queue_command [Boolean] shift+command
@@ -94,6 +95,22 @@ module Sc2
94
95
  subtract_cost(unit_type_id)
95
96
  end
96
97
 
98
+ # Research a specific upgrade
99
+ # @param units [Array<Integer>,Integer,Api::Unit] can be an Api::Unit, array of Api::Unit#tag or single tag
100
+ # @param upgrade_id [Integer] Api::UpgradeId to research
101
+ # @param queue_command [Boolean] shift+command
102
+ def research(units:, upgrade_id:, queue_command: false)
103
+ upgrade = upgrade_data(upgrade_id)
104
+
105
+ # Get build ability from target building type
106
+ action(units:,
107
+ ability_id: upgrade.ability_id,
108
+ queue_command:)
109
+
110
+ @spent_minerals += upgrade.mineral_cost
111
+ @spent_vespene += upgrade.vespene_cost
112
+ end
113
+
97
114
  # Toggles auto-cast ability for units
98
115
  # @param units [Array<Integer>, Integer, Api::Unit] can be an Api::Unit, array of Tags or single Tag
99
116
  # @param ability_id [Integer]