diff --git a/assets/audio/music/EmptyCity.ogg b/assets/audio/music/EmptyCity.ogg new file mode 100644 index 0000000..5b0b3d6 Binary files /dev/null and b/assets/audio/music/EmptyCity.ogg differ diff --git a/assets/audio/music/README.md b/assets/audio/music/README.md new file mode 100644 index 0000000..388a881 --- /dev/null +++ b/assets/audio/music/README.md @@ -0,0 +1,2 @@ +# Music Source +* EmptyCity - CC0 - yd - https://opengameart.org/content/emptycity-background-music \ No newline at end of file diff --git a/lib/camera.rb b/lib/camera.rb index bdda513..dd0c941 100644 --- a/lib/camera.rb +++ b/lib/camera.rb @@ -70,9 +70,9 @@ class IMICRTS def visible?(object) if object.is_a?(Map::Tile) object.position.x - object.size >= @viewport.min.x - @position.x && - object.position.y - object.size >= @viewport.min.y - @position.y && - object.position.x <= @viewport.max.x - @position.x && - object.position.y <= @viewport.max.y - @position.y + object.position.y - object.size >= @viewport.min.y - @position.y && + object.position.x <= @viewport.max.x - @position.x && + object.position.y <= @viewport.max.y - @position.y else pp object.class exit diff --git a/lib/component.rb b/lib/component.rb index 3973a62..c258fd6 100644 --- a/lib/component.rb +++ b/lib/component.rb @@ -1,5 +1,7 @@ class IMICRTS class Component + include CyberarmEngine::Common + @@components = {} def self.get(name) diff --git a/lib/components/build_queue.rb b/lib/components/build_queue.rb index 71057c9..ad760f7 100644 --- a/lib/components/build_queue.rb +++ b/lib/components/build_queue.rb @@ -2,13 +2,13 @@ class IMICRTS class BuildQueue < Component attr_reader :queue - Item = Struct.new(:entity, :progress) + Item = Struct.new(:entity, :progress, :completed) def setup @queue = [] end def add(type) - @queue << Item.new(Entity.get(type), 0.0) + @queue << Item.new(Entity.get(type), 0.0, false) end end end \ No newline at end of file diff --git a/lib/components/building.rb b/lib/components/building.rb index 38f52cc..b1254b1 100644 --- a/lib/components/building.rb +++ b/lib/components/building.rb @@ -2,7 +2,7 @@ class IMICRTS class Building < Component def setup data.construction_progress ||= 0 - data.construction_goal ||= 100 + data.construction_goal ||= Entity.get(@parent.name).build_steps @text = CyberarmEngine::Text.new("", y: @parent.position.y, z: Float::INFINITY, size: 12) data.state = :construct # deconstruct, building, idle @@ -48,8 +48,8 @@ class IMICRTS end def draw_construction - @fencing ||= Gosu::Image.new(IMICRTS::ASSETS_PATH + "/fencing/fencing.png") - @fencing_edge ||= Gosu::Image.new(IMICRTS::ASSETS_PATH + "/fencing/fencing_edge.png") + @fencing ||= get_image(IMICRTS::ASSETS_PATH + "/fencing/fencing.png") + @fencing_edge ||= get_image(IMICRTS::ASSETS_PATH + "/fencing/fencing_edge.png") tiles = [] each_tile(@parent.position / @parent.director.map.tile_size) do |tile, data, x, y| diff --git a/lib/components/movement.rb b/lib/components/movement.rb index b006d85..084dfbb 100644 --- a/lib/components/movement.rb +++ b/lib/components/movement.rb @@ -16,12 +16,12 @@ class IMICRTS end def rotate_towards(vector) - _angle = Gosu.angle(@parent.position.x, @parent.position.y, vector.x, vector.y) - a = (360.0 + (_angle - @parent.angle)) % 360.0 + angle = Gosu.angle(@parent.position.x, @parent.position.y, vector.x, vector.y) + a = (360.0 + (angle - @parent.angle)) % 360.0 # FIXME: Fails if vector is directly behind entity if a.round == 180 - @parent.angle = (_angle + 180.0) % 360.0 + @parent.angle = (angle + 180.0) % 360.0 elsif a < 180 @parent.angle -= 1.0 else diff --git a/lib/components/sidebar_actions.rb b/lib/components/sidebar_actions.rb index 6a1c99f..67de355 100644 --- a/lib/components/sidebar_actions.rb +++ b/lib/components/sidebar_actions.rb @@ -17,7 +17,9 @@ class IMICRTS action.label = ent.name.to_s.split("_").map{ |s| s.capitalize }.join(" ") action.description = "Cost: #{ent.cost}\n#{ent.description}" - action.block = proc { @parent.component(:build_queue).add(data[:entity]) } + action.block = proc do + @parent.director.schedule_order(IMICRTS::Order::BUILD_UNIT, @parent.player.id, @parent.id, data[:entity]) + end when :set_tool ent = IMICRTS::Entity.get(data[:entity]) diff --git a/lib/components/spawner.rb b/lib/components/spawner.rb index 9ff48d2..c6d3a4c 100644 --- a/lib/components/spawner.rb +++ b/lib/components/spawner.rb @@ -1,21 +1,17 @@ class IMICRTS class Spawner < Component def tick(tick_id) - # TODO: Ensure that a build order is created before working on entity - item = @parent.component(:build_queue).queue.first - if item - item.progress += 1 + return unless item - if item.progress >= 100 # TODO: Define work units required for construction - @parent.component(:build_queue).queue.shift + item.progress += 1 - spawn_point = @parent.position.clone - spawn_point.y += 96 # TODO: Use one of entity's reserved tiles for spawning + if item.progress >= item.entity.build_steps + unless item.completed + item.completed = true - ent = @parent.director.spawn_entity(player_id: @parent.player.id, name: item.entity.name, position: spawn_point) - ent.target = @parent.component(:waypoint).waypoint if @parent.component(:waypoint) + @parent.director.schedule_order(IMICRTS::Order::BUILD_UNIT_COMPLETE, @parent.player.id, @parent.id) end end end diff --git a/lib/components/turret.rb b/lib/components/turret.rb index f235926..43bd63f 100644 --- a/lib/components/turret.rb +++ b/lib/components/turret.rb @@ -7,15 +7,15 @@ class IMICRTS end def body_image=(image) - @body_image = Gosu::Image.new("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) + @body_image = get_image("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) end def shell_image=(image) - @shell_image = Gosu::Image.new("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) + @shell_image = get_image("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) end def overlay_image=(image) - @overlay_image = Gosu::Image.new("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) + @overlay_image = get_image("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) end def render @@ -28,8 +28,11 @@ class IMICRTS def draw render unless @render - @angle += 0.1 @render.draw_rot(@parent.position.x, @parent.position.y, @parent.position.z, @angle, @center.x, @center.y) end + + def update + @angle = @parent.angle + end end end \ No newline at end of file diff --git a/lib/components/waypoint.rb b/lib/components/waypoint.rb index 31e71f3..6b3251f 100644 --- a/lib/components/waypoint.rb +++ b/lib/components/waypoint.rb @@ -3,6 +3,7 @@ class IMICRTS def setup @waypoint = @parent.position.clone @waypoint.y += @parent.director.map.tile_size + @waypoint_color = 0xffffff00 end def set(vector) @@ -17,11 +18,11 @@ class IMICRTS return unless @parent.player.selected_entities.include?(@parent) Gosu.draw_line( - @parent.position.x, @parent.position.y, @parent.player.color, - @waypoint.x, @waypoint.y, @parent.player.color, ZOrder::ENTITY_GIZMOS + @parent.position.x, @parent.position.y, @waypoint_color, + @waypoint.x, @waypoint.y, @waypoint_color, ZOrder::ENTITY_GIZMOS ) - Gosu.draw_circle(@waypoint.x, @waypoint.y, 4, 9, @parent.player.color, ZOrder::ENTITY_GIZMOS) + Gosu.draw_circle(@waypoint.x, @waypoint.y, 4, 9, @waypoint_color, ZOrder::ENTITY_GIZMOS) end end end \ No newline at end of file diff --git a/lib/entities/buildings/barracks.rb b/lib/entities/buildings/barracks.rb index e2db606..42abb25 100644 --- a/lib/entities/buildings/barracks.rb +++ b/lib/entities/buildings/barracks.rb @@ -6,7 +6,7 @@ tiles = [ [false, :path, :path, :path, false], ] -IMICRTS::Entity.define_entity(:barracks, :building, 400, "Builds and soldiers", tiles) do |entity| +IMICRTS::Entity.define_entity(:barracks, :building, 400, 40, "Builds and soldiers", tiles) do |entity| entity.has(:building) entity.has(:waypoint) entity.has(:spawner) diff --git a/lib/entities/buildings/construction_yard.rb b/lib/entities/buildings/construction_yard.rb index 969796c..342bd1f 100644 --- a/lib/entities/buildings/construction_yard.rb +++ b/lib/entities/buildings/construction_yard.rb @@ -6,13 +6,13 @@ tiles = [ [false, :path, :path, :path, false], ] -IMICRTS::Entity.define_entity(:construction_yard, :building, 2_000, "Provides radar and builds construction workers", tiles) do |entity| +IMICRTS::Entity.define_entity(:construction_yard, :building, 2_000, 310, "Provides radar and builds construction workers", tiles) do |entity| entity.has(:building) entity.has(:waypoint) entity.has(:spawner) entity.has(:build_queue) entity.has(:sidebar_actions) - entity.component(:sidebar_actions).add(:add_to_build_queue, {entity: :construction_worker}) + entity.component(:sidebar_actions).add(:add_to_build_queue, { entity: :construction_worker }) entity.radius = 40 entity.max_health = 100.0 diff --git a/lib/entities/buildings/helipad.rb b/lib/entities/buildings/helipad.rb index 4bde29c..364eef3 100644 --- a/lib/entities/buildings/helipad.rb +++ b/lib/entities/buildings/helipad.rb @@ -6,13 +6,13 @@ tiles = [ [false, false, false, false, false], ] -IMICRTS::Entity.define_entity(:helipad, :building, 1_000, "Builds and rearms helicopters", tiles) do |entity| +IMICRTS::Entity.define_entity(:helipad, :building, 1_000, 100, "Builds and rearms helicopters", tiles) do |entity| entity.has(:building) entity.has(:waypoint) entity.has(:spawner) entity.has(:build_queue) entity.has(:sidebar_actions) - entity.component(:sidebar_actions).add(:add_to_build_queue, {entity: :helicopter}) + entity.component(:sidebar_actions).add(:add_to_build_queue, { entity: :helicopter }) entity.radius = 26 entity.max_health = 100.0 diff --git a/lib/entities/buildings/power_plant.rb b/lib/entities/buildings/power_plant.rb index 053af19..9db641c 100644 --- a/lib/entities/buildings/power_plant.rb +++ b/lib/entities/buildings/power_plant.rb @@ -6,7 +6,7 @@ tiles = [ [false, false, :path, false, false], ] -IMICRTS::Entity.define_entity(:power_plant, :building, 800, "Generates power", tiles) do |entity| +IMICRTS::Entity.define_entity(:power_plant, :building, 800, 45, "Generates power", tiles) do |entity| entity.has(:building) entity.radius = 24 diff --git a/lib/entities/buildings/refinery.rb b/lib/entities/buildings/refinery.rb index 9168fb1..12e6ebf 100644 --- a/lib/entities/buildings/refinery.rb +++ b/lib/entities/buildings/refinery.rb @@ -6,7 +6,7 @@ tiles = [ [false, false, :path, :path, false], ] -IMICRTS::Entity.define_entity(:refinery, :building, 1_400, "Generates credits", tiles) do |entity| +IMICRTS::Entity.define_entity(:refinery, :building, 1_400, 200, "Generates credits", tiles) do |entity| entity.has(:building) entity.radius = 44 diff --git a/lib/entities/buildings/war_factory.rb b/lib/entities/buildings/war_factory.rb index cf45337..fb2c8f2 100644 --- a/lib/entities/buildings/war_factory.rb +++ b/lib/entities/buildings/war_factory.rb @@ -6,15 +6,15 @@ tiles = [ [false, :path, :path, :path, false], ] -IMICRTS::Entity.define_entity(:war_factory, :building, 2_000, "Builds units", tiles) do |entity| +IMICRTS::Entity.define_entity(:war_factory, :building, 2_000, 310, "Builds units", tiles) do |entity| entity.has(:building) entity.has(:waypoint) entity.has(:spawner) entity.has(:build_queue) entity.has(:sidebar_actions) - entity.component(:sidebar_actions).add(:add_to_build_queue, {entity: :jeep}) - entity.component(:sidebar_actions).add(:add_to_build_queue, {entity: :tank}) - entity.component(:sidebar_actions).add(:add_to_build_queue, {entity: :harvester}) + entity.component(:sidebar_actions).add(:add_to_build_queue, { entity: :jeep }) + entity.component(:sidebar_actions).add(:add_to_build_queue, { entity: :tank }) + entity.component(:sidebar_actions).add(:add_to_build_queue, { entity: :harvester }) entity.radius = 48 entity.max_health = 100.0 diff --git a/lib/entities/units/construction_worker.rb b/lib/entities/units/construction_worker.rb index aa98702..6da8d18 100644 --- a/lib/entities/units/construction_worker.rb +++ b/lib/entities/units/construction_worker.rb @@ -1,9 +1,9 @@ -IMICRTS::Entity.define_entity(:construction_worker, :unit, 1000, "Constructs buildings") do |entity| +IMICRTS::Entity.define_entity(:construction_worker, :unit, 1000, 75, "Constructs buildings") do |entity| entity.has(:movement) entity.has(:build_queue) entity.has(:sidebar_actions) [:power_plant, :refinery, :barracks, :war_factory, :helipad, :construction_yard].each do |ent| - entity.component(:sidebar_actions).add(:set_tool, {tool: :place_entity, entity: ent, construction_worker: entity}) + entity.component(:sidebar_actions).add(:set_tool, { tool: :place_entity, entity: ent, construction_worker: entity }) end entity.radius = 14 diff --git a/lib/entities/units/harvester.rb b/lib/entities/units/harvester.rb index 7e8922a..1a3bb4c 100644 --- a/lib/entities/units/harvester.rb +++ b/lib/entities/units/harvester.rb @@ -1,4 +1,4 @@ -IMICRTS::Entity.define_entity(:harvester, :unit, 1400, "Harvests ore") do |entity, director| +IMICRTS::Entity.define_entity(:harvester, :unit, 1400, 140, "Harvests ore") do |entity, director| entity.has(:movement) entity.radius = 10 diff --git a/lib/entities/units/helicopter.rb b/lib/entities/units/helicopter.rb index 2788368..8b27d9c 100644 --- a/lib/entities/units/helicopter.rb +++ b/lib/entities/units/helicopter.rb @@ -1,6 +1,7 @@ -IMICRTS::Entity.define_entity(:helicopter, :unit, 400, "Attacks ground targets") do |entity| +IMICRTS::Entity.define_entity(:helicopter, :unit, 400, 40, "Attacks ground targets") do |entity| entity.has(:movement) + entity.speed = 2.5 entity.radius = 14 entity.movement = :air entity.max_health = 100.0 diff --git a/lib/entities/units/jeep.rb b/lib/entities/units/jeep.rb index 8700b3a..b79a060 100644 --- a/lib/entities/units/jeep.rb +++ b/lib/entities/units/jeep.rb @@ -1,7 +1,8 @@ -IMICRTS::Entity.define_entity(:jeep, :unit, 400, "Attacks ground targets") do |entity| +IMICRTS::Entity.define_entity(:jeep, :unit, 400, 40, "Attacks ground targets") do |entity| entity.has(:movement) entity.has(:turret) + entity.speed = 1.5 entity.radius = 14 entity.movement = :ground entity.max_health = 100.0 @@ -14,4 +15,8 @@ IMICRTS::Entity.define_entity(:jeep, :unit, 400, "Attacks ground targets") do |e entity.on_tick do end + + entity.component(:movement).define_singleton_method(:rotate_towards) do |target| + entity.angle = Gosu.angle(target.x, target.y, entity.position.x, entity.position.y) + end end diff --git a/lib/entities/units/tank.rb b/lib/entities/units/tank.rb index d4778b4..497851f 100644 --- a/lib/entities/units/tank.rb +++ b/lib/entities/units/tank.rb @@ -1,4 +1,4 @@ -IMICRTS::Entity.define_entity(:tank, :unit, 800, "Attacks ground targets") do |entity| +IMICRTS::Entity.define_entity(:tank, :unit, 800, 80, "Attacks ground targets") do |entity| entity.has(:movement) entity.has(:turret) diff --git a/lib/entity.rb b/lib/entity.rb index 5bd14b5..ddd123a 100644 --- a/lib/entity.rb +++ b/lib/entity.rb @@ -1,24 +1,23 @@ class IMICRTS class Entity - Stub = Struct.new(:name, :type, :cost, :description, :tiles, :setup) + include CyberarmEngine::Common + Stub = Struct.new(:name, :type, :cost, :build_steps, :description, :tiles, :setup) @entities = {} def self.get(name) @entities.dig(name) end - def self.define_entity(name, type, cost, description, tiles = [[]], &block) - if entity = get(name) - raise "#{name.inspect} is already defined!" - else - @entities[name] = Stub.new(name, type, cost, description, tiles, block) - end + def self.define_entity(name, type, cost, build_steps, description, tiles = [[]], &block) + raise "#{name.inspect} is already defined!" if get(name) + + @entities[name] = Stub.new(name, type, cost, build_steps, description, tiles, block) end - attr_reader :director, :player, :id, :name, :type, :speed, :data - attr_accessor :position, :angle, :radius, :target, :state, - :movement, :health, :max_health, - :turret, :center, :particle_emitters, :color + attr_reader :director, :player, :id, :name, :type, :data + attr_accessor :position, :angle, :radius, :target, :state, :movement, :health, :max_health, + :speed, :turret, :center, :particle_emitters, :color + def initialize(name:, player:, id:, position:, angle:, director:, proto_entity: false) @player = player @id = id @@ -77,11 +76,9 @@ class IMICRTS def has(symbol) component = Component.get(symbol) - if component - @components[symbol] = component.new(parent: self) - else - raise "Unknown component: #{symbol.inspect}" - end + raise "Unknown component: #{symbol.inspect}" unless component + + @components[symbol] = component.new(parent: self) end def component(symbol) @@ -89,15 +86,15 @@ class IMICRTS end def body_image=(image) - @body_image = Gosu::Image.new("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) + @body_image = get_image("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) end def shell_image=(image) - @shell_image = Gosu::Image.new("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) + @shell_image = get_image("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) end def overlay_image=(image) - @overlay_image = Gosu::Image.new("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) + @overlay_image = get_image("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) end def target=(entity) @@ -153,10 +150,11 @@ class IMICRTS end def tick(tick_id) - @components.values.each { |com| com.tick(tick_id) } + @components.each_value { |com| com.tick(tick_id) } - @on_tick.call if @on_tick - data.assigned_construction_workers ||= 4 + @on_tick&.call + + data.assigned_construction_workers ||= 1 data.construction_speed ||= 1 component(:building).construction_work(data.assigned_construction_workers * data.construction_speed) if component(:building) end diff --git a/lib/order.rb b/lib/order.rb index dbb0832..5ef2ca4 100644 --- a/lib/order.rb +++ b/lib/order.rb @@ -29,6 +29,7 @@ class IMICRTS end attr_reader :id, :arguments + def initialize(id:, arguments:, &handler) @id = id @arguments = arguments @@ -36,7 +37,7 @@ class IMICRTS end def execute(director, *arguments) - pp Order.order_name(self.id) + pp Order.order_name(id) @handler.call(struct(arguments), director) end @@ -48,7 +49,7 @@ class IMICRTS hash[key] = args[value] end - return hash + hash end def serialize(order, director) @@ -76,13 +77,18 @@ class IMICRTS :ATTACK, :PATROL, - :BUILD_ORDER, - :CANCEL_BUILD_ORDER, - :BUILD_ORDER_COMPLETE, + :CONSTRUCT, + :CANCEL_CONSTRUCTION, + :CONSTRUCTION_COMPLETE, + + :BUILD_UNIT, + :CANCEL_BUILD_UNIT, + :BUILD_UNIT_COMPLETE, :BUILDING_SET_WAYPOINT, :BUILDING_POWER_STATE, - :BUILDING_REPAIR, + :REPAIR_BUILDING, + :CANCEL_BUILDING_REPAIR, :BUILDING_SELL, :MESSAGE_BROADCAST, diff --git a/lib/orders/build_unit.rb b/lib/orders/build_unit.rb new file mode 100644 index 0000000..83d5319 --- /dev/null +++ b/lib/orders/build_unit.rb @@ -0,0 +1,18 @@ +IMICRTS::Order.define_handler(IMICRTS::Order::BUILD_UNIT, arguments: [:player_id, :entity_id, :unit_type]) do |order, director| + director.player(order.player_id).entity(order.entity_id).component(:build_queue)&.add(order.unit_type) +end + +IMICRTS::Order.define_serializer(IMICRTS::Order::BUILD_UNIT) do |order, director| + # Order ID | Player ID | Entity ID | Unit Type + # char | char | integer | string + + [IMICRTS::Order::BUILD_UNIT, order.player_id, order.entity_id, order.unit_type.to_s].pack("CCNA*") +end + +IMICRTS::Order.define_deserializer(IMICRTS::Order::BUILD_UNIT) do |string, director| + # String fed into deserializer has Order ID removed + # Player ID | + # char | integer | string + data = string.unpack("CNA*") + [data[0], data[1], data[2].to_sym] +end diff --git a/lib/orders/build_unit_complete.rb b/lib/orders/build_unit_complete.rb new file mode 100644 index 0000000..15eb0b7 --- /dev/null +++ b/lib/orders/build_unit_complete.rb @@ -0,0 +1,25 @@ +IMICRTS::Order.define_handler(IMICRTS::Order::BUILD_UNIT_COMPLETE, arguments: [:player_id, :entity_id]) do |order, director| + entity = director.player(order.player_id).entity(order.entity_id) + item = entity.component(:build_queue).queue.shift + + spawn_point = entity.position.clone + spawn_point.y += 96 # TODO: Use entity defined spawnpoint + + ent = entity.director.spawn_entity(player_id: entity.player.id, name: item.entity.name, position: spawn_point) + ent.target = entity.component(:waypoint).waypoint if entity.component(:waypoint) +end + +IMICRTS::Order.define_serializer(IMICRTS::Order::BUILD_UNIT_COMPLETE) do |order, director| + # Order ID | Player ID + # char | char + + [IMICRTS::Order::BUILD_UNIT_COMPLETE, order.player_id, order.entity_id].pack("CCN") +end + +IMICRTS::Order.define_deserializer(IMICRTS::Order::BUILD_UNIT_COMPLETE) do |string, director| + # String fed into deserializer has Order ID removed + # Player ID | Entity ID + # char | integer + data = string.unpack("CN") + [data[0], data[1]] +end diff --git a/lib/orders/build_order.rb b/lib/orders/construct.rb similarity index 70% rename from lib/orders/build_order.rb rename to lib/orders/construct.rb index 3d653eb..f757a13 100644 --- a/lib/orders/build_order.rb +++ b/lib/orders/construct.rb @@ -1,4 +1,4 @@ -IMICRTS::Order.define_handler(IMICRTS::Order::BUILD_ORDER, arguments: [:player_id, :vector, :building]) do |order, director| +IMICRTS::Order.define_handler(IMICRTS::Order::CONSTRUCT, arguments: [:player_id, :vector, :building]) do |order, director| tile = director.map.tile_at(order.vector.x, order.vector.y) position = tile.position + director.map.tile_size / 2 @@ -20,14 +20,14 @@ IMICRTS::Order.define_handler(IMICRTS::Order::BUILD_ORDER, arguments: [:player_i end end -IMICRTS::Order.define_serializer(IMICRTS::Order::BUILD_ORDER) do |order, director| +IMICRTS::Order.define_serializer(IMICRTS::Order::CONSTRUCT) do |order, director| # Order ID | Player ID | Vector X | Vector Y | Entity Name # char | char | integer | integer | string - [IMICRTS::Order::BUILD_ORDER, order.player_id, order.vector.x, order.vector.y, order.building.to_s].pack("CCNNA*") + [IMICRTS::Order::CONSTRUCT, order.player_id, order.vector.x, order.vector.y, order.building.to_s].pack("CCNNA*") end -IMICRTS::Order.define_deserializer(IMICRTS::Order::BUILD_ORDER) do |string, director| +IMICRTS::Order.define_deserializer(IMICRTS::Order::CONSTRUCT) do |string, director| # String fed into deserializer has Order ID removed # Player ID | Vector X | Vector Y | Entity Name # char | integer | integer | string diff --git a/lib/pathfinding/base_pathfinder.rb b/lib/pathfinding/base_pathfinder.rb index 1bbaf42..0f841f2 100644 --- a/lib/pathfinding/base_pathfinder.rb +++ b/lib/pathfinding/base_pathfinder.rb @@ -147,7 +147,6 @@ class IMICRTS # DOWN add_node create_node(@current_node.tile.grid_position.x, @current_node.tile.grid_position.y + 1, @current_node) - # TODO: Add diagonal nodes, if requested if @allow_diagonal # LEFT-UP if node_above? && node_above_left? diff --git a/lib/states/boot.rb b/lib/states/boot.rb index c6e889b..44246f0 100644 --- a/lib/states/boot.rb +++ b/lib/states/boot.rb @@ -4,7 +4,7 @@ class IMICRTS @title = Gosu::Font.new(56, name: "Noto Sans Display", bold: true) @text = Gosu::Font.new(18, name: "Noto Sans Thaana", bold: true) @name = IMICRTS::NAME - @logo = Gosu::Image.new("#{ASSETS_PATH}/logo.png") + @logo = get_image("#{ASSETS_PATH}/logo.png") @messages = ["Loading", "Compiling Protons", "Launching Warhead", "git push origin --force"] @messages_index = 0 diff --git a/lib/states/closing.rb b/lib/states/closing.rb index 7842f1c..b3fc21c 100644 --- a/lib/states/closing.rb +++ b/lib/states/closing.rb @@ -2,7 +2,7 @@ class IMICRTS class Closing < CyberarmEngine::GuiState def setup window.show_cursor = false - @logo = Gosu::Image.new("#{ASSETS_PATH}/logo.png") + @logo = get_image("#{ASSETS_PATH}/logo.png") @color = Gosu::Color.new(0xffffffff) @started_at = Gosu.milliseconds diff --git a/lib/states/game.rb b/lib/states/game.rb index cba3563..a63f0c2 100644 --- a/lib/states/game.rb +++ b/lib/states/game.rb @@ -41,7 +41,7 @@ class IMICRTS player_id: player.id, name: :construction_yard, position: CyberarmEngine::Vector.new(player.spawnpoint.x, player.spawnpoint.y, ZOrder::BUILDING) ) - construction_yard.component(:building).data.construction_progress = 100 + construction_yard.component(:building).data.construction_progress = Entity.get(construction_yard.name).build_steps @director.each_tile(@director.map.world_to_grid(construction_yard.position), construction_yard.name) do |tile, space_required| if space_required == true tile.entity = construction_yard diff --git a/lib/tool.rb b/lib/tool.rb index c77b9db..57284f6 100644 --- a/lib/tool.rb +++ b/lib/tool.rb @@ -1,5 +1,7 @@ class IMICRTS class Tool + include CyberarmEngine::Common + @@tools = {} def self.get(tool) @@tools.dig(tool) diff --git a/lib/tools/entity_controller.rb b/lib/tools/entity_controller.rb index f8e0211..13f4d67 100644 --- a/lib/tools/entity_controller.rb +++ b/lib/tools/entity_controller.rb @@ -3,14 +3,17 @@ class IMICRTS class EntityController < Tool def setup @drag_start = CyberarmEngine::Vector.new + @box_color = 0xaa99ff99 + @box_border_size = 2 end def draw - Gosu.draw_rect( - @box.min.x, @box.min.y, - @box.width, @box.height, - Gosu::Color.rgba(50, 50, 50, 150), ZOrder::SELECTION_BOX - ) if @box + return unless @box + + Gosu.draw_rect(@box.min.x, @box.min.y, @box.width, @box_border_size, @box_color, ZOrder::SELECTION_BOX) + Gosu.draw_rect(@box.min.x + @box.width, @box.min.y, @box_border_size, @box.height, @box_color, ZOrder::SELECTION_BOX) + Gosu.draw_rect(@box.min.x, @box.min.y + @box.height, @box.width, @box_border_size, @box_color, ZOrder::SELECTION_BOX) + Gosu.draw_rect(@box.min.x, @box.min.y, @box_border_size, @box.height, @box_color, ZOrder::SELECTION_BOX) end def update @@ -40,16 +43,16 @@ class IMICRTS @selection_start = @player.camera.transform(@game.window.mouse) end when Gosu::MS_RIGHT - if @player.selected_entities.size > 0 + unless @player.selected_entities.empty? if @player.selected_entities.any? { |ent| ent.component(:movement) } @director.schedule_order(Order::MOVE, @player.id, @player.camera.transform(@game.window.mouse)) - @game.overlays << Game::Overlay.new(Gosu::Image.new("#{IMICRTS::ASSETS_PATH}/cursors/move.png"), @player.camera.transform(@game.window.mouse), 0, 255) + @game.overlays << Game::Overlay.new(get_image("#{IMICRTS::ASSETS_PATH}/cursors/move.png"), @player.camera.transform(@game.window.mouse), 0, 255) @game.overlays.last.position.z = ZOrder::OVERLAY elsif @player.selected_entities.size == 1 && @player.selected_entities.first.component(:waypoint) @director.schedule_order(Order::BUILDING_SET_WAYPOINT, @player.id, @player.selected_entities.first.id, @player.camera.transform(@game.window.mouse)) - @game.overlays << Game::Overlay.new(Gosu::Image.new("#{IMICRTS::ASSETS_PATH}/cursors/move.png"), @player.camera.transform(@game.window.mouse), 0, 255) + @game.overlays << Game::Overlay.new(get_image("#{IMICRTS::ASSETS_PATH}/cursors/move.png"), @player.camera.transform(@game.window.mouse), 0, 255) @game.overlays.last.position.z = ZOrder::OVERLAY end end @@ -63,11 +66,13 @@ class IMICRTS @box = nil @selection_start = nil + return if @game.sidebar.hit?(@game.window.mouse_x, @game.window.mouse_y) + diff = (@player.selected_entities - @game.selected_entities) @game.sidebar_actions.clear - @director.schedule_order(Order::DESELECTED_UNITS, @player.id, diff) if diff.size > 0 - if @game.selected_entities.size > 0 + @director.schedule_order(Order::DESELECTED_UNITS, @player.id, diff) if diff.size.positive? + if @game.selected_entities.size.positive? @director.schedule_order(Order::SELECTED_UNITS, @player.id, @game.selected_entities) else pick_entity @@ -79,7 +84,7 @@ class IMICRTS @game.sidebar_actions.clear do ent.component(:sidebar_actions).actions.each do |action| @game.button action.label, tip: action.description, width: 1.0 do - action.block.call if action.block + action.block&.call end end end diff --git a/lib/tools/place_entity.rb b/lib/tools/place_entity.rb index b177efa..587cb26 100644 --- a/lib/tools/place_entity.rb +++ b/lib/tools/place_entity.rb @@ -30,11 +30,10 @@ class IMICRTS end def update - # TODO: ensure that construction worker is alive - cancel_tool if @construction_worker.die? + cancel_tool unless @construction_worker.player.entity(@construction_worker.id) vector = vector_to_grid(@game.window.mouse) - if tile = @director.map.tile_at(vector.x, vector.y) + if (tile = @director.map.tile_at(vector.x, vector.y)) position = tile.position.clone @preview.position = position + @director.map.tile_size / 2 @preview.position.z = ZOrder::OVERLAY @@ -58,7 +57,7 @@ class IMICRTS # position: CyberarmEngine::Vector.new(position.x, position.y, ZOrder::BUILDING) # ) - @director.schedule_order(Order::BUILD_ORDER, @player.id, vector, @entity) + @director.schedule_order(Order::CONSTRUCT, @player.id, vector, @entity) # each_tile(vector) do |tile, space_required| # if space_required == true diff --git a/lib/window.rb b/lib/window.rb index a2fb4e8..cadd048 100644 --- a/lib/window.rb +++ b/lib/window.rb @@ -1,10 +1,11 @@ class IMICRTS class Window < CyberarmEngine::Window attr_reader :mouse + def setup @last_update_time = Gosu.milliseconds @mouse = CyberarmEngine::Vector.new - @cursor = Gosu::Image.new("#{IMICRTS::ASSETS_PATH}/cursors/pointer.png") + @cursor = get_image("#{IMICRTS::ASSETS_PATH}/cursors/pointer.png") self.caption = "#{IMICRTS::NAME} (#{IMICRTS::VERSION} #{IMICRTS::VERSION_NAME})" if ARGV.join.include?("--debug-game") @@ -14,6 +15,10 @@ class IMICRTS else push_state(Boot) end + + # TODO: Jukebox + s = get_song("#{GAME_ROOT_PATH}/assets/audio/music/EmptyCity.ogg") + s.play(true) end def draw @@ -23,14 +28,16 @@ class IMICRTS end def update - @mouse.x, @mouse.y = self.mouse_x, self.mouse_y + @mouse.x = mouse_x + @mouse.y = mouse_y + super @last_update_time = Gosu.milliseconds end def needs_cursor? - return false + false end def close