Added construction complete order, added jeep overlay image to show windscreen, added rotors component for helicopter; more work needed, replaced all build and vehicle assets with ones that are 6x larger to prevent pixelization when zoomed in, misc.
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 478 B After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 613 B After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 764 B After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 511 B After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 768 B After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 14 KiB |
@@ -1,6 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
@@ -14,11 +12,11 @@
|
||||
viewBox="0 0 25.399999 25.400001"
|
||||
version="1.1"
|
||||
id="svg1914"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
|
||||
sodipodi:docname="construction_yard.svg"
|
||||
inkscape:export-filename="/home/cyberarm/Code/i-mic-rts/assets/buildings/construction_yard/construction_yard_overlay.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96">
|
||||
inkscape:export-filename="/home/cyberarm/Code/i-mic-rts/assets/buildings/construction_yard/construction_yard.png"
|
||||
inkscape:export-xdpi="576"
|
||||
inkscape:export-ydpi="576">
|
||||
<defs
|
||||
id="defs1908">
|
||||
<filter
|
||||
@@ -102,8 +100,8 @@
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.730767"
|
||||
inkscape:cx="23.5431"
|
||||
inkscape:cy="40.736561"
|
||||
inkscape:cx="14.700343"
|
||||
inkscape:cy="47.457411"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
@@ -122,13 +120,14 @@
|
||||
units="px"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1006"
|
||||
inkscape:window-height="1024"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-global="true">
|
||||
inkscape:snap-global="true"
|
||||
inkscape:document-rotation="0">
|
||||
<sodipodi:guide
|
||||
position="1.5875,14.410343"
|
||||
orientation="1,0"
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 645 B After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 734 B After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 505 B After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 648 B After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 570 B |
BIN
assets/vehicles/jeep/jeep_overlay.png
Normal file
|
After Width: | Height: | Size: 800 B |
|
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 684 B |
|
Before Width: | Height: | Size: 455 B After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 634 B After Width: | Height: | Size: 3.2 KiB |
@@ -3,6 +3,8 @@ class IMICRTS
|
||||
def setup
|
||||
data.construction_progress ||= 0
|
||||
data.construction_goal ||= Entity.get(@parent.name).build_steps
|
||||
data.construction_complete ||= false
|
||||
data.construction_complete_ordered ||= false
|
||||
|
||||
@text = CyberarmEngine::Text.new("", y: @parent.position.y, z: Float::INFINITY, size: 12)
|
||||
data.state = :construct # deconstruct, building, idle
|
||||
@@ -32,7 +34,7 @@ class IMICRTS
|
||||
end
|
||||
|
||||
def construction_complete?
|
||||
data.construction_progress >= 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
|
||||
|
||||
42
lib/components/rotors.rb
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
raise UndefinedOrderError unless (o = Order.get(Integer(order.serialized_order.unpack1("C"))))
|
||||
|
||||
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)
|
||||
order_data = order.serialized_order
|
||||
order_args = o.deserialize(order_data[1..order_data.length - 1], self)
|
||||
|
||||
execute_order(_order.id, *_order_args)
|
||||
execute_order(o.id, *order_args)
|
||||
|
||||
player.orders.delete(order)
|
||||
else
|
||||
raise UndefinedOrderError
|
||||
end
|
||||
|
||||
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)
|
||||
raise UndefinedOrderError, "Undefined order: #{Order.order_name(order_id)}" unless (order = Order.get(order_id))
|
||||
|
||||
struct = order.struct(args)
|
||||
|
||||
pp Order.order_name(order_id)
|
||||
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)
|
||||
else
|
||||
raise "Undefined order: #{Order.order_name(order_id)}"
|
||||
end
|
||||
end
|
||||
|
||||
def execute_order(order_id, *args)
|
||||
if order = Order.get(order_id)
|
||||
raise UndefinedOrderError, "Undefined order: #{Order.order_name(order_id)}" unless (order = Order.get(order_id))
|
||||
|
||||
order.execute(self, *args)
|
||||
else
|
||||
raise "Undefined order: #{Order.order_name(order_id)}"
|
||||
end
|
||||
end
|
||||
|
||||
def each_tile(vector, entity, &block)
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -37,6 +37,7 @@ 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
|
||||
if entity.component(:building).construction_complete?
|
||||
item = entity.component(:build_queue).queue.first
|
||||
|
||||
entity.particle_emitters.each do |pe|
|
||||
@@ -44,3 +45,4 @@ IMICRTS::Entity.define_entity(:war_factory, :building, 2_000, 310, "Builds and r
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class IMICRTS
|
||||
class DesyncError < Exception
|
||||
end
|
||||
|
||||
class UndefinedOrderError < Exception
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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|
|
||||
|
||||
20
lib/orders/construction_complete.rb
Normal file
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||