Skip to content

Commit

Permalink
[18Uruguay] Adding basic support for loans
Browse files Browse the repository at this point in the history
All corperations will take a loan from RTPLA when floating
It is then optional for the corperation to take one addtional loan per OR
The interest is payed end of OR
It is not possible to repay until Nationalization started

This commit will only add the basic support additional possibilities to take loan and the Nationalization will be handled later
  • Loading branch information
patrikolesen committed Feb 2, 2024
1 parent 8429790 commit b3a5d61
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 1 deletion.
115 changes: 114 additions & 1 deletion lib/engine/game/g_18_uruguay/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
require_relative 'companies'
require_relative 'trains'
require_relative 'phases'
require_relative 'loans'
require_relative '../../loan'

module Engine
module Game
Expand All @@ -21,6 +23,8 @@ class Game < Game::Base
include Companies
include Trains
include Phases
include InterestOnLoans
include Loans

EBUY_SELL_MORE_THAN_NEEDED = true
GOODS_TRAIN = 'Goods'
Expand Down Expand Up @@ -53,6 +57,7 @@ class Game < Game::Base
RPTLA_STARTING_PRICE = 50
RPTLA_STOCK_ROW = 11
NUMBER_OF_LOANS = 99
LOAN_VALUE = 100

GAME_END_CHECK = { custom: :one_more_full_or_set }.freeze

Expand Down Expand Up @@ -259,13 +264,15 @@ def operating_round(round_num)
G18Uruguay::Step::CattleFarm,
Engine::Step::SpecialTrack,
Engine::Step::SpecialToken,
G18Uruguay::Step::TakeLoanBuyCompany,
Engine::Step::HomeToken,
Engine::Step::Track,
Engine::Step::Token,
G18Uruguay::Step::Token,
Engine::Step::Route,
Engine::Step::Dividend,
Engine::Step::DiscardTrain,
Engine::Step::BuyTrain,
[G18Uruguay::Step::TakeLoanBuyCompany, { blocks: true }],
], round_num: round_num)
end

Expand All @@ -290,6 +297,112 @@ def abilities_ignore_owner(entity, type = nil, time: nil, on_phase: nil, passive

active_abilities
end

# Nationalized
def nationalized?
@nationalized
end

def operating_order
super.sort.partition { |c| c.type != :bank }.flatten
end

# Loans
def float_corporation(corporation)
return if corporation == @rptla
return unless @loans

amount = corporation.par_price.price * 5
@bank.spend(amount, corporation)
@log << "#{corporation.name} receives #{format_currency(corporation.cash)}"
take_loan(corporation, @loans[0]) if @loans.size.positive? && !nationalized?
end

def perform_ebuy_loans(entity, remaining)
ebuy = true
while remaining.positive? && entity.share_price.price != 0
# if at max loans, company goes directly into receiverhsip
if @loans.empty?
@log << "There are no more loans available to force buy a train, #{entity.name} goes into receivership"
break
end
loan = @loans.first
take_loan(entity, loan, ebuy: ebuy)
remaining -= loan.amount
end
end

# Goods
def number_of_goods_at_harbor
ability = @rptla.abilities.find { |a| a.type == 'Goods' }
ability.description[/\d+/].to_i
end

def add_good_to_rptla
ability = @rptla.abilities.find { |a| a.type == 'Goods' }
count = number_of_goods_at_harbor + 1
ability.description = GOODS_DESCRIPTION_STR + count.to_s
end

def remove_goods_from_rptla(goods_count)
return if number_of_goods_at_harbor < goods_count

ability = @rptla.abilities.find { |a| a.type == 'Goods' }
count = number_of_goods_at_harbor - goods_count
ability.description = GOODS_DESCRIPTION_STR + count.to_s
end

def check_distance(route, visits, train = nil)
@round.current_routes[route.train] = route
if route.corporation != @rptla && !nationalized?
raise RouteTooLong, 'Need to have goods to run to port' unless check_for_port_if_goods_attached(route,
visits)
raise RouteTooLong, 'Goods needs to be shipped to port' unless check_for_goods_if_run_to_port(route,
visits)
end
raise RouteTooLong, '4D trains cannot deliver goods' if route.train.name == '4D' && visits_include_port?(visits)

super
end

# Revenue
def revenue_str(route)
return super unless route&.corporation == @rptla

'Ship'
end

def routes_revenue(route, corporation)
revenue = super
return revenue if @rptla != corporation

revenue += (corporation.loans.size.to_f / 2).floor * 10
revenue
end

def routes_subsidy(route, corporation)
return super if @rptla != corporation

(corporation.loans.size.to_f / 2).ceil * 10
end

def revenue_for(route, stops)
revenue = super
revenue *= 2 if route.train.name == '4D'
revenue *= 2 if final_operating_round?
return revenue unless route&.corporation == @rptla

train = route.train
revenue * goods_on_train(train)
end

def or_round_finished
corps_pay_interest unless nationalized?
end

def final_operating_round?
@final_turn == @turn
end
end
end
end
Expand Down
91 changes: 91 additions & 0 deletions lib/engine/game/g_18_uruguay/loans.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# frozen_string_literal: true

module Engine
module Game
module G18Uruguay
module Loans
def init_loans
Array.new(self.class::NUMBER_OF_LOANS) { |id| Loan.new(id, self.class::LOAN_VALUE) }
end

def maximum_loans(entity)
entity == @rptla ? self.class::NUMBER_OF_LOANS : entity.num_player_shares
end

def loans_due_interest(entity)
entity.loans.size
end

def interest_owed(entity)
return 0 if entity == @rptla

10 * loans_due_interest(entity)
end

def interest_owed_for_loans(count)
10 * count
end

def can_take_loan?(entity, ebuy: nil)
# return false if nationalized?
return false if entity == @rlpta
return true if ebuy

entity.corporation? &&
entity.loans.size < maximum_loans(entity) &&
!@loans.empty?
end

def take_loan(entity, loan, ebuy: nil)
raise GameError, "Cannot take more than #{maximum_loans(entity)} loans" unless can_take_loan?(entity, ebuy: ebuy)

# raise GameError, "Not allowed to take loans after nationalization" if @game.nationalized?

@bank.spend(loan.amount, entity)
entity.loans << loan
@rptla.loans << loan.dup
@loans.delete(loan)
@log << "#{entity.name} takes a loan and receives #{format_currency(loan.amount)}"
end

def payoff_loan(entity, number_of_loans, spender)
total_amount = 0
number_of_loans.times do |_i|
paid_loan = entity.loans.pop
amount = paid_loan.amount
total_amount += amount
spender.spend(amount, @bank)
end
@log << "#{spender.name} payoff #{number_of_loans} loan(s) for #{entity.name} and pays #{total_amount}"
end

def adjust_stock_market_loan_penalty(entity)
delta = entity.loans.size - maximum_loans(entity)
return unless delta.positive?

delta.times do |_i|
@stock_market.move_left(entity)
end
end

def take_loan_if_needed_for_interest!(entity)
owed = interest_owed(entity)
return if owed.zero?

remaining = owed - entity.cash
perform_ebuy_loans(entity, remaining + 10) if remaining.positive?
end

def corps_pay_interest
corps = @round.entities.select { |entity| entity.loans.size.positive? && entity != @rptla }
corps.each do |corp|
next if corp.closed?

take_loan_if_needed_for_interest!(corp)
pay_interest!(corp)
end
end
end
end
end
end
61 changes: 61 additions & 0 deletions lib/engine/game/g_18_uruguay/step/take_loan_buy_company.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true

require_relative '../../../step/base'

module Engine
module Game
module G18Uruguay
module Step
class TakeLoanBuyCompany < Engine::Step::BuyCompany
def actions(entity)
return [] if !entity.corporation? || entity != current_entity
return [] if entity == @game.rptla
return [] unless can_buy_company?(entity)

actions = []
actions << 'take_loan' if @game.can_take_loan?(entity) && !@round.loan_taken && !@game.nationalized?
actions << 'buy_company' if can_buy_company?(entity)
actions << 'pass' if can_buy_company?(entity) || (@game.can_take_loan?(entity) && !@round.loan_taken)

actions
end

def can_buy_company?(entity)
companies = @game.purchasable_companies(entity)

entity == current_entity &&
@game.phase.status.include?('can_buy_companies') &&
companies.any? &&
companies.map(&:min_price).min <= buying_power(entity)
end

def description
'Take Loans or Buy Company'
end

def blocks?
true
end

def process_take_loan(action)
entity = action.entity
@game.take_loan(entity, action.loan)
@round.loan_taken = true
end

def round_state
super.merge({
# has player taken a loan this or already
loan_taken: false,
})
end

def setup
# you can only take one loan per OR turn
@round.loan_taken = false
end
end
end
end
end
end
31 changes: 31 additions & 0 deletions lib/engine/game/g_18_uruguay/step/token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require_relative '../../../step/token'

module Engine
module Game
module G18Uruguay
module Step
class Token < Engine::Step::Token
def actions(entity)
return [] if entity == @game.rptla
return [] if @game.final_operating_round?

@round.loan_taken |= false
actions = super.map(&:clone)
if !actions.empty? && @game.can_take_loan?(entity) && !@round.loan_taken && !@game.nationalized?
actions << 'take_loan'
end
actions
end

def process_take_loan(action)
entity = action.entity
@game.take_loan(entity, action.loan) unless @round.loan_taken
@round.loan_taken = true
end
end
end
end
end
end

0 comments on commit b3a5d61

Please sign in to comment.