diff --git a/assets/buildings/barracks/barracks.png b/assets/buildings/barracks/barracks.png index 930a18b..cc50b93 100644 Binary files a/assets/buildings/barracks/barracks.png and b/assets/buildings/barracks/barracks.png differ diff --git a/assets/buildings/construction_yard/construction_yard.png b/assets/buildings/construction_yard/construction_yard.png index 14eeff5..d46bfe2 100644 Binary files a/assets/buildings/construction_yard/construction_yard.png and b/assets/buildings/construction_yard/construction_yard.png differ diff --git a/assets/buildings/construction_yard/construction_yard_overlay.png b/assets/buildings/construction_yard/construction_yard_overlay.png index 8808df2..e7e82c8 100644 Binary files a/assets/buildings/construction_yard/construction_yard_overlay.png and b/assets/buildings/construction_yard/construction_yard_overlay.png differ diff --git a/assets/buildings/construction_yard/construction_yard_shell.png b/assets/buildings/construction_yard/construction_yard_shell.png index e8e3200..32d8cfb 100644 Binary files a/assets/buildings/construction_yard/construction_yard_shell.png and b/assets/buildings/construction_yard/construction_yard_shell.png differ diff --git a/assets/buildings/helipad/helipad_overlay.png b/assets/buildings/helipad/helipad_overlay.png index efe89cc..31ab546 100644 Binary files a/assets/buildings/helipad/helipad_overlay.png and b/assets/buildings/helipad/helipad_overlay.png differ diff --git a/assets/buildings/helipad/helipad_shell.png b/assets/buildings/helipad/helipad_shell.png index 77bc0af..5d395df 100644 Binary files a/assets/buildings/helipad/helipad_shell.png and b/assets/buildings/helipad/helipad_shell.png differ diff --git a/assets/buildings/power_plant/power_plant.png b/assets/buildings/power_plant/power_plant.png index b9f6ed1..dc5218a 100644 Binary files a/assets/buildings/power_plant/power_plant.png and b/assets/buildings/power_plant/power_plant.png differ diff --git a/assets/buildings/power_plant/power_plant_overlay.png b/assets/buildings/power_plant/power_plant_overlay.png index 3df69f3..80ba05c 100644 Binary files a/assets/buildings/power_plant/power_plant_overlay.png and b/assets/buildings/power_plant/power_plant_overlay.png differ diff --git a/assets/buildings/power_plant/power_plant_shell.png b/assets/buildings/power_plant/power_plant_shell.png index 55ec828..e00e859 100644 Binary files a/assets/buildings/power_plant/power_plant_shell.png and b/assets/buildings/power_plant/power_plant_shell.png differ diff --git a/assets/buildings/refinery/refinery.png b/assets/buildings/refinery/refinery.png index 9276e93..281a3f5 100644 Binary files a/assets/buildings/refinery/refinery.png and b/assets/buildings/refinery/refinery.png differ diff --git a/assets/buildings/refinery/refinery_overlay.png b/assets/buildings/refinery/refinery_overlay.png index 84c621b..d07166a 100644 Binary files a/assets/buildings/refinery/refinery_overlay.png and b/assets/buildings/refinery/refinery_overlay.png differ diff --git a/assets/buildings/refinery/refinery_shell.png b/assets/buildings/refinery/refinery_shell.png index d369a8e..e4faca3 100644 Binary files a/assets/buildings/refinery/refinery_shell.png and b/assets/buildings/refinery/refinery_shell.png differ diff --git a/assets/buildings/war_factory/war_factory_overlay.png b/assets/buildings/war_factory/war_factory_overlay.png index 6e16716..25960f1 100644 Binary files a/assets/buildings/war_factory/war_factory_overlay.png and b/assets/buildings/war_factory/war_factory_overlay.png differ diff --git a/assets/buildings/war_factory/war_factory_shell.png b/assets/buildings/war_factory/war_factory_shell.png index 6323951..b72d402 100644 Binary files a/assets/buildings/war_factory/war_factory_shell.png and b/assets/buildings/war_factory/war_factory_shell.png differ diff --git a/assets/svg/buildings/construction_yard.svg b/assets/svg/buildings/construction_yard.svg index 7402550..b10f784 100644 --- a/assets/svg/buildings/construction_yard.svg +++ b/assets/svg/buildings/construction_yard.svg @@ -1,6 +1,4 @@ - - + inkscape:export-filename="/home/cyberarm/Code/i-mic-rts/assets/buildings/construction_yard/construction_yard.png" + inkscape:export-xdpi="576" + inkscape:export-ydpi="576"> + inkscape:snap-global="true" + inkscape:document-rotation="0"> = data.construction_goal + data.construction_progress >= data.construction_goal && data.construction_complete end # WARNING: returns a floating point number, not network safe! @@ -44,7 +46,14 @@ class IMICRTS raise TypeError, "Got a non integer value!" unless work.is_a?(Integer) data.construction_progress += work - data.construction_progress = data.construction_goal if construction_complete? + return unless data.construction_progress > data.construction_goal + + data.construction_progress = data.construction_goal + + unless data.construction_complete_ordered + @parent.director.schedule_order(IMICRTS::Order::CONSTRUCTION_COMPLETE, @parent.player.id, @parent.id) + data.construction_complete_ordered = true + end end def draw_construction diff --git a/lib/components/rotors.rb b/lib/components/rotors.rb new file mode 100644 index 0000000..a85cf26 --- /dev/null +++ b/lib/components/rotors.rb @@ -0,0 +1,42 @@ +class IMICRTS + class Rotors < Component + attr_accessor :angle, :speed, :center + + def setup + @angle = 0 + @speed = 1 + @center = CyberarmEngine::Vector.new(0.5, 0.5) + end + + def body_image=(image) + @body_image = get_image("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) + end + + def shell_image=(image) + @shell_image = get_image("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) + end + + def overlay_image=(image) + @overlay_image = get_image("#{IMICRTS::ASSETS_PATH}/#{image}", retro: true) + end + + def render + image = @shell_image || @body_image || @overlay_image + + @render = Gosu.render(image.width, image.height, retro: true) do + @body_image&.draw(0, 0, 0) + @shell_image&.draw_rot(0, 0, 0, 0, 0, 0, 1, 1, @parent.player.color) + @overlay_image&.draw(0, 0, 0) + end + end + + def draw + render unless @render + @render.draw_rot(@parent.position.x, @parent.position.y, @parent.position.z, @angle, @center.x, @center.y, @parent.scale.x, @parent.scale.y) + end + + def update + @angle += @speed + end + end +end \ No newline at end of file diff --git a/lib/components/turret.rb b/lib/components/turret.rb index 43bd63f..6795713 100644 --- a/lib/components/turret.rb +++ b/lib/components/turret.rb @@ -1,6 +1,7 @@ class IMICRTS class Turret < Component attr_accessor :angle, :center + def setup @angle = 0 @center = CyberarmEngine::Vector.new(0.5, 0.5) @@ -19,16 +20,18 @@ class IMICRTS end def render - @render = Gosu.render(32, 32, retro: true) do - @body_image.draw(0, 0, 0) if @body_image - @shell_image.draw_rot(0, 0, 0, 0, 0, 0, 1, 1, @parent.player.color) if @shell_image - @overlay_image.draw(0, 0, 0) if @overlay_image + image = @shell_image || @body_image || @overlay_image + + @render = Gosu.render(image.width, image.height, retro: true) do + @body_image&.draw(0, 0, 0) + @shell_image&.draw_rot(0, 0, 0, 0, 0, 0, 1, 1, @parent.player.color) + @overlay_image&.draw(0, 0, 0) end end def draw render unless @render - @render.draw_rot(@parent.position.x, @parent.position.y, @parent.position.z, @angle, @center.x, @center.y) + @render.draw_rot(@parent.position.x, @parent.position.y, @parent.position.z, @angle, @center.x, @center.y, @parent.scale.x, @parent.scale.y) end def update diff --git a/lib/director.rb b/lib/director.rb index 4a562a8..018f1c8 100644 --- a/lib/director.rb +++ b/lib/director.rb @@ -2,7 +2,7 @@ class IMICRTS class Director attr_reader :current_tick, :map, :game, :players - def initialize(game:, map:, players: [], networking_mode:, tick_rate: 10, local_game: true) + def initialize(game:, map:, players: [], networking_mode:, tick_rate: 10, local_game: true, replay: false) @game = game @map = map @players = players @@ -10,6 +10,7 @@ class IMICRTS @networking_mode = networking_mode @tick_rate = tick_rate @local_game = local_game + @replay = replay @last_tick_at = Gosu.milliseconds @tick_time = 1000.0 / @tick_rate @@ -20,6 +21,10 @@ class IMICRTS @local_game end + def replay? + @replay + end + def add_player(player) @players << player end @@ -40,22 +45,21 @@ class IMICRTS player.tick(@current_tick) # Records where player is looking at tick - # record_order(Order::CAMERA_MOVE, player.id, *player.camera.to_a)# if player.camera_moved? + schedule_order(Order::CAMERA_MOVE, player.id, *player.camera.to_a) if player.camera_moved? - player.orders.sort_by {|order| order.tick_id }.each do |order| + player.orders.sort_by(&:tick_id).each do |order| raise DesyncError, "Have orders from an already processed tick! (#{order.tick_id} < #{current_tick})" if order.tick_id < @current_tick + next if order.tick_id > @current_tick - if _order = Order.get(Integer(order.serialized_order.unpack("C").first)) - # Chop off Order ID - _order_data = order.serialized_order - _order_args = _order.deserialize(_order_data[1.._order_data.length - 1], self) + raise UndefinedOrderError unless (o = Order.get(Integer(order.serialized_order.unpack1("C")))) - execute_order(_order.id, *_order_args) + # Chop off Order ID + order_data = order.serialized_order + order_args = o.deserialize(order_data[1..order_data.length - 1], self) - player.orders.delete(order) - else - raise UndefinedOrderError - end + execute_order(o.id, *order_args) + + player.orders.delete(order) break if order.tick_id > @current_tick end @@ -77,38 +81,22 @@ class IMICRTS klass.new(director: self, entity: entity, goal: goal, travels_along: travels_along, allow_diagonal: allow_diagonal) end - def record_order(order_id, *args) - if order = Order.get(order_id) - struct = order.struct(args) - - scheduled_order = Player::ScheduledOrder.new( order_id, @current_tick + 1, order.serialize(struct, self) ) - @connection.add_order(scheduled_order) - player(struct.player_id).orders.push(scheduled_order) - else - raise "Undefined order: #{Order.order_name(order_id)}" - end - end - def schedule_order(order_id, *args) - if order = Order.get(order_id) - struct = order.struct(args) + raise UndefinedOrderError, "Undefined order: #{Order.order_name(order_id)}" unless (order = Order.get(order_id)) - pp Order.order_name(order_id) + struct = order.struct(args) - scheduled_order = Player::ScheduledOrder.new( order_id, @current_tick + 2, order.serialize(struct, self) ) - @connection.add_order(scheduled_order) - player(struct.player_id).orders.push(scheduled_order) - else - raise "Undefined order: #{Order.order_name(order_id)}" - end + puts "Issued order: #{Order.order_name(order_id).inspect} [tick: #{@current_tick}]" if Setting.enabled?(:debug_mode) + + scheduled_order = Player::ScheduledOrder.new(order_id, @current_tick + 2, order.serialize(struct, self)) + @connection.add_order(scheduled_order) + player(struct.player_id).orders.push(scheduled_order) end def execute_order(order_id, *args) - if order = Order.get(order_id) - order.execute(self, *args) - else - raise "Undefined order: #{Order.order_name(order_id)}" - end + raise UndefinedOrderError, "Undefined order: #{Order.order_name(order_id)}" unless (order = Order.get(order_id)) + + order.execute(self, *args) end def each_tile(vector, entity, &block) diff --git a/lib/entities/buildings/construction_yard.rb b/lib/entities/buildings/construction_yard.rb index 393ecd1..d52fa0e 100644 --- a/lib/entities/buildings/construction_yard.rb +++ b/lib/entities/buildings/construction_yard.rb @@ -48,7 +48,7 @@ IMICRTS::Entity.define_entity(:construction_yard, :building, 2_000, 310, "Provid entity.on_tick do - if entity.component(:building).data.state == :idle + if entity.component(:building).construction_complete? item = entity.component(:build_queue).queue.first entity.particle_emitters.each_with_index do |emitter, i| diff --git a/lib/entities/buildings/war_factory.rb b/lib/entities/buildings/war_factory.rb index 23d559d..b232ed8 100644 --- a/lib/entities/buildings/war_factory.rb +++ b/lib/entities/buildings/war_factory.rb @@ -37,10 +37,12 @@ IMICRTS::Entity.define_entity(:war_factory, :building, 2_000, 310, "Builds and r entity.particle_emitters << IMICRTS::SmokeEmitter.new(position: p2, emitting: false) entity.on_tick do - item = entity.component(:build_queue).queue.first + if entity.component(:building).construction_complete? + item = entity.component(:build_queue).queue.first - entity.particle_emitters.each do |pe| - pe.emitting = !!item + entity.particle_emitters.each do |pe| + pe.emitting = !!item + end end end end diff --git a/lib/entities/units/helicopter.rb b/lib/entities/units/helicopter.rb index 8b27d9c..fe4f265 100644 --- a/lib/entities/units/helicopter.rb +++ b/lib/entities/units/helicopter.rb @@ -1,5 +1,6 @@ IMICRTS::Entity.define_entity(:helicopter, :unit, 400, 40, "Attacks ground targets") do |entity| entity.has(:movement) + entity.has(:rotors) entity.speed = 2.5 entity.radius = 14 @@ -10,6 +11,10 @@ IMICRTS::Entity.define_entity(:helicopter, :unit, 400, 40, "Attacks ground targe entity.shell_image = "vehicles/helicopter/helicopter_shell.png" entity.overlay_image = "vehicles/helicopter/helicopter_overlay.png" + entity.component(:rotors).body_image = "vehicles/helicopter/helicopter_rotors.png" + entity.component(:rotors).center.y = 0.593 + entity.component(:rotors).speed = 500 / 60.0 + entity.on_tick do end end diff --git a/lib/entities/units/jeep.rb b/lib/entities/units/jeep.rb index b79a060..e069563 100644 --- a/lib/entities/units/jeep.rb +++ b/lib/entities/units/jeep.rb @@ -9,9 +9,10 @@ IMICRTS::Entity.define_entity(:jeep, :unit, 400, 40, "Attacks ground targets") d entity.body_image = "vehicles/jeep/jeep.png" entity.shell_image = "vehicles/jeep/jeep_shell.png" + entity.overlay_image = "vehicles/jeep/jeep_overlay.png" entity.component(:turret).shell_image = "vehicles/jeep/jeep_turret_shell.png" - entity.component(:turret).center.y = 0.3125 + entity.component(:turret).center.y = 0.5 entity.on_tick do end diff --git a/lib/entity.rb b/lib/entity.rb index 39d2c9f..c47bac3 100644 --- a/lib/entity.rb +++ b/lib/entity.rb @@ -16,7 +16,7 @@ class IMICRTS attr_reader :director, :player, :id, :name, :type, :data, :proto_entity attr_accessor :position, :angle, :radius, :target, :state, :movement, :health, :max_health, - :speed, :turret, :center, :particle_emitters, :color + :speed, :turret, :center, :scale, :particle_emitters, :color def initialize(name:, player:, id:, position:, angle:, director:, proto_entity: false) @player = player @@ -37,6 +37,8 @@ class IMICRTS @target = nil @state = :idle @center = CyberarmEngine::Vector.new(0.5, 0.5) + @scale = CyberarmEngine::Vector.new(1, 1) + @scale *= 1 / 6.0 @particle_emitters = [] @components = {} @@ -136,7 +138,7 @@ class IMICRTS def draw render unless @render - @render.draw_rot(@position.x, @position.y, @position.z, @angle, @center.x, @center.y, 1, 1, @color) + @render.draw_rot(@position.x, @position.y, @position.z, @angle, @center.x, @center.y, @scale.x, @scale.y, @color) unless @proto_entity @components.values.each(&:draw) diff --git a/lib/errors.rb b/lib/errors.rb index b48438e..ac38a37 100644 --- a/lib/errors.rb +++ b/lib/errors.rb @@ -1,6 +1,7 @@ class IMICRTS class DesyncError < Exception end + class UndefinedOrderError < Exception end end \ No newline at end of file diff --git a/lib/order.rb b/lib/order.rb index 5ef2ca4..95ef3e9 100644 --- a/lib/order.rb +++ b/lib/order.rb @@ -37,7 +37,7 @@ class IMICRTS end def execute(director, *arguments) - pp Order.order_name(id) + puts "Executing order: #{Order.order_name(id).inspect} [tick: #{director.current_tick}]" if Setting.enabled?(:debug_mode) @handler.call(struct(arguments), director) end diff --git a/lib/orders/build_unit_complete.rb b/lib/orders/build_unit_complete.rb index 15eb0b7..82373af 100644 --- a/lib/orders/build_unit_complete.rb +++ b/lib/orders/build_unit_complete.rb @@ -10,8 +10,8 @@ IMICRTS::Order.define_handler(IMICRTS::Order::BUILD_UNIT_COMPLETE, arguments: [: end IMICRTS::Order.define_serializer(IMICRTS::Order::BUILD_UNIT_COMPLETE) do |order, director| - # Order ID | Player ID - # char | char + # Order ID | Player ID | Entity ID + # char | char | Integer [IMICRTS::Order::BUILD_UNIT_COMPLETE, order.player_id, order.entity_id].pack("CCN") end diff --git a/lib/orders/camera_move.rb b/lib/orders/camera_move.rb index ce1b43d..3e1d799 100644 --- a/lib/orders/camera_move.rb +++ b/lib/orders/camera_move.rb @@ -1,5 +1,5 @@ IMICRTS::Order.define_handler(IMICRTS::Order::CAMERA_MOVE, arguments: [:player_id, :x, :y, :zoom]) do |order, director| - director.player(order.player_id).camera.move_to(order.x, order.y, order.zoom) + director.player(order.player_id).camera.move_to(order.x, order.y, order.zoom) if director.replay? end IMICRTS::Order.define_serializer(IMICRTS::Order::CAMERA_MOVE) do |order, director| diff --git a/lib/orders/construction_complete.rb b/lib/orders/construction_complete.rb new file mode 100644 index 0000000..22f6e8f --- /dev/null +++ b/lib/orders/construction_complete.rb @@ -0,0 +1,20 @@ +IMICRTS::Order.define_handler(IMICRTS::Order::CONSTRUCTION_COMPLETE, arguments: [:player_id, :entity_id]) do |order, director| + entity = director.player(order.player_id).entity(order.entity_id) + + entity.component(:building).data.construction_complete = true +end + +IMICRTS::Order.define_serializer(IMICRTS::Order::CONSTRUCTION_COMPLETE) do |order, director| + # Order ID | Player ID | Entity ID + # char | char | Integer + + [IMICRTS::Order::CONSTRUCTION_COMPLETE, order.player_id, order.entity_id].pack("CCN") +end + +IMICRTS::Order.define_deserializer(IMICRTS::Order::CONSTRUCTION_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/player.rb b/lib/player.rb index 492a4de..a9aac2b 100644 --- a/lib/player.rb +++ b/lib/player.rb @@ -12,12 +12,18 @@ class IMICRTS @entities = [] @orders = [] @camera = Camera.new(viewport: [0, 0, $window.width, $window.height]) + @last_camera_position = @camera.position.clone + @camera_moved = true + @camera_move_threshold = 5 @selected_entities = [] @current_entity_id = 0 end def tick(tick_id) + @camera_moved = (@last_camera_position - @camera.position.clone).sum > @camera_move_threshold + @last_camera_position = @camera.position.clone + @entities.each { |ent| ent.tick(tick_id) } end @@ -33,6 +39,10 @@ class IMICRTS @current_entity_id += 1 end + def camera_moved? + @camera_moved + end + class ScheduledOrder attr_reader :order_id, :tick_id, :serialized_order def initialize(order_id, tick_id, serialized_order) diff --git a/lib/states/game.rb b/lib/states/game.rb index be06a86..65bf32f 100644 --- a/lib/states/game.rb +++ b/lib/states/game.rb @@ -60,6 +60,7 @@ class IMICRTS position: CyberarmEngine::Vector.new(player.spawnpoint.x, player.spawnpoint.y, ZOrder::BUILDING) ) construction_yard.component(:building).data.construction_progress = Entity.get(construction_yard.name).build_steps + construction_yard.component(:building).data.construction_complete = true @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