sc2ai 0.0.4 → 0.0.5

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,
@@ -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,163 @@ 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::QUEENMP
152
+ unit_data.mineral_cost = 150
153
+ unit_data.food_required = 2
154
+ when Api::UnitTypeId::RAVAGER
155
+ unit_data.mineral_cost = 25
156
+ unit_data.vespene_cost = 75
157
+ unit_data.food_required = 1
158
+ when Api::UnitTypeId::OVERLORDTRANSPORT
159
+ unit_data.mineral_cost = 25
160
+ unit_data.vespene_cost = 25
161
+ when Api::UnitTypeId::ZERGLING
162
+ unit_data.mineral_cost = 50
163
+ unit_data.food_required = 1
164
+ when Api::UnitTypeId::BANELINGNEST # ZERG - STRUCTURES
165
+ unit_data.mineral_cost = 100
166
+ when Api::UnitTypeId::EVOLUTIONCHAMBER
167
+ unit_data.mineral_cost = 75
168
+ when Api::UnitTypeId::EXTRACTOR
169
+ unit_data.mineral_cost = 25
170
+ when Api::UnitTypeId::GREATERSPIRE
171
+ unit_data.mineral_cost = 100
172
+ unit_data.vespene_cost = 150
173
+ when Api::UnitTypeId::HATCHERY
174
+ unit_data.mineral_cost = 300
175
+ when Api::UnitTypeId::HIVE
176
+ unit_data.mineral_cost = 200
177
+ unit_data.vespene_cost = 150
178
+ when Api::UnitTypeId::LAIR
179
+ unit_data.mineral_cost = 150
180
+ when Api::UnitTypeId::HYDRALISKDEN
181
+ unit_data.mineral_cost = 100
182
+ when Api::UnitTypeId::INFESTATIONPIT
183
+ unit_data.mineral_cost = 100
184
+ when Api::UnitTypeId::LURKERDENMP
185
+ unit_data.mineral_cost = 100
186
+ when Api::UnitTypeId::NYDUSNETWORK
187
+ unit_data.mineral_cost = 150
188
+ when Api::UnitTypeId::ROACHWARREN
189
+ unit_data.mineral_cost = 150
190
+ when Api::UnitTypeId::SPAWNINGPOOL
191
+ unit_data.mineral_cost = 200
192
+ when Api::UnitTypeId::SPINECRAWLER
193
+ unit_data.mineral_cost = 100
194
+ when Api::UnitTypeId::SPIRE
195
+ unit_data.mineral_cost = 200
196
+ when Api::UnitTypeId::SPORECRAWLER
197
+ unit_data.mineral_cost = 75
198
+ when Api::UnitTypeId::ULTRALISKCAVERN
199
+ unit_data.mineral_cost = 150
200
+ when Api::UnitTypeId::ARCHON # PROTOSS - UNITS
201
+ unit_data.mineral_cost = 0
202
+ unit_data.vespene_cost = 0
203
+ unit_data.food_required = 0
204
+ when Api::UnitTypeId::WARPGATE # PROTOSS - STRUCTURES
205
+ unit_data.mineral_cost = 0
206
+ end
207
+
208
+ unit_data
209
+ end
210
+ end
211
+
212
+ # @private
213
+ # Fixes mineral_cost_sum, vespene_cost_sum
214
+ def correct_unit_type_sum
215
+ units.transform_values! do |unit_data|
216
+ unit_data.mineral_cost_sum = unit_data.mineral_cost
217
+ unit_data.vespene_cost_sum = unit_data.vespene_cost
218
+
219
+ # Cost sums where necessary
220
+ case unit_data.unit_id
221
+ when Api::UnitTypeId::OVERSEER, Api::UnitTypeId::OVERLORDTRANSPORT # MORPH TOTALS - UNITS
222
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::OVERLORD].mineral_cost
223
+ when Api::UnitTypeId::BANELING
224
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::ZERGLING].mineral_cost / 2.0
225
+ when Api::UnitTypeId::RAVAGER
226
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::ROACH].mineral_cost
227
+ unit_data.vespene_cost_sum += units[Api::UnitTypeId::ROACH].vespene_cost
228
+ when Api::UnitTypeId::LURKERMP
229
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::HYDRALISK].mineral_cost
230
+ unit_data.vespene_cost_sum += units[Api::UnitTypeId::HYDRALISK].vespene_cost
231
+ when Api::UnitTypeId::BROODLORD
232
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::CORRUPTOR].mineral_cost
233
+ unit_data.vespene_cost_sum += units[Api::UnitTypeId::CORRUPTOR].vespene_cost
234
+ when Api::UnitTypeId::ORBITALCOMMAND, Api::UnitTypeId::PLANETARYFORTRESS # MORPH TOTALS - STRUCTURES
235
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::COMMANDCENTER].mineral_cost
236
+ when Api::UnitTypeId::GREATERSPIRE
237
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::SPIRE].mineral_cost
238
+ unit_data.vespene_cost_sum += units[Api::UnitTypeId::SPIRE].vespene_cost
239
+ when Api::UnitTypeId::LAIR
240
+ unit_data.mineral_cost_sum += 300 + units[Api::UnitTypeId::DRONE].mineral_cost
241
+ when Api::UnitTypeId::HIVE # = Hatchery + Lair + Drone
242
+ # Hardcode, because sequence isn't guaranteed and chained
243
+ unit_data.mineral_cost_sum += 450 + units[Api::UnitTypeId::DRONE].mineral_cost
244
+ unit_data.vespene_cost_sum += 100
245
+ when Api::UnitTypeId::ARCHON
246
+ unit_data.mineral_cost_sum = units[Api::UnitTypeId::HIGHTEMPLAR].mineral_cost * 2
247
+ unit_data.vespene_cost_sum = units[Api::UnitTypeId::HIGHTEMPLAR].vespene_cost * 2
248
+ when Api::UnitTypeId::WARPGATE
249
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::GATEWAY].mineral_cost
250
+ unit_data.vespene_cost_sum += units[Api::UnitTypeId::GATEWAY].vespene_cost
251
+ else
252
+ if unit_data.race == Api::Race::Zerg && unit_data.attributes.include?(:Structure)
253
+ unit_data.mineral_cost_sum += units[Api::UnitTypeId::DRONE].mineral_cost
254
+ end
255
+ end
256
+
257
+ unit_data
258
+ end
259
+ end
100
260
  end
101
261
  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
@@ -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]
@@ -14,6 +14,7 @@ module Sc2
14
14
  # @param debug_command [Api::DebugCommand]
15
15
  # @return [void]
16
16
  def queue_debug_command(debug_command)
17
+ @debug_command_queue ||= []
17
18
  @debug_command_queue << debug_command
18
19
  end
19
20
 
@@ -259,6 +259,12 @@ module Sc2
259
259
  parsed_terrain_height[y.to_i, x.to_i]
260
260
  end
261
261
 
262
+ # Returns the terrain height (z) at position x and y for a point
263
+ # @return [Float] z axis position between -16 and 16
264
+ def terrain_height_for_pos(position)
265
+ terrain_height(x: position.x, y: position.y)
266
+ end
267
+
262
268
  # Returns a parsed terrain_height from bot.game_info.start_raw.
263
269
  # Each value in [row][column] holds a float value which is the z height
264
270
  # @return [Numo::SFloat] Numo array
@@ -36,13 +36,13 @@ module Sc2
36
36
  # Override to modify the previous frame before being set to current
37
37
  # @param bot [Sc2::Player::Bot]
38
38
  def before_reset(bot)
39
- # pp "### before_reset"
39
+ # no op
40
40
  end
41
41
 
42
42
  # Override to modify previous frame after reset is complete
43
43
  # @param bot [Sc2::Player::Bot]
44
44
  def after_reset(bot)
45
- # pp "### after_reset"
45
+ # no op
46
46
  end
47
47
  end
48
48
  end