From 0aee0e00fdbc9401c69c1f9587f63f3425e1b600 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Tue, 25 Jun 2024 09:12:06 +0200 Subject: [PATCH 01/49] [18Norway] Updated harbor --- lib/engine/game/g_18_norway/game.rb | 8 +++ lib/engine/game/g_18_norway/steps/token.rb | 67 +++++++++++++++++----- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/lib/engine/game/g_18_norway/game.rb b/lib/engine/game/g_18_norway/game.rb index 3d71339e97..4e4d2e24c2 100644 --- a/lib/engine/game/g_18_norway/game.rb +++ b/lib/engine/game/g_18_norway/game.rb @@ -104,6 +104,14 @@ def nsb @nsb ||= corporation_by_id('NSB') end + def harbor_city_id_by_harbor_id(hex_id) + CITY_HARBOR_MAP.key(hex_id) + end + + def harbor_hex?(hex) + HARBOR_HEXES.include?(hex.id) + end + def price_movement_chart [ ['Action', 'Share Price Change'], diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index 58d4fdaa29..c7af4ca320 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -8,10 +8,25 @@ module G18Norway module Step class Token < Engine::Step::Token def available_hex(entity, hex) - return true if super(entity, hex) - return true if @game.ferry_graph.reachable_hexes(entity)[hex] + return true if super - false + check_available_harbour(entity, hex) + end + + def tokener_available_hex(entity, hex) + return true if super + + check_available_harbour(entity, hex) + end + + def check_available_harbour(entity, hex) + city = @game.harbor_city_id_by_harbor_id(hex.id) + return false if city.nil? + + other_hex = @game.hex_by_id(city) + return true if @game.graph.reachable_hexes(entity)[other_hex] + + @game.ferry_graph.reachable_hexes(entity)[other_hex] end def can_place_token?(_entity) @@ -21,24 +36,48 @@ def can_place_token?(_entity) def process_place_token(action) entity = action.entity city = action.city - connected_city = @game.loading || @game.token_graph_for_entity(entity).connected_nodes(entity)[city] - place_token(entity, action.city, action.token) if connected_city + hex = city.hex + connected_city = @game.token_graph_for_entity(entity).connected_nodes(entity)[city] + if connected_city + place_token(entity, action.city, action.token) + elsif @game.harbor_hex?(city.hex) && !connected_city + city_string = city.hex.tile.cities.size > 1 ? " city #{city.index}" : '' + city_name = @game.harbor_city_id_by_harbor_id(hex.id) + city_string + raise GameError, "Cannot reach city #{city_name}" unless check_available_harbour(entity, hex) + + place_token(entity, action.city, action.token, connected: false) - unless connected_city - @game.abilities(entity, :token) do |ability| - place_token(entity, action.city, action.token, connected: false, special_ability: ability) - end + else + city_string = hex.tile.cities.size > 1 ? " city #{city.index}" : '' + raise GameError, "Cannot place token on #{hex.name}#{city_string} because it is not connected" end @game.clear_graph pass! end - def check_connected(entity, city, hex) - return if @game.loading || @game.token_graph_for_entity(entity).connected_nodes(entity)[city] - return if @game.loading || @game.ferry_graph.connected_nodes(entity)[city] + def place_token(entity, city, token, connected: true, extra_action: false, + special_ability: nil, check_tokenable: true, spender: nil, same_hex_allowed: false) + hex = city.hex + + check_connected(entity, city, hex) if connected + raise GameError, 'Token already placed this turn' if !extra_action && @round.tokened + + raise GameError, 'Token is already used' if token.used + + free = !token.price.positive? + cheater = false + extra_slot = @game.harbor_hex?(hex) + city.place_token(entity, token, free: free, check_tokenable: check_tokenable, + cheater: cheater, extra_slot: extra_slot, spender: spender, + same_hex_allowed: same_hex_allowed) + pay_token_cost(spender || entity, token.price, city) + price_log = " for #{@game.format_currency(token.price)}" + + hex_description = hex.location_name ? "#{hex.name} (#{hex.location_name}) " : "#{hex.name} " + @log << "#{entity.name} places a token on #{hex_description}#{price_log}" - city_string = hex.tile.cities.size > 1 ? " city #{city.index}" : '' - raise GameError, "Cannot place token on #{hex.name}#{city_string} because it is not connected" + @round.tokened = true + @game.clear_token_graph_for_entity(entity) end end end From cad97c14fd1ea34ac55c25fdf85655d480326e92 Mon Sep 17 00:00:00 2001 From: patrikolesen Date: Sat, 13 Jul 2024 10:55:11 +0200 Subject: [PATCH 02/49] Update lib/engine/game/g_18_norway/steps/token.rb Co-authored-by: Oliver Burnett-Hall --- lib/engine/game/g_18_norway/steps/token.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index c7af4ca320..ed753993ae 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -40,7 +40,7 @@ def process_place_token(action) connected_city = @game.token_graph_for_entity(entity).connected_nodes(entity)[city] if connected_city place_token(entity, action.city, action.token) - elsif @game.harbor_hex?(city.hex) && !connected_city + elsif @game.harbor_hex?(city.hex) city_string = city.hex.tile.cities.size > 1 ? " city #{city.index}" : '' city_name = @game.harbor_city_id_by_harbor_id(hex.id) + city_string raise GameError, "Cannot reach city #{city_name}" unless check_available_harbour(entity, hex) From d631049558b429278dd1e7a5b544b1c84a43b393 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Sat, 13 Jul 2024 10:55:51 +0200 Subject: [PATCH 03/49] [18Norway] New names on some functions --- lib/engine/game/g_18_norway/game.rb | 2 +- lib/engine/game/g_18_norway/steps/token.rb | 19 +++++++------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/engine/game/g_18_norway/game.rb b/lib/engine/game/g_18_norway/game.rb index 4e4d2e24c2..bb87b3c125 100644 --- a/lib/engine/game/g_18_norway/game.rb +++ b/lib/engine/game/g_18_norway/game.rb @@ -104,7 +104,7 @@ def nsb @nsb ||= corporation_by_id('NSB') end - def harbor_city_id_by_harbor_id(hex_id) + def harbor_city_coordinates(hex_id) CITY_HARBOR_MAP.key(hex_id) end diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index ed753993ae..38ac0929d4 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -10,23 +10,18 @@ class Token < Engine::Step::Token def available_hex(entity, hex) return true if super - check_available_harbour(entity, hex) + harbor_available?(entity, hex) end def tokener_available_hex(entity, hex) return true if super - check_available_harbour(entity, hex) + harbor_available?(entity, hex) end - def check_available_harbour(entity, hex) - city = @game.harbor_city_id_by_harbor_id(hex.id) - return false if city.nil? - - other_hex = @game.hex_by_id(city) - return true if @game.graph.reachable_hexes(entity)[other_hex] - - @game.ferry_graph.reachable_hexes(entity)[other_hex] + def harbor_available?(entity, hex) + other_hex = @game.hex_by_id(@game.harbor_city_coordinates(hex.id)) + @game.graph.reachable_hexes(entity)[other_hex] || @game.ferry_graph.reachable_hexes(entity)[other_hex] end def can_place_token?(_entity) @@ -42,8 +37,8 @@ def process_place_token(action) place_token(entity, action.city, action.token) elsif @game.harbor_hex?(city.hex) city_string = city.hex.tile.cities.size > 1 ? " city #{city.index}" : '' - city_name = @game.harbor_city_id_by_harbor_id(hex.id) + city_string - raise GameError, "Cannot reach city #{city_name}" unless check_available_harbour(entity, hex) + city_name = @game.harbor_city_coordinates(hex.id) + city_string + raise GameError, "Cannot reach city #{city_name}" unless harbor_available?(entity, hex) place_token(entity, action.city, action.token, connected: false) From 0c0c2461e594acde4f894b5fe9c882da8a8ac0da Mon Sep 17 00:00:00 2001 From: PistonPercy <173635045+PistonPercy@users.noreply.github.com> Date: Sat, 6 Jul 2024 16:27:24 -0700 Subject: [PATCH 04/49] assets: Emit source maps I had to rename deps because there was a name collision where the file was written to twice once in compile_lib and another time in combine. This made it much easier to debug some issues. --- Makefile | 2 +- api.rb | 2 +- lib/assets.rb | 65 ++++++++++++++++++++++++++++++++++++++++++++------- queue.rb | 2 +- 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 669275f34d..934aab75b1 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CONTAINER_COMPOSE ?= $(CONTAINER_ENGINE)-compose CONTAINER_ENGINE ?= docker clean: - sudo rm -rfv build/ public/assets/*.js public/assets/*.js.gz public/assets/version.json + sudo rm -rfv build/ public/assets/*.js public/assets/*.js.map public/assets/*.js.gz public/assets/version.json cleandeps: sudo rm -rfv public/assets/deps.js diff --git a/api.rb b/api.rb index aaff94a426..296193e886 100644 --- a/api.rb +++ b/api.rb @@ -77,7 +77,7 @@ class Api < Roda } end - ASSETS = Assets.new(precompiled: PRODUCTION) + ASSETS = Assets.new(precompiled: PRODUCTION, source_maps: !PRODUCTION) Bus.configure diff --git a/lib/assets.rb b/lib/assets.rb index d00da96158..d85fbbff6c 100644 --- a/lib/assets.rb +++ b/lib/assets.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'json' require 'opal' require 'snabberb' require 'tempfile' @@ -12,7 +13,40 @@ class Assets OUTPUT_BASE = 'public' PIN_DIR = '/pinned/' - def initialize(compress: false, gzip: false, cache: true, precompiled: false) + class SourceMap + def initialize + @source_map = { + 'version' => 3, + 'sections' => [], + } + @current_line = 0 + end + + def append(file_contents, source_map_file) + @source_map['sections'].append({ + 'offset' => { 'line' => @current_line, 'column' => 0 }, + 'map' => JSON.parse(File.read(source_map_file)), + }) + @current_line += file_contents.lines.count + 1 + end + + def extend(file_contents, source_map_file) + file_source_map = JSON.parse(File.read(source_map_file)) + file_source_map['sections'].each do |section| + @source_map['sections'].append({ + 'offset' => { 'line' => @current_line + section['offset']['line'], 'column' => 0 }, + 'map' => section['map'], + }) + end + @current_line += file_contents.lines.count + 1 + end + + def to_json(*args) + @source_map.to_json(*args) + end + end + + def initialize(compress: false, gzip: false, cache: true, precompiled: false, source_maps: false) @build_path = 'build' @out_path = OUTPUT_BASE + '/assets' @root_path = '/assets' @@ -25,6 +59,7 @@ def initialize(compress: false, gzip: false, cache: true, precompiled: false) @compress = compress @gzip = gzip @precompiled = precompiled + @source_maps = source_maps end def context(titles) @@ -97,8 +132,8 @@ def builds(titles = []) **game_builds(:all), } else - @_opal ||= compile_lib('opal') - @_deps ||= compile_lib('deps', 'assets') + @_opal ||= compile_lib('opal', 'opal') + @_deps ||= compile_lib('deps_only', 'deps', 'assets') @_engine ||= compile('engine', 'lib', 'engine') @_app ||= compile('app', 'assets/app', '') @@ -163,9 +198,17 @@ def combine(titles = []) @_combined.include?(key) ? next : @_combined.add(key) - source = build['files'].map { |file| File.read(file).to_s }.join("\n") + source_map = SourceMap.new + source = build['files'].map do |filepath| + file = File.read(filepath).to_s + source_map.extend(file, filepath + '.map') if @source_maps + file + end.join("\n") + source += "\n//# sourceMappingURL=#{build['path'].delete_prefix('public')}.map\n" if @source_maps + source = compress(key, source) if @compress File.write(build['path'], source) + File.write(build['path'] + '.map', source_map.to_json) if @source_maps next if !@gzip || build['path'] == @server_path @@ -182,13 +225,14 @@ def combine(titles = []) [@deps_path, @main_path, *game_paths] end - def compile_lib(name, *append_paths) + def compile_lib(output_name, name, *append_paths) builder = Opal::Builder.new append_paths.each { |ap| builder.append_paths(ap) } - path = "#{@out_path}/#{name}.js" - if !@cache || !File.exist?(path) || path == @deps_path + path = "#{@out_path}/#{output_name}.js" + if !@cache || !File.exist?(path) time = Time.now File.write(path, builder.build(name)) + File.write("#{path}.map", builder.source_map.map.to_json) if @source_maps puts "Compiling #{name} - #{Time.now - time}" end path @@ -214,17 +258,22 @@ def compile(name, lib_path, ns = nil, game: nil) time = Time.now File.write(opts[:js_path], compiler.compile) + File.write(opts[:js_path] + '.map', compiler.source_map.map.to_json) if @source_maps puts "Compiling #{file} - #{Time.now - time}" end + source_map = SourceMap.new source = metadata.map do |_file, opts| - File.read(opts[:js_path]).to_s + file = File.read(opts[:js_path]) + source_map.append(file, opts[:js_path] + '.map') if @source_maps + file end.join("\n") opal_load = game ? "engine/game/#{game}" : name source += "\nOpal.load('#{opal_load}')" File.write(output, source) + File.write(output + '.map', source_map.to_json) if @source_maps output end diff --git a/queue.rb b/queue.rb index 211cc1073d..33fe5ba4bf 100755 --- a/queue.rb +++ b/queue.rb @@ -20,7 +20,7 @@ Bus.configure -ASSETS = Assets.new(precompiled: PRODUCTION) +ASSETS = Assets.new(precompiled: PRODUCTION, source_maps: !PRODUCTION) scheduler = Rufus::Scheduler.new From 93d299a8ca77d057126c81acb32885c7b2083986 Mon Sep 17 00:00:00 2001 From: Alexander Vansach Date: Tue, 23 Jul 2024 17:34:29 +0200 Subject: [PATCH 05/49] 22Mars: make sure 3 ORs happen in last set --- lib/engine/game/g_22_mars/game.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/engine/game/g_22_mars/game.rb b/lib/engine/game/g_22_mars/game.rb index 9685cd9845..d34afaa0ae 100644 --- a/lib/engine/game/g_22_mars/game.rb +++ b/lib/engine/game/g_22_mars/game.rb @@ -168,6 +168,7 @@ class Game < Game::Base }, ].freeze + # Game specific constants LAST_OR = 11 @or = 0 @@ -179,9 +180,23 @@ def end_now?(_after) def new_operating_round(round_num = 1) @or += 1 + if @or == 9 + @operating_rounds = 3 + @three_or_round = true + end + super end + def or_round_finished + # In case we get phase change during the last OR set we ensure we have 3 ORs + @operating_rounds = 3 if @three_or_round + end + + def ipo_name(_entity = nil) + 'Treasury' + end + def timeline [ 'OR 7: Revolt! happens with 25% chance ', From bfd481802014492cc5e1c9be18383596daee6477 Mon Sep 17 00:00:00 2001 From: Alexander Vansach Date: Tue, 23 Jul 2024 17:35:18 +0200 Subject: [PATCH 06/49] 22Mars: add initial Revolt! support (close permits) --- lib/engine/game/g_22_mars/game.rb | 61 ++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/lib/engine/game/g_22_mars/game.rb b/lib/engine/game/g_22_mars/game.rb index d34afaa0ae..3f146437f5 100644 --- a/lib/engine/game/g_22_mars/game.rb +++ b/lib/engine/game/g_22_mars/game.rb @@ -170,8 +170,14 @@ class Game < Game::Base # Game specific constants LAST_OR = 11 + FIRST_REVOLT_CHECK_OR = 7 + LAST_REVOLT_CHECK_OR = 10 - @or = 0 + def setup + @or = 0 + @revolt_round = -1 + @three_or_round = false + end def end_now?(_after) @or == LAST_OR @@ -185,7 +191,11 @@ def new_operating_round(round_num = 1) @three_or_round = true end - super + round = super + + check_revolt! + + round end def or_round_finished @@ -193,6 +203,37 @@ def or_round_finished @operating_rounds = 3 if @three_or_round end + def revolt_happened? + @revolt_happened + end + + def check_revolt! + return if @or < FIRST_REVOLT_CHECK_OR || revolt_happened? + + # Generate a random number 0 to 3 (2, 1) to simulate 1 in 4 (in OR7, 1 in 3 in OR8 etc) cube draw chance + revolt_check = (rand % (LAST_REVOLT_CHECK_OR - @or + 1)) + + if (@or == LAST_REVOLT_CHECK_OR) || revolt_check.zero? + revolt! + else + @log << 'Revolt! does not happen' + end + end + + def revolt! + @log << '-- Revolt!' + @log << '-- All permits were discarded. You can now use abilities of Revolt! cards.' + @log << '-- All Convict bases are now neutral and cities with them generate no revenue.' + + @revolt_happened = true + @revolt_round = @or + + @companies.each do |company| + company.close! unless abilities(company, :close, on_phase: 'never') + end + + end + def ipo_name(_entity = nil) 'Treasury' end @@ -224,16 +265,24 @@ def progress_information { type: :OR, name: '5' }, { type: :OR, name: '6' }, { type: :SR, name: '4' }, - { type: :OR, name: '7', value: '✘/✔' }, - { type: :OR, name: '8', value: '✘/✔' }, + { type: :OR, name: '7', value: draw_revolt_marker(7) }, + { type: :OR, name: '8', value: draw_revolt_marker(8) }, { type: :SR, name: '5' }, - { type: :OR, name: '9', value: '✘/✔' }, - { type: :OR, name: '10', value: '✔' }, + { type: :OR, name: '9', value: draw_revolt_marker(9) }, + { type: :OR, name: '10', value: draw_revolt_marker(10) },, { type: :OR, name: '11' }, { type: :End }, ] end + def draw_revolt_marker(or_number) + return '—' if @revolt_happened && @revolt_round < or_number + return '✔' if [@revolt_round, LAST_REVOLT_CHECK_OR].include?(or_number) + return '✘' if @or >= or_number + + '✘/✔' + end + def price_movement_chart [ ['Action', 'Share Price Change'], From edc4e9338b856a5a2a699c8d7690722e56cd33db Mon Sep 17 00:00:00 2001 From: Alexander Vansach Date: Tue, 23 Jul 2024 17:39:19 +0200 Subject: [PATCH 07/49] 22Mars: add initial permit distribution round --- lib/engine/game/g_22_mars/game.rb | 39 +++++++++++++- .../game/g_22_mars/round/permit_purchase.rb | 19 +++++++ .../game/g_22_mars/step/select_or_discard.rb | 52 +++++++++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 lib/engine/game/g_22_mars/round/permit_purchase.rb create mode 100644 lib/engine/game/g_22_mars/step/select_or_discard.rb diff --git a/lib/engine/game/g_22_mars/game.rb b/lib/engine/game/g_22_mars/game.rb index 3f146437f5..a383253772 100644 --- a/lib/engine/game/g_22_mars/game.rb +++ b/lib/engine/game/g_22_mars/game.rb @@ -183,6 +183,36 @@ def end_now?(_after) @or == LAST_OR end + def setup_preround + revolts, permits = @companies.partition(&:revolt?) + sorted_permits = permits.sort_by { rand } + sorted_revolts = revolts.sort_by { rand } + + @companies = sorted_permits + sorted_revolts + + @companies.drop(@players.size).each_with_index do |company, index| + if index >= @corporations.size + company.close! + @log << "#{company.name} is discarded" + else + corporation = @corporations[index] + company.owner = corporation + corporation.companies << company + @log << "#{company.name} is assigned to #{corporation.name}" + end + + @companies.delete(company) + end + end + + def unowned_purchasable_companies + @companies + end + + def init_round + G22Mars::Round::PermitPurchase.new(self, [G22Mars::Step::SelectOrDiscard]) + end + def new_operating_round(round_num = 1) @or += 1 @@ -231,7 +261,6 @@ def revolt! @companies.each do |company| company.close! unless abilities(company, :close, on_phase: 'never') end - end def ipo_name(_entity = nil) @@ -269,7 +298,7 @@ def progress_information { type: :OR, name: '8', value: draw_revolt_marker(8) }, { type: :SR, name: '5' }, { type: :OR, name: '9', value: draw_revolt_marker(9) }, - { type: :OR, name: '10', value: draw_revolt_marker(10) },, + { type: :OR, name: '10', value: draw_revolt_marker(10) }, { type: :OR, name: '11' }, { type: :End }, ] @@ -297,6 +326,12 @@ def price_movement_chart def company_header(company) company.revolt? ? 'REVOLT!' : 'PERMIT' end + + def round_description(name, round_number = nil) + return 'Permit Purchase Round' if name == 'PermitPurchase' + + super + end end end end diff --git a/lib/engine/game/g_22_mars/round/permit_purchase.rb b/lib/engine/game/g_22_mars/round/permit_purchase.rb new file mode 100644 index 0000000000..d57345c45e --- /dev/null +++ b/lib/engine/game/g_22_mars/round/permit_purchase.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Engine + module Game + module G22Mars + module Round + class PermitPurchase < Engine::Round::Draft + def self.short_name + 'PPR' + end + + def name + 'Permit Purchase Round' + end + end + end + end + end +end diff --git a/lib/engine/game/g_22_mars/step/select_or_discard.rb b/lib/engine/game/g_22_mars/step/select_or_discard.rb new file mode 100644 index 0000000000..72fd56b876 --- /dev/null +++ b/lib/engine/game/g_22_mars/step/select_or_discard.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Engine + module Game + module G22Mars + module Step + class SelectOrDiscard < Engine::Step::SimpleDraft + ACTIONS = %w[bid pass].freeze + + def setup + @companies = @game.companies + end + + def available + [@companies.first] + end + + def finished? + @companies.none? + end + + def actions(entity) + return [] if finished? + + entity == current_entity ? ACTIONS : [] + end + + def process_pass(action) + company = available.first + player = action.entity + + company.close! + @companies.delete(company) + + @log << "#{player.name} discards #{company.name} from the game" + + @round.next_entity_index! + action_finalized + end + + def description + 'Buy or Discard Permit' + end + + def pass_description + 'Discard Permit' + end + end + end + end + end +end From da93e86022fb021be449ffcf299eb3c3987ea642 Mon Sep 17 00:00:00 2001 From: Oliver Burnett-Hall Date: Sat, 31 Aug 2024 13:31:24 +0100 Subject: [PATCH 08/49] [1858Switzerland] Add game code for 1858 Switzerland map This adds code handling the differences between the original 1858 game, and the prototype of the variant set in Switzerland. These differences include: - Bonuses for running north-south or east-west routes. - Three sets of private railway companies instead of two. - Two private closure rounds instead of one. - Different timings for wounding and rusting mid-game trains. - Mountain railway tiles which can be laid on hexes with a 120SF terrain cost. A public company that has laid one of these will then get a 40SF bonus to its revenue whenever it runs trains. - Special rules for the private railway companies which have Gotthard as a home hex. Normally a public company is allowed to acquire a private railway if there is a connection to anywhere in the private's home hex; for GB you need a connection to the broad gauge track in the Gotthard hex, and for FOB you need a connection to the narrow (metre) gauge track. --- lib/engine/game/g_1858_switzerland/game.rb | 166 ++++++++++++++++++ lib/engine/game/g_1858_switzerland/graph.rb | 26 +++ .../game/g_1858_switzerland/round/closure.rb | 34 ++++ 3 files changed, 226 insertions(+) create mode 100644 lib/engine/game/g_1858_switzerland/graph.rb create mode 100644 lib/engine/game/g_1858_switzerland/round/closure.rb diff --git a/lib/engine/game/g_1858_switzerland/game.rb b/lib/engine/game/g_1858_switzerland/game.rb index 528d775967..6916f64d3c 100644 --- a/lib/engine/game/g_1858_switzerland/game.rb +++ b/lib/engine/game/g_1858_switzerland/game.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative 'entities' +require_relative 'graph' require_relative 'map' require_relative 'meta' require_relative 'tiles' @@ -15,6 +16,7 @@ class Game < G1858::Game include Map include Tiles + GRAPH_CLASS = G1858Switzerland::Graph CURRENCY_FORMAT_STR = '%ssfr' BANK_CASH = 8_000 @@ -122,6 +124,170 @@ def setup super @phase4_train_trigger = PHASE4_TRAINS_RUST end + + def maybe_rust_wounded_trains!(grey_trains_bought, purchased_train) + obsolete_trains!(%w[6H 3M], purchased_train) if grey_trains_bought == PHASE4_TRAINS_OBSOLETE + rust_wounded_trains!(%w[4H 2M], purchased_train) if grey_trains_bought == PHASE3_TRAINS_RUST + rust_wounded_trains!(%w[6H 3M], purchased_train) if grey_trains_bought == PHASE4_TRAINS_RUST + end + + def obsolete_trains!(train_names, purchased_train) + trains.select { |train| train_names.include?(train.name) } + .each { |train| train.obsolete_on = purchased_train.sym } + rust_trains!(purchased_train, purchased_train.owner) + end + + def closure_round(round_num) + G1858Switzerland::Round::Closure.new(self, [ + G1858::Step::ExchangeApproval, + G1858::Step::HomeToken, + G1858::Step::PrivateClosure, + ], round_num: round_num) + end + + BONUS_HEXES = { + north: %w[C4 D3 E2 H1 I2], + south: %w[H15 I16], + east: %w[L5 L7], + west: %w[A14 B7], + revenue_ns: 'B16', + revenue_ew: 'L16', + }.freeze + + def revenue_for(route, stops) + super + north_south_bonus(route, stops) + east_west_bonus(route, stops) + end + + def private_colors_available(phase) + if phase.status.include?('yellow_privates') + %i[yellow] + elsif phase.status.include?('green_privates') + %i[yellow green] + elsif phase.status.include?('all_privates') + %i[yellow green lightblue] + elsif phase.status.include?('blue_privates') + %i[lightblue] + else + [] + end + end + + MOUNTAIN_RAILWAY_TILE = 'X28' + MOUNTAIN_RAILWAY_ASSIGNMENT = '+40' + MOUNTAIN_RAILWAY_BONUS = 40 + ASSIGNMENT_TOKENS = { + MOUNTAIN_RAILWAY_ASSIGNMENT => '/icons/1858_switzerland/mountain.svg', + }.freeze + + def submit_revenue_str(routes, _show_subsidy) + mountain_revenue = mountain_bonus(current_entity, routes) + return super if mountain_revenue.zero? + + train_revenue = routes_revenue(routes) + "#{format_revenue_currency(train_revenue)} + " \ + "#{format_revenue_currency(mountain_revenue)} mountain railway bonus" + end + + def extra_revenue(entity, routes) + mountain_bonus(entity, routes) + end + + def mountain_bonus(entity, routes) + return 0 if routes.empty? + + mountain_railway_built?(entity) ? MOUNTAIN_RAILWAY_BONUS : 0 + end + + def all_potential_upgrades(tile, tile_manifest: false, selected_company: nil) + return super unless mountain_hex?(tile) + + super + Array(@tiles.find { |t| t.name == MOUNTAIN_RAILWAY_TILE }) + end + + def upgrades_to?(from, to, special, selected_company: nil) + valid = super + return valid unless current_entity.corporation? + return valid if mountain_railway_built?(current_entity) + return valid unless mountain_hex?(from) + + valid || to.name == MOUNTAIN_RAILWAY_TILE + end + + def after_lay_tile(_hex, tile, entity) + return unless tile.name == MOUNTAIN_RAILWAY_TILE + + entity.assign!(MOUNTAIN_RAILWAY_ASSIGNMENT) + end + + # This method is called to remove some private railways from 1858 when + # there are two players. This does not happen in 18CH. + def setup_unbuyable_privates; end + + def gotthard + @gotthard ||= hex_by_id('H11') + end + + def fob_minor + @fob_minor ||= minor_by_id('FOB') + end + + def gb_minor + @gb_minor ||= minor_by_id('GB') + end + + def home_hex?(operator, hex, gauge = nil) + home_hex = super + return home_hex if !home_hex || hex != gotthard + + # Gotthard (H11) is different from other hexes. A public company + # doesn't just to have to have a connection to anywhere on the hex to + # be able to absorb a private railway. To absorb the GB private it + # needs to have a connection to the broad gauge track, to absorb the + # FOB it needs to have a connection to the metre (narrow) gauge track. + (operator == fob_minor && gauge == :narrow) || + (operator == gb_minor && gauge == :broad) + end + + private + + def hexes_by_id(coordinates) + coordinates.map { |coord| hex_by_id(coord) } + end + + def r2r_bonus(route, stops, zone1, zone2, bonus) + @bonus_nodes ||= { + north: hexes_by_id(BONUS_HEXES[:north]).map(&:tile).flat_map(&:offboards), + south: hexes_by_id(BONUS_HEXES[:south]).map(&:tile).flat_map(&:offboards), + east: hexes_by_id(BONUS_HEXES[:east]).map(&:tile).flat_map(&:offboards), + west: hexes_by_id(BONUS_HEXES[:west]).map(&:tile).flat_map(&:offboards), + } + @bonus_revenue ||= { + north_south: hex_by_id(BONUS_HEXES[:revenue_ns]).tile.offboards.first, + east_west: hex_by_id(BONUS_HEXES[:revenue_ew]).tile.offboards.first, + } + return 0 unless stops.intersect?(@bonus_nodes[zone1]) + return 0 unless stops.intersect?(@bonus_nodes[zone2]) + + @bonus_revenue[bonus].route_revenue(@phase, route.train) + end + + def north_south_bonus(route, stops) + r2r_bonus(route, stops, :north, :south, :north_south) + end + + def east_west_bonus(route, stops) + r2r_bonus(route, stops, :east, :west, :east_west) + end + + def mountain_hex?(tile) + tile.color == :white && tile.upgrades.any? { |u| u.cost == 120 } + end + + def mountain_railway_built?(entity) + return false unless entity.corporation? + + entity.assignments.key?(MOUNTAIN_RAILWAY_ASSIGNMENT) + end end end end diff --git a/lib/engine/game/g_1858_switzerland/graph.rb b/lib/engine/game/g_1858_switzerland/graph.rb new file mode 100644 index 0000000000..431e0c23e8 --- /dev/null +++ b/lib/engine/game/g_1858_switzerland/graph.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require_relative '../g_1858/graph' + +module Engine + module Game + module G1858Switzerland + class Graph < G1858::Graph + private + + def path_node(path, entity) + node = G1858::Part::PathNode.new(path) + return node unless path.hex == @game.gotthard + + # The pre-printed Gotthard hex is a special case. It is a home hex + # for two private railways (FOB and GB) and it has two paths, one + # broad gauge and one metre (narrow) gauge. FOB is only allowed to + # trace routes from this hex using the metre gauge path, and GB can + # only use the broad gauge path. + return node if (path.track == :broad && entity == @game.gb_minor) || + (path.track == :narrow && entity == @game.fob_minor) + end + end + end + end +end diff --git a/lib/engine/game/g_1858_switzerland/round/closure.rb b/lib/engine/game/g_1858_switzerland/round/closure.rb new file mode 100644 index 0000000000..5cc2dde1c4 --- /dev/null +++ b/lib/engine/game/g_1858_switzerland/round/closure.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require_relative '../../g_1858/round/closure' + +module Engine + module Game + module G1858Switzerland + module Round + class Closure < G1858::Round::Closure + private + + # Is this the final private closure round? + def last_pcr? + @game.phase.name != '5' + end + + def companies + companies = @game.companies.reject(&:closed?) + return companies if last_pcr? + + companies.reject { |company| company.color == :lightblue } + end + + def minors + minors = @game.minors.reject(&:closed?) + return minors if last_pcr? + + minors.reject { |minor| minor.color == :lightblue } + end + end + end + end + end +end From c04c6f1f35db170e83a6e86d533e13f56e191f08 Mon Sep 17 00:00:00 2001 From: Alexander Vansach Date: Wed, 4 Sep 2024 18:15:29 +0200 Subject: [PATCH 09/49] 22Mars: Determine Revolt round on game start --- lib/engine/game/g_22_mars/game.rb | 42 ++++++++++--------- .../game/g_22_mars/step/select_or_discard.rb | 2 +- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/lib/engine/game/g_22_mars/game.rb b/lib/engine/game/g_22_mars/game.rb index a383253772..5a47fb3efa 100644 --- a/lib/engine/game/g_22_mars/game.rb +++ b/lib/engine/game/g_22_mars/game.rb @@ -175,8 +175,8 @@ class Game < Game::Base def setup @or = 0 - @revolt_round = -1 @three_or_round = false + @revolt_round = determine_revolt_round end def end_now?(_after) @@ -188,20 +188,18 @@ def setup_preround sorted_permits = permits.sort_by { rand } sorted_revolts = revolts.sort_by { rand } - @companies = sorted_permits + sorted_revolts + @companies = sorted_permits.take(@players.size) + assigned_companies = sorted_permits.drop(@players.size) + sorted_revolts - @companies.drop(@players.size).each_with_index do |company, index| - if index >= @corporations.size - company.close! - @log << "#{company.name} is discarded" - else - corporation = @corporations[index] + assigned_companies.zip(@corporations).each do |company, corporation| + if corporation company.owner = corporation corporation.companies << company @log << "#{company.name} is assigned to #{corporation.name}" + else + company.close! + @log << "#{company.name} is discarded" end - - @companies.delete(company) end end @@ -237,13 +235,18 @@ def revolt_happened? @revolt_happened end + def determine_revolt_round + (FIRST_REVOLT_CHECK_OR..LAST_REVOLT_CHECK_OR).each do |round| + probability = LAST_REVOLT_CHECK_OR - round + 1 + + return round if (rand % probability).zero? + end + end + def check_revolt! return if @or < FIRST_REVOLT_CHECK_OR || revolt_happened? - # Generate a random number 0 to 3 (2, 1) to simulate 1 in 4 (in OR7, 1 in 3 in OR8 etc) cube draw chance - revolt_check = (rand % (LAST_REVOLT_CHECK_OR - @or + 1)) - - if (@or == LAST_REVOLT_CHECK_OR) || revolt_check.zero? + if @or == @revolt_round revolt! else @log << 'Revolt! does not happen' @@ -256,7 +259,6 @@ def revolt! @log << '-- All Convict bases are now neutral and cities with them generate no revenue.' @revolt_happened = true - @revolt_round = @or @companies.each do |company| company.close! unless abilities(company, :close, on_phase: 'never') @@ -305,11 +307,13 @@ def progress_information end def draw_revolt_marker(or_number) - return '—' if @revolt_happened && @revolt_round < or_number - return '✔' if [@revolt_round, LAST_REVOLT_CHECK_OR].include?(or_number) - return '✘' if @or >= or_number + if @revolt_happened + return '✔' if or_number == @revolt_round - '✘/✔' + or_number < @revolt_round ? '✘' : '—' + else + or_number == LAST_REVOLT_CHECK_OR ? '✔' : '✘/✔' + end end def price_movement_chart diff --git a/lib/engine/game/g_22_mars/step/select_or_discard.rb b/lib/engine/game/g_22_mars/step/select_or_discard.rb index 72fd56b876..7a04d17414 100644 --- a/lib/engine/game/g_22_mars/step/select_or_discard.rb +++ b/lib/engine/game/g_22_mars/step/select_or_discard.rb @@ -16,7 +16,7 @@ def available end def finished? - @companies.none? + @companies.empty? end def actions(entity) From 9e0660460878bdc191ac12aff8a455fb88c19978 Mon Sep 17 00:00:00 2001 From: Galatolol Date: Mon, 9 Sep 2024 23:15:02 +0200 Subject: [PATCH 10/49] 1847 - fix LFK bug --- lib/engine/game/g_1847_ae/step/buy_single_train_of_type.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/engine/game/g_1847_ae/step/buy_single_train_of_type.rb b/lib/engine/game/g_1847_ae/step/buy_single_train_of_type.rb index f6c7601fdd..bf5bf853d7 100644 --- a/lib/engine/game/g_1847_ae/step/buy_single_train_of_type.rb +++ b/lib/engine/game/g_1847_ae/step/buy_single_train_of_type.rb @@ -16,11 +16,11 @@ def buyable_trains(entity) end def process_buy_train(action) - from_depot = action.train.from_depot? + from_discard = @depot.discarded.include?(action.train) super lfk = @game.lfk - return if @game.train_bought_this_round || !lfk.floated? || !from_depot + return if @game.train_bought_this_round || !lfk.floated? || from_discard lfk_owner = lfk.owner if lfk_owner.player? From e11a422cc6d9210df8df130c2dc88c06ac7c1f3d Mon Sep 17 00:00:00 2001 From: Galatolol Date: Mon, 9 Sep 2024 23:18:03 +0200 Subject: [PATCH 11/49] 1847 - improve one private's description --- lib/engine/game/g_1847_ae/entities.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_1847_ae/entities.rb b/lib/engine/game/g_1847_ae/entities.rb index 670708cdc4..e271b70bab 100644 --- a/lib/engine/game/g_1847_ae/entities.rb +++ b/lib/engine/game/g_1847_ae/entities.rb @@ -42,7 +42,7 @@ module Entities revenue: 30, min_price: 100, max_price: 200, - desc: 'Revenue increases to 50M when a tile is laid in E9. '\ + desc: 'Revenue increases to 50M when a tile is laid in E9 (the tile may be laid only in Phase 5 or later). '\ 'May be sold to a corporation for 100 to 200M. '\ 'Never closes.', sym: 'R', From 8d58d0157530fe619a516c177bdb6f1016099447 Mon Sep 17 00:00:00 2001 From: Steve Undy Date: Mon, 9 Sep 2024 22:10:00 -0600 Subject: [PATCH 12/49] S18: Full Britain map --- lib/engine/game/g_system18/game.rb | 55 +- .../g_system18/map_britain_customization.rb | 497 ++++++++++++++++++ .../g_system18/map_poland_customization.rb | 4 +- lib/engine/game/g_system18/meta.rb | 10 +- .../{poland_parliament.rb => parliament.rb} | 2 +- ..._charter_auction.rb => charter_auction.rb} | 2 +- lib/engine/game/g_system18/step/token.rb | 41 +- lib/engine/game/g_system18/step/track.rb | 24 + public/logos/System18/PGS.svg | 36 ++ 9 files changed, 660 insertions(+), 11 deletions(-) create mode 100644 lib/engine/game/g_system18/map_britain_customization.rb rename lib/engine/game/g_system18/round/{poland_parliament.rb => parliament.rb} (76%) rename lib/engine/game/g_system18/step/{poland_charter_auction.rb => charter_auction.rb} (75%) create mode 100644 public/logos/System18/PGS.svg diff --git a/lib/engine/game/g_system18/game.rb b/lib/engine/game/g_system18/game.rb index 2b082d4df3..82706c0aac 100644 --- a/lib/engine/game/g_system18/game.rb +++ b/lib/engine/game/g_system18/game.rb @@ -12,6 +12,7 @@ require_relative 'map_uk_limited_customization' require_relative 'map_china_rapid_development_customization' require_relative 'map_poland_customization' +require_relative 'map_britain_customization' module Engine module Game @@ -28,6 +29,7 @@ class Game < Game::Base include MapUKLimitedCustomization include MapChinaRapidDevelopmentCustomization include MapPolandCustomization + include MapBritainCustomization register_colors(red: '#d1232a', orange: '#f58121', @@ -275,6 +277,7 @@ class Game < Game::Base MUST_EMERGENCY_ISSUE_BEFORE_EBUY = false BANKRUPTCY_ENDS_GAME_AFTER = :one STATUS_TEXT = {}.freeze + TILE_UPGRADES_MUST_USE_MAX_EXITS = [].freeze def find_map_name optional_rules&.find { |r| r.to_s.include?('map_') }&.to_s&.delete_prefix('map_')&.downcase @@ -359,9 +362,13 @@ def game_phases proto = send("map_#{map_name}_game_phases") proto.each { |pp| phases << pp.dup } - # change last phase based on train roster - phases[-1][:name] = game_trains.last[:name] - phases[-1][:on] = game_trains.last[:name] + if respond_to?("map_#{map_name}_post_game_phases") + phases = send("map_#{map_name}_post_game_phases", phases) + else + # change last phase based on train roster + phases[-1][:name] = game_trains.last[:name] + phases[-1][:on] = game_trains.last[:name] + end phases end @@ -625,6 +632,48 @@ def timeline def ipo_name(_corp) game_capitalization == :incremental ? 'Treasury' : 'IPO' end + + def can_remove_icon?(entity) + return false unless respond_to?("map_#{map_name}_can_remove_icon?") + + send("map_#{map_name}_can_remove_icon?", entity) + end + + def icon_hexes(entity) + return [] unless respond_to?("map_#{map_name}_icon_hexes") + + send("map_#{map_name}_icon_hexes", entity) + end + + def remove_icon(entity, hex_id) + return unless respond_to?("map_#{map_name}_remove_icon") + + send("map_#{map_name}_remove_icon", entity, hex_id) + end + + def removable_icon_action_str + return unless respond_to?("map_#{map_name}_removable_icon_action_str") + + send("map_#{map_name}_removable_icon_action_str") + end + + def status_str(corporation) + return unless respond_to?("map_#{map_name}_status_str") + + send("map_#{map_name}_status_str", corporation) + end + + def modify_tile_lay(entity, action) + return action unless respond_to?("map_#{map_name}_modify_tile_lay") + + send("map_#{map_name}_modify_tile_lay", entity, action) + end + + def pre_lay_tile_action(action, entity, tile_lay) + return unless respond_to?("map_#{map_name}_pre_lay_tile_action") + + send("map_#{map_name}_pre_lay_tile_action", action, entity, tile_lay) + end end end end diff --git a/lib/engine/game/g_system18/map_britain_customization.rb b/lib/engine/game/g_system18/map_britain_customization.rb new file mode 100644 index 0000000000..5e190953a1 --- /dev/null +++ b/lib/engine/game/g_system18/map_britain_customization.rb @@ -0,0 +1,497 @@ +# frozen_string_literal: true + +module Engine + module Game + module GSystem18 + module MapBritainCustomization + BRITAIN_REGION_HEXES = { + 'Scotland' => %w[A6 A8 A10 B3 B5 B7 B9 C4 C6], + 'Wales' => %w[G4 H3 I2 I4 J3], + }.freeze + + BRITAIN_MINE_HEXES = %w[B7 D9 G10 I4].freeze + BRITAIN_LONDON_HEX = 'K10' + + BRITAIN_SCOTLAND_BONUS = [10, 20, 20, 30, 30, 30, 40].freeze + BRITAIN_WALES_BONUS = [10, 20, 20, 20, 20, 20, 30].freeze + BRITAIN_LONDON_BONUS = [10, 20, 20, 30, 30, 30, 40].freeze + BRITAIN_MINE_BONUS = [10, 20, 20, 30, 30, 30, 40].freeze + + # rubocop:disable Layout/LineLength + def map_britain_game_tiles(tiles) + tiles['8'] = 6 + tiles['9'] = 6 + tiles['23'] = 2 + tiles['24'] = 2 + tiles['25'] = 2 + tiles['448'] = 3 + tiles.merge!({ + 'X1' => + { + 'count' => 3, + 'color' => 'gray', + 'code' => 'city=revenue:70,slots:2;path=a:0,b:_0;path=a:1,b:_0;path=a:2,b:_0;path=a:3,b:_0;path=a:4,b:_0;path=a:5,b:_0;label=OO', + }, + 'X2' => + { + 'count' => 1, + 'color' => 'gray', + 'code' => 'city=revenue:50,slots:2;path=a:0,b:_0;path=a:1,b:_0;path=a:2,b:_0;path=a:3,b:_0;path=a:4,b:_0;path=a:5,b:_0', + }, + }) + end + # rubocop:enable Layout/LineLength + + def map_britain_layout + :pointy + end + + def map_britain_game_location_names + { + 'A4' => 'Scotland Bonus', + 'A6' => 'Glasgow', + 'A8' => 'Edinburgh', + 'B3' => 'N. Ireland', + 'C4' => '(Scotland)', + 'C8' => 'Carlisle', + 'C10' => 'Newcastle', + 'D11' => 'Sunderland & Middlesbrough', + 'E6' => 'Preston', + 'E10' => 'York', + 'F5' => 'Liverpool', + 'F7' => 'Bolton & Manchester', + 'F9' => 'Leeds & Bradford', + 'F11' => 'Hull', + 'G2' => 'Wales Bonus', + 'G6' => 'Crewe & Stoke on Trend', + 'G8' => 'Derby', + 'G12' => 'Mine Bonus', + 'H3' => '(Wales)', + 'H7' => 'Birmingham & Wolverh\'ton', + 'H9' => 'Nottingham', + 'I2' => 'S. Ireland', + 'I8' => 'Coventry', + 'I12' => 'Norwich', + 'J1' => 'Swansea', + 'J3' => 'Cardiff', + 'J5' => 'Bristol', + 'J9' => 'Bedford & Luton', + 'J11' => 'Cambridge & Colchester', + 'J13' => 'Harwich', + 'K2' => 'Plymouth', + 'K8' => 'Reading & Guildford', + 'K10' => 'London', + 'L5' => 'Southhampton', + 'L7' => 'Portsmouth', + 'L11' => 'London Port Bonus', + } + end + + # rubocop:disable Layout/LineLength + def map_britain_game_hexes + { + gray: { + %w[A4] => 'offboard=revenue:yellow_10|green_20|brown_30|gray_40', + %w[C12] => 'path=a:0,b:1', + %w[G2] => 'offboard=revenue:yellow_10|green_20|brown_20|gray_30', + %w[G12] => 'offboard=revenue:yellow_10|green_20|brown_30|gray_40', + %w[J1] => 'town=revenue:10;path=a:4,b:_0', + %w[L11] => 'offboard=revenue:yellow_10|green_20|brown_30|gray_40', + }, + + red: { + %w[B3] => 'offboard=revenue:yellow_20|green_20|brown_30|gray_30;path=a:4,b:_0;path=a:5,b:_0;icon=image:port', + %w[F5] => 'offboard=revenue:yellow_30|green_40|brown_50|gray_70;path=a:3,b:_0;path=a:4,b:_0;path=a:5,b:_0', + %w[I2] => 'offboard=revenue:yellow_20|green_20|brown_30|gray_30;path=a:3,b:_0;path=a:4,b:_0;path=a:5,b:_0;icon=image:port', + %w[J13] => 'offboard=revenue:yellow_10|green_20|brown_30|gray_40;path=a:0,b:_0;path=a:1,b:_0;path=a:2,b:_0;icon=image:port', + %w[K2] => 'offboard=revenue:yellow_20|green_30|brown_40|gray_40;path=a:4,b:_0;path=a:5,b:_0;icon=image:port', + %w[K10] => 'offboard=revenue:yellow_40|green_50|brown_70|gray_100;path=a:0,b:_0;path=a:1,b:_0;path=a:2,b:_0;path=a:3,b:_0;path=a:4,b:_0', + }, + green: { + %w[F7] => 'city=revenue:40,pos:0;city=revenue:40,pos:2;path=a:0,b:_0;path=a:2,b:_1;label=OO', + %w[H7] => 'city=revenue:40,pos:3;city=revenue:40,pos:5;path=a:5,b:_1;path=a:5,b:_1;label=OO', + }, + yellow: { + %w[A6] => 'city=revenue:30;path=a:0,b:_0;path=a:2,b:_0;path=a:4,b:_0;label=B', + %w[F9] => 'city=revenue:30,pos:0;city=revenue:30,pos:3;path=a:3,b:_1;label=OO', + }, + white: { + %w[B5 C4 H11 I10 J7 K6 K12 L3 L9] => '', + %w[A8] => 'city=revenue:0', + %w[A10] => 'border=type:province,color:brown,edge:5', + %w[B7] => 'upgrade=cost:40,terrain:mountain;icon=image:mine,sticky:1;border=type:province,color:brown,edge:5', + %w[B9] => 'upgrade=cost:40,terrain:mountain;border=type:province,color:brown,edge:0;border=type:province,color:brown,edge:4;border=type:province,color:brown,edge:5', + %w[B11] => 'border=type:province,color:brown,edge:1;border=type:province,color:brown,edge:2', + %w[C6] => 'border=type:province,color:brown,cost:80,edge:5', + %w[C8] => 'city=revenue:0;upgrade=cost:20,terrain:water;border=type:province,color:brown,edge:1;border=type:province,color:brown,edge:2;border=type:province,color:brown,edge:3', + %w[C10] => 'city=revenue:0;upgrade=cost:20,terrain:water;border=type:province,color:brown,edge:2', + %w[D7] => 'upgrade=cost:40,terrain:mountain;border=type:province,color:brown,cost:80,edge:2', + %w[D9] => 'upgrade=cost:40,terrain:mountain;icon=image:mine,sticky:1', + %w[D11] => 'town=revenue:0;town=revenue:0', + %w[E6 E10] => 'city=revenue:0', + %w[E8] => 'upgrade=cost:80,terrain:mountain', + %w[E12] => 'upgrade=cost:40,terrain:mountain', + %w[F11] => 'upgrade=cost:20,terrain:water', + %w[G4] => 'upgrade=cost:40,terrain:mountain;border=type:province,color:brown,edge:4;border=type:province,color:brown,edge:5', + %w[G6] => 'town=revenue:0;town=revenue:0;border=type:province,color:brown,edge:1', + %w[G8] => 'city=revenue:0', + %w[G10] => 'upgrade=cost:40,terrain:water;icon=image:mine,sticky:1', + %w[H3] => 'upgrade=cost:40,terrain:mountain;border=type:province,color:brown,edge:4', + %w[H5] => 'border=type:province,color:brown,edge:0;border=type:province,color:brown,edge:1;border=type:province,color:brown,edge:2', + %w[H9] => 'city=revenue:0', + %w[I4] => 'upgrade=cost:40,terrain:mountain;icon=image:mine,sticky:1;border=type:province,color:brown,edge:3;border=type:province,color:brown,edge:4;border=type:province,color:brown,edge:5', + %w[I6] => 'upgrade=cost:20,terrain:water;border=type:province,color:brown,edge:1', + %w[I8 I12] => 'city=revenue:0', + %w[J3] => 'city=revenue:0;border=type:province,color:brown,cost:80,edge:4;border=type:impassable,edge:5', + %w[J5] => 'city=revenue:0;border=type:province,color:brown,cost:80,edge:1;border=type:province,color:brown,edge:2', + %w[J9 J11] => 'town=revenue:0;town=revenue:0', + %w[K4] => 'border=type:impassable,edge:2', + %w[K8] => 'town=revenue:0;town=revenue:0;upgrade=cost:20,terrain:water', + %w[L5 L7] => 'city=revenue:0', + }, + } + end + # rubocop:enable Layout/LineLength + + def map_britain_game_companies + [ + { + name: 'DGN Charter', + sym: 'DGN', + value: 0, + revenue: 0, + desc: 'Allows opening DGN corporation', + color: '#50c878', + }, + { + name: 'GFN Charter', + sym: 'GFN', + value: 0, + revenue: 0, + desc: 'Allows opening GFN corporation', + color: '#999999', + }, + { + name: 'PHX Charter', + sym: 'PHX', + value: 0, + revenue: 0, + desc: 'Allows opening PHX corporation', + color: '#ff7518', + text_color: 'black', + }, + { + name: 'KKN Charter', + sym: 'KKN', + value: 0, + revenue: 0, + desc: 'Allows opening KKN corporation', + color: '#0096ff', + }, + { + name: 'SPX Charter', + sym: 'SPX', + value: 0, + revenue: 0, + desc: 'Allows opening SPX corporation', + color: '#fafa33', + text_color: 'black', + }, + { + name: 'PGS Charter', + sym: 'PGS', + value: 0, + revenue: 0, + desc: 'Allows opening PGS corporation', + color: '#cd79a7', + text_color: 'black', + }, + ] + end + + # DGN GFN PHX KKN SPX PGS + def map_britain_game_corporations(corps) + corps.append( + { + float_percent: 20, + sym: 'PGS', + name: 'Pegasus', + logo: 'System18/PGS', + tokens: [0, 40, 100], + coordinates: nil, + color: '#cd79a7', + reservation_color: nil, + max_ownership_percent: 60, + } + ) + corps.each_with_index do |c, idx| + c[:float_percent] = 20 + c[:always_market_price] = true + c[:tokens].append(100) + c[:coordinates] = %w[F7 J5 F9 A6 I12 A8][idx] + c[:city] = [1, nil, 1, nil, nil, nil][idx] + end + corps + end + + def map_britain_game_cash + { 3 => 420, 4 => 315, 5 => 250 } + end + + def map_britain_game_cert_limit + { 3 => 16, 4 => 12, 5 => 10 } + end + + def map_britain_game_capitalization + :incremental + end + + def map_britain_game_market + self.class::MARKET_1D + end + + def map_britain_game_trains(trains) + # don't use D trains + trains.delete(find_train(trains, 'D')) + find_train(trains, '4')[:rusts_on] = '8' + # udpate quantities + find_train(trains, '2')[:num] = 6 + find_train(trains, '3')[:num] = 4 + find_train(trains, '4')[:num] = 3 + find_train(trains, '5')[:num] = 3 + find_train(trains, '6')[:num] = 2 + find_train(trains, '8')[:num] = 99 + trains.append({ + name: '4D', + distance: [{ 'nodes' => %w[city offboard town], 'pay' => 4, 'visit' => 99, 'multiplier' => 2 }], + price: 1000, + num: 99, + available_on: '8', + }) + trains + end + + def map_britain_game_phases + self.class::S18_INCCAP_PHASES + end + + def map_britain_post_game_phases(phases) + phases + end + + def map_britain_constants + redef_const(:CURRENCY_FORMAT_STR, '£%s') + redef_const(:TILE_UPGRADES_MUST_USE_MAX_EXITS, %i[unlabeled_cities]) + redef_const(:TILE_LAYS, [{ lay: true, upgrade: true, cost: 0 }, { lay_replaced: :if_green_upgraded }]) + end + + def map_britain_setup + # add 2 DRG and PHX tokens each to map + dragon = corporations.find { |c| c.name == 'DGN' } + hexes.find { |h| h.id == 'F7' }.tile.cities[1].place_token(dragon, dragon.tokens[0], free: true) + hexes.find { |h| h.id == 'I8' }.tile.cities[0].place_token(dragon, dragon.tokens[1], free: true) + + phoenix = corporations.find { |c| c.name == 'PHX' } + hexes.find { |h| h.id == 'F9' }.tile.cities[1].place_token(phoenix, phoenix.tokens[0], free: true) + hexes.find { |h| h.id == 'G8' }.tile.cities[0].place_token(phoenix, phoenix.tokens[1], free: true) + + @britain_mines = BRITAIN_MINE_HEXES.map { |h| hex_by_id(h) } + @britain_corps_with_mines = {} + end + + def map_britain_company_header(_company) + 'CHARTER' + end + + def map_britain_init_round + map_britain_new_parliament_round + end + + def map_britain_new_parliament_round + @log << "-- Parliament Round #{@turn} -- " + GSystem18::Round::Parliament.new(self, [ + GSystem18::Step::CharterAuction, + ]) + end + + def map_britain_next_round! + @round = + case @round + when Engine::Round::Stock + map_britain_stock_round_finished + @operating_rounds = @phase.operating_rounds + reorder_players + new_operating_round + when Round::Operating + if @round.round_num < @operating_rounds + or_round_finished + new_operating_round(@round.round_num + 1) + else + @turn += 1 + or_round_finished + or_set_finished + map_britain_new_parliament_round + end + else # Parliament Round + init_round_finished + new_stock_round + end + end + + # remove un-excersized charters from players + def map_britain_stock_round_finished + @players.each do |player| + player.companies.dup.each do |c| + @log << "Right to open #{c.sym} lapses for #{player.name}" + player.companies.delete(c) + c.owner = nil + end + end + end + + def map_britain_can_par?(corporation, entity) + !corporation.ipoed && entity.companies.find { |c| c.sym == corporation.name } + end + + def map_britain_after_par(corporation) + entity = corporation.owner + + company = entity.companies.find { |c| c.sym == corporation.name } + raise GameError, 'Logic error, no matching company found' unless company + + entity.companies.delete(company) + company.close! + end + + # can the corporation reach an icon? + def map_britain_can_remove_icon?(entity) + return false unless entity.corporation? + return false if @britain_corps_with_mines[entity.name] + + @britain_mines.any? { |m| @graph.reachable_hexes(entity).include?(m) } + end + + def map_britain_icon_hexes(entity) + return [] unless entity.corporation? + return [] if @britain_corps_with_mines[entity.name] + + @britain_mines.select { |m| @graph.reachable_hexes(entity).include?(m) }.map(&:id) + end + + def map_britain_remove_icon(entity, hex_id) + return unless entity.corporation? + return if @britain_corps_with_mines[entity.name] + + hex = hex_by_id(hex_id) + hex.tile.icons.clear # should be the only icon on the tile + @log << "#{entity.name} takes mine token from #{hex_id}" + @britain_corps_with_mines[entity.name] = hex_id + end + + def map_britain_removable_icon_action_str + 'Take a mine token' + end + + def map_britain_extra_revenue_for(route, stops) + map_britain_bonuses(route, stops)[:revenue] + end + + def map_britain_extra_revenue_str(route) + bonus = map_britain_bonuses(route, route.stops)[:description] + bonus ? " + #{bonus}" : '' + end + + def map_britain_bonuses(route, stops) + train = route.train + bonus = { revenue: 0 } + return bonus unless train + + desc = '' + + # mines (no 4D doubling), only on one run + corp = train.owner + lowest_train = route.routes.reject { |r| r.stops.empty? }.min_by { |r| corp.trains.index(r.train) }.train + if (train == lowest_train) && corp && @britain_corps_with_mines[corp.name] + bonus[:revenue] += BRITAIN_MINE_BONUS[@phase.name.to_i - 2] + desc = 'Mine' + end + + # Scotland (doubles with 4D) + scotland_city = stops.find do |stop| + BRITAIN_REGION_HEXES['Scotland'].include?(stop.hex.id) && (stop.city? || stop.offboard?) + end + non_scotland_city = stops.find { |stop| !BRITAIN_REGION_HEXES['Scotland'].include?(stop.hex.id) && stop.city? } + if scotland_city && non_scotland_city + bonus[:revenue] += BRITAIN_SCOTLAND_BONUS[@phase.name.to_i - 2] * (train.name == '4D' ? 2 : 1) + desc += ', ' unless desc == '' + desc += 'Scotland' + end + + # Wales (doubles with 4D) + wales_city = stops.find { |stop| BRITAIN_REGION_HEXES['Wales'].include?(stop.hex.id) && (stop.city? || stop.offboard?) } + non_wales_city = stops.find { |stop| !BRITAIN_REGION_HEXES['Wales'].include?(stop.hex.id) && stop.city? } + if wales_city && non_wales_city + bonus[:revenue] += BRITAIN_WALES_BONUS[@phase.name.to_i - 2] * (train.name == '4D' ? 2 : 1) + desc += ', ' unless desc == '' + desc += 'Wales' + end + + # London-Port (doubles with 4D) + london = stops.find { |stop| stop.hex.id == BRITAIN_LONDON_HEX } + port = stops.find { |stop| stop.tile.icons.any? { |i| i.name == 'port' } } + if london && port + bonus[:revenue] += BRITAIN_LONDON_BONUS[@phase.name.to_i - 2] * (train.name == '4D' ? 2 : 1) + desc += ', ' unless desc == '' + desc += 'London-Port' + end + + bonus[:description] = "(#{desc})" unless desc == '' + bonus + end + + def map_britain_status_str(corporation) + return unless @britain_corps_with_mines[corporation.name] + + "Mine (from #{@britain_corps_with_mines[corporation.name]})" + end + + def map_britain_modify_tile_lay(_entity, action) + return unless action + + if action[:lay_replaced] && @round.upgraded_track && + (@round.laid_hexes.first.tile.color == :green) && + @round.laid_hexes.first.tile.cities.empty? + action[:lay_replaced] = true + action[:lay] = true + else + action[:lay_replaced] = nil + end + + action + end + + def map_britain_pre_lay_tile_action(action, _entity, tile_lay) + tile = action.tile + hex = action.hex + old_tile = hex.tile + + if tile_lay[:lay_replaced] && ((@round.last_old_tile != tile) || + (@tiles.count { |t| t.name == tile.name } != 1) || + (tile.color != :yellow)) + raise GameError, 'Must lay yellow tile just replaced' + end + + @round.last_old_tile = old_tile + end + + # FIXME: add reopen! method to Engine::Company + # + # open company associated with closed corporation + # def map_britain_close_corporation_extra(corporation) + # company = companies.find { |c| c.sym == corporation.name } + # company.reopen! + # end + end + end + end +end diff --git a/lib/engine/game/g_system18/map_poland_customization.rb b/lib/engine/game/g_system18/map_poland_customization.rb index 5759545aae..377778c601 100644 --- a/lib/engine/game/g_system18/map_poland_customization.rb +++ b/lib/engine/game/g_system18/map_poland_customization.rb @@ -257,8 +257,8 @@ def map_poland_init_round def map_poland_new_parliament_round @log << "-- Parliament Round #{@turn} -- " - GSystem18::Round::PolandParliament.new(self, [ - GSystem18::Step::PolandCharterAuction, + GSystem18::Round::Parliament.new(self, [ + GSystem18::Step::CharterAuction, ]) end diff --git a/lib/engine/game/g_system18/meta.rb b/lib/engine/game/g_system18/meta.rb index e7f3f7fb99..68e82f993d 100644 --- a/lib/engine/game/g_system18/meta.rb +++ b/lib/engine/game/g_system18/meta.rb @@ -17,7 +17,7 @@ module Meta GAME_INFO_URL = 'https://github.com/tobymao/18xx/wiki/System18' GAME_RULES_URL = 'https://github.com/tobymao/18xx/wiki/System18' - PLAYER_RANGE = [2, 4].freeze + PLAYER_RANGE = [2, 5].freeze OPTIONAL_RULES = [ { sym: :map_NEUS, @@ -56,10 +56,16 @@ module Meta players: [2, 3, 4], designer: 'Ian Wilson', }, + { + sym: :map_Britain, + short_name: 'Map: Britain', + players: [3, 4, 5], + designer: 'Ian Wilson', + }, ].freeze MUTEX_RULES = [ - %i[map_NEUS map_France map_Twisting_Tracks map_UK_Limited map_China_Rapid_Development map_Poland], + %i[map_NEUS map_France map_Twisting_Tracks map_UK_Limited map_China_Rapid_Development map_Poland map_Britain], ].freeze end end diff --git a/lib/engine/game/g_system18/round/poland_parliament.rb b/lib/engine/game/g_system18/round/parliament.rb similarity index 76% rename from lib/engine/game/g_system18/round/poland_parliament.rb rename to lib/engine/game/g_system18/round/parliament.rb index 2d70736840..877462fa59 100644 --- a/lib/engine/game/g_system18/round/poland_parliament.rb +++ b/lib/engine/game/g_system18/round/parliament.rb @@ -6,7 +6,7 @@ module Engine module Game module GSystem18 module Round - class PolandParliament < Engine::Round::Auction + class Parliament < Engine::Round::Auction end end end diff --git a/lib/engine/game/g_system18/step/poland_charter_auction.rb b/lib/engine/game/g_system18/step/charter_auction.rb similarity index 75% rename from lib/engine/game/g_system18/step/poland_charter_auction.rb rename to lib/engine/game/g_system18/step/charter_auction.rb index 57549b33b7..14e9e1a3ea 100644 --- a/lib/engine/game/g_system18/step/poland_charter_auction.rb +++ b/lib/engine/game/g_system18/step/charter_auction.rb @@ -7,7 +7,7 @@ module Engine module Game module GSystem18 module Step - class PolandCharterAuction < GSystem18::Step::UpwardsAuction + class CharterAuction < GSystem18::Step::UpwardsAuction end end end diff --git a/lib/engine/game/g_system18/step/token.rb b/lib/engine/game/g_system18/step/token.rb index b725d0d49d..66e08ff3f4 100644 --- a/lib/engine/game/g_system18/step/token.rb +++ b/lib/engine/game/g_system18/step/token.rb @@ -8,7 +8,43 @@ module GSystem18 module Step class Token < Engine::Step::Token def actions(entity) - entity.receivership? ? [] : super + return [] if entity.receivership? + return [] unless entity == current_entity + + actions = [] + actions << 'place_token' if can_place_token?(entity) + actions << 'choose' if can_remove_icon?(entity) + actions << 'pass' unless actions.empty? + + actions + end + + def description + if can_place_token?(current_entity) + return "#{@game.removable_icon_action_str} or Place a Token" if can_remove_icon?(current_entity) + elsif can_remove_icon?(current_entity) + return @game.removable_icon_action_str + end + 'Place a Token' + end + + def can_remove_icon?(entity) + @game.can_remove_icon?(entity) + end + + def choices + @game.icon_hexes(current_entity) + end + + def render_choices? + false + end + + def process_choose(action) + entity = action.entity + hex_id = action.choice + + @game.remove_icon(entity, hex_id) end def process_place_token(action) @@ -24,7 +60,8 @@ def check_connected(entity, city, hex) end def tokener_available_hex(entity, hex) - @game.tokener_available_hex(entity, hex) && super + @game.icon_hexes(entity).include?(hex.id) || + (@game.tokener_available_hex(entity, hex) && super) end end end diff --git a/lib/engine/game/g_system18/step/track.rb b/lib/engine/game/g_system18/step/track.rb index 71dfa3ba86..e5b5c90817 100644 --- a/lib/engine/game/g_system18/step/track.rb +++ b/lib/engine/game/g_system18/step/track.rb @@ -15,6 +15,30 @@ def lay_tile(action, extra_cost: 0, entity: nil, spender: nil) super @game.post_lay_tile(action.entity, action.tile) end + + def get_tile_lay(entity) + action = super + @game.modify_tile_lay(entity, action) + end + + def lay_tile_action(action, entity: nil, spender: nil) + @game.pre_lay_tile_action(action, entity, get_tile_lay(action.entity)) + + super + end + + def round_state + super.merge( + { + last_old_tile: nil, + } + ) + end + + def setup + super + @round.last_old_tile = nil + end end end end diff --git a/public/logos/System18/PGS.svg b/public/logos/System18/PGS.svg new file mode 100644 index 0000000000..b4bc28fc75 --- /dev/null +++ b/public/logos/System18/PGS.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 1a20f5a717cf73839387ab4f44d75f614547e89e Mon Sep 17 00:00:00 2001 From: Steve Undy Date: Mon, 9 Sep 2024 22:21:07 -0600 Subject: [PATCH 13/49] Fix base map player count --- lib/engine/game/g_system18/map_base_customization.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/engine/game/g_system18/map_base_customization.rb b/lib/engine/game/g_system18/map_base_customization.rb index bb7c8eb8f4..b06048e83e 100644 --- a/lib/engine/game/g_system18/map_base_customization.rb +++ b/lib/engine/game/g_system18/map_base_customization.rb @@ -37,11 +37,11 @@ def map_base_game_corporations(corps) end def map_base_game_cash - { 2 => 800, 3 => 500, 4 => 400 } + { 2 => 800, 3 => 500, 4 => 400, 5 => 300 } end def map_base_game_cert_limit - { 2 => 20, 3 => 15, 4 => 10 } + { 2 => 20, 3 => 15, 4 => 10, 5 => 8 } end def map_base_game_capitalization From d7b083ab3a7169877a17008a8016a1b65c4730dd Mon Sep 17 00:00:00 2001 From: Steve Undy Date: Mon, 9 Sep 2024 22:28:39 -0600 Subject: [PATCH 14/49] Fixed typo (Trent) --- lib/engine/game/g_system18/map_britain_customization.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_system18/map_britain_customization.rb b/lib/engine/game/g_system18/map_britain_customization.rb index 5e190953a1..4643c7f2d7 100644 --- a/lib/engine/game/g_system18/map_britain_customization.rb +++ b/lib/engine/game/g_system18/map_britain_customization.rb @@ -63,7 +63,7 @@ def map_britain_game_location_names 'F9' => 'Leeds & Bradford', 'F11' => 'Hull', 'G2' => 'Wales Bonus', - 'G6' => 'Crewe & Stoke on Trend', + 'G6' => 'Crewe & Stoke on Trent', 'G8' => 'Derby', 'G12' => 'Mine Bonus', 'H3' => '(Wales)', From d56414d4596185f7ffb0cc4c12b2b3fbe22c2ff9 Mon Sep 17 00:00:00 2001 From: Oliver Burnett-Hall Date: Tue, 10 Sep 2024 22:50:54 +0100 Subject: [PATCH 15/49] [18Ardennes] Fix error on bankruptcy An extra parameter had been added to the G18Ardennes implementation of the `Game.declare_bankrupt` method, which was causing an error when the superclass method was called. This commit renames the 18Ardennes method to `bankrupt!` which calls `declare_bankrupt` with the correct number of parameters. Fixes tobymao#11203. --- lib/engine/game/g_18_ardennes/game.rb | 4 ++-- lib/engine/game/g_18_ardennes/step/bankrupt.rb | 2 +- .../game/g_18_ardennes/step/buy_sell_par_shares_companies.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/engine/game/g_18_ardennes/game.rb b/lib/engine/game/g_18_ardennes/game.rb index 57ceee3c26..c881781164 100644 --- a/lib/engine/game/g_18_ardennes/game.rb +++ b/lib/engine/game/g_18_ardennes/game.rb @@ -208,12 +208,12 @@ def can_go_bankrupt?(player, _corporation) bankrupt?(player) end - def declare_bankrupt(player, cash_target = @bank) + def bankrupt!(player, cash_target) @log << "-- #{player.name} goes bankrupt and sells remaining shares --" bankrupt_sell_shares(player) bankrupt_sell_companies(player) bankrupt_transfer_cash(player, cash_target) - super + declare_bankrupt(player) end # If a player has gone above 60% holding in a major (by exchanging diff --git a/lib/engine/game/g_18_ardennes/step/bankrupt.rb b/lib/engine/game/g_18_ardennes/step/bankrupt.rb index b99fcef979..0f8edf2a0c 100644 --- a/lib/engine/game/g_18_ardennes/step/bankrupt.rb +++ b/lib/engine/game/g_18_ardennes/step/bankrupt.rb @@ -13,7 +13,7 @@ class Bankrupt < Engine::Step::Bankrupt def process_bankrupt(action) corporation = action.entity player = corporation.player - @game.declare_bankrupt(player, corporation) + @game.bankrupt!(player, corporation) receivership_buy_train(corporation) end diff --git a/lib/engine/game/g_18_ardennes/step/buy_sell_par_shares_companies.rb b/lib/engine/game/g_18_ardennes/step/buy_sell_par_shares_companies.rb index 1de9c2556e..53a13e3d2c 100644 --- a/lib/engine/game/g_18_ardennes/step/buy_sell_par_shares_companies.rb +++ b/lib/engine/game/g_18_ardennes/step/buy_sell_par_shares_companies.rb @@ -193,7 +193,7 @@ def process_par(action) end def process_bankrupt(action) - @game.declare_bankrupt(action.entity) + @game.bankrupt!(action.entity, @game.bank) end def log_skip(entity) From 86bb42052131909620a40880c85c696d8ec368bd Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Thu, 12 Sep 2024 09:37:24 +0200 Subject: [PATCH 16/49] [18Uruguay] Fixes #11060 Company should get full cap after nationalization and no destination bonus --- lib/engine/game/g_18_uruguay/game.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/engine/game/g_18_uruguay/game.rb b/lib/engine/game/g_18_uruguay/game.rb index cecd5ef96f..ccd74991cb 100644 --- a/lib/engine/game/g_18_uruguay/game.rb +++ b/lib/engine/game/g_18_uruguay/game.rb @@ -339,6 +339,7 @@ def float_corporation(corporation) float_capitalization = nationalized? ? 10 : 5 amount = corporation.par_price.price * float_capitalization + abilities(corporation, :destination_bonus).use! if nationalized? @bank.spend(amount, corporation) @log << "#{corporation.name} receives #{format_currency(corporation.cash)}" take_loan(corporation, @loans[0]) if @loans.size.positive? && !nationalized? From 5f23060fad852c699ea456fee08614def1b8fb2c Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Thu, 12 Sep 2024 17:18:50 +0200 Subject: [PATCH 17/49] [18Uruguay] Do not ship more than ship capacity for rptla Fixes #11140 --- lib/engine/game/g_18_uruguay/game.rb | 14 ++++++++++++++ lib/engine/game/g_18_uruguay/nationalization.rb | 6 ++++-- lib/engine/game/g_18_uruguay/step/route_rptla.rb | 8 ++------ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/engine/game/g_18_uruguay/game.rb b/lib/engine/game/g_18_uruguay/game.rb index cecd5ef96f..660243e056 100644 --- a/lib/engine/game/g_18_uruguay/game.rb +++ b/lib/engine/game/g_18_uruguay/game.rb @@ -32,6 +32,16 @@ class Game < Game::Base include Goods include Nationalization + SHIP_CAPACITY = + { + 'Ship 1' => 1, + 'Ship 2' => 1, + 'Ship 3' => 2, + 'Ship 4' => 2, + 'Ship 5' => 3, + 'Ship 6' => 3, + }.freeze + EBUY_SELL_MORE_THAN_NEEDED = true EBUY_DEPOT_TRAIN_MUST_BE_CHEAPEST = false @@ -535,6 +545,10 @@ def operating_order super.reject { |c| c == @rptla }.append(@rptla) end + + def ship_capacity(train) + SHIP_CAPACITY[train.name.partition('+')[0]] + end end end end diff --git a/lib/engine/game/g_18_uruguay/nationalization.rb b/lib/engine/game/g_18_uruguay/nationalization.rb index a4b56e1e26..ad6caa663e 100644 --- a/lib/engine/game/g_18_uruguay/nationalization.rb +++ b/lib/engine/game/g_18_uruguay/nationalization.rb @@ -160,8 +160,10 @@ def start_merge(originatior) def nationalization_final_export! return if number_of_goods_at_harbor.zero? - @log << "Nationalization: Final Export #{number_of_goods_at_harbor} goods for #{format_currency(50)} each" - amount_per_share = 5 * number_of_goods_at_harbor + nr_goods = [number_of_goods_at_harbor, @rptla.trains.sum { |train| ship_capacity(train) }].min + @log << "Nationalization: Final Export #{nr_goods} good for #{format_currency(50)}" if nr_goods == 1 + @log << "Nationalization: Final Export #{nr_goods} goods for #{format_currency(50)} each" if nr_goods > 1 + amount_per_share = 5 * nr_goods players.each do |holder| amount = holder.num_shares_of(@rptla) * amount_per_share next unless amount.positive? diff --git a/lib/engine/game/g_18_uruguay/step/route_rptla.rb b/lib/engine/game/g_18_uruguay/step/route_rptla.rb index db0b619d80..0b274631dc 100644 --- a/lib/engine/game/g_18_uruguay/step/route_rptla.rb +++ b/lib/engine/game/g_18_uruguay/step/route_rptla.rb @@ -55,13 +55,9 @@ def choices choices end - def ship_capacity(train) - SHIP_CAPACITY[train.name.partition('+')[0]] - end - def total_ship_capacity?(entity) trains = @game.route_trains(entity) - trains.sum { |train| ship_capacity(train) } + trains.sum { |train| @game.ship_capacity(train) } end def process_choose(action) @@ -74,7 +70,7 @@ def process_choose(action) remaining = goods_to_deliver ships.each do |ship| ship.name = ship.name.partition('+')[0] unless ship.nil? - capacity = ship_capacity(ship) + capacity = @game.ship_capacity(ship) goods_count = [remaining, capacity].min remaining -= goods_count @game.add_goods_to_ship(ship, goods_count) From f22b30d419f4a870b79d23b37b87bb3897728767 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Thu, 12 Sep 2024 20:38:59 +0200 Subject: [PATCH 18/49] [18Uruguay] Allow more than 60% to cover for loans Fixes #11110 --- lib/engine/game/g_18_uruguay/step/buy_sell_par_shares.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/engine/game/g_18_uruguay/step/buy_sell_par_shares.rb b/lib/engine/game/g_18_uruguay/step/buy_sell_par_shares.rb index fdf32f3205..0683918ce4 100644 --- a/lib/engine/game/g_18_uruguay/step/buy_sell_par_shares.rb +++ b/lib/engine/game/g_18_uruguay/step/buy_sell_par_shares.rb @@ -9,9 +9,17 @@ module Step class BuySellParShares < Engine::Step::BuySellParShares def can_buy?(entity, bundle) return false if bundle&.corporation == @game.rptla && !@game.phase.status.include?('rptla_available') + return true if loan_limit(entity, bundle) super(entity, bundle) end + + def loan_limit(_entity, bundle) + return false if bundle.nil? + return false if bundle.owner.corporation? + + bundle.corporation.loans.size > @game.maximum_loans(bundle.corporation) + end end end end From c519f7ecb2e233b53e58a88f9df2c25d616578a1 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Thu, 12 Sep 2024 20:56:25 +0200 Subject: [PATCH 19/49] [18Uruguay] Attempt to solve the dividend_types bug Fixes #11141 Fixes #11194 --- lib/engine/game/g_18_uruguay/step/dividend.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/engine/game/g_18_uruguay/step/dividend.rb b/lib/engine/game/g_18_uruguay/step/dividend.rb index 1b31da150d..b007fbfc58 100644 --- a/lib/engine/game/g_18_uruguay/step/dividend.rb +++ b/lib/engine/game/g_18_uruguay/step/dividend.rb @@ -39,6 +39,7 @@ def description end def auto_actions(entity) + return [] unless entity.corporation? return [] unless @game.nationalized? return [] if entity.loans.empty? From 29d56809f383fe0baf1f1b4867428ba5b26fbf54 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Thu, 12 Sep 2024 22:02:39 +0200 Subject: [PATCH 20/49] [18Uruguay] Company shall only retreive destination_bonus in their own turn Fixes #11073 --- lib/engine/game/g_18_uruguay/game.rb | 1 + .../game/g_18_uruguay/step/destination_bonus.rb | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/engine/game/g_18_uruguay/game.rb b/lib/engine/game/g_18_uruguay/game.rb index cecd5ef96f..cbce649147 100644 --- a/lib/engine/game/g_18_uruguay/game.rb +++ b/lib/engine/game/g_18_uruguay/game.rb @@ -283,6 +283,7 @@ def after_buy_company(player, company, _price) def operating_round(round_num) Round::Operating.new(self, [ + G18Uruguay::Step::DestinationBonus, Engine::Step::Bankrupt, Engine::Step::Exchange, G18Uruguay::Step::Farm, diff --git a/lib/engine/game/g_18_uruguay/step/destination_bonus.rb b/lib/engine/game/g_18_uruguay/step/destination_bonus.rb index 584c32f54b..2098d26743 100644 --- a/lib/engine/game/g_18_uruguay/step/destination_bonus.rb +++ b/lib/engine/game/g_18_uruguay/step/destination_bonus.rb @@ -12,8 +12,10 @@ def description end def log_skip(entity) - return if entity.minor? + return unless entity.corporation? return if entity == @game.rptla + return if @game.abilities(entity, :destination_bonus).nil? + return if destination_node_check?(entity).nil? super end @@ -21,15 +23,16 @@ def log_skip(entity) def actions(entity) return [] if entity.minor? return [] if entity == @game.rptla + return [] if @game.abilities(entity, :destination_bonus).nil? self.class::ACTIONS end def auto_actions(entity) - corporations = @round.entities.select { |c| destination_node_check?(c) } - return [Engine::Action::Pass.new(entity)] if corporations.empty? + return [] unless entity.corporation? + return [Engine::Action::Pass.new(entity)] if destination_node_check?(entity).nil? - [Engine::Action::DestinationConnection.new(entity, corporations: corporations)] + [Engine::Action::DestinationConnection.new(entity, corporations: [entity])] end def destination_node_check?(corporation) From f015f349b3bf20d1d3db68b29c7e152d2d88ef14 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 13 Sep 2024 01:32:16 +0200 Subject: [PATCH 21/49] [18Norway] Adding support for extra_slot in tokener --- lib/engine/game/g_18_norway/steps/token.rb | 27 +--------------------- lib/engine/step/tokener.rb | 3 ++- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index 38ac0929d4..3e7d841fb1 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -40,7 +40,7 @@ def process_place_token(action) city_name = @game.harbor_city_coordinates(hex.id) + city_string raise GameError, "Cannot reach city #{city_name}" unless harbor_available?(entity, hex) - place_token(entity, action.city, action.token, connected: false) + place_token(entity, action.city, action.token, connected: false, extra_slot: true) else city_string = hex.tile.cities.size > 1 ? " city #{city.index}" : '' @@ -49,31 +49,6 @@ def process_place_token(action) @game.clear_graph pass! end - - def place_token(entity, city, token, connected: true, extra_action: false, - special_ability: nil, check_tokenable: true, spender: nil, same_hex_allowed: false) - hex = city.hex - - check_connected(entity, city, hex) if connected - raise GameError, 'Token already placed this turn' if !extra_action && @round.tokened - - raise GameError, 'Token is already used' if token.used - - free = !token.price.positive? - cheater = false - extra_slot = @game.harbor_hex?(hex) - city.place_token(entity, token, free: free, check_tokenable: check_tokenable, - cheater: cheater, extra_slot: extra_slot, spender: spender, - same_hex_allowed: same_hex_allowed) - pay_token_cost(spender || entity, token.price, city) - price_log = " for #{@game.format_currency(token.price)}" - - hex_description = hex.location_name ? "#{hex.name} (#{hex.location_name}) " : "#{hex.name} " - @log << "#{entity.name} places a token on #{hex_description}#{price_log}" - - @round.tokened = true - @game.clear_token_graph_for_entity(entity) - end end end end diff --git a/lib/engine/step/tokener.rb b/lib/engine/step/tokener.rb index b2280f03fb..4190f30379 100644 --- a/lib/engine/step/tokener.rb +++ b/lib/engine/step/tokener.rb @@ -44,7 +44,8 @@ def can_replace_token?(_entity, _token) end def place_token(entity, city, token, connected: true, extra_action: false, - special_ability: nil, check_tokenable: true, spender: nil, same_hex_allowed: false) + special_ability: nil, check_tokenable: true, spender: nil, same_hex_allowed: false, + extra_slot: false) hex = city.hex extra_action ||= special_ability.extra_action if %i[teleport token].include?(special_ability&.type) From fbe23516f7710c4588d5f91ea3ee2c85aba763bc Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 13 Sep 2024 02:19:15 +0200 Subject: [PATCH 22/49] [18Norway] Updated the harbor logic --- lib/engine/game/g_18_norway/game.rb | 2 +- lib/engine/game/g_18_norway/steps/token.rb | 37 +++++++++++----------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/lib/engine/game/g_18_norway/game.rb b/lib/engine/game/g_18_norway/game.rb index bb87b3c125..4e4d2e24c2 100644 --- a/lib/engine/game/g_18_norway/game.rb +++ b/lib/engine/game/g_18_norway/game.rb @@ -104,7 +104,7 @@ def nsb @nsb ||= corporation_by_id('NSB') end - def harbor_city_coordinates(hex_id) + def harbor_city_id_by_harbor_id(hex_id) CITY_HARBOR_MAP.key(hex_id) end diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index 3e7d841fb1..39e5aba22b 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -8,20 +8,22 @@ module G18Norway module Step class Token < Engine::Step::Token def available_hex(entity, hex) - return true if super + return true if super(entity, hex) - harbor_available?(entity, hex) + return true if check_available_harbour(entity, hex) + + false end - def tokener_available_hex(entity, hex) - return true if super + def check_available_harbour(entity, hex) + city = @game.harbor_city_id_by_harbor_id(hex.id) + return false if city.nil? - harbor_available?(entity, hex) - end + other_hex = @game.hex_by_id(city) + + return false unless @game.ferry_graph.reachable_hexes(entity)[other_hex] - def harbor_available?(entity, hex) - other_hex = @game.hex_by_id(@game.harbor_city_coordinates(hex.id)) - @game.graph.reachable_hexes(entity)[other_hex] || @game.ferry_graph.reachable_hexes(entity)[other_hex] + true end def can_place_token?(_entity) @@ -31,20 +33,17 @@ def can_place_token?(_entity) def process_place_token(action) entity = action.entity city = action.city - hex = city.hex - connected_city = @game.token_graph_for_entity(entity).connected_nodes(entity)[city] + connected_city = @game.loading || @game.token_graph_for_entity(entity).connected_nodes(entity)[city] if connected_city place_token(entity, action.city, action.token) - elsif @game.harbor_hex?(city.hex) - city_string = city.hex.tile.cities.size > 1 ? " city #{city.index}" : '' - city_name = @game.harbor_city_coordinates(hex.id) + city_string - raise GameError, "Cannot reach city #{city_name}" unless harbor_available?(entity, hex) - - place_token(entity, action.city, action.token, connected: false, extra_slot: true) + elsif @game.harbor_hex?(city.hex) && !connected_city + reachable_hexes = @game.ferry_graph.reachable_hexes(entity)[city.hex] + @game.abilities(entity, :token) do |ability| + place_token(entity, action.city, action.token, connected: false, special_ability: ability) + end else - city_string = hex.tile.cities.size > 1 ? " city #{city.index}" : '' - raise GameError, "Cannot place token on #{hex.name}#{city_string} because it is not connected" + raise GameError, "Cannot place token on #{city.hex.name} city is not connected" end @game.clear_graph pass! From bbdd5b77f1d5d6ad2d08fa566e01e2ca0f04155d Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 13 Sep 2024 02:26:57 +0200 Subject: [PATCH 23/49] Revert "[18Norway] Adding support for extra_slot in tokener" This reverts commit f015f349b3bf20d1d3db68b29c7e152d2d88ef14. --- lib/engine/step/tokener.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/engine/step/tokener.rb b/lib/engine/step/tokener.rb index 4190f30379..b2280f03fb 100644 --- a/lib/engine/step/tokener.rb +++ b/lib/engine/step/tokener.rb @@ -44,8 +44,7 @@ def can_replace_token?(_entity, _token) end def place_token(entity, city, token, connected: true, extra_action: false, - special_ability: nil, check_tokenable: true, spender: nil, same_hex_allowed: false, - extra_slot: false) + special_ability: nil, check_tokenable: true, spender: nil, same_hex_allowed: false) hex = city.hex extra_action ||= special_ability.extra_action if %i[teleport token].include?(special_ability&.type) From 0ab28fbfc565975f42a89d66b0b7fb42b9806bc8 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 13 Sep 2024 02:34:51 +0200 Subject: [PATCH 24/49] [18Norway] Renamed function according to previous comment --- lib/engine/game/g_18_norway/game.rb | 2 +- lib/engine/game/g_18_norway/steps/token.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/engine/game/g_18_norway/game.rb b/lib/engine/game/g_18_norway/game.rb index 4e4d2e24c2..bb87b3c125 100644 --- a/lib/engine/game/g_18_norway/game.rb +++ b/lib/engine/game/g_18_norway/game.rb @@ -104,7 +104,7 @@ def nsb @nsb ||= corporation_by_id('NSB') end - def harbor_city_id_by_harbor_id(hex_id) + def harbor_city_coordinates(hex_id) CITY_HARBOR_MAP.key(hex_id) end diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index 39e5aba22b..318488d3ba 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -16,7 +16,7 @@ def available_hex(entity, hex) end def check_available_harbour(entity, hex) - city = @game.harbor_city_id_by_harbor_id(hex.id) + city = @game.harbor_city_coordinates(hex.id) return false if city.nil? other_hex = @game.hex_by_id(city) From 7a105cfb6259eb6dbcd22ae0f696ba8e4eb4f0e6 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 13 Sep 2024 02:37:36 +0200 Subject: [PATCH 25/49] [18Norway] Removed extra check --- lib/engine/game/g_18_norway/steps/token.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index 318488d3ba..a6a983c6af 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -36,7 +36,7 @@ def process_place_token(action) connected_city = @game.loading || @game.token_graph_for_entity(entity).connected_nodes(entity)[city] if connected_city place_token(entity, action.city, action.token) - elsif @game.harbor_hex?(city.hex) && !connected_city + elsif @game.harbor_hex?(city.hex) reachable_hexes = @game.ferry_graph.reachable_hexes(entity)[city.hex] @game.abilities(entity, :token) do |ability| From 6239760360937116118c1e6923a46688acf5761e Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 13 Sep 2024 02:39:59 +0200 Subject: [PATCH 26/49] [18Norway] Clean up function names --- lib/engine/game/g_18_norway/steps/token.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index a6a983c6af..9ef97eadf5 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -10,12 +10,12 @@ class Token < Engine::Step::Token def available_hex(entity, hex) return true if super(entity, hex) - return true if check_available_harbour(entity, hex) + return true if harbor_available?(entity, hex) false end - def check_available_harbour(entity, hex) + def harbor_available?(entity, hex) city = @game.harbor_city_coordinates(hex.id) return false if city.nil? From c2828766bf2d2dd553dfabf9884acab96b9a0425 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 13 Sep 2024 10:24:34 +0200 Subject: [PATCH 27/49] [18Norway] Clean up merge mess --- lib/engine/game/g_18_norway/steps/token.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index 9ef97eadf5..b8464dc0c9 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -37,8 +37,6 @@ def process_place_token(action) if connected_city place_token(entity, action.city, action.token) elsif @game.harbor_hex?(city.hex) - reachable_hexes = @game.ferry_graph.reachable_hexes(entity)[city.hex] - @game.abilities(entity, :token) do |ability| place_token(entity, action.city, action.token, connected: false, special_ability: ability) end From 4753794a57cd7cc6896e37b9b2a2ddd315197252 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 13 Sep 2024 14:33:12 +0200 Subject: [PATCH 28/49] [18Norway] Updating harbor handling --- lib/engine/game/g_18_norway/steps/token.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index b8464dc0c9..eec875050a 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -21,9 +21,8 @@ def harbor_available?(entity, hex) other_hex = @game.hex_by_id(city) - return false unless @game.ferry_graph.reachable_hexes(entity)[other_hex] - - true + @game.ferry_graph.reachable_hexes(entity)[other_hex] || + @game.token_graph_for_entity(entity).reachable_hexes(entity)[other_hex] end def can_place_token?(_entity) @@ -33,12 +32,14 @@ def can_place_token?(_entity) def process_place_token(action) entity = action.entity city = action.city - connected_city = @game.loading || @game.token_graph_for_entity(entity).connected_nodes(entity)[city] + connected_city = @game.token_graph_for_entity(entity).connected_nodes(entity)[city] if connected_city place_token(entity, action.city, action.token) - elsif @game.harbor_hex?(city.hex) + elsif harbor_available?(entity, city.hex) @game.abilities(entity, :token) do |ability| place_token(entity, action.city, action.token, connected: false, special_ability: ability) + entity.add_ability(Ability::Token.new(type: 'token', hexes: Engine::Game::G18Norway::Game::HARBOR_HEXES, + from_owner: true, discount: 0, connected: true)) end else raise GameError, "Cannot place token on #{city.hex.name} city is not connected" From d4869428ce2da9d2b9b27f3991fda7cccbe0b16f Mon Sep 17 00:00:00 2001 From: Benjamin Scott Date: Fri, 13 Sep 2024 11:42:43 -0700 Subject: [PATCH 29/49] [18Hiawatha] Fix rules link Looks like the prior link was deprecated and this is the canonical one --- lib/engine/game/g_18_hiawatha/meta.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_18_hiawatha/meta.rb b/lib/engine/game/g_18_hiawatha/meta.rb index 4337c447af..3b4dd5e159 100644 --- a/lib/engine/game/g_18_hiawatha/meta.rb +++ b/lib/engine/game/g_18_hiawatha/meta.rb @@ -15,7 +15,7 @@ module Meta GAME_INFO_URL = 'https://github.com/tobymao/18xx/wiki/18Hiawatha' GAME_LOCATION = 'Midwest USA' GAME_TITLE = '18 Hiawatha' - GAME_RULES_URL = 'https://boardgamegeek.com/filepage/279555/18-hiawatha-official-game-rules' + GAME_RULES_URL = 'https://boardgamegeek.com/filepage/279677/18-hiawatha-rules-from-mainline-issue-1' PLAYER_RANGE = [3, 6].freeze From 1a70ceef89b912bf286548f8cd2dc191545b6190 Mon Sep 17 00:00:00 2001 From: Steve Undy Date: Sat, 14 Sep 2024 16:48:26 -0600 Subject: [PATCH 30/49] Map fixes; rules changes; code improvements --- lib/engine/game/g_system18/game.rb | 10 ++- .../g_system18/map_britain_customization.rb | 74 ++++++++++--------- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/lib/engine/game/g_system18/game.rb b/lib/engine/game/g_system18/game.rb index 82706c0aac..56cc47b759 100644 --- a/lib/engine/game/g_system18/game.rb +++ b/lib/engine/game/g_system18/game.rb @@ -366,8 +366,8 @@ def game_phases phases = send("map_#{map_name}_post_game_phases", phases) else # change last phase based on train roster - phases[-1][:name] = game_trains.last[:name] - phases[-1][:on] = game_trains.last[:name] + phases.last[:name] = game_trains.last[:name] + phases.last[:on] = game_trains.last[:name] end phases @@ -674,6 +674,12 @@ def pre_lay_tile_action(action, entity, tile_lay) send("map_#{map_name}_pre_lay_tile_action", action, entity, tile_lay) end + + def place_home_token(corporation) + return unless respond_to?("map_#{map_name}_place_home_token") + + send("map_#{map_name}_place_home_token", corporation) + end end end end diff --git a/lib/engine/game/g_system18/map_britain_customization.rb b/lib/engine/game/g_system18/map_britain_customization.rb index 4643c7f2d7..7c4d6ce32a 100644 --- a/lib/engine/game/g_system18/map_britain_customization.rb +++ b/lib/engine/game/g_system18/map_britain_customization.rb @@ -12,10 +12,10 @@ module MapBritainCustomization BRITAIN_MINE_HEXES = %w[B7 D9 G10 I4].freeze BRITAIN_LONDON_HEX = 'K10' - BRITAIN_SCOTLAND_BONUS = [10, 20, 20, 30, 30, 30, 40].freeze - BRITAIN_WALES_BONUS = [10, 20, 20, 20, 20, 20, 30].freeze - BRITAIN_LONDON_BONUS = [10, 20, 20, 30, 30, 30, 40].freeze - BRITAIN_MINE_BONUS = [10, 20, 20, 30, 30, 30, 40].freeze + BRITAIN_SCOTLAND_BONUS_VAL_HEX = 'A4' + BRITAIN_WALES_BONUS_VAL_HEX = 'G2' + BRITAIN_LONDON_BONUS_VAL_HEX = 'L11' + BRITAIN_MINE_BONUS_VAL_HEX = 'G12' # rubocop:disable Layout/LineLength def map_britain_game_tiles(tiles) @@ -81,7 +81,7 @@ def map_britain_game_location_names 'K2' => 'Plymouth', 'K8' => 'Reading & Guildford', 'K10' => 'London', - 'L5' => 'Southhampton', + 'L5' => 'Southampton', 'L7' => 'Portsmouth', 'L11' => 'London Port Bonus', } @@ -109,7 +109,7 @@ def map_britain_game_hexes }, green: { %w[F7] => 'city=revenue:40,pos:0;city=revenue:40,pos:2;path=a:0,b:_0;path=a:2,b:_1;label=OO', - %w[H7] => 'city=revenue:40,pos:3;city=revenue:40,pos:5;path=a:5,b:_1;path=a:5,b:_1;label=OO', + %w[H7] => 'city=revenue:40,pos:3;city=revenue:40,pos:5;path=a:3,b:_1;path=a:5,b:_0;label=OO', }, yellow: { %w[A6] => 'city=revenue:30;path=a:0,b:_0;path=a:2,b:_0;path=a:4,b:_0;label=B', @@ -131,11 +131,11 @@ def map_britain_game_hexes %w[E6 E10] => 'city=revenue:0', %w[E8] => 'upgrade=cost:80,terrain:mountain', %w[E12] => 'upgrade=cost:40,terrain:mountain', - %w[F11] => 'upgrade=cost:20,terrain:water', + %w[F11] => 'city=revenue:0;upgrade=cost:20,terrain:water', %w[G4] => 'upgrade=cost:40,terrain:mountain;border=type:province,color:brown,edge:4;border=type:province,color:brown,edge:5', %w[G6] => 'town=revenue:0;town=revenue:0;border=type:province,color:brown,edge:1', %w[G8] => 'city=revenue:0', - %w[G10] => 'upgrade=cost:40,terrain:water;icon=image:mine,sticky:1', + %w[G10] => 'upgrade=cost:20,terrain:water;icon=image:mine,sticky:1', %w[H3] => 'upgrade=cost:40,terrain:mountain;border=type:province,color:brown,edge:4', %w[H5] => 'border=type:province,color:brown,edge:0;border=type:province,color:brown,edge:1;border=type:province,color:brown,edge:2', %w[H9] => 'city=revenue:0', @@ -227,8 +227,8 @@ def map_britain_game_corporations(corps) corps.each_with_index do |c, idx| c[:float_percent] = 20 c[:always_market_price] = true - c[:tokens].append(100) - c[:coordinates] = %w[F7 J5 F9 A6 I12 A8][idx] + c[:tokens] = [40, 100, 100, 100] + c[:coordinates] = [%w[F7 I8], 'J5', %w[F9 G8], 'A6', 'I12', 'A8'][idx] c[:city] = [1, nil, 1, nil, nil, nil][idx] end corps @@ -286,17 +286,12 @@ def map_britain_constants end def map_britain_setup - # add 2 DRG and PHX tokens each to map - dragon = corporations.find { |c| c.name == 'DGN' } - hexes.find { |h| h.id == 'F7' }.tile.cities[1].place_token(dragon, dragon.tokens[0], free: true) - hexes.find { |h| h.id == 'I8' }.tile.cities[0].place_token(dragon, dragon.tokens[1], free: true) - - phoenix = corporations.find { |c| c.name == 'PHX' } - hexes.find { |h| h.id == 'F9' }.tile.cities[1].place_token(phoenix, phoenix.tokens[0], free: true) - hexes.find { |h| h.id == 'G8' }.tile.cities[0].place_token(phoenix, phoenix.tokens[1], free: true) - @britain_mines = BRITAIN_MINE_HEXES.map { |h| hex_by_id(h) } @britain_corps_with_mines = {} + @scotland_bonus_val = hex_by_id(BRITAIN_SCOTLAND_BONUS_VAL_HEX).tile.offboards.first + @wales_bonus_val = hex_by_id(BRITAIN_WALES_BONUS_VAL_HEX).tile.offboards.first + @london_bonus_val = hex_by_id(BRITAIN_LONDON_BONUS_VAL_HEX).tile.offboards.first + @mine_bonus_val = hex_by_id(BRITAIN_MINE_BONUS_VAL_HEX).tile.offboards.first end def map_britain_company_header(_company) @@ -406,14 +401,14 @@ def map_britain_bonuses(route, stops) bonus = { revenue: 0 } return bonus unless train - desc = '' + desc = [] # mines (no 4D doubling), only on one run corp = train.owner lowest_train = route.routes.reject { |r| r.stops.empty? }.min_by { |r| corp.trains.index(r.train) }.train if (train == lowest_train) && corp && @britain_corps_with_mines[corp.name] - bonus[:revenue] += BRITAIN_MINE_BONUS[@phase.name.to_i - 2] - desc = 'Mine' + bonus[:revenue] += @mine_bonus_val.route_revenue(@phase, train) + desc << 'Mine' end # Scotland (doubles with 4D) @@ -422,30 +417,27 @@ def map_britain_bonuses(route, stops) end non_scotland_city = stops.find { |stop| !BRITAIN_REGION_HEXES['Scotland'].include?(stop.hex.id) && stop.city? } if scotland_city && non_scotland_city - bonus[:revenue] += BRITAIN_SCOTLAND_BONUS[@phase.name.to_i - 2] * (train.name == '4D' ? 2 : 1) - desc += ', ' unless desc == '' - desc += 'Scotland' + bonus[:revenue] += @scotland_bonus_val.route_revenue(@phase, train) * (train.name == '4D' ? 2 : 1) + desc << 'Scotland' end # Wales (doubles with 4D) wales_city = stops.find { |stop| BRITAIN_REGION_HEXES['Wales'].include?(stop.hex.id) && (stop.city? || stop.offboard?) } non_wales_city = stops.find { |stop| !BRITAIN_REGION_HEXES['Wales'].include?(stop.hex.id) && stop.city? } if wales_city && non_wales_city - bonus[:revenue] += BRITAIN_WALES_BONUS[@phase.name.to_i - 2] * (train.name == '4D' ? 2 : 1) - desc += ', ' unless desc == '' - desc += 'Wales' + bonus[:revenue] += @wales_bonus_val.route_revenue(@phase, train) * (train.name == '4D' ? 2 : 1) + desc << 'Wales' end # London-Port (doubles with 4D) london = stops.find { |stop| stop.hex.id == BRITAIN_LONDON_HEX } port = stops.find { |stop| stop.tile.icons.any? { |i| i.name == 'port' } } if london && port - bonus[:revenue] += BRITAIN_LONDON_BONUS[@phase.name.to_i - 2] * (train.name == '4D' ? 2 : 1) - desc += ', ' unless desc == '' - desc += 'London-Port' + bonus[:revenue] += @london_bonus_val.route_revenue(@phase, train) * (train.name == '4D' ? 2 : 1) + desc << 'London-Port' end - bonus[:description] = "(#{desc})" unless desc == '' + bonus[:description] = "(#{desc.join(', ')})" unless desc == '' bonus end @@ -459,8 +451,7 @@ def map_britain_modify_tile_lay(_entity, action) return unless action if action[:lay_replaced] && @round.upgraded_track && - (@round.laid_hexes.first.tile.color == :green) && - @round.laid_hexes.first.tile.cities.empty? + (@round.laid_hexes.first.tile.color == :green) action[:lay_replaced] = true action[:lay] = true else @@ -484,6 +475,21 @@ def map_britain_pre_lay_tile_action(action, _entity, tile_lay) @round.last_old_tile = old_tile end + def map_britain_place_home_token(corporation) + return if corporation.tokens.first&.used + + Array(corporation.coordinates).each do |coord| + hex = hex_by_id(coord) + tile = hex&.tile + cities = tile.cities + city = cities.find { |c| c.reserved_by?(corporation) } || cities.first + token = corporation.find_token_by_type + + @log << "#{corporation.name} places a token on #{hex.name}" + city.place_token(corporation, token) + end + end + # FIXME: add reopen! method to Engine::Company # # open company associated with closed corporation From a16f8fb5bd242d9fec54b420cd5929ae3f2c078e Mon Sep 17 00:00:00 2001 From: Phil Campeau Date: Mon, 16 Sep 2024 11:25:37 -0400 Subject: [PATCH 31/49] [1822nrs] fixes spelling of shrewsbury --- lib/engine/game/g_1822_nrs/map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_1822_nrs/map.rb b/lib/engine/game/g_1822_nrs/map.rb index 17f01cbfdd..5473aa0890 100644 --- a/lib/engine/game/g_1822_nrs/map.rb +++ b/lib/engine/game/g_1822_nrs/map.rb @@ -145,7 +145,7 @@ module Map 'G20' => 'Blackpool', 'G22' => 'Liverpool', 'G24' => 'Chester', - 'G28' => 'Shrewbury', + 'G28' => 'Shrewsbury', 'H1' => 'Aberdeen', 'H3' => 'Dunfermline', 'H5' => 'Edinburgh', From 7b397d361187e0d4fe3c74d31794242b70a2149a Mon Sep 17 00:00:00 2001 From: patrikolesen Date: Tue, 17 Sep 2024 18:19:16 +0200 Subject: [PATCH 32/49] Update lib/engine/game/g_18_norway/steps/token.rb Co-authored-by: Oliver Burnett-Hall --- lib/engine/game/g_18_norway/steps/token.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index eec875050a..a589ba17ee 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -16,10 +16,9 @@ def available_hex(entity, hex) end def harbor_available?(entity, hex) - city = @game.harbor_city_coordinates(hex.id) - return false if city.nil? + other_hex = @game.hex_by_id(@game.harbor_city_coordinates(hex.id)) + return false unless other_hex - other_hex = @game.hex_by_id(city) @game.ferry_graph.reachable_hexes(entity)[other_hex] || @game.token_graph_for_entity(entity).reachable_hexes(entity)[other_hex] From 22ac270959f7bbb53e682e831f60bc307f25bfd4 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Tue, 17 Sep 2024 18:29:57 +0200 Subject: [PATCH 33/49] [18Norway] Rubocop --- lib/engine/game/g_18_norway/steps/token.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index a589ba17ee..e0e9dd2ff1 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -19,7 +19,6 @@ def harbor_available?(entity, hex) other_hex = @game.hex_by_id(@game.harbor_city_coordinates(hex.id)) return false unless other_hex - @game.ferry_graph.reachable_hexes(entity)[other_hex] || @game.token_graph_for_entity(entity).reachable_hexes(entity)[other_hex] end From 9de431b515895b0bf63ba0c86c1b434d77ea4cf6 Mon Sep 17 00:00:00 2001 From: Steve Undy Date: Sat, 21 Sep 2024 18:48:25 -0600 Subject: [PATCH 34/49] more fixes --- lib/engine/game/g_system18/game.rb | 12 +++++++ .../g_system18/map_britain_customization.rb | 32 +++++++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lib/engine/game/g_system18/game.rb b/lib/engine/game/g_system18/game.rb index 56cc47b759..4f817d9e44 100644 --- a/lib/engine/game/g_system18/game.rb +++ b/lib/engine/game/g_system18/game.rb @@ -623,6 +623,18 @@ def revenue_str(route) revenue_str + send("map_#{map_name}_extra_revenue_str", route) end + def extra_revenue(entity, routes) + return unless respond_to?("map_#{map_name}_extra_revenue") + + send("map_#{map_name}_extra_revenue", entity, routes) + end + + def submit_revenue_str(routes, show_subsidy) + return unless respond_to?("map_#{map_name}_submit_revenue_str") + + send("map_#{map_name}_submit_revenue_str", routes, show_subsidy) + end + def timeline return super unless respond_to?("map_#{map_name}_timeline") diff --git a/lib/engine/game/g_system18/map_britain_customization.rb b/lib/engine/game/g_system18/map_britain_customization.rb index 7c4d6ce32a..f7d89f9d7a 100644 --- a/lib/engine/game/g_system18/map_britain_customization.rb +++ b/lib/engine/game/g_system18/map_britain_customization.rb @@ -403,14 +403,6 @@ def map_britain_bonuses(route, stops) desc = [] - # mines (no 4D doubling), only on one run - corp = train.owner - lowest_train = route.routes.reject { |r| r.stops.empty? }.min_by { |r| corp.trains.index(r.train) }.train - if (train == lowest_train) && corp && @britain_corps_with_mines[corp.name] - bonus[:revenue] += @mine_bonus_val.route_revenue(@phase, train) - desc << 'Mine' - end - # Scotland (doubles with 4D) scotland_city = stops.find do |stop| BRITAIN_REGION_HEXES['Scotland'].include?(stop.hex.id) && (stop.city? || stop.offboard?) @@ -437,10 +429,32 @@ def map_britain_bonuses(route, stops) desc << 'London-Port' end - bonus[:description] = "(#{desc.join(', ')})" unless desc == '' + bonus[:description] = "(#{desc.join(', ')})" unless desc.empty? bonus end + def map_britain_mine_bonus(routes) + valid_route = routes.find { |r| !r.stops.empty? } + train = valid_route&.train + if train && @britain_corps_with_mines[train.owner.name] + @mine_bonus_val.route_revenue(@phase, train) + else + 0 + end + end + + def map_britain_extra_revenue(_entity, routes) + map_britain_mine_bonus(routes) + end + + def map_britain_submit_revenue_str(routes, _show_subsidy) + train_revenue = routes_revenue(routes) + mine_revenue = map_britain_mine_bonus(routes) + return format_revenue_currency(train_revenue) if mine_revenue.zero? + + "#{format_revenue_currency(train_revenue)} + #{format_revenue_currency(mine_revenue)} mine bonus" + end + def map_britain_status_str(corporation) return unless @britain_corps_with_mines[corporation.name] From ee1ae10c0a75aff72e1cb1825176e67ebe4a29cd Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 27 Sep 2024 04:10:39 +0200 Subject: [PATCH 35/49] [18Norway] Simplify available_hex --- lib/engine/game/g_18_norway/steps/token.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/engine/game/g_18_norway/steps/token.rb b/lib/engine/game/g_18_norway/steps/token.rb index e0e9dd2ff1..bceeafeeed 100644 --- a/lib/engine/game/g_18_norway/steps/token.rb +++ b/lib/engine/game/g_18_norway/steps/token.rb @@ -8,11 +8,7 @@ module G18Norway module Step class Token < Engine::Step::Token def available_hex(entity, hex) - return true if super(entity, hex) - - return true if harbor_available?(entity, hex) - - false + super(entity, hex) || harbor_available?(entity, hex) end def harbor_available?(entity, hex) From 5422b7e24461e0d6f6ac8db2a4e7bb3f3608768c Mon Sep 17 00:00:00 2001 From: patrikolesen Date: Fri, 27 Sep 2024 07:25:56 +0200 Subject: [PATCH 36/49] Update lib/engine/game/g_18_uruguay/step/destination_bonus.rb Co-authored-by: Oliver Burnett-Hall --- lib/engine/game/g_18_uruguay/step/destination_bonus.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/engine/game/g_18_uruguay/step/destination_bonus.rb b/lib/engine/game/g_18_uruguay/step/destination_bonus.rb index 2098d26743..cb74452313 100644 --- a/lib/engine/game/g_18_uruguay/step/destination_bonus.rb +++ b/lib/engine/game/g_18_uruguay/step/destination_bonus.rb @@ -14,8 +14,7 @@ def description def log_skip(entity) return unless entity.corporation? return if entity == @game.rptla - return if @game.abilities(entity, :destination_bonus).nil? - return if destination_node_check?(entity).nil? + return unless destination_node_check?(entity) super end From b4f7903b2766990e85f22b607abfab5ae8ef8196 Mon Sep 17 00:00:00 2001 From: patrikolesen Date: Fri, 27 Sep 2024 07:26:15 +0200 Subject: [PATCH 37/49] Update lib/engine/game/g_18_uruguay/step/destination_bonus.rb Co-authored-by: Oliver Burnett-Hall --- lib/engine/game/g_18_uruguay/step/destination_bonus.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_18_uruguay/step/destination_bonus.rb b/lib/engine/game/g_18_uruguay/step/destination_bonus.rb index cb74452313..c803f5dddc 100644 --- a/lib/engine/game/g_18_uruguay/step/destination_bonus.rb +++ b/lib/engine/game/g_18_uruguay/step/destination_bonus.rb @@ -29,7 +29,7 @@ def actions(entity) def auto_actions(entity) return [] unless entity.corporation? - return [Engine::Action::Pass.new(entity)] if destination_node_check?(entity).nil? + return [Engine::Action::Pass.new(entity)] unless destination_node_check?(entity) [Engine::Action::DestinationConnection.new(entity, corporations: [entity])] end From 6a61a6ee9805825acf19d7c56940b059b8edd54c Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 27 Sep 2024 07:31:09 +0200 Subject: [PATCH 38/49] [18Uruguay] Only return boolean from destination_node_check? --- lib/engine/game/g_18_uruguay/step/destination_bonus.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/engine/game/g_18_uruguay/step/destination_bonus.rb b/lib/engine/game/g_18_uruguay/step/destination_bonus.rb index c803f5dddc..b7849e80ce 100644 --- a/lib/engine/game/g_18_uruguay/step/destination_bonus.rb +++ b/lib/engine/game/g_18_uruguay/step/destination_bonus.rb @@ -35,11 +35,11 @@ def auto_actions(entity) end def destination_node_check?(corporation) - return if corporation.closed? - return if corporation.destination_coordinates.nil? + return false if corporation.closed? + return false if corporation.destination_coordinates.nil? ability = @game.abilities(corporation, :destination_bonus) - return if ability.nil? + return false if ability.nil? destination_hex = @game.hex_by_id(corporation.destination_coordinates) home_node = corporation.tokens.first.city From bc9fb503e3ccaff0f36ab297c89d257caf45be5d Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 27 Sep 2024 08:04:32 +0200 Subject: [PATCH 39/49] [18Uruguay] Remove unused and unessecery code --- lib/engine/game/g_18_uruguay/game.rb | 5 ++++- lib/engine/game/g_18_uruguay/step/route_rptla.rb | 10 ---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/engine/game/g_18_uruguay/game.rb b/lib/engine/game/g_18_uruguay/game.rb index 660243e056..6db5d4568c 100644 --- a/lib/engine/game/g_18_uruguay/game.rb +++ b/lib/engine/game/g_18_uruguay/game.rb @@ -547,7 +547,10 @@ def operating_order end def ship_capacity(train) - SHIP_CAPACITY[train.name.partition('+')[0]] + val = SHIP_CAPACITY[train.name] + return 0 if val.nil? + + val end end end diff --git a/lib/engine/game/g_18_uruguay/step/route_rptla.rb b/lib/engine/game/g_18_uruguay/step/route_rptla.rb index 0b274631dc..e0a1b5fdda 100644 --- a/lib/engine/game/g_18_uruguay/step/route_rptla.rb +++ b/lib/engine/game/g_18_uruguay/step/route_rptla.rb @@ -7,16 +7,6 @@ module Game module G18Uruguay module Step class RouteRptla < Engine::Step::Route - SHIP_CAPACITY = - { - 'Ship 1' => 1, - 'Ship 2' => 1, - 'Ship 3' => 2, - 'Ship 4' => 2, - 'Ship 5' => 3, - 'Ship 6' => 3, - }.freeze - def description 'Ship to England' end From 721fc9cf2089979ce3e3b16c767b77b853687435 Mon Sep 17 00:00:00 2001 From: Patrik Olesen Date: Fri, 27 Sep 2024 08:14:54 +0200 Subject: [PATCH 40/49] [18Uruguay] changed can_buy? to can_gain? and renamed function --- lib/engine/game/g_18_uruguay/step/buy_sell_par_shares.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/engine/game/g_18_uruguay/step/buy_sell_par_shares.rb b/lib/engine/game/g_18_uruguay/step/buy_sell_par_shares.rb index 0683918ce4..e3eb27f06f 100644 --- a/lib/engine/game/g_18_uruguay/step/buy_sell_par_shares.rb +++ b/lib/engine/game/g_18_uruguay/step/buy_sell_par_shares.rb @@ -7,14 +7,14 @@ module Game module G18Uruguay module Step class BuySellParShares < Engine::Step::BuySellParShares - def can_buy?(entity, bundle) + def can_gain?(entity, bundle, exchange: false) return false if bundle&.corporation == @game.rptla && !@game.phase.status.include?('rptla_available') - return true if loan_limit(entity, bundle) + return true if excess_loans?(entity, bundle) - super(entity, bundle) + super end - def loan_limit(_entity, bundle) + def excess_loans?(_entity, bundle) return false if bundle.nil? return false if bundle.owner.corporation? From 268bda89d06b0f7f294da924c0901235bbcd3f11 Mon Sep 17 00:00:00 2001 From: BenA Date: Mon, 30 Sep 2024 11:16:59 +0300 Subject: [PATCH 41/49] remove broken @game from game.rb --- lib/engine/game/g_18_esp/game.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_18_esp/game.rb b/lib/engine/game/g_18_esp/game.rb index 0f5e706b6e..8d650c2504 100644 --- a/lib/engine/game/g_18_esp/game.rb +++ b/lib/engine/game/g_18_esp/game.rb @@ -359,7 +359,7 @@ def mza end def madrid_hex - @madrid_hex ||= @game.hex_by_id(MADRID_HEX) + @madrid_hex ||= hex_by_id(MADRID_HEX) end def setup From a75ddb75e3aa5f5b8168f6c3294a848186a731e8 Mon Sep 17 00:00:00 2001 From: Phil Campeau Date: Wed, 2 Oct 2024 12:34:46 -0400 Subject: [PATCH 42/49] [18Hiawatha] move to beta --- lib/engine/game/g_18_hiawatha/meta.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_18_hiawatha/meta.rb b/lib/engine/game/g_18_hiawatha/meta.rb index 4337c447af..db6d156ac4 100644 --- a/lib/engine/game/g_18_hiawatha/meta.rb +++ b/lib/engine/game/g_18_hiawatha/meta.rb @@ -8,7 +8,7 @@ module G18Hiawatha module Meta include Game::Meta - DEV_STAGE = :alpha + DEV_STAGE = :beta DEPENDS_ON = '1817' GAME_DESIGNER = 'Michael Carter, Anthony Fryer, & Nick Neylon' From f1e4329e9195d08fb79478118f1a29be23a87ee8 Mon Sep 17 00:00:00 2001 From: Phil Campeau Date: Wed, 2 Oct 2024 12:37:34 -0400 Subject: [PATCH 43/49] [1850jr] move to beta --- lib/engine/game/g_1850_jr/meta.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_1850_jr/meta.rb b/lib/engine/game/g_1850_jr/meta.rb index 77657bfad5..2fd2ab955a 100644 --- a/lib/engine/game/g_1850_jr/meta.rb +++ b/lib/engine/game/g_1850_jr/meta.rb @@ -8,7 +8,7 @@ module G1850Jr module Meta include Game::Meta - DEV_STAGE = :alpha + DEV_STAGE = :beta GAME_DESIGNER = 'Gabriele Callari, Fabio Pellegrino' GAME_LOCATION = 'Sicily' From 7c1187e40f0c604ea64893fec048a3023fb2648f Mon Sep 17 00:00:00 2001 From: Arseniy Banayev Date: Sat, 5 Oct 2024 13:05:48 -0400 Subject: [PATCH 44/49] Fix spelling of Yaroslavl in 1861. --- lib/engine/game/g_1861/map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/engine/game/g_1861/map.rb b/lib/engine/game/g_1861/map.rb index 46b4bffd17..9323ac9953 100644 --- a/lib/engine/game/g_1861/map.rb +++ b/lib/engine/game/g_1861/map.rb @@ -138,7 +138,7 @@ module Map 'H8' => 'Moscow', 'H10' => 'Tula', 'H18' => 'Yuzovka', - 'I5' => 'Yaroslav', + 'I5' => 'Yaroslavl', 'I13' => 'Voronezh', 'I17' => 'Lugansk', 'I19' => 'Rostov', From 29b5c77e532904a42289dccb6c0e3ec68f94d8b3 Mon Sep 17 00:00:00 2001 From: Chris Rericha Date: Tue, 10 Sep 2024 01:50:08 -0400 Subject: [PATCH 45/49] [Core] Add subsidies to dividend step --- assets/app/view/game/dividend.rb | 2 +- lib/engine/action/run_routes.rb | 7 +++++-- lib/engine/step/dividend.rb | 12 +++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/assets/app/view/game/dividend.rb b/assets/app/view/game/dividend.rb index 03adb31595..eaee593722 100644 --- a/assets/app/view/game/dividend.rb +++ b/assets/app/view/game/dividend.rb @@ -35,7 +35,7 @@ def render @step.respond_to?(:dividend_name) ? @step.dividend_name(type) : type end - corp_income = option[:corporation] + option[:divs_to_corporation] + corp_income = option[:corporation] + option[:divs_to_corporation] + @step.total_subsidy direction = if (new_share_price = entity.share_price) && option[:share_direction] diff --git a/lib/engine/action/run_routes.rb b/lib/engine/action/run_routes.rb index b7d7fcc268..e4bb9a5d36 100644 --- a/lib/engine/action/run_routes.rb +++ b/lib/engine/action/run_routes.rb @@ -6,12 +6,13 @@ module Engine module Action class RunRoutes < Base - attr_reader :routes, :extra_revenue + attr_reader :routes, :extra_revenue, :subsidy - def initialize(entity, routes:, extra_revenue: 0) + def initialize(entity, routes:, extra_revenue: 0, subsidy: 0) super(entity) @routes = routes @extra_revenue = extra_revenue + @subsidy = subsidy end def self.h_to_args(h, game) @@ -41,6 +42,7 @@ def self.h_to_args(h, game) { routes: routes, extra_revenue: h['extra_revenue'], + subsidy: h['subsidy'], } end @@ -62,6 +64,7 @@ def args_to_h { 'routes' => routes, 'extra_revenue' => extra_revenue, + 'subsidy' => subsidy, } end end diff --git a/lib/engine/step/dividend.rb b/lib/engine/step/dividend.rb index 42a55aa8b5..215afe2581 100644 --- a/lib/engine/step/dividend.rb +++ b/lib/engine/step/dividend.rb @@ -67,6 +67,7 @@ def variable_max def process_dividend(action) entity = action.entity revenue = total_revenue + subsidy = total_subsidy kind = action.kind.to_sym payout = dividend_options(entity)[kind] @@ -85,9 +86,9 @@ def process_dividend(action) @round.routes = [] @round.extra_revenue = 0 - log_run_payout(entity, kind, revenue, action, payout) + log_run_payout(entity, kind, revenue, subsidy, action, payout) - payout_corporation(payout[:corporation], entity) + payout_corporation(payout[:corporation] + subsidy, entity) payout_shares(entity, revenue - payout[:corporation]) if payout[:per_share].positive? @@ -100,7 +101,7 @@ def payout_corporation(amount, entity) @game.bank.spend(amount, entity) if amount.positive? end - def log_run_payout(entity, kind, revenue, action, payout) + def log_run_payout(entity, kind, revenue, subsidy, action, payout) unless Dividend::DIVIDEND_TYPES.include?(kind) @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays #{action.kind}" end @@ -110,6 +111,7 @@ def log_run_payout(entity, kind, revenue, action, payout) elsif payout[:per_share].zero? @log << "#{entity.name} does not run" end + @log << "#{entity.name} earns subsidy of #{@game.format_currency(subsidy)}" if subsidy.positive? end def share_price_change(_entity, revenue) @@ -217,6 +219,10 @@ def total_revenue @game.routes_revenue(routes) + extra_revenue end + def total_subsidy + @game.routes_subsidy(routes) + end + def rust_obsolete_trains!(entity, log: true) rusted_trains = entity.trains.select(&:obsolete).each do |train| @game.rust(train) From 7de6e43291e23d25a9bd5b742d0bfb4779d557b7 Mon Sep 17 00:00:00 2001 From: Chris Rericha Date: Mon, 16 Sep 2024 02:09:06 -0400 Subject: [PATCH 46/49] Update games to support dividend subsidy logic in core --- lib/engine/game/base.rb | 10 +-- lib/engine/game/g_1822/step/dividend.rb | 24 ++++--- lib/engine/game/g_1825/step/dividend.rb | 2 +- lib/engine/game/g_1840/step/dividend.rb | 2 +- lib/engine/game/g_1856/step/dividend.rb | 40 ++---------- lib/engine/game/g_1860/step/dividend.rb | 49 ++------------ lib/engine/game/g_1862/step/dividend.rb | 23 +++---- lib/engine/game/g_1866/step/dividend.rb | 55 ++-------------- lib/engine/game/g_1868_wy/step/dividend.rb | 2 +- .../game/g_1870/step/connection_dividend.rb | 2 +- lib/engine/game/g_1873/step/dividend.rb | 4 +- lib/engine/game/g_1888/step/dividend.rb | 58 ++--------------- lib/engine/game/g_18_esp/step/dividend.rb | 59 ++--------------- lib/engine/game/g_18_mag/game.rb | 2 +- lib/engine/game/g_18_mag/step/dividend.rb | 9 +-- lib/engine/game/g_18_tn/step/dividend.rb | 2 +- lib/engine/game/g_18_uruguay/step/dividend.rb | 20 +++--- lib/engine/game/g_18_va/step/dividend.rb | 65 ++----------------- lib/engine/game/g_18_zoo/step/dividend.rb | 14 ++-- spec/game_state_spec.rb | 4 +- spec/lib/engine/games/g_18_zoo_spec.rb | 12 ++-- spec/lib/engine/games/g_18eu_spec.rb | 2 +- 22 files changed, 97 insertions(+), 363 deletions(-) diff --git a/lib/engine/game/base.rb b/lib/engine/game/base.rb index 0ba169774b..703fad9398 100644 --- a/lib/engine/game/base.rb +++ b/lib/engine/game/base.rb @@ -810,9 +810,9 @@ def process_action(action, add_auto_actions: false, validate_auto_actions: false @last_processed_action = action.id self - rescue StandardError => e - rescue_exception(e, action) - self + #rescue StandardError => e + # rescue_exception(e, action) + # self end def process_single_action(action) @@ -838,8 +838,8 @@ def process_single_action(action) transition_to_next_round! end end - rescue Engine::GameError => e - rescue_exception(e, action) + #rescue Engine::GameError => e + # rescue_exception(e, action) end def rescue_exception(e, action) diff --git a/lib/engine/game/g_1822/step/dividend.rb b/lib/engine/game/g_1822/step/dividend.rb index ded272902b..1a1fdbe000 100644 --- a/lib/engine/game/g_1822/step/dividend.rb +++ b/lib/engine/game/g_1822/step/dividend.rb @@ -41,15 +41,13 @@ def dividend_options(entity) if extra_train train = find_extra_train(entity) extra_train_revenue = routes.find { |r| r.train == train }.revenue - extra_train_payout = send(@extra_train_choice, entity, extra_train_revenue, 0) + extra_train_payout = send(@extra_train_choice, entity, extra_train_revenue) revenue = total_revenue - extra_train_revenue else revenue = total_revenue end - subsidy = @game.routes_subsidy(routes) - total_revenue += subsidy dividend_types.to_h do |type| - payout = send(type, entity, revenue, subsidy) + payout = send(type, entity, revenue) if extra_train payout[:corporation] += extra_train_payout[:corporation] payout[:per_share] += extra_train_payout[:per_share] @@ -67,9 +65,9 @@ def find_extra_train(entity) revenue.positive? && revenue != @game.routes_revenue(routes) ? train : nil end - def half(entity, revenue, subsidy) + def half(entity, revenue) withheld = half_pay_withhold_amount(entity, revenue) - { corporation: withheld + subsidy, per_share: payout_per_share(entity, revenue - withheld) } + { corporation: withheld, per_share: payout_per_share(entity, revenue - withheld) } end def half_pay_withhold_amount(entity, revenue) @@ -83,7 +81,7 @@ def holder_for_corporation(entity) def log_run_payout(entity, kind, revenue, subsidy, _action, payout) @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays half" if kind == 'half' - withhold = payout[:corporation] - subsidy + withhold = payout[:corporation] if withhold.positive? @log << "#{entity.name} withholds #{@game.format_currency(withhold)}" elsif payout[:per_share].zero? @@ -92,8 +90,8 @@ def log_run_payout(entity, kind, revenue, subsidy, _action, payout) @log << "#{entity.name} earns subsidy of #{@game.format_currency(subsidy)}" if subsidy.positive? end - def payout(entity, revenue, subsidy) - { corporation: subsidy, per_share: payout_per_share(entity, revenue) } + def payout(entity, revenue) + { corporation: 0, per_share: payout_per_share(entity, revenue) } end def payout_shares(entity, revenue) @@ -138,8 +136,8 @@ def process_dividend(action) @round.routes = [] log_run_payout(entity, kind, revenue, subsidy, action, payout) - @game.bank.spend(payout[:corporation], entity) if payout[:corporation].positive? - payout_shares(entity, revenue + subsidy - payout[:corporation]) if payout[:per_share].positive? + payout_corporation(payout[:corporation] + subsidy, entity) + payout_shares(entity, revenue - payout[:corporation]) if payout[:per_share].positive? change_share_price(entity, payout) pass! @@ -171,8 +169,8 @@ def share_price_change(entity, revenue = 0) end end - def withhold(_entity, revenue, subsidy) - { corporation: revenue + subsidy, per_share: 0 } + def withhold(_entity, revenue) + { corporation: revenue, per_share: 0 } end end end diff --git a/lib/engine/game/g_1825/step/dividend.rb b/lib/engine/game/g_1825/step/dividend.rb index 8090dd2722..4f9f7f54b3 100644 --- a/lib/engine/game/g_1825/step/dividend.rb +++ b/lib/engine/game/g_1825/step/dividend.rb @@ -57,7 +57,7 @@ def process_dividend(action) @round.receivership_loan = 0 @round.routes = [] - log_run_payout(entity, kind, revenue, action, payout) + log_run_payout(entity, kind, revenue, 0, action, payout) payout_corporation(payout[:corporation], entity) diff --git a/lib/engine/game/g_1840/step/dividend.rb b/lib/engine/game/g_1840/step/dividend.rb index 65abc0d444..62c55f1afa 100644 --- a/lib/engine/game/g_1840/step/dividend.rb +++ b/lib/engine/game/g_1840/step/dividend.rb @@ -95,7 +95,7 @@ def payout_corporation(amount, entity) major.spend(to_pay, @game.bank) end - def log_run_payout(entity, kind, revenue, action, payout) + def log_run_payout(entity, kind, revenue, subsidy, action, payout) unless Dividend::DIVIDEND_TYPES.include?(kind) @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays #{action.kind}" end diff --git a/lib/engine/game/g_1856/step/dividend.rb b/lib/engine/game/g_1856/step/dividend.rb index 36a194add1..d30f4aadf6 100644 --- a/lib/engine/game/g_1856/step/dividend.rb +++ b/lib/engine/game/g_1856/step/dividend.rb @@ -17,8 +17,7 @@ def actions(entity) end def dividend_options(entity) - penalty = @round.interest_penalty[entity] || 0 - revenue = @game.routes_revenue(routes) - penalty + revenue = total_revenue dividend_types.to_h do |type| payout = send(type, entity, revenue) @@ -32,37 +31,8 @@ def payout_per_share(entity, revenue) (revenue / entity.total_shares.to_f) end - def process_dividend(action) - entity = action.entity - penalty = @round.interest_penalty[entity] || 0 - revenue = @game.routes_revenue(routes) - penalty - kind = action.kind.to_sym - payout = dividend_options(entity)[kind] - - rust_obsolete_trains!(entity) - - entity.operating_history[[@game.turn, @round.round_num]] = OperatingInfo.new( - routes, - action, - revenue, - @round.laid_hexes - ) - - entity.trains.each { |train| train.operated = true } - - @round.routes = [] - - log_run_payout(entity, kind, revenue, action, payout) - - @game.bank.spend(payout[:corporation], entity) if payout[:corporation].positive? - - payout_shares(entity, revenue - payout[:corporation]) if payout[:per_share].positive? - - change_share_price(entity, payout) - - pass! - end + def share_price_change(entity, _revenue) # Share price of national does not change until it owns a permanent return {} if entity == @game.national && !@game.national_ever_owned_permanent @@ -75,7 +45,11 @@ def holder_for_corporation(_entity) @game.share_pool end - def log_run_payout(entity, kind, revenue, action, payout) + def total_revenue + super - (@round.interest_penalty[@round.current_operator] || 0) + end + + def log_run_payout(entity, kind, revenue, subsidy, action, payout) if (@round.interest_penalty[entity] || 0).positive? @log << "#{entity.name} deducts #{@game.format_currency(@round.interest_penalty[entity])}"\ ' for unpaid interest' diff --git a/lib/engine/game/g_1860/step/dividend.rb b/lib/engine/game/g_1860/step/dividend.rb index 90ab75be56..b2f2385e65 100644 --- a/lib/engine/game/g_1860/step/dividend.rb +++ b/lib/engine/game/g_1860/step/dividend.rb @@ -11,52 +11,17 @@ module Step class Dividend < Engine::Step::Dividend def dividend_options(entity) revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) dividend_types.to_h do |type| - payout = send(type, entity, revenue, subsidy) + payout = send(type, entity, revenue) payout[:divs_to_corporation] = 0 [type, payout.merge(share_price_change(entity, payout[:per_share].positive? ? revenue : 0))] end end def process_dividend(action) - entity = action.entity - revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) - kind = action.kind.to_sym - payout = dividend_options(entity)[kind] - - entity.operating_history[[@game.turn, @round.round_num]] = OperatingInfo.new( - routes, - (@game.insolvent?(entity) ? @game.actions.last : action), - revenue, - @round.laid_hexes - ) - - entity.trains.each { |train| train.operated = true } unless @game.insolvent?(entity) - - @round.routes = [] - - log_run_payout(entity, kind, revenue, subsidy, action, payout) - @game.bank.spend(payout[:corporation], entity) if payout[:corporation].positive? - payout_shares(entity, revenue) if payout[:per_share].positive? - change_share_price(entity, payout) + super @game.check_bank_broken! - pass! - @game.check_bankruptcy!(entity) - end - - def log_run_payout(entity, kind, revenue, subsidy, action, payout) - unless Dividend::DIVIDEND_TYPES.include?(kind) - @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays #{action.kind}" - end - - if payout[:per_share].zero? && payout[:corporation].zero? - @log << "#{entity.name} does not run" - elsif payout[:per_share].zero? - @log << "#{entity.name} withholds #{@game.format_currency(revenue)}" - end - @log << "#{entity.name} earns subsidy of #{@game.format_currency(subsidy)}" if subsidy.positive? + @game.check_bankruptcy!(action.entity) end def share_price_change(entity, revenue) @@ -78,12 +43,12 @@ def share_price_change(entity, revenue) end end - def withhold(_entity, revenue, subsidy) - { corporation: revenue + subsidy, per_share: 0 } + def withhold(_entity, revenue) + { corporation: revenue, per_share: 0 } end - def payout(entity, revenue, subsidy) - { corporation: subsidy, per_share: payout_per_share(entity, revenue) } + def payout(entity, revenue) + { corporation: 0, per_share: payout_per_share(entity, revenue) } end def payout_shares(entity, revenue) diff --git a/lib/engine/game/g_1862/step/dividend.rb b/lib/engine/game/g_1862/step/dividend.rb index 0eb9c98afc..92eee1c33e 100644 --- a/lib/engine/game/g_1862/step/dividend.rb +++ b/lib/engine/game/g_1862/step/dividend.rb @@ -65,7 +65,7 @@ def dividend_options(entity) revenue = @game.routes_revenue(routes) subsidy = @game.routes_subsidy(routes) actual_dividend_types(entity, revenue, subsidy).to_h do |type| - payout = send(type, entity, revenue, subsidy) + payout = send(type, entity, revenue) payout[:divs_to_corporation] = corporation_dividends(entity, payout[:per_share]) net_revenue = revenue net_revenue += hudson_delta(entity, revenue) if type == :hudson @@ -95,10 +95,11 @@ def process_dividend(action) @round.routes = [] log_run_payout(entity, kind, revenue, subsidy, action, payout) - if kind == :hudson && payout[:corporation].negative? - entity.spend(-payout[:corporation], @game.bank) - elsif payout[:corporation].positive? - @game.bank.spend(payout[:corporation], entity) + corporation_total_payout = payout[:corporation] + subsidy + if kind == :hudson && corporation_total_payout.negative? + entity.spend(-corporation_total_payout, @game.bank) + elsif corporation_total_payout.positive? + @game.bank.spend(corporation_total_payout, entity) end payout_shares(entity, revenue) if payout[:per_share].positive? change_share_price(entity, payout) @@ -151,17 +152,17 @@ def share_price_change(entity, revenue) end end - def withhold(_entity, revenue, subsidy) - { corporation: revenue + subsidy, per_share: 0 } + def withhold(_entity, revenue) + { corporation: revenue, per_share: 0 } end - def payout(entity, revenue, subsidy) - { corporation: subsidy, per_share: payout_per_share(entity, revenue) } + def payout(entity, revenue) + { corporation: 0, per_share: payout_per_share(entity, revenue) } end - def hudson(entity, revenue, subsidy) + def hudson(entity, revenue) diff = hudson_delta(entity, revenue) - { corporation: subsidy - diff, per_share: payout_per_share(entity, revenue + diff) } + { corporation: -diff, per_share: payout_per_share(entity, revenue + diff) } end def handle_warranties!(entity) diff --git a/lib/engine/game/g_1866/step/dividend.rb b/lib/engine/game/g_1866/step/dividend.rb index 9d8d1ae76b..797d18013e 100644 --- a/lib/engine/game/g_1866/step/dividend.rb +++ b/lib/engine/game/g_1866/step/dividend.rb @@ -17,18 +17,16 @@ def actions(entity) def dividend_options(entity) revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) - total_revenue = revenue + subsidy dividend_types.to_h do |type| - payout = send(type, entity, revenue, subsidy) + payout = send(type, entity, revenue) payout[:divs_to_corporation] = corporation_dividends(entity, payout[:per_share]) [type, payout.merge(share_price_change(entity, total_revenue - payout[:corporation]))] end end - def half(entity, revenue, subsidy) + def half(entity, revenue) withheld = half_pay_withhold_amount(entity, revenue) - { corporation: withheld + subsidy, per_share: payout_per_share(entity, revenue - withheld) } + { corporation: withheld, per_share: payout_per_share(entity, revenue - withheld) } end def half_pay_withhold_amount(entity, revenue) @@ -44,47 +42,8 @@ def log_payout_shares(entity, revenue, per_share, receivers) "#{@game.format_currency(per_share)} per share (#{receivers})" end - def log_run_payout(entity, kind, revenue, subsidy, _action, payout) - @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays half" if kind == 'half' - - withhold = payout[:corporation] - subsidy - if withhold.positive? && !@game.national_corporation?(entity) - @log << "#{entity.name} withholds #{@game.format_currency(withhold)}" - elsif payout[:per_share].zero? - @log << "#{entity.name} does not run" - end - @log << "#{entity.name} earns subsidy of #{@game.format_currency(subsidy)}" if subsidy.positive? - end - - def process_dividend(action) - entity = action.entity - revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) - kind = action.kind.to_sym - payout = dividend_options(entity)[kind] - - rust_obsolete_trains!(entity) - - entity.operating_history[[@game.turn, @round.round_num]] = OperatingInfo.new( - routes, - action, - revenue, - @round.laid_hexes - ) - - entity.trains.each { |train| train.operated = true } - - @round.routes = [] - log_run_payout(entity, kind, revenue, subsidy, action, payout) - payout_corporation(payout[:corporation], entity) unless @game.national_corporation?(entity) - payout_shares(entity, revenue + subsidy - payout[:corporation]) if payout[:per_share].positive? - change_share_price(entity, payout) - - pass! - end - - def payout(entity, revenue, subsidy) - { corporation: subsidy, per_share: payout_per_share(entity, revenue) } + def payout(entity, revenue) + { corporation: 0, per_share: payout_per_share(entity, revenue) } end def payout_shares(entity, revenue) @@ -128,8 +87,8 @@ def share_price_change(entity, revenue = 0) end end - def withhold(_entity, revenue, subsidy) - { corporation: revenue + subsidy, per_share: 0 } + def withhold(_entity, revenue) + { corporation: revenue, per_share: 0 } end end end diff --git a/lib/engine/game/g_1868_wy/step/dividend.rb b/lib/engine/game/g_1868_wy/step/dividend.rb index 741e5170dd..f7c7341b51 100644 --- a/lib/engine/game/g_1868_wy/step/dividend.rb +++ b/lib/engine/game/g_1868_wy/step/dividend.rb @@ -21,7 +21,7 @@ def share_price_change(entity, revenue = 0) end end - def log_run_payout(entity, kind, revenue, action, payout) + def log_run_payout(entity, kind, revenue, subsidy, action, payout) super unless entity.minor? end diff --git a/lib/engine/game/g_1870/step/connection_dividend.rb b/lib/engine/game/g_1870/step/connection_dividend.rb index 5e2987f0fc..067833895a 100644 --- a/lib/engine/game/g_1870/step/connection_dividend.rb +++ b/lib/engine/game/g_1870/step/connection_dividend.rb @@ -38,7 +38,7 @@ def process_dividend(action) @round.routes = [] - log_run_payout(entity, kind, revenue, action, payout) + log_run_payout(entity, kind, revenue, 0, action, payout) @game.bank.spend(payout[:corporation], entity) if payout[:corporation].positive? diff --git a/lib/engine/game/g_1873/step/dividend.rb b/lib/engine/game/g_1873/step/dividend.rb index 913247bd03..dec94d3a06 100644 --- a/lib/engine/game/g_1873/step/dividend.rb +++ b/lib/engine/game/g_1873/step/dividend.rb @@ -96,7 +96,7 @@ def process_dividend(action) entity.trains.each { |train| train.operated = true } @round.routes = [] - log_run_payout(entity, kind, revenue, action, payout) + log_run_payout(entity, kind, revenue, 0, action, payout) @game.bank.spend(payout[:corporation], entity) if payout[:corporation].positive? @@ -110,7 +110,7 @@ def process_dividend(action) pass! end - def log_run_payout(entity, kind, revenue, action, payout) + def log_run_payout(entity, kind, revenue, subsidy, action, payout) unless Dividend::DIVIDEND_TYPES.include?(kind) @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays #{action.kind}" end diff --git a/lib/engine/game/g_1888/step/dividend.rb b/lib/engine/game/g_1888/step/dividend.rb index 248f2e0e29..4a81f75056 100644 --- a/lib/engine/game/g_1888/step/dividend.rb +++ b/lib/engine/game/g_1888/step/dividend.rb @@ -9,65 +9,19 @@ module Step class Dividend < Engine::Step::Dividend def dividend_options(entity) revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) dividend_types.to_h do |type| - payout = send(type, entity, revenue, subsidy) + payout = send(type, entity, revenue) payout[:divs_to_corporation] = corporation_dividends(entity, payout[:per_share]) - [type, payout.merge(share_price_change(entity, revenue - payout[:corporation] + subsidy))] + [type, payout.merge(share_price_change(entity, revenue - payout[:corporation]))] end end - def process_dividend(action) - entity = action.entity - revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) - kind = action.kind.to_sym - payout = dividend_options(entity)[kind] - - rust_obsolete_trains!(entity) - - entity.operating_history[[@game.turn, @round.round_num]] = OperatingInfo.new( - routes, - action, - revenue, - @round.laid_hexes - ) - - entity.trains.each { |train| train.operated = true } - - @round.routes = [] - - log_run_payout(entity, kind, revenue, subsidy, action, payout) - - payout_corporation(payout[:corporation], entity) - - payout_shares(entity, revenue - payout[:corporation] + subsidy) if payout[:per_share].positive? - - change_share_price(entity, payout) - - pass! - end - - def log_run_payout(entity, kind, revenue, subsidy, action, payout) - unless Dividend::DIVIDEND_TYPES.include?(kind) - @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays #{action.kind}" - end - - if (payout[:corporation] - subsidy).positive? - @log << "#{entity.name} withholds #{@game.format_currency(payout[:corporation])}" - elsif subsidy.positive? - @log << "#{entity.name} retains a subsidy of #{@game.format_currency(subsidy)}" - elsif payout[:per_share].zero? - @log << "#{entity.name} does not run" - end - end - - def withhold(_entity, revenue, subsidy) - { corporation: revenue + subsidy, per_share: 0 } + def withhold(_entity, revenue) + { corporation: revenue, per_share: 0 } end - def payout(entity, revenue, subsidy) - { corporation: subsidy, per_share: payout_per_share(entity, revenue) } + def payout(entity, revenue) + { corporation: 0, per_share: payout_per_share(entity, revenue) } end end end diff --git a/lib/engine/game/g_18_esp/step/dividend.rb b/lib/engine/game/g_18_esp/step/dividend.rb index 1b401257dc..4fb32ac731 100644 --- a/lib/engine/game/g_18_esp/step/dividend.rb +++ b/lib/engine/game/g_18_esp/step/dividend.rb @@ -19,10 +19,8 @@ def actions(entity) def dividend_options(entity) revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) - total_revenue = revenue + subsidy dividend_types.to_h do |type| - payout = send(type, entity, revenue, subsidy) + payout = send(type, entity, revenue) payout[:divs_to_corporation] = corporation_dividends(entity, payout[:per_share]) [type, payout.merge(share_price_change(entity, total_revenue - payout[:corporation]))] end @@ -44,66 +42,21 @@ def movement_str(times, dir) "#{times} #{dir}" end - def process_dividend(action) - entity = action.entity - revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) - kind = action.kind.to_sym - payout = dividend_options(entity)[kind] - - rust_obsolete_trains!(entity) - - entity.operating_history[[@game.turn, @round.round_num]] = OperatingInfo.new( - routes, - action, - revenue, - @round.laid_hexes - ) - - entity.trains.each { |train| train.operated = true } - - @round.routes = [] - - log_run_payout(entity, kind, revenue, subsidy, action, payout) - - payout_corporation(payout[:corporation], entity) - - payout_shares(entity, revenue - payout[:corporation] + subsidy) if payout[:per_share].positive? - - change_share_price(entity, payout) - - pass! - end - - def log_run_payout(entity, kind, revenue, subsidy, action, payout) - unless Dividend::DIVIDEND_TYPES.include?(kind) - @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays #{action.kind}" - end - - if (payout[:corporation] - subsidy).positive? - @log << "#{entity.name} withholds #{@game.format_currency(payout[:corporation])}" - elsif subsidy.positive? - @log << "#{entity.name} retains a subsidy of #{@game.format_currency(subsidy)}" - elsif payout[:per_share].zero? - @log << "#{entity.name} does not run" - end - end - def payout_per_share(entity, revenue) revenue * 1.0 / entity.total_shares end - def payout(entity, revenue, subsidy) + def payout(entity, revenue) if entity.corporation? && entity.type != :minor - { corporation: subsidy, per_share: payout_per_share(entity, revenue) } + { corporation: 0, per_share: payout_per_share(entity, revenue) } else amount = revenue / 2 - { corporation: amount + subsidy, per_share: amount } + { corporation: amount, per_share: amount } end end - def withhold(_entity, revenue, subsidy) - { corporation: revenue + subsidy, per_share: 0 } + def withhold(_entity, revenue) + { corporation: revenue, per_share: 0 } end end end diff --git a/lib/engine/game/g_18_mag/game.rb b/lib/engine/game/g_18_mag/game.rb index ee9a66e512..1de40ce286 100644 --- a/lib/engine/game/g_18_mag/game.rb +++ b/lib/engine/game/g_18_mag/game.rb @@ -727,7 +727,7 @@ def ciwl_delta end def routes_subsidy(routes) - routes.sum(&:subsidy) + routes.first&.train&.owner&.minor? ? routes.sum(&:subsidy) : 0 end # see if minor bought unused rail-cars diff --git a/lib/engine/game/g_18_mag/step/dividend.rb b/lib/engine/game/g_18_mag/step/dividend.rb index 1636a28c43..480901d83d 100644 --- a/lib/engine/game/g_18_mag/step/dividend.rb +++ b/lib/engine/game/g_18_mag/step/dividend.rb @@ -47,14 +47,7 @@ def skip! end def process_dividend(action) - if action.entity.minor? - subsidy = @game.routes_subsidy(routes) - if subsidy.positive? - @game.bank.spend(subsidy, action.entity) - @log << "#{action.entity.name} retains a subsidy of #{@game.format_currency(subsidy)}" - end - return super - end + return super if action.entity.minor? entity = action.entity action_kind = action.kind diff --git a/lib/engine/game/g_18_tn/step/dividend.rb b/lib/engine/game/g_18_tn/step/dividend.rb index 14b67936f5..933ac05747 100644 --- a/lib/engine/game/g_18_tn/step/dividend.rb +++ b/lib/engine/game/g_18_tn/step/dividend.rb @@ -28,7 +28,7 @@ def dividend_options(entity) { withhold: { corporation: 0, per_share: 0, divs_to_corporation: 0 } } end - def log_run_payout(entity, kind, revenue, action, payout) + def log_run_payout(entity, kind, revenue, subsidy, action, payout) return super unless civil_war_effect_with_single_train?(entity) @log << "#{entity.name}'s run is ignored due to Civil War" diff --git a/lib/engine/game/g_18_uruguay/step/dividend.rb b/lib/engine/game/g_18_uruguay/step/dividend.rb index b007fbfc58..a8b7e3192d 100644 --- a/lib/engine/game/g_18_uruguay/step/dividend.rb +++ b/lib/engine/game/g_18_uruguay/step/dividend.rb @@ -50,10 +50,8 @@ def dividend_options(entity) total_revenue = routes_revenue(routes, entity) revenue = total_revenue - subsidy = routes_subsidy(routes, entity) - total_revenue += subsidy dividend_types.to_h do |type| - payout = send(type, entity, revenue, subsidy) + payout = send(type, entity, revenue) payout[:divs_to_corporation] = corporation_dividends(entity, payout[:per_share]) [type, payout.merge(share_price_change(entity, total_revenue - payout[:corporation]))] end @@ -63,19 +61,19 @@ def holder_for_corporation(_entity) @game.share_pool end - def payout(entity, revenue, subsidy) + def payout(entity, revenue) if @game.nationalized? && entity.loans.size.positive? return { - corporation: subsidy + (payout_per_share(entity, revenue) * 10), + corporation: payout_per_share(entity, revenue) * 10, per_share: 0, } end - { corporation: subsidy, per_share: payout_per_share(entity, revenue) } + { corporation: 0, per_share: payout_per_share(entity, revenue) } end - def withhold(_entity, revenue, subsidy) - { corporation: revenue + subsidy, per_share: 0 } + def withhold(_entity, revenue) + { corporation: revenue, per_share: 0 } end def process_dividend_rptla(action) @@ -95,8 +93,8 @@ def process_dividend_rptla(action) @round.routes = [] log_run_payout_sub(entity, kind, revenue, subsidy, action, payout) - @game.bank.spend(payout[:corporation], entity) if payout[:corporation].positive? - payout_shares(entity, revenue + subsidy - payout[:corporation]) if payout[:per_share].positive? + @game.bank.spend(payout[:corporation] + subsidy, entity) if payout[:corporation].positive? + payout_shares(entity, revenue - payout[:corporation]) if payout[:per_share].positive? change_share_price(entity, payout) pass! @@ -112,7 +110,7 @@ def process_dividend(action) @game.payoff_loan(current_entity, loans_to_pay_off, current_entity) end - def log_run_payout(entity, kind, revenue, action, payout) + def log_run_payout(entity, kind, revenue, subisdy, action, payout) super unless entity.minor? end diff --git a/lib/engine/game/g_18_va/step/dividend.rb b/lib/engine/game/g_18_va/step/dividend.rb index c91781247c..f344d24625 100644 --- a/lib/engine/game/g_18_va/step/dividend.rb +++ b/lib/engine/game/g_18_va/step/dividend.rb @@ -8,69 +8,12 @@ module Game module G18VA module Step class Dividend < Engine::Step::Dividend - def withhold(_entity, revenue, subsidy) - { corporation: revenue + subsidy, per_share: 0 } + def withhold(_entity, revenue) + { corporation: revenue, per_share: 0 } end - def payout(entity, revenue, subsidy) - { corporation: subsidy, per_share: payout_per_share(entity, revenue) } - end - - def dividend_options(entity) - revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) - dividend_types.to_h do |type| - payout = send(type, entity, revenue, subsidy) - payout[:divs_to_corporation] = corporation_dividends(entity, payout[:per_share]) - [type, payout.merge(share_price_change(entity, revenue + subsidy - payout[:corporation]))] - end - end - - def process_dividend(action) - entity = action.entity - revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) - kind = action.kind.to_sym - payout = dividend_options(entity)[kind] - - rust_obsolete_trains!(entity) - - entity.operating_history[[@game.turn, @round.round_num]] = OperatingInfo.new( - routes, - action, - revenue, - @round.laid_hexes - ) - - entity.trains.each { |train| train.operated = true } - - @round.routes = [] - - log_run_payout(entity, kind, revenue, subsidy, action, payout) - - payout_corporation(payout[:corporation], entity) - - adjusted_revenue = subsidy ? revenue + subsidy : revenue - payout_shares(entity, adjusted_revenue - payout[:corporation]) if payout[:per_share].positive? - - change_share_price(entity, payout) - - pass! - end - - def log_run_payout(entity, kind, revenue, subsidy, action, payout) - unless Dividend::DIVIDEND_TYPES.include?(kind) - @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays #{action.kind}" - end - - withheld_amount = payout[:corporation] - subsidy - if withheld_amount.positive? - @log << "#{entity.name} withholds #{@game.format_currency(withheld_amount)}" - elsif payout[:per_share].zero? - @log << "#{entity.name} does not run" - end - - @log << "#{entity.name} earns subsidy of #{@game.format_currency(subsidy)}" if subsidy.positive? + def payout(entity, revenue) + { corporation: 0, per_share: payout_per_share(entity, revenue) } end def share_price_change(entity, revenue = 0) diff --git a/lib/engine/game/g_18_zoo/step/dividend.rb b/lib/engine/game/g_18_zoo/step/dividend.rb index f5bd9fdf00..5a1e1d1c1b 100644 --- a/lib/engine/game/g_18_zoo/step/dividend.rb +++ b/lib/engine/game/g_18_zoo/step/dividend.rb @@ -11,10 +11,9 @@ class Dividend < Engine::Step::Dividend def dividend_options(entity) revenue = @game.routes_revenue(routes) - subsidy = @game.routes_subsidy(routes) dividend_types.to_h do |type| - [type, send(type, entity, revenue, subsidy)] + [type, send(type, entity, revenue)] end end @@ -22,9 +21,9 @@ def share_price_change(entity, revenue) :right if revenue >= @game.threshold(entity) end - def withhold(_entity, revenue, subsidy) + def withhold(_entity, revenue) { - corporation: (revenue / 25.0).ceil + subsidy, + corporation: (revenue / 25.0).ceil, per_share: 0, share_direction: :left, share_times: 1, @@ -32,9 +31,9 @@ def withhold(_entity, revenue, subsidy) } end - def payout(entity, revenue, subsidy) + def payout(entity, revenue) { - corporation: subsidy, + corporation: 0, per_share: payout_per_share(entity, revenue), share_direction: revenue >= @game.threshold(entity) ? :right : nil, share_times: 1, @@ -63,11 +62,8 @@ def payout_shares(entity, revenue) def process_dividend(action) @subsidy = @game.routes_subsidy(routes) - super - @subsidy = 0 - action.entity.remove_assignment!('BARREL') if @game.two_barrels_used_this_or?(action.entity) end diff --git a/spec/game_state_spec.rb b/spec/game_state_spec.rb index e05e98a7d2..46e67af01d 100644 --- a/spec/game_state_spec.rb +++ b/spec/game_state_spec.rb @@ -355,7 +355,7 @@ module Engine }, } expect(game.exception).to be_nil - expect(game.process_action(action).exception).to be_a(GameError) + expect { game.process_action(action) }.to raise_error(GameError) end end @@ -391,7 +391,7 @@ module Engine 'tokener' => 'BB', } expect(game.exception).to be_nil - expect(game.process_action(action).exception).to be_a(GameError) + expect { game.process_action(action) }.to raise_error(GameError) end end end diff --git a/spec/lib/engine/games/g_18_zoo_spec.rb b/spec/lib/engine/games/g_18_zoo_spec.rb index 162073ffea..dd404fbcb0 100644 --- a/spec/lib/engine/games/g_18_zoo_spec.rb +++ b/spec/lib/engine/games/g_18_zoo_spec.rb @@ -457,7 +457,7 @@ def first_player_buy_power_on_isr(company) 'slot' => 1, } expect(game.exception).to be_nil - expect(game.process_action(action).exception).to be_a(GameError) + expect { game.process_action(action) }.to raise_error(GameError) end end @@ -474,7 +474,7 @@ def first_player_buy_power_on_isr(company) 'slot' => 0, } expect(game.exception).to be_nil - expect(game.process_action(action).exception).to be_a(GameError) + expect { game.process_action(action) }.to raise_error(GameError) end end @@ -491,7 +491,7 @@ def first_player_buy_power_on_isr(company) 'slot' => 0, } expect(game.exception).to be_nil - expect(game.process_action(action).exception).to be_a(GameError) + expect { game.process_action(action) }.to raise_error(GameError) end end @@ -508,7 +508,7 @@ def first_player_buy_power_on_isr(company) 'slot' => 0, } expect(game.exception).to be_nil - expect(game.process_action(action).exception).to be_a(GameError) + expect { game.process_action(action) }.to raise_error(GameError) end end @@ -525,7 +525,7 @@ def first_player_buy_power_on_isr(company) 'target_type' => 'hex', } expect(game.exception).to be_nil - expect(game.process_action(action).exception).to be_a(GameError) + expect { game.process_action(action) }.to raise_error(GameError) end end @@ -543,7 +543,7 @@ def first_player_buy_power_on_isr(company) 'tokener' => 'PB', } expect(game.exception).to be_nil - expect(game.process_action(action).exception).to be_a(GameError) + expect { game.process_action(action) }.to raise_error(GameError) end end end diff --git a/spec/lib/engine/games/g_18eu_spec.rb b/spec/lib/engine/games/g_18eu_spec.rb index 43d0a5595a..e75886d3c3 100644 --- a/spec/lib/engine/games/g_18eu_spec.rb +++ b/spec/lib/engine/games/g_18eu_spec.rb @@ -75,7 +75,7 @@ module Engine expect(game.current_entity.shares.length).to be 6 # Prevent player from attempting to purchase > 60% through normal stock buy. - expect(game.process_action(action).exception).to be_a(GameError) + expect { game.process_action(action) }.to raise_error(GameError) expect(game.current_entity.shares.length).to be 6 end end From 14a7feed4c2b68de41a9d599973db7374a48d3fc Mon Sep 17 00:00:00 2001 From: Chris Rericha Date: Mon, 16 Sep 2024 02:21:22 -0400 Subject: [PATCH 47/49] rubocop --- lib/engine/game/base.rb | 10 +++++----- lib/engine/game/g_1840/step/dividend.rb | 2 +- lib/engine/game/g_1856/step/dividend.rb | 4 +--- lib/engine/game/g_1873/step/dividend.rb | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/engine/game/base.rb b/lib/engine/game/base.rb index 703fad9398..ccf8bf9ac5 100644 --- a/lib/engine/game/base.rb +++ b/lib/engine/game/base.rb @@ -810,9 +810,9 @@ def process_action(action, add_auto_actions: false, validate_auto_actions: false @last_processed_action = action.id self - #rescue StandardError => e - # rescue_exception(e, action) - # self + # rescue StandardError => e + # rescue_exception(e, action) + # self end def process_single_action(action) @@ -838,8 +838,8 @@ def process_single_action(action) transition_to_next_round! end end - #rescue Engine::GameError => e - # rescue_exception(e, action) + # rescue Engine::GameError => e + # rescue_exception(e, action) end def rescue_exception(e, action) diff --git a/lib/engine/game/g_1840/step/dividend.rb b/lib/engine/game/g_1840/step/dividend.rb index 62c55f1afa..e17e6e2c33 100644 --- a/lib/engine/game/g_1840/step/dividend.rb +++ b/lib/engine/game/g_1840/step/dividend.rb @@ -95,7 +95,7 @@ def payout_corporation(amount, entity) major.spend(to_pay, @game.bank) end - def log_run_payout(entity, kind, revenue, subsidy, action, payout) + def log_run_payout(entity, kind, revenue, _subsidy, action, payout) unless Dividend::DIVIDEND_TYPES.include?(kind) @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays #{action.kind}" end diff --git a/lib/engine/game/g_1856/step/dividend.rb b/lib/engine/game/g_1856/step/dividend.rb index d30f4aadf6..ce1999b37d 100644 --- a/lib/engine/game/g_1856/step/dividend.rb +++ b/lib/engine/game/g_1856/step/dividend.rb @@ -31,8 +31,6 @@ def payout_per_share(entity, revenue) (revenue / entity.total_shares.to_f) end - - def share_price_change(entity, _revenue) # Share price of national does not change until it owns a permanent return {} if entity == @game.national && !@game.national_ever_owned_permanent @@ -49,7 +47,7 @@ def total_revenue super - (@round.interest_penalty[@round.current_operator] || 0) end - def log_run_payout(entity, kind, revenue, subsidy, action, payout) + def log_run_payout(entity, kind, revenue, _subsidy, action, payout) if (@round.interest_penalty[entity] || 0).positive? @log << "#{entity.name} deducts #{@game.format_currency(@round.interest_penalty[entity])}"\ ' for unpaid interest' diff --git a/lib/engine/game/g_1873/step/dividend.rb b/lib/engine/game/g_1873/step/dividend.rb index dec94d3a06..b8b475d517 100644 --- a/lib/engine/game/g_1873/step/dividend.rb +++ b/lib/engine/game/g_1873/step/dividend.rb @@ -110,7 +110,7 @@ def process_dividend(action) pass! end - def log_run_payout(entity, kind, revenue, subsidy, action, payout) + def log_run_payout(entity, kind, revenue, _subsidy, action, payout) unless Dividend::DIVIDEND_TYPES.include?(kind) @log << "#{entity.name} runs for #{@game.format_currency(revenue)} and pays #{action.kind}" end From 139b6080e4d7f5582f9ec1ff0625236b66191973 Mon Sep 17 00:00:00 2001 From: Chris Rericha Date: Wed, 9 Oct 2024 00:53:58 -0400 Subject: [PATCH 48/49] [1856] Fix dividends step being skipped when route revenue equals interest payment --- lib/engine/game/g_1856/step/dividend.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/engine/game/g_1856/step/dividend.rb b/lib/engine/game/g_1856/step/dividend.rb index ce1999b37d..b3a7255482 100644 --- a/lib/engine/game/g_1856/step/dividend.rb +++ b/lib/engine/game/g_1856/step/dividend.rb @@ -12,8 +12,9 @@ class Dividend < Engine::Step::Dividend def actions(entity) # National must withhold if it never owned a permanent return [] if entity.corporation? && entity == @game.national && !@game.national_ever_owned_permanent + return [] if entity.company? || routes.empty? - super + Engine::Step::Dividend::ACTIONS end def dividend_options(entity) From e9a250042b71485473341af8db0958f8ed842a77 Mon Sep 17 00:00:00 2001 From: Steve Undy Date: Wed, 9 Oct 2024 21:49:30 -0600 Subject: [PATCH 49/49] S18: add super to game overriding methods --- lib/engine/game/g_system18/game.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/engine/game/g_system18/game.rb b/lib/engine/game/g_system18/game.rb index 4f817d9e44..38088d6bc9 100644 --- a/lib/engine/game/g_system18/game.rb +++ b/lib/engine/game/g_system18/game.rb @@ -624,13 +624,13 @@ def revenue_str(route) end def extra_revenue(entity, routes) - return unless respond_to?("map_#{map_name}_extra_revenue") + return super unless respond_to?("map_#{map_name}_extra_revenue") send("map_#{map_name}_extra_revenue", entity, routes) end def submit_revenue_str(routes, show_subsidy) - return unless respond_to?("map_#{map_name}_submit_revenue_str") + return super unless respond_to?("map_#{map_name}_submit_revenue_str") send("map_#{map_name}_submit_revenue_str", routes, show_subsidy) end @@ -670,7 +670,7 @@ def removable_icon_action_str end def status_str(corporation) - return unless respond_to?("map_#{map_name}_status_str") + return super unless respond_to?("map_#{map_name}_status_str") send("map_#{map_name}_status_str", corporation) end @@ -688,7 +688,7 @@ def pre_lay_tile_action(action, entity, tile_lay) end def place_home_token(corporation) - return unless respond_to?("map_#{map_name}_place_home_token") + return super unless respond_to?("map_#{map_name}_place_home_token") send("map_#{map_name}_place_home_token", corporation) end