sc2ai 0.5.0 → 0.6.1
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.
- checksums.yaml +4 -4
- data/data/sc2ai/protocol/data.proto +1 -1
- data/data/sc2ai/protocol/raw.proto +1 -1
- data/docker_build/Dockerfile.ruby +16 -16
- data/lib/sc2ai/api/data.rb +59 -1
- data/lib/sc2ai/cli/cli.rb +5 -20
- data/lib/sc2ai/connection/requests.rb +4 -1
- data/lib/sc2ai/local_play/match.rb +2 -2
- data/lib/sc2ai/player/game_state.rb +0 -7
- data/lib/sc2ai/player/units.rb +12 -8
- data/lib/sc2ai/player.rb +26 -1
- data/lib/sc2ai/protocol/extensions/player_common.rb +25 -0
- data/lib/sc2ai/protocol/extensions/position.rb +1 -1
- data/lib/sc2ai/protocol/extensions/unit.rb +59 -14
- data/lib/sc2ai/protocol/extensions/unit_type_data.rb +30 -0
- data/lib/sc2ai/unit_group/filter_ext.rb +23 -2
- data/lib/sc2ai/version.rb +1 -1
- data/lib/templates/new/api/data.proto +1 -1
- data/lib/templates/new/api/raw.proto +1 -1
- data/lib/templates/new/run_example_match.rb.tt +1 -1
- data/sig/sc2ai.rbs +143 -86
- metadata +13 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddd8bd1e37b50ba6d4e89c24fb330d646589d869cc53cf96a69624eaa0e6133c
|
4
|
+
data.tar.gz: d88b2674f094621c11aa7de9a0c59ae82a6668aa6c882783a54236776ceb9f37
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10c922d086149c4f2e4cac906607d7f5fb2c31fde1ace225186b0d83342efaea6720c48b799eb01e4d29fec5b295a4890d44b9cf803bb0c47e75e5830489c466
|
7
|
+
data.tar.gz: 7430bdcd6935e457b88b562838c0dc112f28ecdfbacd072293ddfdf358d065287dcdbc300bb7cc76b4efe0e671a82006f9d53766b79bd2d006496982061e3fc7
|
@@ -89,7 +89,7 @@ message UnitTypeData {
|
|
89
89
|
optional float sight_range = 25; // Range unit reveals vision.
|
90
90
|
|
91
91
|
repeated uint32 tech_alias = 21 [packed=false]; // Other units that satisfy the same tech requirement.
|
92
|
-
optional uint32 unit_alias = 22
|
92
|
+
optional uint32 unit_alias = 22; // The morphed variant of this unit.
|
93
93
|
|
94
94
|
optional uint32 tech_requirement = 23; // Structure required to build this unit. (Or any with the same tech_alias)
|
95
95
|
optional bool require_attached = 24; // Whether tech_requirement is an add-on.
|
@@ -4,12 +4,12 @@ LABEL service="bot-ruby-local"
|
|
4
4
|
USER root
|
5
5
|
WORKDIR /root/ruby-builder
|
6
6
|
|
7
|
-
ARG RUBY_VERSION=3.4.
|
7
|
+
ARG RUBY_VERSION=3.4.2
|
8
8
|
ARG DEBIAN_DISABLE_RUBYGEMS_INTEGRATION=true
|
9
9
|
|
10
10
|
# Deps - Ruby build
|
11
11
|
RUN apt-get update
|
12
|
-
RUN apt install --assume-yes rustc curl build-essential libssl-dev zlib1g-dev libgmp-dev libffi-dev uuid-dev
|
12
|
+
RUN apt install --assume-yes rustc curl build-essential libssl-dev zlib1g-dev libgmp-dev libffi-dev uuid-dev libopenblas0-serial
|
13
13
|
|
14
14
|
# Deps - libyaml from source
|
15
15
|
RUN curl https://pyyaml.org/download/libyaml/yaml-0.2.5.tar.gz -o yaml-0.2.5.tar.gz
|
@@ -33,20 +33,20 @@ RUN rm yaml-0.2.5.tar.gz
|
|
33
33
|
|
34
34
|
# Package config
|
35
35
|
# numo-linalg needs openblas, copy to ruby-prefix/lib/ dir.
|
36
|
-
RUN apt download libopenblas0-serial
|
37
|
-
RUN mkdir openblas
|
38
|
-
RUN dpkg-deb -x ./libopenblas*.deb openblas
|
39
|
-
RUN cp -d openblas/usr/lib/x86_64-linux-gnu/openblas-serial/* /root/ruby-builder/.ruby/lib/
|
40
|
-
RUN rm -rf ./openblas
|
41
|
-
RUN rm ./libopenblas0-serial*.deb
|
42
|
-
|
43
|
-
RUN apt download libgfortran5
|
44
|
-
RUN mkdir libgfortran5
|
45
|
-
RUN dpkg-deb -x ./libgfortran*.deb libgfortran5
|
46
|
-
RUN find libgfortran5
|
47
|
-
RUN cp libgfortran5/usr/lib/x86_64-linux-gnu/libgfortran.so.5 /root/ruby-builder/.ruby/lib/
|
48
|
-
RUN rm -rf ./libgfortran5
|
49
|
-
RUN rm ./libgfortran5*.deb
|
36
|
+
#RUN apt download libopenblas0-serial
|
37
|
+
#RUN mkdir openblas
|
38
|
+
#RUN dpkg-deb -x ./libopenblas*.deb openblas
|
39
|
+
#RUN cp -d openblas/usr/lib/x86_64-linux-gnu/openblas-serial/* /root/ruby-builder/.ruby/lib/
|
40
|
+
#RUN rm -rf ./openblas
|
41
|
+
#RUN rm ./libopenblas0-serial*.deb
|
42
|
+
|
43
|
+
#RUN apt download libgfortran5
|
44
|
+
#RUN mkdir libgfortran5
|
45
|
+
#RUN dpkg-deb -x ./libgfortran*.deb libgfortran5
|
46
|
+
#RUN find libgfortran5
|
47
|
+
#RUN cp libgfortran5/usr/lib/x86_64-linux-gnu/libgfortran.so.5 /root/ruby-builder/.ruby/lib/
|
48
|
+
#RUN rm -rf ./libgfortran5
|
49
|
+
#RUN rm ./libgfortran5*.deb
|
50
50
|
|
51
51
|
RUN /root/ruby-builder/.ruby/bin/ruby --yjit -v
|
52
52
|
|
data/lib/sc2ai/api/data.rb
CHANGED
@@ -108,6 +108,8 @@ module Sc2
|
|
108
108
|
correct_unit_type_costs
|
109
109
|
correct_unit_type_sum
|
110
110
|
decorate_unit_type_placement_length
|
111
|
+
decorate_missing_values
|
112
|
+
decorate_weapon_helpers
|
111
113
|
end
|
112
114
|
|
113
115
|
# @private
|
@@ -256,9 +258,11 @@ module Sc2
|
|
256
258
|
end
|
257
259
|
end
|
258
260
|
|
261
|
+
private
|
262
|
+
|
259
263
|
# @private
|
260
264
|
# Adds placement_length to units if applicable
|
261
|
-
|
265
|
+
def decorate_unit_type_placement_length
|
262
266
|
@units.each_value do |unit_type_data|
|
263
267
|
unit_type_data.placement_length = 0
|
264
268
|
next unless unit_type_data.ability_id
|
@@ -269,5 +273,59 @@ module Sc2
|
|
269
273
|
end
|
270
274
|
end
|
271
275
|
end
|
276
|
+
|
277
|
+
# @private
|
278
|
+
# Adds values missing from the API
|
279
|
+
def decorate_missing_values
|
280
|
+
# Battlecruiser has no weapons. Force these in by hand.
|
281
|
+
@units[Api::UnitTypeId::BATTLECRUISER].weapons = [
|
282
|
+
Api::Weapon.new(
|
283
|
+
type: Api::Weapon::TargetType::GROUND,
|
284
|
+
damage: 8.0,
|
285
|
+
damage_bonus: [],
|
286
|
+
attacks: 1,
|
287
|
+
range: 6.0,
|
288
|
+
speed: 0.224
|
289
|
+
),
|
290
|
+
Api::Weapon.new(
|
291
|
+
type: Api::Weapon::TargetType::AIR,
|
292
|
+
damage: 5.0,
|
293
|
+
damage_bonus: [],
|
294
|
+
attacks: 1,
|
295
|
+
range: 6.0,
|
296
|
+
speed: 0.224
|
297
|
+
)
|
298
|
+
]
|
299
|
+
end
|
300
|
+
|
301
|
+
# @private
|
302
|
+
# Adds ground_damage, air_damage, ground_range, air_range, ground_dps and air_dps
|
303
|
+
def decorate_weapon_helpers
|
304
|
+
@units.each do |unit_type_id, unit_type_data|
|
305
|
+
ground_weapon = unit_type_data.weapons.find do |weapon|
|
306
|
+
weapon.type == :GROUND || weapon.type == :ANY
|
307
|
+
end
|
308
|
+
|
309
|
+
air_weapon = unit_type_data.weapons.find do |weapon|
|
310
|
+
weapon.type == :AIR || weapon.type == :ANY
|
311
|
+
end
|
312
|
+
|
313
|
+
unit_type_data.ground_range = ground_weapon&.range || 0.0
|
314
|
+
unit_type_data.air_range = air_weapon&.range || 0.0
|
315
|
+
|
316
|
+
ground_attacks = ground_weapon&.attacks || 0
|
317
|
+
air_attacks = air_weapon&.attacks || 0
|
318
|
+
base_ground_damage = ground_weapon&.damage || 0.0
|
319
|
+
base_air_damage = air_weapon&.damage || 0.0
|
320
|
+
ground_attack_speed = ground_weapon&.speed || 1.0
|
321
|
+
air_attack_speed = air_weapon&.speed || 1.0
|
322
|
+
|
323
|
+
unit_type_data.ground_damage = base_ground_damage * ground_attacks
|
324
|
+
unit_type_data.air_damage = base_air_damage * air_attacks
|
325
|
+
|
326
|
+
unit_type_data.ground_dps = unit_type_data.ground_damage / ground_attack_speed
|
327
|
+
unit_type_data.air_dps = unit_type_data.air_damage / air_attack_speed
|
328
|
+
end
|
329
|
+
end
|
272
330
|
end
|
273
331
|
end
|
data/lib/sc2ai/cli/cli.rb
CHANGED
@@ -21,31 +21,16 @@ module Sc2
|
|
21
21
|
desc "setup410", "Downloads and install SC2 v4.10"
|
22
22
|
# downloads and install SC2 v4.10
|
23
23
|
def setup410
|
24
|
-
say " "
|
25
|
-
say "This script sets up SC2 at version 4.10, which we use competitively."
|
26
|
-
say "Press any key to continue..."
|
27
|
-
ask ""
|
28
|
-
|
29
|
-
say "You must accept the Blizzard® StarCraft® II AI and Machine Learning License at"
|
30
|
-
say "https://blzdistsc2-a.akamaihd.net/AI_AND_MACHINE_LEARNING_LICENSE.html"
|
31
|
-
say "It is PERMISSIVE and grants you freedoms over the standard EULA."
|
32
|
-
say "We do not record this action, but depend on software goverend by that license to continue."
|
33
|
-
puts 'If you accept, type "iagreetotheeula" (without quotes) and press ENTER to continue:'
|
34
|
-
|
35
|
-
while $stdin.gets.chomp != "iagreetotheeula"
|
36
|
-
say ""
|
37
|
-
puts 'Type "iagreetotheeula" (without quotes) and press ENTER to continue:'
|
38
|
-
end
|
39
|
-
say ""
|
40
|
-
say ""
|
41
|
-
say "Great decision."
|
42
|
-
|
43
24
|
require "sc2ai"
|
44
25
|
Async do
|
45
26
|
Sc2.logger.level = :fatal
|
27
|
+
say " "
|
28
|
+
say "This script sets up SC2 at version 4.10, which we use competitively."
|
46
29
|
say "SC2 will launch a blank window, be unresponsive, but download about 100mb in the background."
|
30
|
+
say ""
|
31
|
+
say "It will appear to hang as it updates. This is normal."
|
47
32
|
say "Let it finish and close itself."
|
48
|
-
say "Press
|
33
|
+
say "Press any key to continue..."
|
49
34
|
ask ""
|
50
35
|
|
51
36
|
say ""
|
@@ -110,8 +110,9 @@ module Sc2
|
|
110
110
|
# game_info = observer.api.game_info
|
111
111
|
# end
|
112
112
|
# ensure
|
113
|
+
# observer.disconnect
|
113
114
|
# Sc2::ClientManager.stop(0)
|
114
|
-
# end
|
115
|
+
# end.wait
|
115
116
|
# @param replay_path [String] path to replay
|
116
117
|
# @param replay_data [String] alternative to file, binary string of replay_file.read
|
117
118
|
# @param map_data [String] optional binary string of SC2 map if not present in paths
|
@@ -195,6 +196,7 @@ module Sc2
|
|
195
196
|
|
196
197
|
# Snapshot of the current game state. Primary source for raw information
|
197
198
|
# @param game_loop [Integer] you wish to wait for (realtime only)
|
199
|
+
# @return [Api::ResponseObservation]
|
198
200
|
def observation(game_loop: nil)
|
199
201
|
# Sc2.logger.debug { "#{self.class}.#{__method__} game_loop: #{game_loop}" }
|
200
202
|
if game_loop.nil?
|
@@ -275,6 +277,7 @@ module Sc2
|
|
275
277
|
# Advances the game simulation by step_count. Not used in realtime mode.
|
276
278
|
# Only constant step size supported - subsequent requests use cache.
|
277
279
|
def step(step_count = 1)
|
280
|
+
step_count = step_count.to_i
|
278
281
|
@_cached_request_step ||= {}
|
279
282
|
@_cached_request_step[step_count] ||= Api::Request.new(
|
280
283
|
step: Api::RequestStep.new(count: step_count)
|
@@ -68,7 +68,7 @@ module Sc2
|
|
68
68
|
ensure
|
69
69
|
Sc2.logger.debug { "Game over, disconnect players." }
|
70
70
|
# Suppress interrupt errors #$stderr.reopen File.new(File::NULL, "w")
|
71
|
-
player.quit if
|
71
|
+
player.quit if [Paths::PF_WINDOWS, Paths::PF_WSL1, Paths::PF_WSL2].include?(Paths.platform)
|
72
72
|
player.disconnect
|
73
73
|
ClientManager.stop(player_index) # unless keep_clients_alive
|
74
74
|
end
|
@@ -95,7 +95,7 @@ module Sc2
|
|
95
95
|
response = player.api.save_replay
|
96
96
|
path = Pathname(Paths.bot_data_replay_dir).join("autosave-#{safe_player_name}.SC2Replay")
|
97
97
|
f = File.new(path, "wb:ASCII-8BIT")
|
98
|
-
f.
|
98
|
+
f.write_nonblock(response.data)
|
99
99
|
f.close
|
100
100
|
end
|
101
101
|
|
@@ -36,16 +36,9 @@ module Sc2
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def game_info=(new_info)
|
39
|
-
@game_info_loop = game_loop
|
40
39
|
@game_info = new_info
|
41
40
|
end
|
42
41
|
|
43
|
-
# @!attribute game_info_loop
|
44
|
-
# This is the last loop at which game_info was set.
|
45
|
-
# Used to determine staleness.
|
46
|
-
# @return [Integer]
|
47
|
-
attr_accessor :game_info_loop
|
48
|
-
|
49
42
|
# @!attribute data
|
50
43
|
# @return [Api::ResponseData]
|
51
44
|
attr_accessor :data
|
data/lib/sc2ai/player/units.rb
CHANGED
@@ -35,6 +35,11 @@ module Sc2
|
|
35
35
|
# @return [Sc2::UnitGroup] a group of neutral units
|
36
36
|
attr_accessor :effects # not a unit
|
37
37
|
|
38
|
+
# An array of Units which are (red) blips on radar
|
39
|
+
# @!attribute blips
|
40
|
+
# @return [Sc2::UnitGroup] a group of blip units
|
41
|
+
attr_accessor :blips
|
42
|
+
|
38
43
|
# Returns the upgrade ids you have acquired such as weapon upgrade and armor upgrade ids.
|
39
44
|
# Shorthand for observation.raw_data.player.upgrade_ids
|
40
45
|
# @!attribute [r] upgrades_completed
|
@@ -254,7 +259,7 @@ module Sc2
|
|
254
259
|
|
255
260
|
# Checks whether you have the resources to construct quantity of unit type
|
256
261
|
# @return [Boolean]
|
257
|
-
def can_afford?(unit_type_id
|
262
|
+
def can_afford?(unit_type_id, quantity: 1)
|
258
263
|
unit_type_data = unit_data(unit_type_id)
|
259
264
|
return false if unit_type_data.nil?
|
260
265
|
|
@@ -343,10 +348,7 @@ module Sc2
|
|
343
348
|
enemy_alliance = self.enemy_alliance
|
344
349
|
|
345
350
|
# To prevent several loops over all units per frame, use this single loop for all checks
|
346
|
-
|
347
|
-
i = 0
|
348
|
-
while i < all_unit_size
|
349
|
-
unit = observation.raw_data.units[i]
|
351
|
+
observation.raw_data.units.each do |unit|
|
350
352
|
tag = unit.tag
|
351
353
|
tag = unit.tag = unit.hash if tag.zero?
|
352
354
|
# Reluctantly assigning player to unit
|
@@ -377,7 +379,7 @@ module Sc2
|
|
377
379
|
@neutral[tag] = unit
|
378
380
|
end
|
379
381
|
|
380
|
-
#
|
382
|
+
# Don't parse callbacks on first loop or for neutral units
|
381
383
|
if !@previous.all_units.nil? &&
|
382
384
|
unit.alliance != :NEUTRAL &&
|
383
385
|
unit.display_type != :PLACEHOLDER &&
|
@@ -392,11 +394,13 @@ module Sc2
|
|
392
394
|
issue_existing_unit_callbacks(unit, previous_unit)
|
393
395
|
end
|
394
396
|
end
|
397
|
+
end
|
395
398
|
|
399
|
+
# Due to race condition with callbacks and Unit#bot not being set,
|
400
|
+
# rather do a second iteration just for the callbacks
|
401
|
+
observation.raw_data.units.each do |unit|
|
396
402
|
# Allow user to fiddle with unit
|
397
403
|
on_parse_observation_unit(unit)
|
398
|
-
|
399
|
-
i += 1
|
400
404
|
end
|
401
405
|
end
|
402
406
|
|
data/lib/sc2ai/player.rb
CHANGED
@@ -213,6 +213,7 @@ module Sc2
|
|
213
213
|
# enable_feature_layer = true
|
214
214
|
#
|
215
215
|
# end
|
216
|
+
# @return [void]
|
216
217
|
def configure
|
217
218
|
end
|
218
219
|
|
@@ -276,12 +277,14 @@ module Sc2
|
|
276
277
|
|
277
278
|
# Override to perform steps before first on_step gets called.
|
278
279
|
# Current game_loop is 0 and @api is available
|
280
|
+
# @return [void]
|
279
281
|
def on_start
|
280
282
|
# Sc2.logger.debug { "#{self.class} on_start" }
|
281
283
|
end
|
282
284
|
|
283
285
|
# Override to implement your own game logic.
|
284
286
|
# Gets called whenever the game moves forward.
|
287
|
+
# @return [void]
|
285
288
|
def on_step
|
286
289
|
return unless is_a? Bot
|
287
290
|
|
@@ -305,6 +308,7 @@ module Sc2
|
|
305
308
|
# puts "Lets try again!"
|
306
309
|
# end
|
307
310
|
# end
|
311
|
+
# @return [void]
|
308
312
|
def on_finish(result)
|
309
313
|
# Sc2.logger.debug { "#{self.class} on_finish" }
|
310
314
|
end
|
@@ -312,12 +316,14 @@ module Sc2
|
|
312
316
|
# Called when Random race is first detected.
|
313
317
|
# Override to handle race identification of random enemy.
|
314
318
|
# @param race [Integer] see {Api::Race}
|
319
|
+
# @return [void]
|
315
320
|
def on_random_race_detected(race)
|
316
321
|
end
|
317
322
|
|
318
323
|
# Called on step if errors are present. Equivalent of UI red text errors.
|
319
324
|
# Override to read action errors.
|
320
325
|
# @param errors [Array<Api::ActionError>]
|
326
|
+
# @return [void]
|
321
327
|
def on_action_errors(errors)
|
322
328
|
# Sc2.logger.debug errors
|
323
329
|
end
|
@@ -325,6 +331,7 @@ module Sc2
|
|
325
331
|
# Actions this player performed since the last Observation.
|
326
332
|
# Override to read actions successfully performed
|
327
333
|
# @param actions [Array<Api::Action>] a list of actions which were performed
|
334
|
+
# @return [void]
|
328
335
|
def on_actions_performed(actions)
|
329
336
|
# Sc2.logger.debug actions
|
330
337
|
end
|
@@ -342,11 +349,13 @@ module Sc2
|
|
342
349
|
# end
|
343
350
|
# end
|
344
351
|
# @param alerts [Array<Integer>] array of Api::Alert::*
|
352
|
+
# @return [void]
|
345
353
|
def on_alerts(alerts)
|
346
354
|
end
|
347
355
|
|
348
356
|
# Callback when upgrades are completed, multiple might finish on the same observation.
|
349
357
|
# @param upgrade_ids [Array<Integer>] Api::UpgradeId::*
|
358
|
+
# @return [void]
|
350
359
|
def on_upgrades_completed(upgrade_ids)
|
351
360
|
end
|
352
361
|
|
@@ -359,6 +368,7 @@ module Sc2
|
|
359
368
|
# Callback, on observation parse when iterating over every unit
|
360
369
|
# Can be useful for decorating additional properties on a unit before on_step
|
361
370
|
# A Sc2::Player should override this to decorate additional properties
|
371
|
+
# @return [void]
|
362
372
|
def on_parse_observation_unit(unit)
|
363
373
|
end
|
364
374
|
|
@@ -367,12 +377,14 @@ module Sc2
|
|
367
377
|
# Override to use in your bot class or use Player.
|
368
378
|
# @param unit [Api::Unit]
|
369
379
|
# @see Sc2::Player::Units#units_destroyed
|
380
|
+
# @return [void]
|
370
381
|
def on_unit_destroyed(unit)
|
371
382
|
end
|
372
383
|
|
373
384
|
# Callback for unit created.
|
374
385
|
# Override to use in your bot class.
|
375
386
|
# @param unit [Api::Unit]
|
387
|
+
# @return [void]
|
376
388
|
def on_unit_created(unit)
|
377
389
|
end
|
378
390
|
|
@@ -381,18 +393,21 @@ module Sc2
|
|
381
393
|
# Override to use in your bot class or use Player.
|
382
394
|
# @param unit [Api::Unit]
|
383
395
|
# @param previous_unit_type_id [Integer] Api::UnitTypeId::*
|
396
|
+
# @return [void]
|
384
397
|
def on_unit_type_changed(unit, previous_unit_type_id)
|
385
398
|
end
|
386
399
|
|
387
400
|
# Callback for structure building began
|
388
401
|
# Override to use in your bot class.
|
389
402
|
# @param unit [Api::Unit]
|
403
|
+
# @return [void]
|
390
404
|
def on_structure_started(unit)
|
391
405
|
end
|
392
406
|
|
393
407
|
# Callback for structure building is completed
|
394
408
|
# Override to use in your bot class or use Player.
|
395
409
|
# @param unit [Api::Unit]
|
410
|
+
# @return [void]
|
396
411
|
def on_structure_completed(unit)
|
397
412
|
end
|
398
413
|
|
@@ -400,6 +415,7 @@ module Sc2
|
|
400
415
|
# Override to use in your bot class or use Player.
|
401
416
|
# @param unit [Api::Unit]
|
402
417
|
# @param amount [Integer] of damage
|
418
|
+
# @return [void]
|
403
419
|
def on_unit_damaged(unit, amount)
|
404
420
|
end
|
405
421
|
|
@@ -530,18 +546,27 @@ module Sc2
|
|
530
546
|
on_structure_completed
|
531
547
|
on_unit_damaged]
|
532
548
|
|
549
|
+
# @private
|
550
|
+
# @return [Array<Symbol>] callbacks implemented on player class
|
551
|
+
attr_accessor :callbacks_defined
|
552
|
+
|
533
553
|
# @private
|
534
554
|
# Checks if callback method is defined on our bot
|
535
555
|
# Used to skip processing on unused callbacks
|
536
556
|
# @param callback [Symbol]
|
537
557
|
def callback_defined?(callback)
|
538
|
-
|
558
|
+
if @callbacks_defined.nil?
|
559
|
+
# Cache the intersection check, assuming nobody defines a callback method while actually running
|
560
|
+
@callbacks_defined = CALLBACK_METHODS.intersection(self.class.instance_methods(false))
|
561
|
+
end
|
562
|
+
@callbacks_defined.include?(callback)
|
539
563
|
end
|
540
564
|
|
541
565
|
# Initialize data on step 0 before stepping and before on_start is called
|
542
566
|
def prepare_start
|
543
567
|
@data = Sc2::Data.new(@api.data)
|
544
568
|
clear_action_queue
|
569
|
+
clear_action_errors
|
545
570
|
clear_debug_command_queue
|
546
571
|
end
|
547
572
|
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Api
|
2
|
+
# Adds additional functionality to message object Api::Point2D
|
3
|
+
module PlayerCommonExt
|
4
|
+
# @!attribute supply_cap
|
5
|
+
# Maximum supply. alias of #food_cap
|
6
|
+
# @return [Integer]
|
7
|
+
def supply_cap = food_cap
|
8
|
+
|
9
|
+
# @!attribute supply_used
|
10
|
+
# Supply used. alias of #food_used
|
11
|
+
# @return [Integer]
|
12
|
+
def supply_used = food_used
|
13
|
+
|
14
|
+
# @!attribute supply_army
|
15
|
+
# Supply used for army. alias of #food_army
|
16
|
+
# @return [Integer]
|
17
|
+
def supply_army = food_army
|
18
|
+
|
19
|
+
# @!attribute supply_workers
|
20
|
+
# Supply used in workers. alias of #food_workers
|
21
|
+
# @return [Integer]
|
22
|
+
def supply_workers = food_workers
|
23
|
+
end
|
24
|
+
end
|
25
|
+
Api::PlayerCommon.prepend Api::PlayerCommonExt
|
@@ -23,7 +23,7 @@ module Api
|
|
23
23
|
# Get the unit as from the previous frame. Good for comparison.
|
24
24
|
# @return [Api::Unit, nil] this unit from the previous frame or nil if it wasn't present
|
25
25
|
def previous
|
26
|
-
@bot.previous.all_units[tag
|
26
|
+
@bot.previous.all_units&.[](tag)
|
27
27
|
end
|
28
28
|
|
29
29
|
# Returns whether a unit is alive or not
|
@@ -48,7 +48,7 @@ module Api
|
|
48
48
|
|
49
49
|
# Attributes ---
|
50
50
|
|
51
|
-
# Returns static
|
51
|
+
# Returns static an array of attributes for a unit
|
52
52
|
# @return [Array<Api::Attribute>]
|
53
53
|
def attributes
|
54
54
|
unit_data.attributes
|
@@ -111,7 +111,7 @@ module Api
|
|
111
111
|
has_attribute?(:STRUCTURE)
|
112
112
|
end
|
113
113
|
|
114
|
-
# Checks if unit is
|
114
|
+
# Checks if unit is hovering
|
115
115
|
# @return [Boolean] whether unit has attribute :Hover
|
116
116
|
def is_hover?
|
117
117
|
has_attribute?(:HOVER)
|
@@ -133,13 +133,31 @@ module Api
|
|
133
133
|
|
134
134
|
# Helpers for unit properties
|
135
135
|
|
136
|
-
def width = radius * 2
|
136
|
+
def width = radius * 2.0
|
137
137
|
# @!parse
|
138
138
|
# # @!attribute width
|
139
139
|
# # width = radius * 2
|
140
140
|
# # @return [Float]
|
141
141
|
# attr_reader :width
|
142
142
|
|
143
|
+
# @!attribute [r] full_health?
|
144
|
+
# @return [Boolean] health is at maximum
|
145
|
+
def full_health?
|
146
|
+
health == health_max
|
147
|
+
end
|
148
|
+
|
149
|
+
# @!attribute [r] full_shields?
|
150
|
+
# @return [Boolean] shields are at maximum
|
151
|
+
def full_shields?
|
152
|
+
shield == shield_max
|
153
|
+
end
|
154
|
+
|
155
|
+
# @!attribute [r] full_energy?
|
156
|
+
# @return [Boolean] energy is at maximum
|
157
|
+
def full_energy?
|
158
|
+
energy == energy_max
|
159
|
+
end
|
160
|
+
|
143
161
|
# Some overrides to allow question mark references to boolean properties
|
144
162
|
|
145
163
|
# @!attribute [r] is_flying?
|
@@ -179,6 +197,14 @@ module Api
|
|
179
197
|
# @return [Boolean]
|
180
198
|
def is_ground? = !is_flying?
|
181
199
|
|
200
|
+
# @!attribute [r] is_cloaked??
|
201
|
+
# Returns whether the unit is cloaked. Revealed cloak units also return true.
|
202
|
+
# For further distinction, use Unit#cloaked, which uses enum CloakedState
|
203
|
+
# @return [Boolean]
|
204
|
+
def is_cloaked?
|
205
|
+
cloak != :NOT_CLOAKED && cloak != :CLOAKED_UNKNOWN
|
206
|
+
end
|
207
|
+
|
182
208
|
# @!endgroup Virtual properties
|
183
209
|
|
184
210
|
# Whether unit is effected by buff_id
|
@@ -315,14 +341,18 @@ module Api
|
|
315
341
|
)
|
316
342
|
end
|
317
343
|
|
318
|
-
# Draws a sphere around the unit's attack range
|
319
|
-
# @param weapon_index [
|
344
|
+
# Draws a sphere around the unit's attack range (weapon range + radius)
|
345
|
+
# @param weapon_index [Integer] default first weapon, see UnitTypeData#weapons
|
320
346
|
# @param color [Api::Color] optional api color, default red
|
321
347
|
def debug_fire_range(weapon_index = 0, color = nil)
|
348
|
+
attack_range = unit_data.weapons[weapon_index]&.range
|
349
|
+
return if attack_range.nil?
|
350
|
+
attack_range += radius
|
351
|
+
|
322
352
|
color = Api::Color.new(r: 255, b: 0, g: 0) if color.nil?
|
323
|
-
attack_range = unit_data.weapons[weapon_index].range
|
324
353
|
raised_position = pos.dup
|
325
354
|
raised_position.z += 0.01
|
355
|
+
|
326
356
|
@bot.debug_draw_sphere(point: raised_position, radius: attack_range, color:)
|
327
357
|
end
|
328
358
|
|
@@ -413,22 +443,30 @@ module Api
|
|
413
443
|
end
|
414
444
|
|
415
445
|
# Checks whether enemy is within range of weapon or ability and can target ground/air.
|
416
|
-
#
|
446
|
+
# By default, checks all weapons.
|
447
|
+
# Pass weapon_index or ability_id to target a specific source of damage.
|
417
448
|
# @param unit [Api::Unit] enemy
|
418
|
-
# @param weapon_index [Integer]
|
449
|
+
# @param weapon_index [Integer] passing this will select a specific Weapon
|
419
450
|
# @param ability_id [Integer] passing this will override weapon Api::AbilityId::*
|
420
451
|
# @return [Boolean]
|
421
452
|
# @example
|
453
|
+
# queen.can_attack?(enemy, weapon_index: 0) # Air attack
|
454
|
+
# queen.can_attack?(enemy, weapon_index: 1) # Ground attack
|
455
|
+
# queen.can_attack?(enemy) # Auto detect (scans all weapons)
|
422
456
|
# ghost.can_attack?(enemy, weapon_index: 0, ability_id: Api::AbilityId::SNIPE)
|
423
|
-
def can_attack?(unit:, weapon_index:
|
424
|
-
if
|
457
|
+
def can_attack?(unit:, weapon_index: nil, ability_id: nil)
|
458
|
+
return false if unit.nil?
|
459
|
+
if ability_id
|
460
|
+
# ability
|
461
|
+
ability = @bot.ability_data(ability_id)
|
462
|
+
can_ability_target_unit?(unit:, ability:)
|
463
|
+
elsif weapon_index
|
425
464
|
# weapon
|
426
465
|
source_weapon = weapon(weapon_index)
|
427
466
|
can_weapon_target_unit?(unit:, weapon: source_weapon)
|
428
467
|
else
|
429
|
-
#
|
430
|
-
|
431
|
-
can_ability_target_unit?(unit:, ability:)
|
468
|
+
# auto-detect weapon
|
469
|
+
unit_data.weapons.any? { |weapon| can_weapon_target_unit?(unit:, weapon:) }
|
432
470
|
end
|
433
471
|
end
|
434
472
|
|
@@ -445,6 +483,7 @@ module Api
|
|
445
483
|
# @param weapon [Api::Weapon]
|
446
484
|
# @return [Boolean]
|
447
485
|
def can_weapon_target_unit?(unit:, weapon:)
|
486
|
+
return false if unit.nil? || weapon.nil?
|
448
487
|
# false if enemy is air and we can only shoot ground
|
449
488
|
return false if unit.is_flying && weapon.type == :GROUND # Api::Weapon::TargetType::GROUND
|
450
489
|
|
@@ -455,7 +494,12 @@ module Api
|
|
455
494
|
in_attack_range?(unit:, range: weapon.range)
|
456
495
|
end
|
457
496
|
|
497
|
+
# Checks whether an ability can target a unit
|
498
|
+
# @param unit [Api::Unit]
|
499
|
+
# @param ability [Api::AbilityData]
|
500
|
+
# @return [Boolean]
|
458
501
|
def can_ability_target_unit?(unit:, ability:)
|
502
|
+
return false if unit.nil? || ability.nil?
|
459
503
|
# false if enemy is air and we can only shoot ground
|
460
504
|
return false if ability.target == Api::AbilityData::Target::NONE
|
461
505
|
|
@@ -495,6 +539,7 @@ module Api
|
|
495
539
|
# This value should be correct for building placement math (unit.radius is not good for this)
|
496
540
|
# @return [Float] placement radius
|
497
541
|
def footprint_radius
|
542
|
+
return 0.0 if unit_data.ability_id == 0
|
498
543
|
@bot.data.abilities[unit_data.ability_id].footprint_radius
|
499
544
|
end
|
500
545
|
|