From b3eb4c5f2eb38b06d57b3b401c39a8e501d658bc Mon Sep 17 00:00:00 2001 From: roseundy Date: Sun, 27 Sep 2020 14:59:42 -0600 Subject: [PATCH 1/2] Add crossover rendering --- assets/app/view/game/part/track_node_path.rb | 31 ++++++++++++++- lib/engine/config/tile.rb | 2 +- lib/engine/part/path.rb | 40 ++++++++++++++++++-- lib/engine/tile.rb | 37 ++++++++++++++++++ 4 files changed, 105 insertions(+), 5 deletions(-) diff --git a/assets/app/view/game/part/track_node_path.rb b/assets/app/view/game/part/track_node_path.rb index a4892d2259..fd7944e9f0 100644 --- a/assets/app/view/game/part/track_node_path.rb +++ b/assets/app/view/game/part/track_node_path.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'lib/settings' require 'view/game/part/base' require 'view/game/part/town_location' @@ -8,6 +9,7 @@ module Game module Part class TrackNodePath < Base include TownLocation + include Lib::Settings needs :tile needs :path @@ -15,6 +17,8 @@ class TrackNodePath < Base needs :width, default: 8 needs :dash, default: '0' + CROSSOVER_GAP = 3 + PARALLEL_SPACING = [8, 6, 4].freeze EDGE_PERP_ANGLES = [90, 30, -30, -90, -150, 150].freeze @@ -256,6 +260,13 @@ def load_from_tile @end_x = edge_x_pos(@end_edge, 87) @end_y = edge_y_pos(@end_edge, 87) lanes = @path.lanes + + if @tile.crossover? && @path.straight? + @crossover_dash = '1 55 63 56' + elsif @tile.crossover? && @path.gentle_curve? + @crossover_dash = '1 55 47 56' + end + elsif @num_exits == 1 @begin_edge = @path.exits[0] @begin_x = edge_x_pos(@begin_edge, 87) @@ -281,6 +292,12 @@ def load_from_tile end lanes = @path.lanes lanes.reverse! if @path.b.edge? + + if @tile.crossover? && @path.straight?(@ct_edge0) + @crossover_dash = '1 55 63 56' + elsif @tile.crossover? && @path.gentle_curve?(@ct_edge0) + @crossover_dash = '1 55 47 56' + end else # city/town - city/town @ct_edge0 = @tile.preferred_city_town_edges[@stop0] if @stop0 @@ -411,7 +428,19 @@ def render_part 'stroke-dasharray': @dash, ) if @terminal - h(:path, props) + children = [] + children.append(h(:path, props)) + if @crossover_dash + intersect_props = props.dup + intersect_props[:attrs].merge!( + stroke: setting_for(@tile.color), + 'stroke-width': @width + CROSSOVER_GAP * 2, + 'stroke-dasharray': @crossover_dash, + 'stroke-dashoffset': 1, + ) + children.prepend(h(:path, intersect_props)) + end + children end end end diff --git a/lib/engine/config/tile.rb b/lib/engine/config/tile.rb index 7268c8601e..059d1d3f82 100644 --- a/lib/engine/config/tile.rb +++ b/lib/engine/config/tile.rb @@ -514,7 +514,7 @@ module Tile '988' => 'city=revenue:50,slots:3;path=a:0,b:_0,track:narrow;path=a:1,b:_0,track:narrow;path=a:2,b:_0,track:narrow;path=a:3,b:_0,track:narrow;path=a:4,b:_0,track:narrow', '989' => 'city=revenue:70,slots:3;path=a:0,b:_0,track:narrow;path=a:1,b:_0,track:narrow;path=a:2,b:_0,track:narrow;path=a:3,b:_0,track:narrow;path=a:4,b:_0,track:narrow', '990' => 'city=revenue:100,slots:3;path=a:0,b:_0,track:narrow;path=a:1,b:_0,track:narrow;path=a:2,b:_0,track:narrow;path=a:3,b:_0,track:narrow;path=a:4,b:_0,track:narrow;path=a:5,b:_0,track:narrow;label=B', - '1167' => 'city=revenue:70;city=revenue:70;path=a:0,b:_0;path=a:_0,b:1;path=a:2,b:_1;path=a:_1,b:3;path=a:4,b:_0;path=a:5,b:_1', + '1167' => 'city=revenue:70,loc:0;city=revenue:70,loc:3;path=a:0,b:_0;path=a:_0,b:1;path=a:2,b:_1;path=a:_1,b:3;path=a:4,b:_0;path=a:5,b:_1', '1168' => 'city=revenue:60,slots:3;path=a:0,b:_0;path=a:1,b:_0;path=a:2,b:_0;path=a:3,b:_0', }.freeze diff --git a/lib/engine/part/path.rb b/lib/engine/part/path.rb index 940fa1495e..edd0a9b3e4 100644 --- a/lib/engine/part/path.rb +++ b/lib/engine/part/path.rb @@ -98,21 +98,55 @@ def path? end def node? - @_node ||= @nodes.any? + return @_node if defined?(@_node) + + @_node = @nodes.any? end def terminal? - @_terminal ||= !!@terminal + return @_terminal if defined?(@_terminal) + + @_terminal = !!@terminal end def single? - @_single ||= @lanes.first[0] == 1 && @lanes.last[0] == 1 + return @_single if defined?(@_single) + + @_single = @lanes.first[0] == 1 && @lanes.last[0] == 1 end def exits @exits ||= @edges.map(&:num) end + def straight?(ct = nil) + return @_straight if defined?(@_straight) + + @_straight = if @a.edge? && @b.edge? + (@a.num - @b.num).abs == 3 + elsif @a.edge? && ct + (@a.num - ct).abs == 3 + elsif @b.edge? && ct + (@b.num - ct).abs == 3 + else + false + end + end + + def gentle_curve?(ct = nil) + return @_gentle_curve if defined?(@_gentle_curve) + + @_gentle_curve = if @a.edge? && @b.edge? + (((d = (@a.num - @b.num).abs) == 2) || d == 4) + elsif @a.edge? && ct + (((d = (@a.num - ct).abs) == 2) || d == 4 || d == 2.5 || d == 3.5) + elsif @b.edge? && ct + (((d = (@b.num - ct).abs) == 2) || d == 4 || d == 2.5 || d == 3.5) + else + false + end + end + def rotate(ticks) path = Path.new(@a.rotate(ticks), @b.rotate(ticks), @terminal, @lanes) path.index = index diff --git a/lib/engine/tile.rb b/lib/engine/tile.rb index 8e92a47f33..d91fb72cc7 100644 --- a/lib/engine/tile.rb +++ b/lib/engine/tile.rb @@ -360,6 +360,43 @@ def compute_city_town_edges ct_edges end + # this is invariant over rotations + def crossover? + return @_crossover if defined?(@_crossover) + + @_crossover = compute_crossover + end + + def compute_crossover + edge_paths = Hash.new { |h, k| h[k] = [] } + @paths.each do |p| + edge_paths[p.a.num].append(p) if p.a.edge? + edge_paths[p.b.num].append(p) if p.b.edge? + next unless p.nodes.size == 1 + + ct_edge = preferred_city_town_edges[p.nodes.first] + p.straight?(ct_edge) + p.gentle_curve?(ct_edge) + end + + @paths.each do |p| + next if p.nodes.size > 2 + + ct_edge = preferred_city_town_edges[p.nodes.first] if p.node? + a_num = p.a.edge? ? p.a.num : ct_edge + b_num = p.b.edge? ? p.b.num : ct_edge + if p.straight? + return true if edge_paths[(a_num + 1) % 6].any?(&:straight?) + return true if edge_paths[(a_num - 1) % 6].any?(&:straight?) + elsif p.gentle_curve? + low = [a_num, b_num].min + middle = (a_num - b_num).abs == 2 ? (low + 1) % 6 : (low - 1) % 6 + return true if edge_paths[middle].any? { |ep| ep.straight? || ep.gentle_curve? } + end + end + false + end + def revenue_to_render @revenue_to_render ||= @stops.map(&:revenue_to_render) end From 062f3ae289043d8efcf9e1d481c8920fc4094c27 Mon Sep 17 00:00:00 2001 From: roseundy Date: Sun, 27 Sep 2020 17:20:20 -0600 Subject: [PATCH 2/2] Code cleanup for handling crossovers. --- assets/app/view/game/part/track_node_path.rb | 23 +++++++------ lib/engine/part/path.rb | 35 +++++++------------- lib/engine/tile.rb | 18 ++++------ 3 files changed, 31 insertions(+), 45 deletions(-) diff --git a/assets/app/view/game/part/track_node_path.rb b/assets/app/view/game/part/track_node_path.rb index fd7944e9f0..442c2ed6a3 100644 --- a/assets/app/view/game/part/track_node_path.rb +++ b/assets/app/view/game/part/track_node_path.rb @@ -18,6 +18,8 @@ class TrackNodePath < Base needs :dash, default: '0' CROSSOVER_GAP = 3 + STRAIGHT_CROSSOVER = '1 55 63 56' + GENTLE_CROSSOVER = '1 55 47 56' PARALLEL_SPACING = [8, 6, 4].freeze @@ -262,9 +264,9 @@ def load_from_tile lanes = @path.lanes if @tile.crossover? && @path.straight? - @crossover_dash = '1 55 63 56' + @crossover_dash = STRAIGHT_CROSSOVER elsif @tile.crossover? && @path.gentle_curve? - @crossover_dash = '1 55 47 56' + @crossover_dash = GENTLE_CROSSOVER end elsif @num_exits == 1 @@ -293,11 +295,12 @@ def load_from_tile lanes = @path.lanes lanes.reverse! if @path.b.edge? - if @tile.crossover? && @path.straight?(@ct_edge0) - @crossover_dash = '1 55 63 56' - elsif @tile.crossover? && @path.gentle_curve?(@ct_edge0) - @crossover_dash = '1 55 47 56' + if @tile.crossover? && @path.straight? + @crossover_dash = STRAIGHT_CROSSOVER + elsif @tile.crossover? && @path.gentle_curve? + @crossover_dash = GENTLE_CROSSOVER end + else # city/town - city/town @ct_edge0 = @tile.preferred_city_town_edges[@stop0] if @stop0 @@ -428,17 +431,15 @@ def render_part 'stroke-dasharray': @dash, ) if @terminal - children = [] - children.append(h(:path, props)) + children = [h(:path, props)] if @crossover_dash - intersect_props = props.dup - intersect_props[:attrs].merge!( + props[:attrs].merge!( stroke: setting_for(@tile.color), 'stroke-width': @width + CROSSOVER_GAP * 2, 'stroke-dasharray': @crossover_dash, 'stroke-dashoffset': 1, ) - children.prepend(h(:path, intersect_props)) + children.prepend(h(:path, props)) end children end diff --git a/lib/engine/part/path.rb b/lib/engine/part/path.rb index edd0a9b3e4..1313cfcb06 100644 --- a/lib/engine/part/path.rb +++ b/lib/engine/part/path.rb @@ -104,9 +104,7 @@ def node? end def terminal? - return @_terminal if defined?(@_terminal) - - @_terminal = !!@terminal + !!@terminal end def single? @@ -119,32 +117,23 @@ def exits @exits ||= @edges.map(&:num) end - def straight?(ct = nil) + def straight? return @_straight if defined?(@_straight) - @_straight = if @a.edge? && @b.edge? - (@a.num - @b.num).abs == 3 - elsif @a.edge? && ct - (@a.num - ct).abs == 3 - elsif @b.edge? && ct - (@b.num - ct).abs == 3 - else - false - end + ct_edge = @tile.preferred_city_town_edges[@nodes.first] if @nodes.one? + a_num = @a.edge? ? @a.num : ct_edge + b_num = @b.edge? ? @b.num : ct_edge + + @_straight = a_num && b_num && (a_num - b_num).abs == 3 end - def gentle_curve?(ct = nil) + def gentle_curve? return @_gentle_curve if defined?(@_gentle_curve) - @_gentle_curve = if @a.edge? && @b.edge? - (((d = (@a.num - @b.num).abs) == 2) || d == 4) - elsif @a.edge? && ct - (((d = (@a.num - ct).abs) == 2) || d == 4 || d == 2.5 || d == 3.5) - elsif @b.edge? && ct - (((d = (@b.num - ct).abs) == 2) || d == 4 || d == 2.5 || d == 3.5) - else - false - end + ct_edge = @tile.preferred_city_town_edges[@nodes.first] if @nodes.one? + a_num = @a.edge? ? @a.num : ct_edge + b_num = @b.edge? ? @b.num : ct_edge + @_gentle_curve = a_num && b_num && (((d = (a_num - b_num).abs) == 2) || d == 4 || d == 2.5 || d == 3.5) end def rotate(ticks) diff --git a/lib/engine/tile.rb b/lib/engine/tile.rb index d91fb72cc7..2687353f0b 100644 --- a/lib/engine/tile.rb +++ b/lib/engine/tile.rb @@ -369,22 +369,18 @@ def crossover? def compute_crossover edge_paths = Hash.new { |h, k| h[k] = [] } - @paths.each do |p| - edge_paths[p.a.num].append(p) if p.a.edge? - edge_paths[p.b.num].append(p) if p.b.edge? - next unless p.nodes.size == 1 - - ct_edge = preferred_city_town_edges[p.nodes.first] - p.straight?(ct_edge) - p.gentle_curve?(ct_edge) + paths.each do |p| + edge_paths[p.a.num] << p if p.a.edge? + edge_paths[p.b.num] << p if p.b.edge? end - @paths.each do |p| - next if p.nodes.size > 2 + paths.each do |p| + next if p.nodes.size > 1 - ct_edge = preferred_city_town_edges[p.nodes.first] if p.node? + ct_edge = preferred_city_town_edges[p.nodes.first] if p.nodes.one? a_num = p.a.edge? ? p.a.num : ct_edge b_num = p.b.edge? ? p.b.num : ct_edge + if p.straight? return true if edge_paths[(a_num + 1) % 6].any?(&:straight?) return true if edge_paths[(a_num - 1) % 6].any?(&:straight?)