sc2ai 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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]